From nobody Thu Oct 2 00:58:11 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 07319241664 for ; Tue, 30 Sep 2025 03:55:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204528; cv=none; b=VcjXyuKltel96JvUuiDEG/S+v3/EnGAvAucV7py1Vm8TmBtSum+/r0eZOlorrVi3Nf1QgGptj3R+WY2C9FGY3atQlPEtZtsR1AofVKLGX4gqIuedqa8nl/YYB9782W7TVHa5LGoycLpl4W9LSjZ+UmZyiPH++YfDtp+iz0dsYKs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204528; c=relaxed/simple; bh=/bZiWZ58aCldGJC8t/nRfCKBq5N7NH0IgEUv4RdZGEI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=HnhKnzBlKVx9rMiCMUapxMI51tuqMuyVSWDZoXEwG3E0H7FV+GPM851SgYZBIFvqBvUc2S4WYNyuXg69w9hkOhnQJkvik5c6q4ormyFN77wvRPEohbzvdnzgdOuv8no4sOLztdkgp4uj2pjgOelxZSsvd3l/DuCwvvWtTtoWfdM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=ifyQouFS; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="ifyQouFS" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035523epoutp020083add01a07103341bb5d494417c2c0~p8zF8qH0M2603326033epoutp02L for ; Tue, 30 Sep 2025 03:55:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035523epoutp020083add01a07103341bb5d494417c2c0~p8zF8qH0M2603326033epoutp02L DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204523; bh=OAKHIMC+SGgb607BRAlenii1CobqGR9qOX07bcaTIu0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ifyQouFSGONTM7Qqzq+msnwCGfAhGfONx4PDxuP83CVvFnrzLo8TvsZjFHNq+mhdA ouTs1fgA0IQeOUghS71iWxf/2PjnCqo09nBN+wSAwoLU8e71ZQUg1qmEuWpLu7AEgb T408bQus/Cr4pQrlUmbH6ztIxLJVLJKu6TiHikcA= Received: from epsnrtp03.localdomain (unknown [182.195.42.155]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPS id 20250930035523epcas5p23c238981ff1efe5bf2c24f04766373e8~p8zFo_kiA2195921959epcas5p2G; Tue, 30 Sep 2025 03:55:23 +0000 (GMT) Received: from epcas5p2.samsung.com (unknown [182.195.38.94]) by epsnrtp03.localdomain (Postfix) with ESMTP id 4cbPN967Dtz3hhT3; Tue, 30 Sep 2025 03:55:21 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPA id 20250930035521epcas5p428367b20b9498eb8862d17f4bb75f663~p8zEG_8yz2319723197epcas5p4h; Tue, 30 Sep 2025 03:55:21 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035518epsmtip13069665488924124e1d46773ced32431~p8zBxTW5-2942629426epsmtip1Q; Tue, 30 Sep 2025 03:55:18 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 01/29] dt-bindings: media: mfc: Add Exynos MFC devicetree binding Date: Tue, 30 Sep 2025 09:33:20 +0530 Message-Id: <20250930040348.3702923-2-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035521epcas5p428367b20b9498eb8862d17f4bb75f663 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035521epcas5p428367b20b9498eb8862d17f4bb75f663 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce a new DT binding file for exynos-mfc Documentation/devicetree/bindings/media/samsung,exynos-mfc.yaml which describes the Exynos Multi=E2=80=91Format Codec (MFC) IP. The schema covers the core node properties, required fields, and provides an example snippet. Signed-off-by: Himanshu Dewangan Signed-off-by: Nagaraju Siddineni --- .../bindings/media/samsung,exynos-mfc.yaml | 77 +++++++++++++++++++ MAINTAINERS | 10 +++ 2 files changed, 87 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/samsung,exynos-= mfc.yaml diff --git a/Documentation/devicetree/bindings/media/samsung,exynos-mfc.yam= l b/Documentation/devicetree/bindings/media/samsung,exynos-mfc.yaml new file mode 100644 index 000000000000..fbed987fb9cf --- /dev/null +++ b/Documentation/devicetree/bindings/media/samsung,exynos-mfc.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/samsung,exynos-mfc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung Exynos Multi Format Codec (MFC) + +maintainers: + - Nagaraju Siddineni + - Himanshu Dewangan + +description: + Multi Format Codec (MFC) is the IP present in Samsung SoCs which + supports high resolution decoding and encoding functionalities. + +properties: + compatible: + oneOf: + - enum: + - samsung,exynos-mfc # Exynos920 + - samsung,mfc_core0_mem # Reserved Memory + - samsung,mfc_core1_mem # Reserved Memory + + power-domains: + maximum: 1 + + memory-region: + minimum: 1 + maximum: 2 + +additionalProperties: true + +patternProperties: + "^mfc_core[0-9]$": + type: object + properties: + compatible: + enum: + - samsung,exynos-mfc-core + reg: + maximum: 1 + interrupts: + maximum: 1 + clocks: + minimum: 1 + maximum: 1 + clock-names: + items: + - const: aclk_mfc + + required: + - compatible + - reg + - clocks + - clock-names + - interrupts + +examples: + - | + #include + #include + mfc { + #address-cells =3D <2>; + #size-cells =3D <2>; + compatible =3D "samsung,exynos-mfc"; + mfc_core0: MFC-0@19cd0000 { + #address-cells =3D <2>; + #size-cells =3D <2>; + compatible =3D "samsung,exynos-mfc-core"; + reg =3D <0x0 0x19ED0000 0x0 0x10000>; + interrupts =3D ; + power-domains =3D <&pd_mfc>; + clocks =3D <&cmu_top 2000>; + clock-names =3D "aclk_mfc"; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 156fa8eefa69..e47016804ac8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3374,6 +3374,16 @@ S: Maintained F: Documentation/devicetree/bindings/media/samsung,s5p-mfc.yaml F: drivers/media/platform/samsung/s5p-mfc/ =20 +ARM/SAMSUNG EXYNOS SERIES Multi Format Codec (MFC) SUPPORT +M: Nagaraju Siddineni +M: Himanshu Dewangan +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/samsung,exynos-mfc.yaml +F: drivers/media/platform/samsung/exynos-mfc/ +F: drivers/media/platform/samsung/exynos-mfc/base/ + ARM/SOCFPGA ARCHITECTURE M: Dinh Nguyen S: Maintained --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) (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 0C89E246BC1 for ; Tue, 30 Sep 2025 03:55:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204530; cv=none; b=qDjQdLgDNi+iltvaKHxykYeo/MKKi7vPDWkAeIB3Cpik4i/ZwFgx6HVRWqic7sIs0TNyMZ73XEdnuxRUAiNH4NXgo4ZH/MAmuL7DHFn5XAEfwBYaH/Jcoa89J/AybwG0QJtf6EK0LW+Tj6D2bR+OISQThcQVOGeNpj2nqrOCkwM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204530; c=relaxed/simple; bh=/rr6fkKgpyd9I1sGDA1z4qN/504HunCyZVLAJ9zIIVY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=LP7baG2FED6saXK3YGXPAH+knXqKAbAjDpxdIK0Zvml/SyTAfiGuF7R6F2t65SHKXhVGALeb7PsbvOOqFhZKgt0ZLBVYqGeGUOAYLWFpOcR/IKbUamFjLG/OsGSTFCKYgBHtRSo4U9lipatm31sIeNGoDhqsnNe65U9WOs1/O2k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=kEIXz/O3; arc=none smtp.client-ip=203.254.224.24 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="kEIXz/O3" Received: from epcas5p1.samsung.com (unknown [182.195.41.39]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250930035527epoutp0129198e2b73d7642964bb30d740f58756~p8zJlcsTz3124431244epoutp01U for ; Tue, 30 Sep 2025 03:55:27 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250930035527epoutp0129198e2b73d7642964bb30d740f58756~p8zJlcsTz3124431244epoutp01U DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204527; bh=BSBr8yFx9WBcmfMiEeqIr3+j6OCPbBkalNH3vdDG14M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kEIXz/O3A8tkbLbzJPRO6wiYUQHzdo77H4KcE75DCx35tPFw6mo1riaoGewGVUc7w Yrjuvfyo4nfHpS2jq3NIecUHDQYjx4jNCZoReD9IleBJkNpcMMQXi/BsvEbxmN7XEl 1hKDEdiDncHVHiUgYlzODMIR8X7799XWo2DuT0Xg= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035526epcas5p41bc3d59b8653b402b3d5f9d419e645ec~p8zJG7qMb2319723197epcas5p40; Tue, 30 Sep 2025 03:55:26 +0000 (GMT) Received: from epcas5p4.samsung.com (unknown [182.195.38.90]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPNF54QMz2SSKb; Tue, 30 Sep 2025 03:55:25 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035525epcas5p3a1238a3a7bc6e52113838397187e36ba~p8zHi0lrJ2823728237epcas5p3Y; Tue, 30 Sep 2025 03:55:25 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035521epsmtip124585f13192326b1a3573b5d5d6e8b01~p8zEagPfI2885328853epsmtip1I; Tue, 30 Sep 2025 03:55:21 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 02/29] arm64: dts: mfc: Add MFC device tree for Auto V920 SoC Date: Tue, 30 Sep 2025 09:33:21 +0530 Message-Id: <20250930040348.3702923-3-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035525epcas5p3a1238a3a7bc6e52113838397187e36ba X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035525epcas5p3a1238a3a7bc6e52113838397187e36ba References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce the device=E2=80=91tree entries for the Samsung Exynos AUTO V920 multimedia=E2=80=91function controller (MFC). The patch defines: - Reserved memory regions for firmware and CMA buffers. - Core0 and Core1 memory mappings. - The main MFC node with basic properties (compatible, reg, interrupts, clocks, DMA window, firmware name, debug mode, etc.). - Per=E2=80=91core sub=E2=80=91nodes (MFC=E2=80=910 and MFC=E2=80=911) with= their own sysmmu, firmware region, and configuration parameters. - Resource table listing supported codecs and their capabilities. Adds full support for the V920 MFC hardware to the mainline kernel device=E2=80=91tree, enabling proper memory allocation, interrupt handling = and codec operation on this platform. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../dts/exynos/exynosautov920-evt2-mfc.dtsi | 187 ++++++++++++++++++ .../arm64/boot/dts/exynos/exynosautov920.dtsi | 1 + 2 files changed, 188 insertions(+) create mode 100644 arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi diff --git a/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi b/arch= /arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi new file mode 100644 index 000000000000..49c61958467e --- /dev/null +++ b/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SAMSUNG EXYNOS AUTO V920 SoC MFC device tree source + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + */ + +#define MFC_CORE0_MEM_ADDR 0xE9C00000 +#define MFC_CORE1_MEM_ADDR 0xEFB00000 +#define MFC_CMA_MEM_ADDR 0xB0000000 +#define MFC_CMA_MEM_SIZE 0x040000000 /* 64MB */ +#define MFC_FW_MEM_ADDR 0x9F000000 +#define MFC_FW_MEM_SIZE 0x00800000 /* 8MB */ +#define MFC_MEM_SIZE 0x00100000 /* 1MB */ + +/ { +reserved_memory: reserved-memory { + #address-cells =3D <2>; + #size-cells =3D <2>; + ranges; + + mfc_fw_rmem: mfc_fw_rmem@9f000000 { + compatible =3D "shared-dma-pool"; + reg =3D <0x0 MFC_FW_MEM_ADDR 0x0 MFC_FW_MEM_SIZE>; + alignment =3D <0x0 0x00010000>; + reusable; + }; + + mfc_buf_rmem: mfc_buf_rmem { + compatible =3D "shared-dma-pool"; + reg =3D <0x0 MFC_CMA_MEM_ADDR 0x0 MFC_CMA_MEM_SIZE>; + alignment =3D <0x0 0x00010000>; + reusable; + linux,cma-default; + }; + + mfc_core0_mem: mfc_core0_mem@e9c00000 { + compatible =3D "samsung,mfc_core0_mem"; + reg =3D <0x0 MFC_CORE0_MEM_ADDR 0x0 MFC_MEM_SIZE>; + }; + + mfc_core1_mem: mfc_core1_mem@efb00000 { + compatible =3D "samsung,mfc_core1_mem"; + reg =3D <0x0 MFC_CORE1_MEM_ADDR 0x0 MFC_MEM_SIZE>; + }; +}; + + mfc: mfc@19cd0000 { + /* Basic setting */ + compatible =3D "samsung,exynos-mfc"; + + reg =3D <0x0 0x19CD0000 0x0 0x10000>; + interrupts =3D ; + clock-names =3D "aclk_mfc"; + clocks =3D <&cmu_top 1900>; + + /* for vb2 device */ + samsung,tzmp; + + /* for F/W buffer to support 36bit-DMA */ + memory-region =3D <&mfc_fw_rmem &mfc_buf_rmem>; + + /* MFC DMA bit (32 or 36) */ + dma_bit_mask =3D <32>; + + /* MFC version */ + ip_ver =3D <0x1600010C>; + + /* MFC firmware name */ + fw_name =3D "mfc_fw_v16.0.bin"; + + /* Debug mode */ + debug_mode =3D <1>; + + /* Features */ + mem_clear =3D <1 0x0>; + /* Support from v11.0 (except 11.2) */ + wait_fw_status =3D <1 0x190122>; + + /* Scheduler 0: round-robin, 1: PBS */ + scheduler =3D <1>; + /* The number of priority in PBS */ + pbs_num_prio =3D <1>; + + /* MFC IOVA threshold (MB) */ + iova_threshold =3D <1700>; + + /* Sub nodes for MFC core */ + #address-cells =3D <2>; + #size-cells =3D <2>; + ranges; + + /* + * Resource of standard + * MFC_REG_CODEC_XXX + * 0: MFC only, 1: MFD only, 2: ALL + * 245760: 240Mbps, 122880: 120Mbps, 81920: 80Mbps + */ + mfc_resource { + /* codec name { codec mode, op core type, max Kbps } */ + H264_dec { info =3D <0 2 245760>; }; + VP8_dec { info =3D <14 0 81920>; }; + HEVC_dec { info =3D <17 2 245760>; }; + VP9_dec { info =3D <18 0 81920>; }; + AV1_dec { info =3D <19 1 122880>; }; + H264_enc { info =3D <20 0 245760>; }; + VP8_enc { info =3D <25 0 81920>; }; + HEVC_enc { info =3D <26 0 245760>; }; + VP9_enc { info =3D <27 0 81920>; }; + }; + + /* MFC core device */ + mfc_core0: MFC-0@19cd0000 { + /* Basic setting */ + compatible =3D "samsung,exynos-mfc-core"; + id =3D <0>; + #address-cells =3D <2>; + #size-cells =3D <2>; + ranges; + reg =3D <0x0 0x19CD0000 0x0 0x10000>; + interrupts =3D ; + clock-names =3D "aclk_mfc"; + clocks =3D <&cmu_top 1900>; + samsung,tzmp; + + /* MFC version */ + ip_ver =3D <0x1600010C>; + + /* for F/W buffer */ + fw-region =3D <&mfc_core0_mem>; + + /* Sysmmu check */ + share_sysmmu =3D <0>; + axid_mask =3D <0xFFFF>; + tsmux_axid =3D <0x1>; + + /* mem-log buffer size */ + memlog_size =3D <0x80000>; + memlog_sfr_size =3D <0x1000>; + + /* Device virtual address */ + #dma-address-cells =3D <1>; + #dma-size-cells =3D <1>; + dma-window =3D <0x0 0xF0000000>; + + /* Sub nodes for sysmmu, hwfc and mmcache */ + iommu@19c70000 { + reg =3D <0x0 0x19C70000 0x0 0x9000>; + }; + }; + + mfc_core1: MFC-1@19ed0000 { + /* Basic setting */ + compatible =3D "samsung,exynos-mfc-core"; + id =3D <1>; + #address-cells =3D <2>; + #size-cells =3D <2>; + ranges; + reg =3D <0x0 0x19ED0000 0x0 0x10000>; + interrupts =3D ; + clock-names =3D "aclk_mfc"; + clocks =3D <&cmu_top 2000>; + samsung,tzmp; + + /* MFC version */ + ip_ver =3D <0x1600010D>; + + /* for F/W buffer */ + fw-region =3D <&mfc_core1_mem>; + + /* Sysmmu check */ + share_sysmmu =3D <0>; + axid_mask =3D <0xFFFF>; + + /* Device virtual address */ + #dma-address-cells =3D <1>; + #dma-size-cells =3D <1>; + dma-window =3D <0x0 0xF0000000>; + + /* Sub nodes for sysmmu, hwfc and mmcache */ + iommu@19e70000 { + reg =3D <0x0 0x19E70000 0x0 0x9000>; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/exynos/exynosautov920.dtsi b/arch/arm64/bo= ot/dts/exynos/exynosautov920.dtsi index 0fdf2062930a..f35441d29cdd 100644 --- a/arch/arm64/boot/dts/exynos/exynosautov920.dtsi +++ b/arch/arm64/boot/dts/exynos/exynosautov920.dtsi @@ -1507,3 +1507,4 @@ timer { }; =20 #include "exynosautov920-pinctrl.dtsi" +#include "exynosautov920-evt2-mfc.dtsi" --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) (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 388EF2561B9 for ; Tue, 30 Sep 2025 03:55:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.33 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204540; cv=none; b=W8CEu9RUXf5qXg+nNm4caAwepaG095SNysp4gojx7vLwYTDsV5aT1imh7qd9JO5/rDqY675vGmhdQL4bTZISMPt1xdvAS+aR+rv3/7ZG5isv40iObn/KkK7oaKQRnphM0SaYdlpIidz090FYjReeJlE5QG8R04YGrFHK5e7yvAc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204540; c=relaxed/simple; bh=emFROhc1Hf8kkob8XmNcjoq1G6CHfX+0CUpBc2P7zuk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=HrNZeyFWU/PYQ41KptO3Dm9PcDDCPvR2ef2paNEWQIDdpzOMiACNSM1UJ4o4oOTAJ5f5oMtSG0UxwS5c/5yQemAs+pqXuMyUEYC6b+p6RkRji3L2CZwSLxNkx0TC4pVKK10Kd6Skq3GF0vQdbDJ1mYKtJitHwJUjdbHEhzhCzM8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=CX6JPlWk; arc=none smtp.client-ip=203.254.224.33 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="CX6JPlWk" Received: from epcas5p1.samsung.com (unknown [182.195.41.39]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20250930035535epoutp0356a828571c738cda67b235d78da02277~p8zRQDKpx3045230452epoutp03t for ; Tue, 30 Sep 2025 03:55:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20250930035535epoutp0356a828571c738cda67b235d78da02277~p8zRQDKpx3045230452epoutp03t DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204535; bh=1M5DyEI/r7OAsRulfbScNOurvCqrSGtcLH7HJ2j50Uc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CX6JPlWkpB1uHdFoiX8wSgYLSUl0QO+lIV3ivDkI2MGdjp2iQw0J6lVwzoEXNUU4B V4Bt0t6u/BWVleYX4faabXJwzBiubi3Ow9cHUeCx7ujZINbzUzl2HUjgixGFtnkEO1 pUgHm1meOpVP1AyQejzlVB7SBCn8brU/41XTV6xU= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035534epcas5p10478f6c6370cbbba27c9f3d4d4c73cec~p8zQjUx4I2443624436epcas5p1G; Tue, 30 Sep 2025 03:55:34 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.86]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPNK2NJRz6B9mB; Tue, 30 Sep 2025 03:55:29 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035527epcas5p3e6b8c8040341cd99e6e73c9db7a4d74e~p8zKNbC002823728237epcas5p3m; Tue, 30 Sep 2025 03:55:27 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035525epsmtip130b1aa521afca33e581f2fe5e454eaef~p8zHySW_K2908429084epsmtip1i; Tue, 30 Sep 2025 03:55:25 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 03/29] media: mfc: Add MFC driver data structures and debugging macros Date: Tue, 30 Sep 2025 09:33:22 +0530 Message-Id: <20250930040348.3702923-4-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035527epcas5p3e6b8c8040341cd99e6e73c9db7a4d74e X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035527epcas5p3e6b8c8040341cd99e6e73c9db7a4d74e References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni This patch introduces the core data structures and debugging infrastructure for the Samsung Exynos MFC (Multi=E2=80=91Format Codec) driv= er. mfc_data_struct.h Defines all driver=E2=80=91wide constants, enums, and structures required for MFC core, contexts, buffers, QoS, and platform data. Includes definitions for codec types, instance states, buffer usage, logging options, feature flags, and hardware resources. Provides the main struct mfc_dev, struct mfc_core, struct mfc_ctx, and related helper structures. mfc_debug.h Implements a comprehensive set of debug macros (mfc_debug, mfc_ctx_debug, mfc_core_debug, etc.) with configurable logging levels. Adds error=E2=80=91handling macros and trace utilities for device, core, and context tracing. Supports printing to the kernel log based on the driver=E2=80=99s debugfs.logging_option settings. These additions lay the groundwork for the MFC driver=E2=80=99s functionali= ty, enabling detailed logging, state tracking, and proper management of codec resources. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_data_struct.h | 985 ++++++++++++++++++ .../samsung/exynos-mfc/base/mfc_debug.h | 247 +++++ 2 files changed, 1232 insertions(+) create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_data= _struct.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_debu= g.h diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct= .h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h new file mode 100644 index 000000000000..59fef39095d2 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h @@ -0,0 +1,985 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_data_struct.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_DATA_STRUCT_H +#define __MFC_DATA_STRUCT_H __FILE__ + +#include +#include +#include +#include + +#include +#include +#include + +#include "mfc_media.h" + +/* DEBUGFS */ +#define MFC_DEFAULT_LOGGING_OPTION 0x7 + +#define MFC_NUM_CORE 2 +#define MFC_NUM_CONTEXTS 32 +#define MFC_MAX_PLANES 3 +#define MFC_MAX_DPBS 64 +#define MFC_MAX_BUFFERS 32 +#define MFC_MAX_EXTRA_BUF 10 +#define MFC_SFR_LOGGING_COUNT_SET0 10 +#define MFC_SFR_LOGGING_COUNT_SET1 28 +#define MFC_SFR_LOGGING_COUNT_SET2 32 +#define MFC_LOGGING_DATA_SIZE 950 +/* the number of priority is 2N(num of OPP) + 2 */ +#define MFC_MAX_PRIO 12 +/* The number of display DRC max frames that can occur continuously in NAL= _Q */ +#define MFC_MAX_DRC_FRAME MFC_MAX_BUFFERS + +/* The last number of the standard(MFC_REG_CODEC_BPG_ENC) supported by MFC= + 1 */ +#define MFC_MAX_CODEC_TYPE (33 + 1) + +/* Maximum number of temporal layers */ +#define VIDEO_MAX_TEMPORAL_LAYERS 7 + +/* Batch mode */ +#define MAX_NUM_IMAGES_IN_VB 8 + +/* QoS */ +#define MAX_TIME_INDEX 15 +#define MAX_NUM_CLUSTER 3 +#define MAX_NUM_MFC_BPS 2 +#define MAX_NUM_MFC_FREQ 10 +#define MAX_NUM_QOS_DYNAMIC 10 + +#define MFC_FMT_FLAG_MULTI_VIEW 0x0010 +#define MFC_FMT_FLAG_DEPTH_MAP 0x0020 + +#define MFC_NUM_SPECIAL_BUF_NAME 25 + +#define BANK_L_CTX 0 +#define BANK_R_CTX 1 +#define BANK_NUM 2 + +/** + * enum mfc_inst_type - The type of an MFC device node. + */ +enum mfc_node_type { + MFCNODE_INVALID =3D -1, + MFCNODE_DECODER =3D 0, + MFCNODE_ENCODER =3D 1, + MFCNODE_DECODER_DRM =3D 2, + MFCNODE_ENCODER_DRM =3D 3, + MFCNODE_ENCODER_OTF =3D 4, + MFCNODE_ENCODER_OTF_DRM =3D 5, +}; + +/** + * enum mfc_dev_state - The type of an MFC device. + */ +enum mfc_core_state { + MFCCORE_INIT =3D 0, + MFCCORE_ERROR =3D 1, +}; + +/** + * enum mfc_inst_type - The type of an MFC instance. + */ +enum mfc_inst_type { + MFCINST_INVALID =3D 0, + MFCINST_DECODER =3D 1, + MFCINST_ENCODER =3D 2, +}; + +/** + * enum mfc_inst_state - The state of an MFC instance. + */ +enum mfc_inst_state { + MFCINST_FREE =3D 0, + MFCINST_INIT =3D 100, + MFCINST_RUNNING, + MFCINST_RETURN_INST, + MFCINST_ERROR, + MFCINST_ABORT +}; + +enum mfc_buf_usage_type { + MFCBUF_INVALID =3D 0, + MFCBUF_NORMAL, + MFCBUF_DRM, + MFCBUF_NORMAL_FW, + MFCBUF_DRM_FW, +}; + +enum mfc_frame_error_type { + MFC_ERR_FRAME_NO_ERR =3D 0, + MFC_ERR_FRAME_CONCEALMENT =3D 1, + MFC_ERR_FRAME_SYNC_POINT =3D 2, + MFC_ERR_FRAME_BROKEN =3D 3, +}; + +enum mfc_do_cache_flush { + MFC_NO_CACHEFLUSH =3D 0, + MFC_CACHEFLUSH =3D 1, +}; + +enum mfc_idle_mode { + MFC_IDLE_MODE_NONE =3D 0, + MFC_IDLE_MODE_RUNNING =3D 1, + MFC_IDLE_MODE_IDLE =3D 2, + MFC_IDLE_MODE_CANCEL =3D 3, +}; + +enum mfc_debug_cause { + /* panic cause */ + MFC_CAUSE_0WRITE_PAGE_FAULT =3D 0, + MFC_CAUSE_0PAGE_FAULT =3D 1, + MFC_CAUSE_1WRITE_PAGE_FAULT =3D 2, + MFC_CAUSE_1PAGE_FAULT =3D 3, + MFC_CAUSE_NO_INTERRUPT =3D 4, + MFC_CAUSE_NO_SCHEDULING =3D 5, + MFC_CAUSE_FAIL_STOP_NAL_Q =3D 6, + MFC_CAUSE_FAIL_STOP_NAL_Q_FOR_OTHER =3D 7, + MFC_CAUSE_FAIL_CLOSE_INST =3D 8, + MFC_CAUSE_FAIL_SLEEP =3D 9, + MFC_CAUSE_FAIL_WAKEUP =3D 10, + MFC_CAUSE_FAIL_RISC_ON =3D 11, + MFC_CAUSE_FAIL_DPB_FLUSH =3D 12, + MFC_CAUSE_FAIL_CACHE_FLUSH =3D 13, + MFC_CAUSE_FAIL_MOVE_INST =3D 14, +}; + +enum mfc_core_type { + MFC_CORE_INVALID =3D -1, + MFC_CORE_MAIN =3D 0, + MFC_CORE_SUB =3D 1, + MFC_CORE_TYPE_NUM =3D 2, +}; + +enum mfc_op_core_type { + MFC_OP_CORE_NOT_FIXED =3D -1, + MFC_OP_CORE_FIXED_0 =3D 0, + MFC_OP_CORE_FIXED_1 =3D 1, + MFC_OP_CORE_ALL =3D 2, +}; + +enum mfc_op_mode { + MFC_OP_SINGLE =3D 0, + MFC_OP_TWO_MODE1 =3D 1, + MFC_OP_TWO_MODE2 =3D 2, + MFC_OP_SWITCHING =3D 3, + MFC_OP_SWITCH_TO_SINGLE =3D 4, + MFC_OP_SWITCH_BUT_MODE2 =3D 5, +}; + +enum mfc_enc_gdc_type { + MFC_GDC_VOTF =3D 1, + MFC_GDC_OTF =3D 2, +}; + +enum mfc_hwacg_type { + MFC_HWACG_NONE =3D 0, + MFC_HWACG_DRV_CTRL =3D 1, + MFC_HWACG_HWFW_CTRL =3D 2, +}; + +enum mfc_real_time { + /* real-time */ + MFC_RT =3D 0, + /* low-priority real-time */ + MFC_RT_LOW =3D 1, + /* constrained real-time */ + MFC_RT_CON =3D 2, + /* non real-time */ + MFC_NON_RT =3D 3, + MFC_RT_UNDEFINED =3D 4, +}; + +enum mfc_sched_type { + MFC_SCHED_RR =3D 0, + MFC_SCHED_PRIO =3D 1, +}; + +/* core driver */ +extern struct platform_driver mfc_core_driver; + +struct mfc_ctx; +struct mfc_core_ctx; +struct mfc_sched_class; + +struct mfc_debug { + u32 fw_version; + u32 cause; + u8 fault_status; + u32 fault_trans_info; + u64 fault_addr; + u32 sfrs_set0[MFC_SFR_LOGGING_COUNT_SET0]; + u32 sfrs_set1[MFC_SFR_LOGGING_COUNT_SET1]; + u32 sfrs_set2[MFC_SFR_LOGGING_COUNT_SET2]; + u8 curr_ctx; + u8 state; + u8 last_cmd; + u32 last_cmd_sec; + u32 last_cmd_nsec; + u8 last_int; + u32 last_int_sec; + u32 last_int_nsec; + u32 frame_cnt; + u8 hwlock_dev; + u32 hwlock_ctx; + u8 num_inst; + u8 power_cnt; + u8 clock_cnt; + /* for decoder only */ + u32 last_src_addr; + u32 last_dst_addr[MFC_MAX_PLANES]; + /* total logging data */ + char errorinfo[MFC_LOGGING_DATA_SIZE]; +}; + +enum mfc_view_id { + MFC_VIEW_ID_MAIN =3D 0, + MFC_VIEW_ID_SUB =3D 1, +}; + +/** + * struct mfc_buf - MFC buffer + * + */ +struct mfc_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; + dma_addr_t addr[MAX_NUM_IMAGES_IN_VB][MFC_MAX_PLANES]; + phys_addr_t paddr; + struct dma_buf *dmabufs[MAX_NUM_IMAGES_IN_VB][MFC_MAX_PLANES]; + struct dma_buf_attachment *attachments[MAX_NUM_IMAGES_IN_VB][MFC_MAX_PLAN= ES]; + size_t sg_size; + int src_index; + int dpb_index; + int next_index; + int done_index; + int used; + int num_valid_bufs; + unsigned char *vir_addr[MFC_MAX_PLANES]; + u32 flag; + unsigned long i_ino; +}; + +struct mfc_buf_queue { + struct list_head head; + unsigned int count; +}; + +struct mfc_hwlock { + struct list_head waiting_list; + unsigned int wl_count; + unsigned long bits; + unsigned long dev; + unsigned int owned_by_irq; + unsigned int transfer_owner; + /* protection */ + spinlock_t lock; +}; + +struct mfc_listable_wq { + struct list_head list; + wait_queue_head_t wait_queue; + /* protection */ + struct mutex wait_mutex; + struct mfc_dev *dev; + struct mfc_core *core; + struct mfc_ctx *ctx; + struct mfc_core_ctx *core_ctx; +}; + +struct mfc_core_intlock { + int lock; + unsigned long bits; + unsigned long pending; + /* protection */ + struct mutex core_mutex; +}; + +struct mfc_core_lock { + int cnt; + /* protection */ + spinlock_t lock; + wait_queue_head_t wq; +}; + +struct mfc_pm { + struct clk *clock; + atomic_t pwr_ref; + atomic_t protect_ref; + struct device *device; + /* protection */ + spinlock_t clklock; + + int clock_on_steps; + int clock_off_steps; + enum mfc_buf_usage_type base_type; +}; + +enum mfc_fw_status { + MFC_FW_NONE =3D 0, + MFC_FW_ALLOC =3D BIT(0), // 0x1 + MFC_CTX_ALLOC =3D BIT(1), // 0x2 + MFC_FW_LOADED =3D BIT(2), // 0x4 + MFC_FW_VERIFIED =3D BIT(3), // 0x8 + MFC_FW_PROTECTED =3D BIT(4), // 0x10 + MFC_FW_INITIALIZED =3D BIT(5), // 0x20 +}; + +struct mfc_fw { + int date; + int fimv_info; + size_t fw_size; + enum mfc_fw_status status; +}; + +struct mfc_ctx_buf_size { + size_t dev_ctx; + size_t dbg_info_buf; +}; + +struct mfc_buf_size { + size_t firmware_code; + unsigned int cpb_buf; + struct mfc_ctx_buf_size *ctx_buf; +}; + +struct mfc_variant { + struct mfc_buf_size *buf_size; + int num_entities; +}; + +enum mfc_sfr_dump_type { + MFC_DUMP_NONE =3D 0, + MFC_DUMP_DEC_SEQ_START =3D BIT(0), + MFC_DUMP_DEC_INIT_BUFS =3D BIT(1), + MFC_DUMP_DEC_FIRST_NAL_START =3D BIT(2), + MFC_DUMP_ENC_SEQ_START =3D BIT(3), + MFC_DUMP_ENC_INIT_BUFS =3D BIT(4), + MFC_DUMP_ENC_FIRST_NAL_START =3D BIT(5), + MFC_DUMP_ERR_INT =3D BIT(6), + MFC_DUMP_WARN_INT =3D BIT(7), + MFC_DUMP_DEC_NAL_START =3D BIT(8), + MFC_DUMP_DEC_FRAME_DONE =3D BIT(9), + MFC_DUMP_ENC_NAL_START =3D BIT(10), + MFC_DUMP_ENC_FRAME_DONE =3D BIT(11), + MFC_DUMP_MOVE_INSTANCE_RET =3D BIT(12), + MFC_DUMP_UNKNOWN_INT =3D BIT(13), + MFC_DUMP_DEC_SEQ_DONE =3D BIT(15), + MFC_DUMP_DEC_INIT_BUF_DONE =3D BIT(16), + MFC_DUMP_DEC_FIRST_FRAME_DONE =3D BIT(17), + MFC_DUMP_ENC_SEQ_DONE =3D BIT(18), + MFC_DUMP_ENC_INIT_BUF_DONE =3D BIT(19), + MFC_DUMP_ENC_FIRST_FRAME_DONE =3D BIT(20), + MFC_DUMP_DEC_CRC =3D BIT(29), + MFC_DUMP_FIRMWARE =3D BIT(30), + MFC_DUMP_ALL_INFO =3D BIT(31), +}; + +enum mfc_logging_option { + MFC_LOGGING_NONE =3D 0, + MFC_LOGGING_PRINTK =3D BIT(0), + MFC_LOGGING_MEMLOG_PRINTF =3D BIT(1), + MFC_LOGGING_MEMLOG_SFR_DUMP =3D BIT(2), + MFC_LOGGING_MEMLOG =3D (BIT(1) | BIT(2)), + MFC_LOGGING_ALL =3D 0x7, +}; + +enum mfc_feature_option { + MFC_OPTION_NONE =3D 0, + MFC_OPTION_RECON_SBWC_DISABLE =3D BIT(0), + MFC_OPTION_DECODING_ORDER =3D BIT(1), + MFC_OPTION_MEERKAT_DISABLE =3D BIT(2), + MFC_OPTION_OTF_PATH_TEST_ENABLE =3D BIT(3), + MFC_OPTION_MULTI_CORE_DISABLE =3D BIT(4), + MFC_OPTION_SET_MULTI_CORE_FORCE =3D BIT(5), + MFC_OPTION_BLACK_BAR_ENABLE =3D BIT(6), + MFC_OPTION_DEC_ENC_SBWC_ENABLE =3D BIT(7), + MFC_OPTION_DYNAMIC_QOS_DISABLE =3D BIT(8), + MFC_OPTION_USE_FIXED_WEIGHT =3D BIT(9), + MFC_OPTION_FPS_SBWC_ENABLE =3D BIT(10), + MFC_OPTION_FILMGRAIN_DISABLE =3D BIT(11), + MFC_OPTION_SW_MEMCPY_PLUGIN =3D BIT(12), + MFC_OPTION_INTER_SBWC_DISABLE =3D BIT(13), + MFC_OPTION_MSR_ENABLE =3D BIT(14), + MFC_OPTION_STREAM_COPY_DISABLE =3D BIT(15), +}; + +enum mfc_get_img_size { + MFC_GET_RESOL_SIZE =3D 0, + MFC_GET_RESOL_DPB_SIZE =3D 1, +}; + +enum mfc_color_space { + MFC_COLORSPACE_UNSPECIFICED =3D 0, + MFC_COLORSPACE_BT601 =3D 1, + MFC_COLORSPACE_BT709 =3D 2, + MFC_COLORSPACE_SMPTE_170 =3D 3, + MFC_COLORSPACE_SMPTE_240 =3D 4, + MFC_COLORSPACE_BT2020 =3D 5, + MFC_COLORSPACE_RESERVED =3D 6, + MFC_COLORSPACE_SRGB =3D 7, +}; + +enum mfc_color_primaries { + MFC_PRIMARIES_RESERVED =3D 0, + MFC_PRIMARIES_BT709_5 =3D 1, + MFC_PRIMARIES_UNSPECIFIED =3D 2, + MFC_PRIMARIES_BT470_6M =3D 4, + MFC_PRIMARIES_BT601_6_625 =3D 5, + MFC_PRIMARIES_BT601_6_525 =3D 6, + MFC_PRIMARIES_SMPTE_240M =3D 7, + MFC_PRIMARIES_GENERIC_FILM =3D 8, + MFC_PRIMARIES_BT2020 =3D 9, + MFC_PRIMARIES_EG432 =3D 12, +}; + +enum mfc_transfer_characteristics { + MFC_TRANSFER_RESERVED =3D 0, + MFC_TRANSFER_BT709 =3D 1, + MFC_TRANSFER_UNSPECIFIED =3D 2, + /* RESERVED =3D 3, */ + MFC_TRANSFER_GAMMA_22 =3D 4, + MFC_TRANSFER_GAMMA_28 =3D 5, + MFC_TRANSFER_SMPTE_170M =3D 6, + MFC_TRANSFER_SMPTE_240M =3D 7, + MFC_TRANSFER_LINEAR =3D 8, + MFC_TRANSFER_LOGARITHMIC =3D 9, + MFC_TRANSFER_LOGARITHMIC_S =3D 10, + MFC_TRANSFER_XvYCC =3D 11, + MFC_TRANSFER_BT1361 =3D 12, + MFC_TRANSFER_SRGB =3D 13, + MFC_TRANSFER_BT2020_1 =3D 14, + MFC_TRANSFER_BT2020_2 =3D 15, + MFC_TRANSFER_ST2084 =3D 16, + MFC_TRANSFER_ST428 =3D 17, + MFC_TRANSFER_HLG =3D 18, +}; + +enum mfc_matrix_coeff { + MFC_MATRIX_COEFF_IDENTITY =3D 0, + MFC_MATRIX_COEFF_REC709 =3D 1, + MFC_MATRIX_COEFF_UNSPECIFIED =3D 2, + MFC_MATRIX_COEFF_RESERVED =3D 3, + MFC_MATRIX_COEFF_470_SYSTEM_M =3D 4, + MFC_MATRIX_COEFF_470_SYSTEM_BG =3D 5, + MFC_MATRIX_COEFF_SMPTE170M =3D 6, + MFC_MATRIX_COEFF_SMPTE240M =3D 7, + MFC_MATRIX_COEFF_BT2020 =3D 9, + MFC_MATRIX_COEFF_BT2020_CONSTANT =3D 10, +}; + +struct mfc_debugfs { + struct dentry *root; + unsigned int debug_level; + unsigned int debug_ts; + unsigned int debug_mode_en; + unsigned int dbg_enable; + unsigned int sfr_dump; + unsigned int logging_option; + unsigned int feature_option; + unsigned int sched_perf_disable; + unsigned int sched_type; +}; + +/** + * struct mfc_special_buf - represents internal used buffer + * @daddr: device virtual address + * @iova: device virtual address allocated to the reserved address for F/= W, + * it only used for specific internal buffer + * @virt: kernel virtual address, only valid when the + * buffer accessed by driver + * @priv_data: special purpose private data required for each buffer. + * - internal_dpb: timestamp + */ +struct mfc_special_buf { + enum mfc_buf_usage_type buftype; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attachment; + struct sg_table *sgt; + dma_addr_t daddr; + dma_addr_t iova; + phys_addr_t paddr; + void *vaddr; + size_t size; + size_t map_size; + struct cma *cma_area; + char name[MFC_NUM_SPECIAL_BUF_NAME]; + u64 priv_data; + struct iosys_map map; +}; + +struct mfc_mem { + struct list_head list; + dma_addr_t addr; + size_t size; +}; + +struct mfc_feature { + unsigned int support; + unsigned int version; +}; + +struct mfc_resource { + enum mfc_op_core_type op_core_type; + int max_kbps; +}; + +struct mfc_platdata { + /* Debug mode */ + unsigned int debug_mode; + + /* Resource */ + struct mfc_resource mfc_resource[MFC_MAX_CODEC_TYPE]; + /* Features */ + struct mfc_feature mem_clear; + struct mfc_feature wait_fw_status; + + const char *fw_name; + unsigned int fw_mem_size; + unsigned int reserved_start; + unsigned int dma_bit_mask; + unsigned int ip_ver; + unsigned int iova_threshold; + + unsigned int memlog_size; + + unsigned int scheduler; + unsigned int pbs_num_prio; + enum mfc_hwacg_type support_hwacg; +}; + +struct mfc_core_platdata { + /* MFC version */ + unsigned int ip_ver; + /* Sysmmu check */ + unsigned int share_sysmmu; + unsigned int fault_status; + unsigned int fault_info1; + unsigned int axid_mask; + unsigned int mfc_axid; + unsigned int tsmux_axid; + unsigned int fault_info2; + unsigned int pmmuid_shift; + unsigned int pmmuid_mask; + unsigned int tsmux_pmmuid; + unsigned int masterid_shift; + unsigned int masterid_mask; + unsigned int tsmux_masterid; +}; + +struct mfc_dev_memlog { + unsigned int log_enable; +}; + +struct mfc_core_memlog { + unsigned int sfr_enable; +}; + +/** + * struct mfc_dev - The struct containing driver internal parameters. + */ +struct mfc_dev { + struct mfc_core *core[MFC_NUM_CORE]; + struct mfc_core *plugin; + int num_core; + int num_subsystem; + int fw_date; + int fw_changed_info; + size_t fw_rmem_offset; + + struct device *device; + struct device *mem_dev[BANK_NUM]; + struct device *cache_op_dev; + struct v4l2_device v4l2_dev; + struct video_device *vfd_dec; + struct video_device *vfd_enc; + + struct mfc_platdata *pdata; + struct mfc_variant *variant; + + struct iommu_domain *domain; + struct gen_pool *iova_pool; + + int num_inst; + int num_dec_inst; + int num_enc_inst; + + unsigned long multi_core_inst_bits; + + /* protection */ + struct mutex mfc_mutex; + + struct mfc_ctx *ctx[MFC_NUM_CONTEXTS]; + struct mfc_ctx *move_ctx[MFC_NUM_CONTEXTS]; + dma_addr_t dma_base[BANK_NUM]; + + struct list_head ctx_list; + /* protection */ + spinlock_t ctx_list_lock; + + atomic_t queued_bits; + /* protection */ + spinlock_t idle_bits_lock; + + /* Trace */ + atomic_t trace_ref; + struct _mfc_trace *mfc_trace; + atomic_t trace_ref_longterm; + struct _mfc_trace *mfc_trace_longterm; + atomic_t trace_ref_rm; + struct _mfc_trace *mfc_trace_rm; + + /* Debugfs and dump */ + struct mfc_debugfs debugfs; + + struct mfc_dev_memlog memlog; + +}; + +struct mfc_core_ops { + int (*instance_init)(struct mfc_core *core, struct mfc_ctx *ctx); + int (*instance_deinit)(struct mfc_core *core, struct mfc_ctx *ctx); +}; + +struct dump_info { + char *name; + void *addr; + u64 size; +}; + +struct mfc_core { + struct device *device; + struct iommu_domain *domain; + + const struct mfc_core_ops *core_ops; + + void __iomem *regs_base; + void __iomem *sysmmu0_base; + void __iomem *sysmmu1_base; + void __iomem *pmu_base; + void __iomem *cmu_busc_base; + void __iomem *cmu_mif0_base; + void __iomem *cmu_mif1_base; + void __iomem *cmu_mif2_base; + void __iomem *cmu_mif3_base; + + unsigned int id; + char name[10]; + int irq; + struct resource *mfc_mem; + + struct mfc_variant *variant; + struct mfc_core_platdata *core_pdata; + + struct mfc_sched_class *sched; + enum mfc_sched_type sched_type; + + enum mfc_core_state state; + + bool has_2sysmmu; + bool has_cmu; + bool has_pmu; + + /* Power and Clock */ + atomic_t clk_ref; + struct mfc_pm pm; + bool continue_clock_on; + bool sleep; + bool shutdown; + + /* Internal buffers */ + struct mfc_fw fw; + struct mfc_special_buf fw_buf; + struct mfc_special_buf common_ctx_buf; + struct mfc_special_buf dbg_info_buf; + + /* Context information */ + struct mfc_dev *dev; + struct mfc_core_ctx *core_ctx[MFC_NUM_CONTEXTS]; + int curr_core_ctx; + int preempt_core_ctx; + int num_inst; + + int int_condition; + int int_reason; + unsigned int int_err; + + /* PBS */ + int num_prio; + int total_num_prio; + unsigned long prio_work_bits[MFC_MAX_PRIO]; + /* protection */ + spinlock_t prio_work_lock; + int last_core_ctx[MFC_MAX_PRIO]; + int max_runtime; + int next_ctx_idx; + + /* HW lock */ + struct mfc_hwlock hwlock; + struct mfc_listable_wq hwlock_wq; + wait_queue_head_t cmd_wq; + + /* batch mode */ + int batch_enable; + int batch_index; + /* protection */ + spinlock_t batch_lock; + + /* QoS idle */ + atomic_t hw_run_bits; + /* protection */ + struct mutex idle_qos_mutex; + enum mfc_idle_mode idle_mode; + struct timer_list mfc_idle_timer; + struct workqueue_struct *mfc_idle_wq; + struct work_struct mfc_idle_work; + + /* for DRM */ + int cache_flush_flag; + int last_cmd_has_cache_flush; + + /* QoS */ + struct list_head qos_queue; + atomic_t qos_req_cur; + + /* protection */ + struct mutex qos_mutex; + int mfc_freq_by_bps; + int last_mfc_freq; + int last_table_type; + unsigned long total_mb; + unsigned int cpu_boost_enable; + + /* Logging trace data */ + atomic_t trace_ref_log; + struct _mfc_trace_logging *mfc_trace_logging; + struct mfc_debug *logging_data; + int last_cmd; + int last_int; + struct timespec64 last_cmd_time; + struct timespec64 last_int_time; + + /* debug info dump */ + struct dump_info dbg_info; + + /* Debug */ + struct mfc_core_memlog memlog; +}; + +struct mfc_ctx_ctrl_val { + int has_new; + int val; +}; + +struct mfc_ctx_ctrl { + struct list_head list; + unsigned int id; + unsigned int addr; + struct mfc_ctx_ctrl_val set; + struct mfc_ctx_ctrl_val get; +}; + +struct mfc_buf_ctrl { + struct list_head list; + unsigned int id; + int has_new; + int val; + unsigned int old_val; /* only for MFC_CTRL_TYPE_SET */ + unsigned int old_val2; /* only for MFC_CTRL_TYPE_SET */ + unsigned int is_volatile; /* only for MFC_CTRL_TYPE_SET */ + unsigned int updated; + unsigned int mode; + unsigned int addr; + unsigned int mask; + unsigned int shft; + unsigned int flag_mode; /* only for MFC_CTRL_TYPE_SET */ + unsigned int flag_addr; /* only for MFC_CTRL_TYPE_SET */ + unsigned int flag_shft; /* only for MFC_CTRL_TYPE_SET */ +}; + +struct mfc_ctrl_cfg { + unsigned int id; + unsigned int is_volatile; /* only for MFC_CTRL_TYPE_SET */ + unsigned int mode; + unsigned int addr; + unsigned int mask; + unsigned int shft; + unsigned int flag_mode; /* only for MFC_CTRL_TYPE_SET */ + unsigned int flag_addr; /* only for MFC_CTRL_TYPE_SET */ + unsigned int flag_shft; /* only for MFC_CTRL_TYPE_SET */ +}; + +struct mfc_user_shared_handle { + int fd; + struct dma_buf *dma_buf; + void *vaddr; + size_t data_size; +}; + +struct mfc_raw_info { + int num_planes; + int stride[3]; + int plane_size[3]; + int stride_2bits[3]; + int plane_size_2bits[3]; + unsigned int total_plane_size; +}; + +struct dpb_table { + dma_addr_t addr[MFC_MAX_PLANES]; + phys_addr_t paddr; + size_t size; + int fd[MFC_MAX_PLANES]; + int new_fd; /* it means first plane only */ + int mapcnt; + int ref; + int queued; + struct dma_buf *dmabufs[MFC_MAX_PLANES]; + struct dma_buf_attachment *attach[MFC_MAX_PLANES]; + struct sg_table *sgt[MFC_MAX_PLANES]; +}; + +struct mfc_fmt { + char *name; + u32 fourcc; + u32 codec_mode; + u32 type; + u32 num_planes; + u32 mem_planes; +}; + +enum mfc_multi_view_buf_idx { + MFC_MV_BUF_IDX_VIEW0 =3D 0, + MFC_MV_BUF_IDX_VIEW0_DEPTH, + MFC_MV_BUF_IDX_VIEW1, + MFC_MV_BUF_IDX_VIEW1_DEPTH, + MFC_MV_BUF_IDX_VIEW1_META, + /* the number of index */ + MFC_MV_BUF_IDX_MAX, +}; + +/** + * struct mfc_ctx - This struct contains the instance context + */ +struct mfc_ctx { + struct mfc_dev *dev; + + int num; + int prio; + int user_prio; + enum mfc_real_time rt; + + enum mfc_inst_type type; + + /* operation mode */ + int op_core_num[MFC_NUM_CORE]; + int move_core_num[MFC_NUM_CORE]; + enum mfc_op_mode stream_op_mode; + enum mfc_op_mode op_mode; + enum mfc_op_core_type op_core_type; + struct mfc_core_lock corelock; + + /* protection */ + struct mutex op_mode_mutex; + int last_op_core; + + /* interrupt lock */ + struct mfc_core_intlock intlock; + + /* Control values */ + int codec_mode; + __u32 pix_format; + + int is_heif_mode; + + int is_dpb_realloc; + /* protection */ + int clear_work_bit; + + /* Extra Buffers */ + int mv_buffer_allocated; + struct mfc_special_buf mv_buf; + + unsigned long framerate; + + /* bitrate control for QoS*/ + struct list_head bitrate_list; + int bitrate_index; + int bitrate_is_full; + int kbps; + int last_bps_section; + int load; + unsigned long weighted_mb; + struct list_head list; + + int disp_ratio; + + int buf_process_type; + + int frame_cnt; + dma_addr_t last_src_addr; + dma_addr_t last_dst_addr[MFC_MAX_PLANES]; + + bool mem_type_10bit; + + unsigned long gdc_ready_buf_ino; + /* protection */ + spinlock_t gdc_lock; + + /* QoS idle */ + enum mfc_idle_mode idle_mode; + + /* external structure */ + struct v4l2_fh fh; + struct vb2_queue vq_src; + struct vb2_queue vq_dst; + + u32 ready_to_be_multi_view_enable; + u32 multi_view_enable; + u32 left_view_id; + + /* DRC (Display Resolution Change) */ + u32 handle_drc_multi_mode; +}; + +struct mfc_core_ctx { + struct mfc_core *core; + struct mfc_ctx *ctx; + + int num; + int inst_no; + int int_condition; + int int_reason; + unsigned int int_err; + bool check_dump; + + enum mfc_inst_state state; + enum mfc_inst_state prev_state; + + /* Extra Buffers */ + struct mfc_special_buf instance_ctx_buf; + + /* wait queue */ + wait_queue_head_t cmd_wq; + + struct mfc_listable_wq hwlock_wq; +}; + +struct mfc_sched_class { + void (*create_work)(struct mfc_core *core); + void (*init_work)(struct mfc_core *core); + void (*clear_all_work)(struct mfc_core *core); + void (*set_work)(struct mfc_core *core, struct mfc_core_ctx *core_ctx); + void (*clear_work)(struct mfc_core *core, struct mfc_core_ctx *core_ctx); +}; +#endif /* __MFC_DATA_STRUCT_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_debug.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_debug.h new file mode 100644 index 000000000000..02a6a0bbbceb --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_debug.h @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_debug.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_DEBUG_H +#define __MFC_DEBUG_H __FILE__ + +#define DEBUG + +#ifdef DEBUG + +#define mfc_debug(level, fmt, args...) \ + do { \ + if ((core_ctx->core->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) &= & \ + core_ctx->core->dev->debugfs.debug_level >=3D (level)) \ + dev_info(core_ctx->core->device, "[c:%d] %s:%d: " fmt,\ + core_ctx->num, __func__, __LINE__, ##args); \ + } while (0) + +#define mfc_ctx_debug(level, fmt, args...) \ + do { \ + if ((ctx->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) && \ + ctx->dev->debugfs.debug_level >=3D (level)) \ + dev_info(ctx->dev->device, "[c:%d] %s:%d: " fmt, \ + ctx->num, __func__, __LINE__, ##args); \ + } while (0) + +#define mfc_core_debug(level, fmt, args...) \ + do { \ + if ((core->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) && \ + core->dev->debugfs.debug_level >=3D (level)) \ + dev_info(core->device, "%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + } while (0) + +#define mfc_dev_debug(level, fmt, args...) \ + do { \ + if ((dev->debugfs.logging_option & MFC_LOGGING_PRINTK) && \ + dev->debugfs.debug_level >=3D (level)) \ + dev_info(dev->device, "%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + } while (0) + +#else +#define mfc_debug(fmt, args...) +#define mfc_ctx_debug(fmt, args...) +#define mfc_core_debug(fmt, args...) +#define mfc_dev_debug(fmt, args...) +#endif + +#define mfc_debug_enter() mfc_debug(5, "enter\n") +#define mfc_debug_leave() mfc_debug(5, "leave\n") +#define mfc_ctx_debug_enter() mfc_ctx_debug(5, "enter\n") +#define mfc_ctx_debug_leave() mfc_ctx_debug(5, "leave\n") +#define mfc_core_debug_enter() mfc_core_debug(5, "enter\n") +#define mfc_core_debug_leave() mfc_core_debug(5, "leave\n") +#define mfc_dev_debug_enter() mfc_dev_debug(5, "enter\n") +#define mfc_dev_debug_leave() mfc_dev_debug(5, "leave\n") + +/* ERROR */ +#define mfc_pr_err(fmt, args...) \ + pr_err("[Exynos][MFC][ ERROR]: %s:%d: " fmt, __func__, __LINE__, ##args) + +#define mfc_dev_err(fmt, args...) \ + do { \ + if (dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_err(dev->device, "%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + \ + } while (0) + +#define mfc_core_err(fmt, args...) \ + do { \ + if (core->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_err(core->device, "%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + \ + } while (0) + +#define mfc_ctx_err(fmt, args...) \ + do { \ + if (ctx->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_err(ctx->dev->device, \ + "[c:%d] %s:%d: " fmt, \ + ctx->num, __func__, __LINE__, ##args); \ + \ + } while (0) + +#define mfc_err(fmt, args...) \ + do { \ + if (core_ctx->core->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_err(core_ctx->core->device, \ + "[c:%d] %s:%d: " fmt, \ + core_ctx->num, __func__, __LINE__, ##args); \ + \ + } while (0) + +#define mfc_dev_info(fmt, args...) \ + do { \ + if (dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_info(dev->device, "%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + \ + } while (0) + +#define mfc_core_info(fmt, args...) \ + do { \ + if (core->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_info(core->device, "%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + \ + } while (0) + +#define mfc_ctx_info(fmt, args...) \ + do { \ + if (ctx->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_info(ctx->dev->device, \ + "[c:%d] %s:%d: " fmt, \ + ctx->num, __func__, __LINE__, ##args); \ + } while (0) + +#define mfc_info(fmt, args...) \ + do { \ + if (core_ctx->core->dev->debugfs.logging_option & MFC_LOGGING_PRINTK) \ + dev_info(core_ctx->core->device, \ + "[c:%d] %s:%d: " fmt, \ + core_ctx->num, __func__, __LINE__, ##args); \ + } while (0) + +#define MFC_TRACE_STR_LEN 80 +#define MFC_TRACE_COUNT_MAX 1024 +#define MFC_TRACE_COUNT_PRINT 50 +#define MFC_TRACE_COUNT_PRINT_LONG 100 +#define MFC_TRACE_LOG_STR_LEN 25 +#define MFC_TRACE_LOG_COUNT_MAX 256 +#define MFC_TRACE_LOG_COUNT_PRINT 20 + +#define MFC_DUMP_BUF_SIZE SZ_6M + +struct _mfc_trace { + unsigned long long time; + char str[MFC_TRACE_STR_LEN]; +}; + +struct _mfc_trace_logging { + unsigned long long time; + char str[MFC_TRACE_LOG_STR_LEN]; +}; + +/* If there is no core structure */ +#define MFC_TRACE_DEV(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&dev->trace_ref) & (MFC_TRACE_COUNT_MAX - 1); = \ + dev->mfc_trace[cnt].time =3D cpu_clock(cpu); \ + snprintf(dev->mfc_trace[cnt].str, MFC_TRACE_STR_LEN, \ + fmt, ##args); \ + } while (0) + +/* If there is core structure */ +#define MFC_TRACE_CORE(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&core->dev->trace_ref) & (MFC_TRACE_COUNT_MAX = - 1); \ + core->dev->mfc_trace[cnt].time =3D cpu_clock(cpu); \ + snprintf(core->dev->mfc_trace[cnt].str, MFC_TRACE_STR_LEN, \ + "[MFC%d] "fmt, core->id, ##args); \ + } while (0) + +/* If there is ctx structure */ +#define MFC_TRACE_CTX(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&ctx->dev->trace_ref) & (MFC_TRACE_COUNT_MAX -= 1); \ + ctx->dev->mfc_trace[cnt].time =3D cpu_clock(cpu); \ + snprintf(ctx->dev->mfc_trace[cnt].str, MFC_TRACE_STR_LEN, \ + "[c:%d] " fmt, ctx->num, ##args); \ + } while (0) + +/* If there is core_ctx structure */ +#define MFC_TRACE_CORE_CTX(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&core_ctx->core->dev->trace_ref) & \ + (MFC_TRACE_COUNT_MAX - 1); \ + core_ctx->core->dev->mfc_trace[cnt].time =3D cpu_clock(cpu); \ + snprintf(core_ctx->core->dev->mfc_trace[cnt].str, MFC_TRACE_STR_LEN, \ + "[MFC%d][c:%d] " fmt, core_ctx->core->id, \ + core_ctx->num, ##args); \ + } while (0) + +/* If there is no ctx structure */ +#define MFC_TRACE_DEV_LT(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&dev->trace_ref_longterm) & (MFC_TRACE_COUNT_M= AX - 1); \ + dev->mfc_trace_longterm[cnt].time =3D cpu_clock(cpu); \ + snprintf(dev->mfc_trace_longterm[cnt].str, MFC_TRACE_STR_LEN, \ + fmt, ##args); \ + } while (0) + +/* If there is ctx structure */ +#define MFC_TRACE_CTX_LT(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&ctx->dev->trace_ref_longterm) & \ + (MFC_TRACE_COUNT_MAX - 1); \ + ctx->dev->mfc_trace_longterm[cnt].time =3D cpu_clock(cpu); \ + snprintf(ctx->dev->mfc_trace_longterm[cnt].str, MFC_TRACE_STR_LEN, \ + "[c:%d] " fmt, ctx->num, ##args); \ + } while (0) + +/* If there is core structure */ +#define MFC_TRACE_LOG_CORE(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&core->trace_ref_log) & (MFC_TRACE_LOG_COUNT_M= AX - 1); \ + core->mfc_trace_logging[cnt].time =3D cpu_clock(cpu); \ + snprintf(core->mfc_trace_logging[cnt].str, MFC_TRACE_LOG_STR_LEN, \ + fmt, ##args); \ + } while (0) + +/* Resource manager dedicated */ +#define MFC_TRACE_RM(fmt, args...) \ + do { \ + int cpu =3D raw_smp_processor_id(); \ + int cnt; \ + cnt =3D atomic_inc_return(&dev->trace_ref_rm) & (MFC_TRACE_COUNT_MAX - 1= ); \ + dev->mfc_trace_rm[cnt].time =3D cpu_clock(cpu); \ + snprintf(dev->mfc_trace_rm[cnt].str, MFC_TRACE_STR_LEN, \ + fmt, ##args); \ + } while (0) +#endif /* __MFC_DEBUG_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) (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 CBD99246BC1 for ; Tue, 30 Sep 2025 03:55:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.33 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204539; cv=none; b=G07VVEUTdbDFoJnlv49sbpV1lgxn7GaySUMSuPeECUJKwOIrpIhuLFdF1r7lPG5TW75x+j3R74FSIXWbhOMFUwLhkCVI52IdE7rr1CbIiEV9eOLcqCNSZQFja8rXCEi58lFIGUpVc5wNWJg0M1UmFiWqdvX/p7eZ72codfMpwOA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204539; c=relaxed/simple; bh=kQe/FLcs+jS7T64E5bwXUNR4V17VbV94pA7Ut7gkPBc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=ZDoM4ED25HTxNxWw8GhVRdXZALEF2YSC4g8Zczf7b7Sl2ssB0VgI+wdiIUiGJ2H/ddkomOVKXaRz0K14Z0Zn3/kTIyrFz7jiGEtNZh5rTm1huWq1HVC9I+zrZjnNUSQOYYDxdta1uvM5zd1YmtumY8s3SnykbtX4uMBVGg/5IRg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=GNY4N0SH; arc=none smtp.client-ip=203.254.224.33 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="GNY4N0SH" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20250930035533epoutp03eb979fd40b1291a65ef38a831aeaab31~p8zP1MA6F3045230452epoutp03o for ; Tue, 30 Sep 2025 03:55:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20250930035533epoutp03eb979fd40b1291a65ef38a831aeaab31~p8zP1MA6F3045230452epoutp03o DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204533; bh=G9pxIdZnEbgzBeHGcYvgwNprAuvPesqmPXOpd6bOFHc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GNY4N0SHfIRxdxDW1lESl5iiJeZZWjjyGrq8Ewk1WHfBFQthH77FDGTYwxyByW3zh zr1zfOC6ngIqKRVMia7qr+xnZb12m3ykANj+3RXzOor00NMGFQd5H9PXLiIiLrQRJp McGzbFBN1vJ/HAvPeid/1X8J0zrZgsYvm6dGACSE= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPS id 20250930035533epcas5p3c2880032a8359d6ce90ae8c495b73c9a~p8zPEucZa2823728237epcas5p33; Tue, 30 Sep 2025 03:55:33 +0000 (GMT) Received: from epcas5p4.samsung.com (unknown [182.195.38.93]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cbPNN0ZJsz6B9m8; Tue, 30 Sep 2025 03:55:32 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPA id 20250930035531epcas5p22f3bc84d7e1f3bce78459f4f159e3d1a~p8zNm7Vy62331123311epcas5p2y; Tue, 30 Sep 2025 03:55:31 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035528epsmtip1b9d42fd4aca3396fec91c87e445724ec~p8zKc3cMC2938429384epsmtip1Z; Tue, 30 Sep 2025 03:55:28 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 04/29] media: mfc: Add full register map and bit definitions for MFC hardware Date: Tue, 30 Sep 2025 09:33:23 +0530 Message-Id: <20250930040348.3702923-5-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035531epcas5p22f3bc84d7e1f3bce78459f4f159e3d1a X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035531epcas5p22f3bc84d7e1f3bce78459f4f159e3d1a References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce mfc_regs_mfc.h header Define all MFC register addresses, including common, decoder, encoder, and NAL queue registers. Provide bit=E2=80=91field masks and shifts for command/status registers, codec types, error codes, and feature flags. Add detailed comments for each register group and macro to improve readability and maintainability. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_regs_mfc.h | 1002 +++++++++++++++++ 1 file changed, 1002 insertions(+) create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_regs= _mfc.h diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_regs_mfc.h = b/drivers/media/platform/samsung/exynos-mfc/base/mfc_regs_mfc.h new file mode 100644 index 000000000000..d10d3fb478ef --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_regs_mfc.h @@ -0,0 +1,1002 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_batch.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_REGS_MFC_H +#define __MFC_REGS_MFC_H __FILE__ + +#define MFC_MMU_INTERRUPT_STATUS 0x0060 +#define MFC_MMU_FAULT_TRANS_INFO 0x0078 +#define MFC_MMU_FAULT_TRANS_INFO_RW_MASK 0x100000 + +/* Codec Common Registers */ +#define MFC_REG_RISC_ON 0x0000 +#define MFC_REG_RISC2HOST_INT 0x003C +#define MFC_REG_RISC_STATUS 0x0040 +#define MFC_REG_HOST2RISC_INT 0x0044 +#define MFC_REG_RISC_BASE_ADDRESS 0x0054 +#define MFC_REG_RISC_NONSECURE_BASE_ADDR 0x0068 +#define MFC_REG_FW1_BASE_ADDR 0x1000 + +#define MFC_REG_MFC_FW_CLOCK 0x1060 +#define MFC_REG_MFC_RESET 0x1070 + +#define MFC_REG_HOST2RISC_CMD 0x1100 +#define MFC_REG_RISC2HOST_CMD 0x1104 + +#define MFC_REG_HWAPG_HOST_CTRL 0x11D8 + +#define MFC_REG_MFC_BUS_STATUS 0x7018 +#define MFC_REG_MFC_RPEND 0x7028 +#define MFC_REG_MFC_WPEND 0x702C +#define MFC_REG_MFC_BUS_RESET_CTRL 0x7110 +#define MFC_REG_MFC_OFF 0x7120 +#define MFC_REG_MFC_STATE 0x7124 + +#define MFC_REG_FW_VERSION 0xF000 +#define MFC_REG_INSTANCE_ID 0xF008 +#define MFC_REG_CODEC_TYPE 0xF00C +#define MFC_REG_CONTEXT_MEM_ADDR 0xF014 +#define MFC_REG_CONTEXT_MEM_SIZE 0xF018 +#define MFC_REG_SHARED_MEM_ADDR 0xF01C +#define MFC_REG_PIXEL_FORMAT 0xF020 + +#define MFC_REG_METADATA_ENABLE 0xF024 +#define MFC_REG_MFC_VERSION 0xF028 +#define MFC_REG_DBG_INFO_ENABLE 0xF02C +#define MFC_REG_DBG_BUFFER_ADDR 0xF030 +#define MFC_REG_DBG_BUFFER_SIZE 0xF034 + +#define MFC_REG_CODEC_CONTROL 0xF038 +#define MFC_REG_TIMEOUT_VALUE 0xF03C +#define MFC_REG_HED_SHARED_MEM_ADDR 0xF040 + +/* NAL QUEUE */ +#define MFC_REG_NAL_QUEUE_INPUT_ADDR 0xF044 +#define MFC_REG_NAL_QUEUE_INPUT_SIZE 0xF048 +#define MFC_REG_NAL_QUEUE_OUTPUT_ADDR 0xF04C +#define MFC_REG_NAL_QUEUE_OUTPUT_SIZE 0xF050 +#define MFC_REG_NAL_QUEUE_INPUT_COUNT 0xF054 + +#define MFC_REG_RET_INSTANCE_ID 0xF070 +#define MFC_REG_ERROR_CODE 0xF074 +#define MFC_REG_DBG_BUFFER_OUTPUT_SIZE 0xF078 +#define MFC_REG_METADATA_STATUS 0xF07C + +#define MFC_REG_DBG_INFO_STAGE_COUNTER 0xF088 + +/* NAL QUEUE */ +#define MFC_REG_NAL_QUEUE_OUTPUT_COUNT 0xF08C +#define MFC_REG_NAL_QUEUE_INPUT_EXE_COUNT 0xF090 +#define MFC_REG_NAL_QUEUE_INFO 0xF094 +#define MFC_REG_NAL_LL_IN_USE_BY_MFC 0xF098 +#define MFC_REG_NAL_LL_IN_USE_BY_DRV 0xF09C +#define MFC_REG_NAL_LL_TURN 0xF0A0 + +#define MFC_REG_FIRMWARE_STATUS_INFO 0xF0A4 +#define MFC_REG_PROCESSING_CYCLE 0xF0AC + +/* Decoder Registers */ +#define MFC_REG_D_CRC_CTRL 0xF0B0 +#define MFC_REG_D_DEC_OPTIONS 0xF0B4 + +#define MFC_REG_D_DISPLAY_DELAY 0xF0B8 + +#define MFC_REG_D_SET_FRAME_WIDTH 0xF0BC +#define MFC_REG_D_SET_FRAME_HEIGHT 0xF0C0 + +#define MFC_REG_D_SEI_ENABLE 0xF0C4 + +#define MFC_REG_D_FORCE_PIXEL_VAL 0xF0C8 + +/* Buffer setting registers */ +/* Session return */ +#define MFC_REG_D_MIN_NUM_DPB 0xF0F0 +#define MFC_REG_D_MIN_FIRST_PLANE_DPB_SIZE 0xF0F4 +#define MFC_REG_D_MIN_SECOND_PLANE_DPB_SIZE 0xF0F8 +#define MFC_REG_D_MIN_THIRD_PLANE_DPB_SIZE 0xF0FC +#define MFC_REG_D_MIN_NUM_MV 0xF100 +#define MFC_REG_D_MVC_NUM_VIEWS 0xF104 +#define MFC_REG_D_MIN_SCRATCH_BUFFER_SIZE 0xF108 +#define MFC_REG_D_MIN_FIRST_PLANE_2BIT_DPB_SIZE 0xF10C +#define MFC_REG_D_MIN_SECOND_PLANE_2BIT_DPB_SIZE 0xF110 +#define MFC_REG_D_POST_FILTER_LUMA_DPB0 0xF120 +#define MFC_REG_D_POST_FILTER_LUMA_DPB1 0xF124 +#define MFC_REG_D_POST_FILTER_CHROMA_DPB0 0xF128 +#define MFC_REG_D_POST_FILTER_CHROMA_DPB1 0xF12C + +/* Buffers */ +#define MFC_REG_D_NUM_DPB 0xF130 +#define MFC_REG_D_NUM_MV 0xF134 +#define MFC_REG_D_FIRST_PLANE_DPB_STRIDE_SIZE 0xF138 +#define MFC_REG_D_SECOND_PLANE_DPB_STRIDE_SIZE 0xF13C +#define MFC_REG_D_THIRD_PLANE_DPB_STRIDE_SIZE 0xF140 +#define MFC_REG_D_FIRST_PLANE_DPB_SIZE 0xF144 +#define MFC_REG_D_SECOND_PLANE_DPB_SIZE 0xF148 +#define MFC_REG_D_THIRD_PLANE_DPB_SIZE 0xF14C +#define MFC_REG_D_MV_BUFFER_SIZE 0xF150 +#define MFC_REG_D_INIT_BUFFER_OPTIONS 0xF154 +#define MFC_REG_D_FIRST_PLANE_DPB0 0xF160 +#define MFC_REG_D_SECOND_PLANE_DPB0 0xF260 +#define MFC_REG_D_THIRD_PLANE_DPB0 0xF360 +#define MFC_REG_D_MV_BUFFER0 0xF460 +#define MFC_REG_D_SCRATCH_BUFFER_ADDR 0xF560 +#define MFC_REG_D_SCRATCH_BUFFER_SIZE 0xF564 +#define MFC_REG_D_METADATA_BUFFER_ADDR 0xF568 +#define MFC_REG_D_METADATA_BUFFER_SIZE 0xF56C + +#define MFC_REG_D_STATIC_BUFFER_ADDR 0xF570 +#define MFC_REG_D_STATIC_BUFFER_SIZE 0xF574 +#define MFC_REG_D_FIRST_PLANE_2BIT_DPB_SIZE 0xF578 +#define MFC_REG_D_SECOND_PLANE_2BIT_DPB_SIZE 0xF57C +#define MFC_REG_D_FIRST_PLANE_2BIT_DPB_STRIDE_SIZE 0xF580 +#define MFC_REG_D_SECOND_PLANE_2BIT_DPB_STRIDE_SIZE 0xF584 + +#define MFC_REG_D_NAL_START_OPTIONS 0xF5AC + +/* Nal cmd */ +#define MFC_REG_D_CPB_BUFFER_ADDR 0xF5B0 +#define MFC_REG_D_CPB_BUFFER_SIZE 0xF5B4 +#define MFC_REG_D_AVAILABLE_DPB_FLAG_UPPER 0xF5B8 +#define MFC_REG_D_AVAILABLE_DPB_FLAG_LOWER 0xF5BC +#define MFC_REG_D_CPB_BUFFER_OFFSET 0xF5C0 +#define MFC_REG_D_SLICE_IF_ENABLE 0xF5C4 +#define MFC_REG_D_PICTURE_TAG 0xF5C8 +#define MFC_REG_D_STREAM_DATA_SIZE 0xF5D0 +#define MFC_REG_D_DYNAMIC_DPB_FLAG_UPPER 0xF5D4 +#define MFC_REG_D_DYNAMIC_DPB_FLAG_LOWER 0xF5D8 + +/* Nal return */ +#define MFC_REG_D_DISPLAY_FRAME_WIDTH 0xF600 +#define MFC_REG_D_DISPLAY_FRAME_HEIGHT 0xF604 +#define MFC_REG_D_DISPLAY_STATUS 0xF608 +#define MFC_REG_D_DISPLAY_FIRST_PLANE_ADDR 0xF60C +#define MFC_REG_D_DISPLAY_SECOND_PLANE_ADDR 0xF610 +#define MFC_REG_D_DISPLAY_THIRD_PLANE_ADDR 0xF614 +#define MFC_REG_D_DISPLAY_FRAME_TYPE 0xF618 +#define MFC_REG_D_DISPLAY_CROP_INFO1 0xF61C +#define MFC_REG_D_DISPLAY_CROP_INFO2 0xF620 +#define MFC_REG_D_DISPLAY_PICTURE_PROFILE 0xF624 +#define MFC_REG_D_DISPLAY_FIRST_PLANE_CRC 0xF628 +#define MFC_REG_D_DISPLAY_SECOND_PLANE_CRC 0xF62C +#define MFC_REG_D_DISPLAY_THIRD_PLANE_CRC 0xF630 +#define MFC_REG_D_DISPLAY_ASPECT_RATIO 0xF634 +#define MFC_REG_D_DISPLAY_EXTENDED_AR 0xF638 +#define MFC_REG_D_DECODED_FRAME_WIDTH 0xF63C +#define MFC_REG_D_DECODED_FRAME_HEIGHT 0xF640 +#define MFC_REG_D_DECODED_STATUS 0xF644 +#define MFC_REG_D_DECODED_FIRST_PLANE_ADDR 0xF648 +#define MFC_REG_D_DECODED_SECOND_PLANE_ADDR 0xF64C +#define MFC_REG_D_DECODED_THIRD_PLANE_ADDR 0xF650 +#define MFC_REG_D_DECODED_FRAME_TYPE 0xF654 +#define MFC_REG_D_DECODED_CROP_INFO1 0xF658 +#define MFC_REG_D_DECODED_CROP_INFO2 0xF65C +#define MFC_REG_D_DECODED_PICTURE_PROFILE 0xF660 +#define MFC_REG_D_DECODED_NAL_SIZE 0xF664 +#define MFC_REG_D_DECODED_FIRST_PLANE_CRC 0xF668 +#define MFC_REG_D_DECODED_SECOND_PLANE_CRC 0xF66C +#define MFC_REG_D_DECODED_THIRD_PLANE_CRC 0xF670 +#define MFC_REG_D_RET_PICTURE_TAG_TOP 0xF674 +#define MFC_REG_D_RET_PICTURE_TAG_BOT 0xF678 +#define MFC_REG_D_RET_PICTURE_TIME_TOP 0xF67C +#define MFC_REG_D_RET_PICTURE_TIME_BOT 0xF680 +#define MFC_REG_D_CHROMA_FORMAT 0xF684 + +#define MFC_REG_D_VC1_INFO 0xF688 +#define MFC_REG_D_MPEG4_INFO 0xF68C +#define MFC_REG_D_H264_INFO 0xF690 +#define MFC_REG_D_HEVC_INFO 0xF6A0 +#define MFC_REG_D_VP9_INFO 0xF6A4 +#define MFC_REG_D_AV1_INFO 0xF6AC + +#define MFC_REG_D_METADATA_ADDR_CONCEALED_MB 0xF6B0 +#define MFC_REG_D_METADATA_SIZE_CONCEALED_MB 0xF6B4 +#define MFC_REG_D_METADATA_ADDR_VC1_PARAM 0xF6B8 +#define MFC_REG_D_METADATA_SIZE_VC1_PARAM 0xF6BC +#define MFC_REG_D_METADATA_ADDR_SEI_NAL 0xF6C0 +#define MFC_REG_D_METADATA_SIZE_SEI_NAL 0xF6C4 +#define MFC_REG_D_METADATA_ADDR_VUI 0xF6C8 +#define MFC_REG_D_METADATA_SIZE_VUI 0xF6CC +#define MFC_REG_D_METADATA_ADDR_MVCVUI 0xF6D0 +#define MFC_REG_D_METADATA_SIZE_MVCVUI 0xF6D4 + +#define MFC_REG_D_MVC_VIEW_ID 0xF6D8 + +#define MFC_REG_D_SEI_AVAIL 0xF6DC +#define MFC_REG_D_FRAME_PACK_ARRGMENT_ID 0xF6E0 +#define MFC_REG_D_FRAME_PACK_SEI_INFO 0xF6E4 +#define MFC_REG_D_FRAME_PACK_GRID_POS 0xF6E8 + +#define MFC_REG_D_DISPLAY_RECOVERY_SEI_INFO 0xF6EC +#define MFC_REG_D_DECODED_RECOVERY_SEI_INFO 0xF6F0 + +#define MFC_REG_D_DISPLAY_FIRST_PLANE_2BIT_CRC 0xF6FC +#define MFC_REG_D_DISPLAY_SECOND_PLANE_2BIT_CRC 0xF700 +#define MFC_REG_D_DECODED_FIRST_PLANE_2BIT_CRC 0xF704 +#define MFC_REG_D_DECODED_SECOND_PLANE_2BIT_CRC 0xF708 + +#define MFC_REG_D_VIDEO_SIGNAL_TYPE 0xF70C +#define MFC_REG_D_CONTENT_LIGHT_LEVEL_INFO_SEI 0xF710 +#define MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_0 0xF714 +#define MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_1 0xF718 + +#define MFC_REG_D_USED_DPB_FLAG_UPPER 0xF720 +#define MFC_REG_D_USED_DPB_FLAG_LOWER 0xF724 + +#define MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_2 0xF728 +#define MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_3 0xF72C +#define MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_4 0xF730 +#define MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_5 0xF734 + +#define MFC_REG_D_BLACK_BAR_START_POS 0xF738 +#define MFC_REG_D_BLACK_BAR_IMAGE_SIZE 0xF73C + +#define MFC_REG_D_DECODED_FRAME_COUNT 0xF740 +#define MFC_REG_D_DISPLAY_FRAME_COUNT 0xF744 + +#define MFC_REG_D_DISPLAY_LUMA_ADDR 0xF60C +#define MFC_REG_D_DISPLAY_CHROMA_ADDR 0xF610 + +#define MFC_REG_D_DECODED_LUMA_ADDR 0xF648 +#define MFC_REG_D_DECODED_CHROMA_ADDR 0xF64C + +/* SEI NAL Metadata (reuse encoder register) */ +#define MFC_REG_D_PAYLOAD_TYPE 0xF770 +#define MFC_REG_D_PAYLOAD_SIZE 0xF774 +#define MFC_REG_D_PAYLOAD_DATA_0 0xF778 + +/* Encoder Registers */ +#define MFC_REG_E_CROPPED_FRAME_WIDTH 0xF778 +#define MFC_REG_E_CROPPED_FRAME_HEIGHT 0xF77C +#define MFC_REG_E_FRAME_CROP_OFFSET 0xF780 +#define MFC_REG_E_ENC_OPTIONS 0xF784 +#define MFC_REG_E_PICTURE_PROFILE 0xF788 +#define MFC_REG_E_VBV_BUFFER_SIZE 0xF78C +#define MFC_REG_E_VBV_INIT_DELAY 0xF790 +#define MFC_REG_E_FIXED_PICTURE_QP 0xF794 +#define MFC_REG_E_RC_CONFIG 0xF798 +#define MFC_REG_E_RC_QP_BOUND 0xF79C +#define MFC_REG_E_RC_QP_BOUND_PB 0xF7A0 +#define MFC_REG_E_RC_MODE 0xF7A4 + +#define MFC_REG_E_RC_OPTIONS 0xF7A8 +#define MFC_REG_E_PADDING_CTRL 0xF7AC +#define MFC_REG_E_AIR_THRESHOLD 0xF7B0 + +#define MFC_REG_E_MV_HOR_RANGE 0xF7B4 +#define MFC_REG_E_MV_VER_RANGE 0xF7B8 + +#define MFC_REG_E_HIGH_QUALITY_MODE 0xF7C0 +#define MFC_REG_E_VIDEO_SIGNAL_TYPE 0xF7C4 + +#define MFC_REG_E_SAO_WEIGHT0 0xF7C8 +#define MFC_REG_E_SAO_WEIGHT1 0xF7CC + +#define MFC_REG_E_NUM_DPB 0xF890 +#define MFC_REG_E_MIN_SCRATCH_BUFFER_SIZE 0xF894 +#define MFC_REG_E_MIN_LUMA_DPB_SIZE 0xF898 +#define MFC_REG_E_MIN_CHROMA_DPB_SIZE 0xF89C + +#define MFC_REG_E_LUMA_DPB 0xF8C0 +#define MFC_REG_E_CHROMA_DPB 0xF904 +#define MFC_REG_E_ME_BUFFER 0xF948 + +#define MFC_REG_E_SCRATCH_BUFFER_ADDR 0xF98C +#define MFC_REG_E_SCRATCH_BUFFER_SIZE 0xF990 +#define MFC_REG_E_TMV_BUFFER0 0xF994 +#define MFC_REG_E_TMV_BUFFER1 0xF998 +#define MFC_REG_E_IR_BUFFER_ADDR 0xF99C +#define MFC_REG_E_TILE1_STREAM_BUFFER_ADDR 0xF9A0 +#define MFC_REG_E_TILE1_STREAM_BUFFER_SIZE 0xF9A4 +#define MFC_REG_E_SOURCE_VOTF_BUF_INDEX 0xF9CC +#define MFC_REG_E_SOURCE_FIRST_2BIT_ADDR 0xF9D0 +#define MFC_REG_E_SOURCE_SECOND_2BIT_ADDR 0xF9D4 +#define MFC_REG_E_SOURCE_FIRST_2BIT_STRIDE 0xF9D8 +#define MFC_REG_E_SOURCE_SECOND_2BIT_STRIDE 0xF9DC +#define MFC_REG_E_SOURCE_FIRST_ADDR 0xF9E0 +#define MFC_REG_E_SOURCE_SECOND_ADDR 0xF9E4 +#define MFC_REG_E_SOURCE_THIRD_ADDR 0xF9E8 +#define MFC_REG_E_SOURCE_FIRST_STRIDE 0xF9EC +#define MFC_REG_E_SOURCE_SECOND_STRIDE 0xF9F0 +#define MFC_REG_E_SOURCE_THIRD_STRIDE 0xF9F4 +#define MFC_REG_E_STREAM_BUFFER_ADDR 0xF9F8 +#define MFC_REG_E_STREAM_BUFFER_SIZE 0xF9FC +#define MFC_REG_E_ROI_BUFFER_ADDR 0xFA00 + +#define MFC_REG_E_PARAM_CHANGE 0xFA04 +#define MFC_REG_E_IR_SIZE 0xFA08 +#define MFC_REG_E_GOP_CONFIG 0xFA0C +#define MFC_REG_E_MSLICE_MODE 0xFA10 +#define MFC_REG_E_MSLICE_SIZE_MB 0xFA14 +#define MFC_REG_E_MSLICE_SIZE_BITS 0xFA18 +#define MFC_REG_E_FRAME_INSERTION 0xFA1C + +#define MFC_REG_E_RC_FRAME_RATE 0xFA20 +#define MFC_REG_E_RC_BIT_RATE 0xFA24 +#define MFC_REG_E_RC_ROI_CTRL 0xFA2C +#define MFC_REG_E_PICTURE_TAG 0xFA30 +#define MFC_REG_E_BIT_COUNT_ENABLE 0xFA34 +#define MFC_REG_E_MAX_BIT_COUNT 0xFA38 +#define MFC_REG_E_MIN_BIT_COUNT 0xFA3C + +#define MFC_REG_E_METADATA_BUFFER_ADDR 0xFA40 +#define MFC_REG_E_METADATA_BUFFER_SIZE 0xFA44 + +#define MFC_REG_E_ENCODING_ORDER_TIME_INFO 0xFA50 +#define MFC_REG_E_ENCODING_ORDER_INFO 0xFA54 +#define MFC_REG_E_STREAM_BUFFER_OFFSET 0xFA58 +#define MFC_REG_E_GOP_CONFIG2 0xFA5C +#define MFC_REG_E_WEIGHT_FOR_WEIGHTED_PREDICTION 0xFA60 +#define MFC_REG_E_TIME_STAMP_DELTA 0xFA68 + +#define MFC_REG_E_ENCODED_SOURCE_FIRST_ADDR 0xFA70 +#define MFC_REG_E_ENCODED_SOURCE_SECOND_ADDR 0xFA74 +#define MFC_REG_E_ENCODED_SOURCE_THIRD_ADDR 0xFA78 +#define MFC_REG_E_SOURCE_STAT_ADDR 0xFA7C +#define MFC_REG_E_STREAM_SIZE 0xFA80 +#define MFC_REG_E_SLICE_TYPE 0xFA84 +#define MFC_REG_E_PICTURE_COUNT 0xFA88 +#define MFC_REG_E_RET_PICTURE_TAG 0xFA8C +#define MFC_REG_E_TILE0_STREAM_SIZE 0xFA94 +#define MFC_REG_E_TILE1_STREAM_SIZE 0xFA98 + +#define MFC_REG_E_RECON_LUMA_DPB_ADDR 0xFA9C +#define MFC_REG_E_RECON_CHROMA_DPB_ADDR 0xFAA0 +#define MFC_REG_E_METADATA_ADDR_ENC_SLICE 0xFAA4 +#define MFC_REG_E_METADATA_SIZE_ENC_SLICE 0xFAA8 +#define MFC_REG_E_SUM_SKIP_MB 0xFAAC +#define MFC_REG_E_SUM_INTRA_MB 0xFAB0 +#define MFC_REG_E_SUM_ZERO_MV_MB 0xFAB4 + +#define MFC_REG_E_HDR10_PLUS_INFO 0xFAE8 +#define MFC_REG_E_NAL_DONE_INFO 0xFAEC + +#define MFC_REG_E_MPEG4_OPTIONS 0xFB10 +#define MFC_REG_E_MPEG4_HEC_PERIOD 0xFB14 + +#define MFC_REG_E_H264_HD_SVC_EXTENSION_0 0xFB44 +#define MFC_REG_E_H264_HD_SVC_EXTENSION_1 0xFB48 +#define MFC_REG_E_ASPECT_RATIO 0xFB4C +#define MFC_REG_E_EXTENDED_SAR 0xFB50 + +#define MFC_REG_E_H264_OPTIONS 0xFB54 +#define MFC_REG_E_H264_OPTIONS_2 0xFB58 +#define MFC_REG_E_H264_LF_ALPHA_OFFSET 0xFB5C +#define MFC_REG_E_H264_LF_BETA_OFFSET 0xFB60 +#define MFC_REG_E_H264_REFRESH_PERIOD 0xFB64 + +#define MFC_REG_E_H264_FMO_SLICE_GRP_MAP_TYPE 0xFB68 +#define MFC_REG_E_H264_FMO_NUM_SLICE_GRP_MINUS1 0xFB6C +#define MFC_REG_E_H264_FMO_SLICE_GRP_CHANGE_DIR 0xFB70 +#define MFC_REG_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1 0xFB74 +#define MFC_REG_E_H264_FMO_RUN_LENGTH_MINUS1_0 0xFB78 +#define MFC_REG_E_H264_FMO_RUN_LENGTH_MINUS1_1 0xFB7C +#define MFC_REG_E_H264_FMO_RUN_LENGTH_MINUS1_2 0xFB80 +#define MFC_REG_E_H264_FMO_RUN_LENGTH_MINUS1_3 0xFB84 + +#define MFC_REG_E_H264_ASO_SLICE_ORDER_0 0xFB88 +#define MFC_REG_E_H264_ASO_SLICE_ORDER_1 0xFB8C +#define MFC_REG_E_H264_ASO_SLICE_ORDER_2 0xFB90 +#define MFC_REG_E_H264_ASO_SLICE_ORDER_3 0xFB94 +#define MFC_REG_E_H264_ASO_SLICE_ORDER_4 0xFB98 +#define MFC_REG_E_H264_ASO_SLICE_ORDER_5 0xFB9C +#define MFC_REG_E_H264_ASO_SLICE_ORDER_6 0xFBA0 +#define MFC_REG_E_H264_ASO_SLICE_ORDER_7 0xFBA4 +#define MFC_REG_E_H264_CHROMA_QP_OFFSET 0xFBA8 + +#define MFC_REG_E_NUM_T_LAYER 0xFBAC +#define MFC_REG_E_HIERARCHICAL_QP_LAYER0 0xFBB0 +#define MFC_REG_E_HIERARCHICAL_QP_LAYER1 0xFBB4 +#define MFC_REG_E_HIERARCHICAL_QP_LAYER2 0xFBB8 +#define MFC_REG_E_HIERARCHICAL_QP_LAYER3 0xFBBC +#define MFC_REG_E_HIERARCHICAL_QP_LAYER4 0xFBC0 +#define MFC_REG_E_HIERARCHICAL_QP_LAYER5 0xFBC4 +#define MFC_REG_E_HIERARCHICAL_QP_LAYER6 0xFBC8 + +/* For backward compatibility */ +#define MFC_REG_E_H264_FRAME_PACKING_SEI_INFO 0xFC4C + +#define MFC_REG_E_H264_NAL_CONTROL 0xFD14 +#define MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0 0xFD18 +#define MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER1 0xFD1C +#define MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER2 0xFD20 +#define MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER3 0xFD24 +#define MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER4 0xFD28 +#define MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER5 0xFD2C +#define MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER6 0xFD30 + +#define MFC_REG_E_MVC_FRAME_QP_VIEW1 0xFD40 +#define MFC_REG_E_MVC_RC_FRAME_RATE_VIEW1 0xFD44 +#define MFC_REG_E_MVC_RC_BIT_RATE_VIEW1 0xFD48 +#define MFC_REG_E_MVC_RC_QBOUND_VIEW1 0xFD4C +#define MFC_REG_E_MVC_RC_MODE_VIEW1 0xFD50 +#define MFC_REG_E_MVC_INTER_VIEW_PREDICTION_ON 0xFD80 + +#define MFC_REG_E_VP9_OPTION 0xFD90 +#define MFC_REG_E_VP9_FILTER_OPTION 0xFD94 +#define MFC_REG_E_VP9_GOLDEN_FRAME_OPTION 0xFD98 +#define MFC_REG_E_VP8_OPTION 0xFDB0 +#define MFC_REG_E_VP8_FILTER_OPTION 0xFDB4 +#define MFC_REG_E_VP8_GOLDEN_FRAME_OPTION 0xFDB8 + +#define MFC_REG_E_HEVC_OPTIONS_2 0xFDC4 + +#define MFC_REG_E_HEVC_OPTIONS 0xFDD4 +#define MFC_REG_E_HEVC_REFRESH_PERIOD 0xFDD8 +#define MFC_REG_E_HEVC_CHROMA_QP_OFFSET 0xFDDC +#define MFC_REG_E_HEVC_LF_BETA_OFFSET_DIV2 0xFDE0 +#define MFC_REG_E_HEVC_LF_TC_OFFSET_DIV2 0xFDE4 +#define MFC_REG_E_HEVC_NAL_CONTROL 0xFDE8 + +#define MFC_REG_E_VP8_NAL_CONTROL 0xFDF0 +#define MFC_REG_E_VP9_NAL_CONTROL 0xFDF4 +#define MFC_REG_E_CONTENT_LIGHT_LEVEL_INFO_SEI 0xFDF8 +#define MFC_REG_E_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_0 0xFDFC +#define MFC_REG_E_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_1 0xFE00 +#define MFC_REG_E_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_2 0xFE04 +#define MFC_REG_E_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_3 0xFE08 +#define MFC_REG_E_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_4 0xFE0C +#define MFC_REG_E_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_5 0xFE10 + +#define MFC_REG_E_ST_2094_40_SEI_0 0xFE14 +#define MFC_REG_E_ST_2094_40_SEI_1 0xFE18 +#define MFC_REG_E_ST_2094_40_SEI_2 0xFE1C +#define MFC_REG_E_ST_2094_40_SEI_3 0xFE20 +#define MFC_REG_E_ST_2094_40_SEI_4 0xFE24 +#define MFC_REG_E_ST_2094_40_SEI_5 0xFE28 +#define MFC_REG_E_ST_2094_40_SEI_6 0xFE2C +#define MFC_REG_E_ST_2094_40_SEI_7 0xFE30 +#define MFC_REG_E_ST_2094_40_SEI_8 0xFE34 +#define MFC_REG_E_ST_2094_40_SEI_9 0xFE38 +#define MFC_REG_E_ST_2094_40_SEI_10 0xFE3C +#define MFC_REG_E_ST_2094_40_SEI_11 0xFE40 +#define MFC_REG_E_ST_2094_40_SEI_12 0xFE44 +#define MFC_REG_E_ST_2094_40_SEI_13 0xFE48 +#define MFC_REG_E_ST_2094_40_SEI_14 0xFE4C +#define MFC_REG_E_ST_2094_40_SEI_15 0xFE50 +#define MFC_REG_E_ST_2094_40_SEI_16 0xFE54 +#define MFC_REG_E_ST_2094_40_SEI_17 0xFE58 +#define MFC_REG_E_ST_2094_40_SEI_18 0xFE5C +#define MFC_REG_E_ST_2094_40_SEI_19 0xFE60 +#define MFC_REG_E_ST_2094_40_SEI_20 0xFE64 +#define MFC_REG_E_ST_2094_40_SEI_21 0xFE68 +#define MFC_REG_E_ST_2094_40_SEI_22 0xFE6C +#define MFC_REG_E_ST_2094_40_SEI_23 0xFE70 +#define MFC_REG_E_ST_2094_40_SEI_24 0xFE74 +#define MFC_REG_E_ST_2094_40_SEI_25 0xFE78 +#define MFC_REG_E_ST_2094_40_SEI_26 0xFE7C +#define MFC_REG_E_ST_2094_40_SEI_27 0xFE80 +#define MFC_REG_E_ST_2094_40_SEI_28 0xFE84 +#define MFC_REG_E_ST_2094_40_SEI_29 0xFE88 + +#define MFC_REG_D_ST_2094_40_SEI_0 0xFF00 +#define MFC_REG_D_ST_2094_40_SEI_1 0xFF04 +#define MFC_REG_D_ST_2094_40_SEI_2 0xFF08 +#define MFC_REG_D_ST_2094_40_SEI_3 0xFF0C +#define MFC_REG_D_ST_2094_40_SEI_4 0xFF10 +#define MFC_REG_D_ST_2094_40_SEI_5 0xFF14 +#define MFC_REG_D_ST_2094_40_SEI_6 0xFF18 +#define MFC_REG_D_ST_2094_40_SEI_7 0xFF1C +#define MFC_REG_D_ST_2094_40_SEI_8 0xFF20 +#define MFC_REG_D_ST_2094_40_SEI_9 0xFF24 +#define MFC_REG_D_ST_2094_40_SEI_10 0xFF28 +#define MFC_REG_D_ST_2094_40_SEI_11 0xFF2C +#define MFC_REG_D_ST_2094_40_SEI_12 0xFF30 +#define MFC_REG_D_ST_2094_40_SEI_13 0xFF34 +#define MFC_REG_D_ST_2094_40_SEI_14 0xFF38 +#define MFC_REG_D_ST_2094_40_SEI_15 0xFF3C +#define MFC_REG_D_ST_2094_40_SEI_16 0xFF40 +#define MFC_REG_D_ST_2094_40_SEI_17 0xFF44 +#define MFC_REG_D_ST_2094_40_SEI_18 0xFF48 +#define MFC_REG_D_ST_2094_40_SEI_19 0xFF4C +#define MFC_REG_D_ST_2094_40_SEI_20 0xFF50 +#define MFC_REG_D_ST_2094_40_SEI_21 0xFF54 +#define MFC_REG_D_ST_2094_40_SEI_22 0xFF58 +#define MFC_REG_D_ST_2094_40_SEI_23 0xFF5C +#define MFC_REG_D_ST_2094_40_SEI_24 0xFF60 +#define MFC_REG_D_ST_2094_40_SEI_25 0xFF64 +#define MFC_REG_D_ST_2094_40_SEI_26 0xFF68 +#define MFC_REG_D_ST_2094_40_SEI_27 0xFF6C +#define MFC_REG_D_ST_2094_40_SEI_28 0xFF70 +#define MFC_REG_D_ST_2094_40_SEI_29 0xFF74 + +/* + * Below It is valid only for AV1 + * when FILM_GRAIN_AVAIL of D_SEI_AVAIL is enabled + */ +#define MFC_REG_D_FILM_GRAIN_0 0xFE00 +#define MFC_REG_D_FILM_GRAIN_1 0xFE04 +#define MFC_REG_D_FILM_GRAIN_2 0xFE08 +#define MFC_REG_D_FILM_GRAIN_3 0xFE0C +#define MFC_REG_D_FILM_GRAIN_4 0xFE10 +#define MFC_REG_D_FILM_GRAIN_5 0xFE14 +#define MFC_REG_D_FILM_GRAIN_6 0xFE18 +#define MFC_REG_D_FILM_GRAIN_7 0xFE1C +#define MFC_REG_D_FILM_GRAIN_8 0xFE20 +#define MFC_REG_D_FILM_GRAIN_9 0xFE24 +#define MFC_REG_D_FILM_GRAIN_10 0xFE28 +#define MFC_REG_D_FILM_GRAIN_11 0xFE2C +#define MFC_REG_D_FILM_GRAIN_12 0xFE30 +#define MFC_REG_D_FILM_GRAIN_13 0xFE34 +#define MFC_REG_D_FILM_GRAIN_14 0xFE38 +#define MFC_REG_D_FILM_GRAIN_15 0xFE3C +#define MFC_REG_D_FILM_GRAIN_16 0xFE40 +#define MFC_REG_D_FILM_GRAIN_17 0xFE44 +#define MFC_REG_D_FILM_GRAIN_18 0xFE48 +#define MFC_REG_D_FILM_GRAIN_19 0xFE4C +#define MFC_REG_D_FILM_GRAIN_20 0xFE50 +#define MFC_REG_D_FILM_GRAIN_21 0xFE54 +#define MFC_REG_D_FILM_GRAIN_22 0xFE58 +#define MFC_REG_D_FILM_GRAIN_23 0xFE5C +#define MFC_REG_D_FILM_GRAIN_24 0xFE60 +#define MFC_REG_D_FILM_GRAIN_25 0xFE64 +#define MFC_REG_D_FILM_GRAIN_26 0xFE68 +#define MFC_REG_D_FILM_GRAIN_27 0xFE6C +#define MFC_REG_D_FILM_GRAIN_28 0xFE70 +#define MFC_REG_D_FILM_GRAIN_29 0xFE74 +#define MFC_REG_D_FILM_GRAIN_30 0xFE78 +#define MFC_REG_D_FILM_GRAIN_31 0xFE7C +#define MFC_REG_D_FILM_GRAIN_32 0xFE80 +#define MFC_REG_D_FILM_GRAIN_33 0xFE84 +#define MFC_REG_D_FILM_GRAIN_34 0xFE88 +#define MFC_REG_D_FILM_GRAIN_35 0xFE8C +#define MFC_REG_D_FILM_GRAIN_36 0xFE90 +#define MFC_REG_D_FILM_GRAIN_37 0xFE94 +#define MFC_REG_D_FILM_GRAIN_38 0xFE98 +#define MFC_REG_D_FILM_GRAIN_39 0xFE9C +#define MFC_REG_D_FILM_GRAIN_40 0xFEA0 +#define MFC_REG_D_FILM_GRAIN_41 0xFEA4 +#define MFC_REG_D_FILM_GRAIN_42 0xFEA8 +#define MFC_REG_D_FILM_GRAIN_43 0xFEAC + +#define MFC0_REG_COMMON_CONTEXT_MEM_ADDR 0xFF90 +#define MFC0_REG_RISC_BASE_ADDR 0xFF98 +#define MFC1_REG_COMMON_CONTEXT_MEM_ADDR 0xFFDC + +#define MFC_REG_CLEAR_BEGIN 0xf000 +#define MFC_REG_CLEAR_COUNT 1024 + +#define MFC0_REG_CONTEXT_FLUSH_DONE 0xFFA0 + +/* Bit Definitions */ +/* 0x1100: MFC_REG_HOST2RISC_CMD */ +#define MFC_REG_H2R_CMD_EMPTY 0 +#define MFC_REG_H2R_CMD_SYS_INIT 1 +#define MFC_REG_H2R_CMD_OPEN_INSTANCE 2 +#define MFC_REG_H2R_CMD_SEQ_HEADER 3 +#define MFC_REG_H2R_CMD_INIT_BUFFERS 4 +#define MFC_REG_H2R_CMD_NAL_START 5 +#define MFC_REG_H2R_CMD_CLOSE_INSTANCE 6 +#define MFC_REG_H2R_CMD_SLEEP 7 +#define MFC_REG_H2R_CMD_WAKEUP 8 +#define MFC_REG_H2R_CMD_LAST_FRAME 9 +#define MFC_REG_H2R_CMD_DPB_FLUSH 10 +#define MFC_REG_H2R_CMD_NAL_ABORT 11 +#define MFC_REG_H2R_CMD_CACHE_FLUSH 12 +#define MFC_REG_H2R_CMD_NAL_QUEUE 13 +#define MFC_REG_H2R_CMD_STOP_QUEUE 14 +#define MFC_REG_H2R_CMD_NAL_LL 15 +#define MFC_REG_H2R_CMD_MOVE_INSTANCE 19 + +#define MFC_REG_H2R_HWAPG_EN_FLAG 19 +#define MFC_REG_H2R_NAL_Q_OPT_FLAG 17 +#define MFC_REG_H2R_CACHE_FLUSH_FLAG 16 + +/* 0x1104: MFC_REG_RISC2HOST_CMD */ +#define MFC_REG_RISC2HOST_CMD_MASK 0x1FFFF +#define MFC_REG_R2H_CMD_EMPTY 0 +#define MFC_REG_R2H_CMD_SYS_INIT_RET 1 +#define MFC_REG_R2H_CMD_OPEN_INSTANCE_RET 2 +#define MFC_REG_R2H_CMD_SEQ_DONE_RET 3 +#define MFC_REG_R2H_CMD_INIT_BUFFERS_RET 4 +#define MFC_REG_R2H_CMD_CLOSE_INSTANCE_RET 6 +#define MFC_REG_R2H_CMD_SLEEP_RET 7 +#define MFC_REG_R2H_CMD_WAKEUP_RET 8 +#define MFC_REG_R2H_CMD_COMPLETE_SEQ_RET 9 +#define MFC_REG_R2H_CMD_DPB_FLUSH_RET 10 +#define MFC_REG_R2H_CMD_NAL_ABORT_RET 11 +#define MFC_REG_R2H_CMD_FW_STATUS_RET 12 +#define MFC_REG_R2H_CMD_FRAME_DONE_RET 13 +#define MFC_REG_R2H_CMD_FIELD_DONE_RET 14 +#define MFC_REG_R2H_CMD_SLICE_DONE_RET 15 +#define MFC_REG_R2H_CMD_ENC_BUFFER_FULL_RET 16 +#define MFC_REG_R2H_CMD_QUEUE_DONE_RET 17 +#define MFC_REG_R2H_CMD_COMPLETE_QUEUE_RET 18 +#define MFC_REG_R2H_CMD_MOVE_INSTANCE_RET 19 +#define MFC_REG_R2H_CMD_CACHE_FLUSH_RET 20 +#define MFC_REG_R2H_CMD_ERR_RET 32 + +/* 0xF000: MFC_REG_FW_VERSION */ +#define MFC_REG_FW_VER_INFO_MASK 0xFF +#define MFC_REG_FW_VER_INFO_SHFT 24 +#define MFC_REG_FW_VER_YEAR_MASK 0xFF +#define MFC_REG_FW_VER_YEAR_SHFT 16 +#define MFC_REG_FW_VER_MONTH_MASK 0xFF +#define MFC_REG_FW_VER_MONTH_SHFT 8 +#define MFC_REG_FW_VER_DATE_MASK 0xFF +#define MFC_REG_FW_VER_DATE_SHFT 0 +#define MFC_REG_FW_VER_ALL_MASK 0xFFFFFF +#define MFC_REG_FW_VER_ALL_SHFT 0 + +/* 0xF00C: MFC_REG_CODEC_TYPE */ +#define MFC_FORMATS_NO_CODEC -1 +/* Decoder */ +#define MFC_REG_CODEC_H264_DEC 0 +#define MFC_REG_CODEC_H264_MVC_DEC 1 +#define MFC_REG_CODEC_MPEG4_DEC 3 +#define MFC_REG_CODEC_FIMV1_DEC 4 +#define MFC_REG_CODEC_FIMV2_DEC 5 +#define MFC_REG_CODEC_FIMV3_DEC 6 +#define MFC_REG_CODEC_FIMV4_DEC 7 +#define MFC_REG_CODEC_H263_DEC 8 +#define MFC_REG_CODEC_VC1_RCV_DEC 9 +#define MFC_REG_CODEC_VC1_DEC 10 +#define MFC_REG_CODEC_MPEG2_DEC 13 +#define MFC_REG_CODEC_VP8_DEC 14 +#define MFC_REG_CODEC_HEVC_DEC 17 +#define MFC_REG_CODEC_VP9_DEC 18 +#define MFC_REG_CODEC_AV1_DEC 19 +/* Encoder */ +#define MFC_REG_CODEC_H264_ENC 20 +#define MFC_REG_CODEC_H264_MVC_ENC 21 +#define MFC_REG_CODEC_MPEG4_ENC 23 +#define MFC_REG_CODEC_H263_ENC 24 +#define MFC_REG_CODEC_VP8_ENC 25 +#define MFC_REG_CODEC_HEVC_ENC 26 +#define MFC_REG_CODEC_VP9_ENC 27 + +#define MFC_REG_CODEC_BPG_DEC 32 +#define MFC_REG_CODEC_BPG_ENC 33 + +/* 0xF00C: MFC_REG_CODEC_TYPE */ +#define MFC_REG_CLEAR_CTX_MEM_SHIFT 16 +#define MFC_REG_CODEC_TYPE_MASK 0xFFFF + +/* 0xF024: MFC_REG_METADATA_ENABLE */ +#define MFC_REG_SEI_NAL_ENABLE_SHIFT 3 + +/* 0xF028: MFC_REG_MFC_VERSION */ +#define MFC_REG_MFC_VER_MASK 0xFFFFFFFF +#define MFC_REG_MFC_VER_SHFT 0 +/* bits of [8:4] can be changed if there is minor change between EVT0 and = EVT1. */ +#define MFC_REG_MFC_VER_MAJOR_MASK 0xFFFFFF0F +#define MFC_REG_MFC_VER_MINOR_MASK 0x000000F0 + +/* 0xF02C: MFC_REG_DBG_INFO_ENABLE */ +#define MFC_REG_DBG_INFO_ENABLE_SHIFT 0 +#define MFC_REG_DBG_INFO_DPB_CLEAR_SHIFT 1 +#define MFC_REG_DBG_INFO_TWO_MFC_FORCING_SHIFT 2 + +/* 0xF070: MFC_REG_RET_INSTANCE_ID */ +#define MFC_REG_RET_INSTANCE_ID_OF_MFC1_MASK 0xFFFF +#define MFC_REG_RET_INSTANCE_ID_OF_MFC1_SHIFT 16 +#define MFC_REG_RET_INSTANCE_ID_MASK 0xFFFF + +/* 0xF074: MFC_REG_ERROR_CODE */ +#define MFC_REG_ERR_STATUS_MASK 0xFFFF +#define MFC_REG_ERR_STATUS_SHIFT 0 +#define MFC_REG_WARN_STATUS_MASK 0xFFFF +#define MFC_REG_WARN_STATUS_SHIFT 16 + +/* 0xF07C: MFC_METADATA_STATUS */ +#define MFC_REG_SEI_NAL_STATUS_MASK 0x1 +#define MFC_REG_SEI_NAL_STATUS_SHIFT 3 + +/* Error number */ +#define MFC_REG_ERR_INVALID 9 +#define MFC_REG_ERR_BUFFER_FULL 18 +#define MFC_REG_ERR_WARNINGS_END 222 +/* Dump debug error */ +#define MFC_REG_ERR_NO_AVAILABLE_DPB 33 +#define MFC_REG_ERR_INSUFFICIENT_DPB_SIZE 57 +#define MFC_REG_ERR_INSUFFICIENT_NUM_DPB 58 +#define MFC_REG_ERR_INSUFFICIENT_MV_BUF_SIZE 60 +#define MFC_REG_ERR_INSUFFICIENT_SCRATCH_BUF_SIZE 62 +/* HEVC VPS header error */ +#define MFC_REG_ERR_VPS_ONLY_ERROR 42 +/* Input decoding error */ +#define MFC_REG_ERR_NO_KEY_FRAME 34 +#define MFC_REG_ERR_NO_VALID_SEQ_HDR 66 +#define MFC_REG_ERR_NO_VALID_PIC_HDR 67 +#define MFC_REG_ERR_MISSING_NON_BASE_VIEW_DETECTED 69 +#define MFC_REG_ERR_MISSING_BASE_VIEW_DETECTED 70 +#define MFC_REG_ERR_NO_VALID_REF_FOR_SKIP 72 +#define MFC_REG_ERR_UNSUPPORTED_FEATURE 100 +#define MFC_REG_ERR_UNSUPPORTED_RESOLUTION 101 +#define MFC_REG_ERR_HEADER_NOT_FOUND 102 +#define MFC_REG_ERR_INVALID_NAL_TYPE 103 +#define MFC_REG_ERR_SEQUENCE_HEADER_ERROR 104 +#define MFC_REG_ERR_PICTURE_HEADER_ERROR 105 +#define MFC_REG_ERR_SLICE_HEADER_ERROR 106 +#define MFC_REG_ERR_MISSING_FIRST_FIELD 108 +#define MFC_REG_ERR_SLICE_COUNT_IS_OVER_ASO 109 +#define MFC_REG_ERR_TILE_HEADER_ERROR 111 +#define MFC_REG_ERR_MAX_VIEW_NUM_OVER 112 +/* Timeout */ +#define MFC_REG_ERR_MFC_TIMEOUT 140 +#define MFC_REG_ERR_TS_MUX_TIMEOUT 141 +#define MFC_REG_ERR_G2D_TIMEOUT 142 +/* Exception */ +#define MFC_REG_ERR_UNDEFINED_EXCEPTION 145 +/* Frame concealment and warning */ +#define MFC_REG_ERR_FRAME_CONCEAL 150 +#define MFC_REG_ERR_BROKEN_LINK 161 +#define MFC_REG_ERR_SYNC_POINT_NOT_RECEIVED 190 +#define MFC_REG_ERR_NON_PAIRED_FIELD 191 + +/* 0xF0B4: MFC_REG_D_DEC_OPTIONS */ +#define MFC_REG_D_DEC_OPT_DISPLAY_DELAY_EN_SHIFT 3 +#define MFC_REG_D_DEC_OPT_FMO_ASO_CTRL_MASK 0x1 +#define MFC_REG_D_DEC_OPT_FMO_ASO_CTRL_SHIFT 4 +#define MFC_REG_D_DEC_OPT_IDR_DECODING_MASK 0x1 +#define MFC_REG_D_DEC_OPT_IDR_DECODING_SHIFT 6 +#define MFC_REG_D_DEC_OPT_DISCARD_RCV_HEADER_SHIFT 7 +#define MFC_REG_D_DEC_OPT_CONCEAL_CONTROL_SHIFT 8 +#define MFC_REG_D_DEC_OPT_PARALLEL_DISABLE_SHIFT 11 +#define MFC_REG_D_DEC_OPT_REALLOC_CONTROL_SHIFT 13 +#define MFC_REG_D_DEC_OPT_SPECIAL_PARSING_SHIFT 15 +#define MFC_REG_D_DEC_OPT_THUMBNAIL_DECODING 16 +#define MFC_REG_D_DEC_OPT_DECODING_ORDER_ENABLE 17 +#define MFC_REG_D_DEC_OPT_AV1_ANNEX_B_FORMAT_SHIFT 18 + +/* 0xF094: MFC_REG_NAL_QUEUE_INFO*/ +#define MFC_REG_NAL_QUEUE_EXE_NAL_LL_SHIFT 8 +#define MFC_REG_NAL_QUEUE_EXE_NAL_LL_MASK 0x7F +#define MFC_REG_NAL_QUEUE_OUT_CMD_COUNT_SHIFT 0 +#define MFC_REG_NAL_QUEUE_OUT_CMD_COUNT_MASK 0xFF + +/* 0xF0C4: MFC_REG_D_SEI_ENABLE */ +#define MFC_REG_D_SEI_ENABLE_NEED_INIT_BUFFER_SHIFT 1 +#define MFC_REG_D_SEI_ENABLE_RECOVERY_PARSING_SHIFT 2 +#define MFC_REG_D_SEI_ENABLE_CONTENT_LIGHT_SHIFT 4 +#define MFC_REG_D_SEI_ENABLE_MASTERING_DISPLAY_SHIFT 5 +#define MFC_REG_D_SEI_ENABLE_ST_2094_40_SEI_SHIFT 6 +#define MFC_REG_D_SEI_ENABLE_FILM_GRAIN_SHIFT 7 + +/* 0xF154: MFC_REG_D_INIT_BUFFER_OPTIONS */ +#define MFC_REG_D_INIT_BUF_OPT_LF_CTRL_MASK 0x3 +#define MFC_REG_D_INIT_BUF_OPT_LF_CTRL_SHIFT 1 +#define MFC_REG_D_INIT_BUF_OPT_DYNAMIC_DPB_SET_SHIFT 3 +#define MFC_REG_D_INIT_BUF_OPT_COPY_NOT_CODED_SHIFT 4 +#define MFC_REG_D_INIT_BUF_OPT_DITHERING_EN_SHIFT 6 +#define MFC_REG_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN 7 +#define MFC_REG_D_INIT_BUF_OPT_TWO_MODE_ENABLE_SHIFT 9 +#define MFC_REG_D_INIT_BUF_OPT_ENABLE_DECODE_VIEW1 10 + +/* 0xF5AC: MFC_REG_D_NAL_START_OPTIONS */ +#define MFC_REG_D_NAL_START_OPT_DIS_COMPRESSOR_SHIFT 5 +#define MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT 4 +#define MFC_REG_D_NAL_START_OPT_BLACK_BAR_SHIFT 3 +#define MFC_REG_D_NAL_START_OPT_NEW_SCRATCH_SHIFT 2 +#define MFC_REG_D_NAL_START_OPT_NEW_DPB_SHIFT 1 + +/* 0xF608: MFC_REG_D_DISPLAY_STATUS */ +#define MFC_REG_DISP_STATUS_DISPLAY_STATUS_MASK 0x7 +#define MFC_REG_DISP_STATUS_INTERLACE_MASK 0x1 +#define MFC_REG_DISP_STATUS_INTERLACE_SHIFT 3 +#define MFC_REG_DISP_STATUS_RES_CHANGE_MASK 0x3 +#define MFC_REG_DISP_STATUS_RES_CHANGE_SHIFT 4 +#define MFC_REG_DISP_STATUS_NEED_DPB_CHANGE_MASK 0x1 +#define MFC_REG_DISP_STATUS_NEED_DPB_CHANGE_SHIFT 9 +#define MFC_REG_DISP_STATUS_NEED_SCRATCH_CHANGE_MASK 0x1 +#define MFC_REG_DISP_STATUS_NEED_SCRATCH_CHANGE_SHIFT 10 +#define MFC_REG_DISP_STATUS_LAST_DISPLAY_FRAME_MASK 0x1 +#define MFC_REG_DISP_STATUS_LAST_DISPLAY_FRAME_SHIFT 11 +#define MFC_REG_DISP_STATUS_NEED_EMPTY_DPB_MASK 0x1 +#define MFC_REG_DISP_STATUS_NEED_EMPTY_DPB_SHIFT 12 +#define MFC_REG_DISP_STATUS_BLACK_BAR_DETECT_MASK 0x3 +#define MFC_REG_DISP_STATUS_BLACK_BAR_DETECT_SHIFT 13 +#define MFC_REG_DISP_STATUS_NOT_DETECTED 0x0 +#define MFC_REG_DISP_STATUS_BLACK_BAR 0x1 +#define MFC_REG_DISP_STATUS_BLACK_SCREEN 0x2 +#define MFC_REG_DISP_STATUS_COMP_SHIFT 19 +#define MFC_REG_DISP_STATUS_COMP_MASK 0x1 +#define MFC_REG_DISP_STATUS_UNCOMP_SHIFT 21 +#define MFC_REG_DISP_STATUS_UNCOMP_MASK 0x1 +#define MFC_REG_DISP_STATUS_FIELD_SHIFT 22 +#define MFC_REG_DISP_STATUS_FIELD_MASK 0x1 + +/* 0xF618: MFC_REG_D_DISPLAY_FRAME_TYPE */ +#define MFC_REG_DISPLAY_TEMP_INFO_MASK 0x1 +#define MFC_REG_DISPLAY_TEMP_INFO_SHIFT 7 +#define MFC_REG_DISPLAY_IDR_FLAG_MASK 0x1 +#define MFC_REG_DISPLAY_IDR_FLAG_SHIFT 6 +#define MFC_REG_DISPLAY_FRAME_MASK 0x7 +#define MFC_REG_DISPLAY_FRAME_NOT_CODED 0 +#define MFC_REG_DISPLAY_FRAME_I 1 +#define MFC_REG_DISPLAY_FRAME_P 2 +#define MFC_REG_DISPLAY_FRAME_B 3 + +/* 0xF61C: MFC_REG_D_DISPLAY_CROP_INFO1 */ +#define MFC_REG_D_SHARED_CROP_LEFT_MASK 0xFFFF +#define MFC_REG_D_SHARED_CROP_RIGHT_SHIFT 16 + +/* 0xF620: MFC_REG_D_DISPLAY_CROP_INFO2 */ +#define MFC_REG_D_SHARED_CROP_TOP_MASK 0xFFFF +#define MFC_REG_D_SHARED_CROP_BOTTOM_SHIFT 16 + +/* 0xF644: MFC_REG_D_DECODED_STATUS */ +#define MFC_REG_DEC_STATUS_DECODED_STATUS_MASK 0x7 +#define MFC_REG_DEC_STATUS_DECODING_ONLY 0 +#define MFC_REG_DEC_STATUS_DECODING_DISPLAY 1 +#define MFC_REG_DEC_STATUS_DISPLAY_ONLY 2 +#define MFC_REG_DEC_STATUS_DECODING_EMPTY 3 +#define MFC_REG_DEC_STATUS_NUM_OF_TILE_MASK 0xF +#define MFC_REG_DEC_STATUS_NUM_OF_TILE_SHIFT 15 +#define MFC_REG_DEC_STATUS_INTERLACE_MASK 0x1 +#define MFC_REG_DEC_STATUS_INTERLACE_SHIFT 3 +#define MFC_REG_DEC_STATUS_UNCOMP_SHIFT 21 +#define MFC_REG_DEC_STATUS_UNCOMP_MASK 0x1 + +/* 0xF654: MFC_REG_D_DECODED_FRAME_TYPE */ +#define MFC_REG_DECODED_IDR_FLAG_MASK 0x1 +#define MFC_REG_DECODED_IDR_FLAG_SHIFT 3 +#define MFC_REG_DECODED_FRAME_MASK 0x7 +#define MFC_REG_DECODED_FRAME_NOT_CODED 0 +#define MFC_REG_DECODED_FRAME_I 1 +#define MFC_REG_DECODED_FRAME_P 2 +#define MFC_REG_DECODED_FRAME_B 3 + +/* 0xF660: MFC_REG_D_DECODED_PICTURE_PROFILE */ +#define MFC_REG_D_TWO_MFC_MODE_MASK 0x3 +#define MFC_REG_D_TWO_MFC_MODE_SHIFT 28 +#define MFC_REG_D_DISPLAY_DELAY_MASK 0x1F +#define MFC_REG_D_DISPLAY_DELAY_SHIFT 23 +#define MFC_REG_D_BIT_DEPTH_CHROMA_MINUS8_MASK 0x7 +#define MFC_REG_D_BIT_DEPTH_CHROMA_MINUS8_SHIFT 19 +#define MFC_REG_D_BIT_DEPTH_LUMA_MINUS8_MASK 0x7 +#define MFC_REG_D_BIT_DEPTH_LUMA_MINUS8_SHIFT 16 +#define MFC_REG_D_PIC_LEVEL_MASK 0xFF +#define MFC_REG_D_PIC_LEVEL_SHIFT 8 +#define MFC_REG_D_DECODED_PIC_PROFILE_MASK 0x1F +#define MFC_REG_D_PROFILE_HEVC_MAIN 1 +#define MFC_REG_D_PROFILE_HEVC_MAIN_10 2 +#define MFC_REG_D_PROFILE_MULTIVIEW_HEVC_MAIN 5 +#define MFC_REG_D_PROFILE_MULTIVIEW_HEVC_MAIN_10 6 +#define MFC_REG_D_PROFILE_HEVC_RANGE_EXT 4 + +/* 0xF684: MFC_REG_D_CHROMA_FORMAT */ +#define MFC_REG_D_CHROMA_FORMAT_MASK 0x3 +#define MFC_REG_D_COLOR_RANGE_MASK 0x1 +#define MFC_REG_D_COLOR_RANGE_SHIFT 3 +#define MFC_REG_D_COLOR_SPACE_MASK 0xF +#define MFC_REG_D_COLOR_SPACE_SHIFT 4 +#define MFC_REG_D_COLOR_UNKNOWN 0 +#define MFC_REG_D_CHROMA_400 0 +#define MFC_REG_D_CHROMA_420 1 +#define MFC_REG_D_CHROMA_422 2 +#define MFC_REG_D_CHROMA_444 3 + +/* 0xF690: MFC_REG_D_H264_INFO */ +#define MFC_REG_D_H264_INFO_MBAFF_FRAME_FLAG_SHIFT 9 +#define MFC_REG_D_H264_INFO_MBAFF_FRAME_FLAG_MASK 0x1 +#define MFC_REG_D_H264_INFO_TEMPORAL_ID_SHIFT 6 +#define MFC_REG_D_H264_INFO_TEMPORAL_ID_MASK 0x7 + +/* 0xF6A0: MFC_REG_D_HEVC_INFO */ +#define MFC_REG_D_HEVC_INFO_PIC_OUTPUT_FLAG_SHIFT 11 +#define MFC_REG_D_HEVC_INFO_PIC_OUTPUT_FLAG_MASK 0x1 +#define MFC_REG_D_HEVC_INFO_LCU_SIZE_MASK 0x3 + +/* 0xF6A4: MFC_REG_D_VP9_INFO */ +#define MFC_REG_D_VP9_INFO_DISP_RES_SHIFT 14 +#define MFC_REG_D_VP9_INFO_DISP_RES_MASK 0x1 + +/* 0xF6AC: MFC_REG_D_AV1_INFO */ +#define MFC_REG_D_AV1_INFO_FILMGRAIN_PRESENT_SHIFT 18 +#define MFC_REG_D_AV1_INFO_FILMGRAIN_PRESENT_MASK 0x1 +#define MFC_REG_D_AV1_INFO_MULTIPLE_SHOW_SHIFT 17 +#define MFC_REG_D_AV1_INFO_MULTIPLE_SHOW_MASK 0x1 +#define MFC_REG_D_AV1_INFO_DISP_RES_SHIFT 14 +#define MFC_REG_D_AV1_INFO_DISP_RES_MASK 0x1 +#define MFC_REG_D_AV1_INFO_SHOWABLE_FRAME_SHIFT 5 +#define MFC_REG_D_AV1_INFO_SHOWABLE_FRAME_MASK 0x1 + +/* 0xF6D8: MFC_REG_D_MVC_VIEW_ID */ +#define MFC_REG_D_MVC_VIEW_ID_DEC_SHIFT 16 +#define MFC_REG_D_MVC_VIEW_ID_DEC_MASK 0xFFFF +#define MFC_REG_D_MVC_VIEW_ID_DISP_MASK 0xFFFF +#define MFC_REG_D_MVC_LEFT_VIEW_ID_SHIFT 28 +#define MFC_REG_D_MVC_LEFT_VIEW_ID_MASK 0x3 +#define MFC_REG_D_MVC_RIGHT_VIEW_ID_SHIFT 30 +#define MFC_REG_D_MVC_RIGHT_VIEW_ID_MASK 0x3 + +/* 0xF6DC: MFC_REG_D_SEI_AVAIL */ +#define MFC_REG_D_SEI_AVAIL_FRAME_PACK_MASK 0x1 +#define MFC_REG_D_SEI_AVAIL_CONTENT_LIGHT_MASK 0x1 +#define MFC_REG_D_SEI_AVAIL_CONTENT_LIGHT_SHIFT 1 +#define MFC_REG_D_SEI_AVAIL_MASTERING_DISPLAY_MASK 0x1 +#define MFC_REG_D_SEI_AVAIL_MASTERING_DISPLAY_SHIFT 2 +#define MFC_REG_D_SEI_AVAIL_ST_2094_40_MASK 0x1 +#define MFC_REG_D_SEI_AVAIL_ST_2094_40_SHIFT 3 +#define MFC_REG_D_SEI_AVAIL_FILM_GRAIN_MASK 0x1 +#define MFC_REG_D_SEI_AVAIL_FILM_GRAIN_SHIFT 4 + +/* 0xF70C: MFC_REG_D_VIDEO_SIGNAL_TYPE */ +#define MFC_REG_D_VIDEO_SIGNAL_TYPE_FLAG_MASK 0x1 +#define MFC_REG_D_VIDEO_SIGNAL_TYPE_FLAG_SHIFT 29 +#define MFC_REG_D_COLOUR_DESCRIPTION_FLAG_MASK 0x1 +#define MFC_REG_D_COLOUR_DESCRIPTION_FLAG_SHIFT 24 +#define MFC_REG_D_COLOUR_PRIMARIES_MASK 0xFF +#define MFC_REG_D_COLOUR_PRIMARIES_SHIFT 16 +#define MFC_REG_D_TRANSFER_CHARACTERISTICS_MASK 0xFF +#define MFC_REG_D_TRANSFER_CHARACTERISTICS_SHIFT 8 +#define MFC_REG_D_MATRIX_COEFFICIENTS_MASK 0xFF +#define MFC_REG_D_MATRIX_COEFFICIENTS_SHIFT 0 + +/* 0xF738: MFC_REG_D_BLACK_BAR_START_POS */ +#define MFC_REG_D_BLACK_BAR_START_X_SHIFT 0 +#define MFC_REG_D_BLACK_BAR_START_X_MASK 0xFFFF +#define MFC_REG_D_BLACK_BAR_START_Y_SHIFT 16 +#define MFC_REG_D_BLACK_BAR_START_Y_MASK 0xFFFF + +/* 0xF73C: MFC_REG_D_BLACK_BAR_IMAGE_SIZE */ +#define MFC_REG_D_BLACK_BAR_IMAGE_W_SHIFT 0 +#define MFC_REG_D_BLACK_BAR_IMAGE_W_MASK 0xFFFF +#define MFC_REG_D_BLACK_BAR_IMAGE_H_SHIFT 16 +#define MFC_REG_D_BLACK_BAR_IMAGE_H_MASK 0xFFFF + +/* 0xF780: MFC_REG_E_FRAME_CROP_OFFSET */ +#define MFC_REG_E_FRAME_CROP_OFFSET_TOP 16 +#define MFC_REG_E_FRAME_CROP_OFFSET_LEFT 0 +#define MFC_REG_E_FRAME_CROP_OFFSET_MASK 0x3FFF + +/* 0xF788: MFC_REG_E_PICTURE_PROFILE */ +#define MFC_REG_E_PROFILE_H264_BASELINE 0 +#define MFC_REG_E_PROFILE_H264_MAIN 1 +#define MFC_REG_E_PROFILE_H264_HIGH 2 +#define MFC_REG_E_PROFILE_H264_CONSTRAINED_BASELINE 3 +#define MFC_REG_E_PROFILE_H264_CONSTRAINED_HIGH 5 +#define MFC_REG_E_PROFILE_MPEG4_SIMPLE 0 +#define MFC_REG_E_PROFILE_MPEG4_ADVANCED_SIMPLE 1 +#define MFC_REG_E_PROFILE_HEVC_MAIN 0 +#define MFC_REG_E_PROFILE_HEVC_MAIN_422_10_INTRA 2 +#define MFC_REG_E_PROFILE_HEVC_MAIN_10 3 +#define MFC_REG_E_PROFILE_HEVC_MAIN_422_10 4 +#define MFC_REG_E_PROFILE_VP9_PROFILE0 0 +#define MFC_REG_E_PROFILE_VP9_PROFILE1 1 +#define MFC_REG_E_PROFILE_VP9_PROFILE2 2 +#define MFC_REG_E_PROFILE_VP9_PROFILE3 3 + +/* 0xF7A4: MFC_REG_E_RC_MODE */ +#define MFC_REG_E_RC_CBR_FIX 0 +#define MFC_REG_E_RC_CBR_VAR 1 +#define MFC_REG_E_RC_VBR 2 +#define MFC_REG_E_RC_CBR_I_LIMIT_VT 3 +#define MFC_REG_E_RC_VBR_BS 4 +#define MFC_REG_E_RC_CBR_I_LIMIT_WFD 5 + +/* 0xFA84: MFC_REG_E_SLICE_TYPE */ +#define MFC_REG_E_SLICE_TYPE_NOT_CODED 0 +#define MFC_REG_E_SLICE_TYPE_MASK 0xF +#define MFC_REG_E_SLICE_TYPE_I 1 +#define MFC_REG_E_SLICE_TYPE_P 2 +#define MFC_REG_E_SLICE_TYPE_B 3 +#define MFC_REG_E_SLICE_TYPE_SKIPPED 4 + +/* 0xFAE8: MFC_REG_E_HDR10_PLUS_INFO */ +#define MFC_REG_E_HDR10_PLUS_INFO_STAT_DONE_MASK 1 +#define MFC_REG_E_HDR10_PLUS_INFO_STAT_DONE_SHIFT 31 +#define MFC_REG_E_HDR10_PLUS_INFO_SEI_SIZE_MASK 0x1FF +#define MFC_REG_E_HDR10_PLUS_INFO_SEI_SIZE_SHIFT 8 +#define MFC_REG_E_HDR10_PLUS_INFO_OFFSET_MASK 0xFF +#define MFC_REG_E_HDR10_PLUS_INFO_OFFSET_SHIFT 0 + +/* 0xFAEC: MFC_REG_E_NAL_DONE_INFO */ +#define MFC_REG_E_NAL_DONE_INFO_COMP_ERR_MASK 0x3 +#define MFC_REG_E_NAL_DONE_INFO_COMP_ERR_SHIFT 7 +#define MFC_REG_E_NAL_DONE_INFO_IDR_MASK 0x1 +#define MFC_REG_E_NAL_DONE_INFO_IDR_SHIFT 20 + +/* SEI Metadata format */ +#define MFC_META_SEI_NAL_SIZE_OFFSET 0x4 +#define MFC_META_SEI_NAL_PAYLOAD_OFFSET 0x8 + +#endif /* __MFC_REGS_MFC_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) (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 030112586C9 for ; Tue, 30 Sep 2025 03:55:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204545; cv=none; b=RRqyUK/wwbgmZiioZSlK2PB+nnr+OM0t28iwtjfraHt8Bp8vHMIahS5WiNm+/EGnf6tFsBTSOWG+Q1p9RmHjckzR5I3FLBdjMxLOAMnlBrusuvIXQzVYSAPRgceyCyjDfMVjVbXvjDIEq9xDWDwN1UBQHSJU6q1Z8aF9vtocYa8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204545; c=relaxed/simple; bh=zODiJauaLwEWhpCV+wp0iprnabgbOYzG2vwpZCyqdpg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=ZDjaXtr7G1Jx+VV9q4GmG2H/5z1eusQmdjNMPVLaqPh3UJhKv1OYeM8Y4nc/cMWOGSrs/M31dOZuIsRHxF52LB2qcA3lGCA5vLPIs01y67G1ID6gxAtQ1hoWg+k9ots7dG0XGk1sZBmJHIedfl/7i04Q+ebPHPGNDFxXYfAjdfA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=kTpteuLV; arc=none smtp.client-ip=203.254.224.24 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="kTpteuLV" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250930035538epoutp011000f88e46c66a0dc6c2b758df2c8c03~p8zT7Tx2x3202532025epoutp01E for ; Tue, 30 Sep 2025 03:55:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250930035538epoutp011000f88e46c66a0dc6c2b758df2c8c03~p8zT7Tx2x3202532025epoutp01E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204538; bh=4Xu/MsBJmxCSwDpo2xSTI4a7uCG2vamd7m4a7sW0ahg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kTpteuLVAiT5RcJFWp6jWj5ITKiXH4hE30uQ5AYXD5FMBrVsc67lPfrd7bbSviWll glue86eN0WWWwYNtYYjOJeXpK6Jzcb5he9Q+tMpFEQDPY9LfGUyC1u8NMnMVfhe0gq G7IdLXd/RPK4QEFlyPj84mwU4ALU5rPDHGhC0y0A= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPS id 20250930035537epcas5p366a0f47db2a7c46dabb0454c223c59f5~p8zTATxwU3089530895epcas5p3W; Tue, 30 Sep 2025 03:55:37 +0000 (GMT) Received: from epcas5p4.samsung.com (unknown [182.195.38.88]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPNS2xgpz6B9mD; Tue, 30 Sep 2025 03:55:36 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035535epcas5p3def6efaf88b04542ab6c27ecaa8e86fd~p8zRfemcp3089530895epcas5p3P; Tue, 30 Sep 2025 03:55:35 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035531epsmtip1a50adf3bee592bffd132b68f4263ba67~p8zN24WLU2938529385epsmtip1H; Tue, 30 Sep 2025 03:55:31 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 05/29] media: mfc: Add MFC driver header files and core utilities Date: Tue, 30 Sep 2025 09:33:24 +0530 Message-Id: <20250930040348.3702923-6-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035535epcas5p3def6efaf88b04542ab6c27ecaa8e86fd X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035535epcas5p3def6efaf88b04542ab6c27ecaa8e86fd References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce new driver headers: mfc_buf.h, mfc_common.h, mfc_media.h, mfc_mem.h, mfc_regs.h, mfc_sched.h, and mfc_utils.h. mfc_buf.h Define buffer allocation/release APIs, common driver constants, video node identifiers, and feature=E2=80=91specific V4L2 control IDs. mfc_common.h Define driver metadata, node IDs, timeout values, format flags, error/warning macros, and helper macros for buffer/context conversion mfc_media.h Expand V4L2 control IDs for a wide range of codec features (HEVC, VP8/9, AV1, HDR, ROI, etc.) mfc_mem.h Implement memory management helpers for DMA buffers, IOVA pool handling, and firmware mapping. mfc_regs.h Add register access macros for core, MMU, and PMU registers, handling both 32=E2=80=91bit and 64=E2=80=91bit DMA address spaces. mfc_sched.h Supply scheduler helper for priority calculation and core initialization. Supply utility inline functions for state changes, interrupt flag handling, idle checking, and tracing. mfc_utils.h Provide inline utility functions for clearing interrupt flags, state changes, operation=E2=80=91mode updates, node=E2=80=91type detection,= and idle=E2=80=91checker management. These changes collectively lay the groundwork for the Samsung MFC (Multi=E2=80=91Format Codec) driver, enabling buffer handling, register acc= ess, and core scheduling functionalities. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_buf.c | 289 ++++++++ .../samsung/exynos-mfc/base/mfc_buf.h | 32 + .../samsung/exynos-mfc/base/mfc_common.h | 198 ++++++ .../samsung/exynos-mfc/base/mfc_media.h | 554 ++++++++++++++++ .../samsung/exynos-mfc/base/mfc_mem.c | 618 ++++++++++++++++++ .../samsung/exynos-mfc/base/mfc_mem.h | 80 +++ .../samsung/exynos-mfc/base/mfc_regs.h | 58 ++ .../samsung/exynos-mfc/base/mfc_sched.h | 30 + .../samsung/exynos-mfc/base/mfc_utils.h | 168 +++++ 9 files changed, 2027 insertions(+) create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_comm= on.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_medi= a.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_regs= .h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_sche= d.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_util= s.h diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.c new file mode 100644 index 000000000000..b8b140824aab --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2010-2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_buf.c file + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * nagaraju siddineni, + */ +#include +#include + +#include "mfc_buf.h" + +#include "mfc_mem.h" + +#include "mfc_utils.h" + +/* Release context buffers for SYS_INIT */ +static void __mfc_release_common_context(struct mfc_core *core) +{ + struct mfc_special_buf *ctx_buf; + + ctx_buf =3D &core->common_ctx_buf; + + mfc_iova_pool_free(core->dev, ctx_buf); + + mfc_mem_special_buf_free(core->dev, ctx_buf); + + ctx_buf->dma_buf =3D NULL; + ctx_buf->vaddr =3D NULL; + ctx_buf->daddr =3D 0; +} + +/* Release context buffers for SYS_INIT */ +void mfc_release_common_context(struct mfc_core *core) +{ + if (core->fw.status & MFC_CTX_ALLOC) { + __mfc_release_common_context(core); + mfc_core_change_fw_state(core, MFC_CTX_ALLOC, 0); + } +} + +static int __mfc_alloc_common_context(struct mfc_core *core) +{ + struct mfc_dev *dev =3D core->dev; + struct mfc_special_buf *ctx_buf; + struct mfc_ctx_buf_size *buf_size; + + mfc_core_debug_enter(); + + ctx_buf =3D &core->common_ctx_buf; + ctx_buf->buftype =3D MFCBUF_NORMAL; + + buf_size =3D dev->variant->buf_size->ctx_buf; + ctx_buf->size =3D buf_size->dev_ctx; + + snprintf(ctx_buf->name, MFC_NUM_SPECIAL_BUF_NAME, "MFC%d common context",= core->id); + if (mfc_mem_special_buf_alloc(dev, ctx_buf)) { + mfc_core_err("Allocating context buffer failed\n"); + return -ENOMEM; + } + + if (mfc_iova_pool_alloc(dev, ctx_buf)) { + mfc_core_err("[POOL] failed to get iova\n"); + __mfc_release_common_context(core); + return -ENOMEM; + } + + mfc_core_debug_leave(); + + return 0; +} + +/* Wrapper : allocate context buffers for SYS_INIT */ +int mfc_alloc_common_context(struct mfc_core *core) +{ + int ret =3D 0; + + ret =3D __mfc_alloc_common_context(core); + if (ret) + return ret; + mfc_core_change_fw_state(core, MFC_CTX_ALLOC, 1); + + return ret; +} + +/* Allocation buffer of debug infor memory for FW debugging */ +int mfc_alloc_dbg_info_buffer(struct mfc_core *core) +{ + struct mfc_dev *dev =3D core->dev; + struct mfc_ctx_buf_size *buf_size =3D dev->variant->buf_size->ctx_buf; + + mfc_core_debug(2, "Allocate a debug-info buffer\n"); + + core->dbg_info_buf.buftype =3D MFCBUF_NORMAL; + core->dbg_info_buf.size =3D buf_size->dbg_info_buf; + snprintf(core->dbg_info_buf.name, MFC_NUM_SPECIAL_BUF_NAME, "MFC%d debug"= , core->id); + if (mfc_mem_special_buf_alloc(dev, &core->dbg_info_buf)) { + mfc_core_err("Allocating debug info buffer failed\n"); + return -ENOMEM; + } + + if (mfc_iova_pool_alloc(dev, &core->dbg_info_buf)) { + mfc_core_err("[POOL] failed to get iova\n"); + mfc_release_dbg_info_buffer(core); + return -ENOMEM; + } + + return 0; +} + +/* Release buffer of debug infor memory for FW debugging */ +void mfc_release_dbg_info_buffer(struct mfc_core *core) +{ + if (!core->dbg_info_buf.dma_buf) + mfc_core_debug(2, "debug info buffer is already freed\n"); + + mfc_iova_pool_free(core->dev, &core->dbg_info_buf); + + mfc_mem_special_buf_free(core->dev, &core->dbg_info_buf); +} + +/* Allocate firmware */ +int mfc_alloc_firmware(struct mfc_core *core) +{ + struct mfc_dev *dev =3D core->dev; + + mfc_core_debug_enter(); + + if (core->fw_buf.sgt) + return 0; + + mfc_core_debug(4, "[F/W] Allocating memory for firmware\n"); + + if (dev->pdata->fw_mem_size) + core->fw_buf.size =3D dev->pdata->fw_mem_size; + else + core->fw_buf.size =3D dev->variant->buf_size->firmware_code; + core->fw_buf.buftype =3D MFCBUF_NORMAL_FW; + + strscpy(core->fw_buf.name, core->name, sizeof(core->fw_buf.name)); + if (mfc_mem_special_buf_alloc(dev, &core->fw_buf)) { + mfc_core_err("[F/W] Allocating normal firmware buffer failed\n"); + return -ENOMEM; + } + +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + if (mfc_iommu_map_firmware(core, &core->fw_buf)) + goto err_reserve_iova; +#endif + mfc_core_change_fw_state(core, MFC_FW_ALLOC, 1); + + mfc_core_debug_leave(); + + return 0; + +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) +err_reserve_iova: + mfc_core_change_fw_state(core, MFC_FW_ALLOC, 0); + iommu_unmap(core->domain, fw_buf->daddr, fw_buf->map_size); + mfc_mem_special_buf_free(dev, &core->fw_buf); + return -ENOMEM; +#endif +} + +/* Load firmware to MFC */ +int mfc_load_firmware(struct mfc_core *core, + struct mfc_special_buf *fw_buf, + const u8 *fw_data, + size_t fw_size) +{ + mfc_core_debug(2, + "[MEMINFO][F/W] loaded %s F/W Size: %zu\n", + fw_buf->buftype =3D=3D MFCBUF_NORMAL_FW ? "normal" : "secure", + fw_size); + + if (fw_size > fw_buf->size) { + mfc_core_err + ("[MEMINFO][F/W] MFC firmware(%zu) is too big to be loaded in memory(%zu= )\n", + fw_size, + fw_buf->size); + return -ENOMEM; + } + + core->fw.fw_size =3D fw_size; + + if (fw_buf->daddr =3D=3D 0) { + mfc_core_err("[F/W] MFC firmware is not allocated\n"); + return -EINVAL; + } +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + if (!fw_buf->sgt) { + mfc_core_err("[F/W] MFC firmware was not mapped correctly\n"); + return -EINVAL; + } +#endif + /* This adds to clear with '0' for firmware memory except code region. */ + mfc_core_debug(4, + "[F/W] memset before memcpy for %s fw\n", + fw_buf->buftype =3D=3D MFCBUF_NORMAL_FW ? "normal" : "secure"); + memset((fw_buf->vaddr + fw_size), 0, (fw_buf->size - fw_size)); + memcpy(fw_buf->vaddr, fw_data, fw_size); + + /* cache flush for memcpy by CPU */ +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + dma_sync_sgtable_for_device(core->device, fw_buf->sgt, DMA_TO_DEVICE); +#else + dma_sync_single_for_device(core->device, fw_buf->paddr, fw_buf->size, DMA= _TO_DEVICE); +#endif + mfc_core_debug(4, + "[F/W] cache flush for %s FW region\n", + fw_buf->buftype =3D=3D MFCBUF_NORMAL_FW ? "normal" : "secure"); + + mfc_core_change_fw_state(core, MFC_FW_LOADED, 1); + + if (core->dev->debugfs.sfr_dump & MFC_DUMP_FIRMWARE) { + print_hex_dump(KERN_ERR, + "FW dump ", + DUMP_PREFIX_OFFSET, + 32, + 1, + fw_buf->vaddr, + 0x200, + false); + mfc_core_info("......\n"); + print_hex_dump(KERN_ERR, + "FW dump + 0xffe00 ", + DUMP_PREFIX_OFFSET, + 32, + 1, + fw_buf->vaddr + 0xffe00, + 0x200, + false); + } + + return 0; +} + +/* Request and load firmware to MFC */ +int mfc_request_load_firmware(struct mfc_core *core, struct mfc_special_bu= f *fw_buf) +{ + const struct firmware *fw_blob; + int ret; + + mfc_core_debug_enter(); + + mfc_core_debug(4, + "[F/W] Requesting %s F/W\n", + fw_buf->buftype =3D=3D MFCBUF_NORMAL_FW ? "normal" : "secure"); + + ret =3D request_firmware(&fw_blob, core->dev->pdata->fw_name, core->dev->= v4l2_dev.dev); + if (ret !=3D 0) { + mfc_core_err("[F/W] Couldn't find the F/W invalid path\n"); + return ret; + } + + ret =3D mfc_load_firmware(core, fw_buf, fw_blob->data, fw_blob->size); + if (ret) { + mfc_core_err("[F/W] Failed to load the MFC F/W\n"); + release_firmware(fw_blob); + return ret; + } + + release_firmware(fw_blob); + + mfc_core_debug_leave(); + + return 0; +} + +/* Release firmware memory */ +int mfc_release_firmware(struct mfc_core *core) +{ +/* Before calling this function one has to make sure + * that MFC is no longer processing + */ +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + if (core->fw_buf.daddr) + iommu_unmap(core->domain, core->fw_buf.daddr, core->fw_buf.map_size); +#endif + + mfc_mem_special_buf_free(core->dev, &core->fw_buf); + + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.h new file mode 100644 index 000000000000..8291e043b81a --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_buf.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_BUF_H +#define __MFC_BUF_H __FILE__ + +#include "mfc_common.h" + +/* Memory allocation */ +void mfc_release_common_context(struct mfc_core *core); +int mfc_alloc_common_context(struct mfc_core *core); + +int mfc_alloc_firmware(struct mfc_core *core); +int mfc_load_firmware(struct mfc_core *core, + struct mfc_special_buf *fw_buf, + const u8 *fw_data, + size_t fw_size); +int mfc_request_load_firmware(struct mfc_core *core, struct mfc_special_bu= f *fw_buf); +int mfc_release_firmware(struct mfc_core *core); + +int mfc_alloc_dbg_info_buffer(struct mfc_core *core); +void mfc_release_dbg_info_buffer(struct mfc_core *core); + +#endif /* __MFC_BUF_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h new file mode 100644 index 000000000000..4a1ec714fbb5 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_common.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_COMMON_H +#define __MFC_COMMON_H __FILE__ + +#include +#include +#include +#include +#include +#include +#include "mfc_data_struct.h" +#include "mfc_regs.h" +#include "mfc_debug.h" +#include "mfc_media.h" + +#define MFC_DRIVER_INFO 230330 + +#define MFC_MAX_REF_BUFS 2 +#define MFC_FRAME_PLANES 2 +#define MFC_INFO_INIT_FD -1 +#define MFC_FW_MEM_SIZE SZ_1M /* 1MB */ + +/* MFC video node */ +#define EXYNOS_VIDEONODE_MFC_DEC 6 +#define EXYNOS_VIDEONODE_MFC_ENC 7 +#define EXYNOS_VIDEONODE_MFC_DEC_DRM 8 +#define EXYNOS_VIDEONODE_MFC_ENC_DRM 9 +#define EXYNOS_VIDEONODE_MFC_ENC_OTF 10 +#define EXYNOS_VIDEONODE_MFC_ENC_OTF_DRM 11 + +/* Core information */ +#define MFC_DEC_DEFAULT_CORE 0 +#define MFC_ENC_DEFAULT_CORE 0 +#define MFC_SURPLUS_CORE 1 + +/* Interrupt timeout */ +#define MFC_INT_TIMEOUT 4000 +/* Interrupt short timeout */ +#define MFC_INT_SHORT_TIMEOUT 800 +/* hwlock timeout */ +#define MFC_HWLOCK_TIMEOUT 12000 +/* Busy wait timeout */ +#define MFC_BW_TIMEOUT 500 + +/* Interrupt timeout count*/ +#define MFC_INT_TIMEOUT_CNT 3 + +/* + * This value guarantees 299.4msec ~ 2.25sec according to MFC clock (668MH= z ~ 89MHz) + * related with MFC_REG_TIMEOUT_VALUE + */ +#define MFC_TIMEOUT_VALUE 200000000 +/* 250ms is the mfc firmware timeout value*/ +#define MFC_TIMEOUT_VALUE_IN_MSEC 250 + +#define DEFAULT_TAG (0xE05) +#define IGNORE_TAG (0xD5C) /* ex) encoder DRC */ +#define HEADER_TAG (0xC5D) +#define UNUSED_TAG (-1) + +#define MFC_NO_INSTANCE_SET -1 + +#define STUFF_BYTE 4 +#define MFC_EXTRA_DPB 5 +#define MFC_ALL_AVAILABLE_DPB 0xffffffffffffffff + +#define MFC_BASE_MASK (BIT(17) - 1) + +/* Error & Warning */ +#define mfc_get_err(x) (((x) >> MFC_REG_ERR_STATUS_SHIFT) \ + & MFC_REG_ERR_STATUS_MASK) +#define mfc_get_warn(x) (((x) >> MFC_REG_WARN_STATUS_SHIFT) \ + & MFC_REG_WARN_STATUS_MASK) + +#define vb_to_mfc_buf(x) \ + container_of(x, struct mfc_buf, vb.vb2_buf) + +#define fh_to_mfc_ctx(x) \ + container_of(x, struct mfc_ctx, fh) + +#define MFC_FMT_STREAM BIT(0) +#define MFC_FMT_FRAME BIT(1) +#define MFC_FMT_10BIT BIT(2) +#define MFC_FMT_422 BIT(3) +#define MFC_FMT_RGB BIT(4) +#define MFC_FMT_SBWC BIT(5) +#define MFC_FMT_SBWCL BIT(6) +#define MFC_FMT_DEC BIT(7) +#define MFC_FMT_ENC BIT(8) +#define MFC_FMT_SBWCLH BIT(9) +#define MFC_FMT_SBWCLHFR BIT(10) + +/* node check */ +#define IS_DEC_NODE(n) ({ \ + typeof(n) _n =3D (n); \ + ((_n =3D=3D EXYNOS_VIDEONODE_MFC_DEC) || (_n =3D=3D EXYNOS_VIDEONODE_MFC_= DEC_DRM)); \ +}) +#define IS_ENC_NODE(n) ({ \ + typeof(n) _n =3D (n); \ + ((_n =3D=3D EXYNOS_VIDEONODE_MFC_ENC) || (_n =3D=3D EXYNOS_VIDEONODE_MFC_= ENC_DRM) || \ + (_n =3D=3D EXYNOS_VIDEONODE_MFC_ENC_OTF) || \ + (_n =3D=3D EXYNOS_VIDEONODE_MFC_ENC_OTF_DRM)); \ +}) + +#define IS_MULTI_CORE_DEVICE(dev) ((dev)->num_core > 1) +#define IS_SINGLE_MODE(ctx) ((ctx)->op_mode =3D=3D MFC_OP_SINGLE) +#define IS_TWO_MODE1(ctx) ((ctx)->op_mode =3D=3D MFC_OP_TWO_MODE1) +#define IS_TWO_MODE2(ctx) ((ctx)->op_mode =3D=3D MFC_OP_TWO_MODE2) +#define IS_MULTI_MODE(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->op_mode =3D=3D MFC_OP_TWO_MODE1) || \ + (_ctx->op_mode =3D=3D MFC_OP_TWO_MODE2)); \ +}) + +#define IS_SWITCH_SINGLE_MODE(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->op_mode =3D=3D MFC_OP_SWITCH_TO_SINGLE) || \ + (_ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2)); \ +}) + +#define IS_SWITCH_SINGLE_MODE(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->op_mode =3D=3D MFC_OP_SWITCH_TO_SINGLE) || \ + (_ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2)); \ +}) + +/* Extra information for Decoder */ +#define DEC_SET_DUAL_DPB BIT(0) +#define DEC_SET_DYNAMIC_DPB BIT(1) +#define DEC_SET_LAST_FRAME_INFO BIT(2) +#define DEC_SET_SKYPE_FLAG BIT(3) +#define DEC_SET_HDR10_PLUS BIT(4) +/* new C2_INTERFACE: DISPLAY_DELAY, FRAME_POC */ +#define DEC_SET_C2_INTERFACE BIT(6) +#define DEC_SET_FRAME_ERR_TYPE BIT(7) +#define DEC_SET_OPERATING_FPS BIT(8) +#define DEC_SET_HDR10_PLUS_FULL BIT(9) +#define DEC_SET_BUF_FLAG_CTRL BIT(16) +#define DEC_SET_PRIORITY BIT(23) + +/* Extra information for Encoder */ +#define ENC_SET_RGB_INPUT BIT(0) +#define ENC_SET_SPARE_SIZE BIT(1) +#define ENC_SET_TEMP_SVC_CH BIT(2) +#define ENC_SET_SKYPE_FLAG BIT(3) +#define ENC_SET_ROI_CONTROL BIT(4) +#define ENC_SET_QP_BOUND_PB BIT(5) +#define ENC_SET_FIXED_SLICE BIT(6) +#define ENC_SET_PVC_MODE BIT(7) +#define ENC_SET_RATIO_OF_INTRA BIT(8) +#define ENC_SET_COLOR_ASPECT BIT(9) +#define ENC_SET_HP_BITRATE_CONTROL BIT(10) +#define ENC_SET_STATIC_INFO BIT(11) +#define ENC_SET_HDR10_PLUS BIT(12) +#define ENC_SET_VP9_PROFILE_LEVEL BIT(13) +#define ENC_SET_DROP_CONTROL BIT(14) +#define ENC_SET_CHROMA_QP_CONTROL BIT(15) +#define ENC_SET_BUF_FLAG_CTRL BIT(16) +#define ENC_SET_GDC_VOTF BIT(17) +#define ENC_SET_OPERATING_FPS BIT(18) +#define ENC_SET_AVERAGE_QP BIT(19) +#define ENC_SET_MV_SEARCH_MODE BIT(20) +#define ENC_SET_GOP_CTRL BIT(21) +#define ENC_SET_HDR10_PLUS_STAT_INFO BIT(22) +#define ENC_SET_PRIORITY BIT(23) +#define ENC_SET_CAPABILITY BIT(24) +#define ENC_SET_IFRAME_SIZE BIT(25) +#define ENC_SET_TIMING_INFO_ENABLE BIT(26) + +#define MFC_FEATURE_SUPPORT(dev, f) ({ \ + typeof(f) _f =3D (f); \ + (_f).support && ((dev)->fw_date >=3D (_f).version); \ +}) + +/* Low memory check */ +#define IS_LOW_MEM (totalram_pages() <=3D ((SZ_1G + SZ_512M) >> PAGE_SHI= FT)) + +static inline int mfc_core_get_pwr_ref_cnt(struct mfc_core *core) +{ + return atomic_read(&core->pm.pwr_ref); +} + +static inline int mfc_core_get_clk_ref_cnt(struct mfc_core *core) +{ + return atomic_read(&core->clk_ref); +} + +#endif /* __MFC_COMMON_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_media.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_media.h new file mode 100644 index 000000000000..cf37700d78a0 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_media.h @@ -0,0 +1,554 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_media.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_MEDIA_H +#define __MFC_MEDIA_H __FILE__ + +/* RGB formats */ +#define V4L2_PIX_FMT_RGB32X v4l2_fourcc('R', 'G', 'B', 'X') /* 32 RGB-= 8-8-8-8 */ + +/* compressed formats */ +#define V4L2_PIX_FMT_FIMV v4l2_fourcc('F', 'I', 'M', 'V') /* FIMV */ +#define V4L2_PIX_FMT_FIMV1 v4l2_fourcc('F', 'I', 'M', '1') /* FIMV1 */ +#define V4L2_PIX_FMT_FIMV2 v4l2_fourcc('F', 'I', 'M', '2') /* FIMV2 */ +#define V4L2_PIX_FMT_FIMV3 v4l2_fourcc('F', 'I', 'M', '3') /* FIMV3 */ +#define V4L2_PIX_FMT_FIMV4 v4l2_fourcc('F', 'I', 'M', '4') /* FIMV4 */ +#define V4L2_PIX_FMT_AV1 v4l2_fourcc('A', 'V', '0', '1') /* AV1 */ + +enum v4l2_mpeg_mfc51_video_frame_type { + V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_NOT_CODED =3D 0, + V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_I_FRAME =3D 1, + V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_P_FRAME =3D 2, + V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_B_FRAME =3D 3, + V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_SKIPPED =3D 4, + V4L2_MPEG_MFC51_VIDEO_FRAME_TYPE_OTHERS =3D 5, +}; + +/* new entry for enum v4l2_mpeg_video_mpeg4_level */ +#define V4L2_MPEG_VIDEO_MPEG4_LEVEL_6 8 + +/* new entry for enum v4l2_mpeg_video_header_mode */ +#define V4L2_MPEG_VIDEO_HEADER_MODE_AT_THE_READY 2 + +/* new entry for enum v4l2_mpeg_video_multi_slice_mode */ +#define V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW 3 +#define V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES 4 + +/* new entry for enum v4l2_mpeg_video_h264_profile */ +#define V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH 17 + +#define V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_S_B \ + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY + +#define V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH V4L2_CID_MPEG_VIDEO_GOP_SIZE +#define V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH V4L2_CID_MPEG_VIDEO_BITRATE +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH \ + V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE + +/* CID base for MFC controls (MPEG_CLASS) */ +#define V4L2_CID_MPEG_MFC_BASE (V4L2_CTRL_CLASS_CODEC | 0x2000) + +#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL \ + (V4L2_CID_MPEG_MFC_BASE + 1) +#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRGMENT_ID \ + (V4L2_CID_MPEG_MFC_BASE + 2) +#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_INFO \ + (V4L2_CID_MPEG_MFC_BASE + 3) +#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_GRID_POS \ + (V4L2_CID_MPEG_MFC_BASE + 4) +#define V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB \ + (V4L2_CID_MPEG_MFC_BASE + 5) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG \ + (V4L2_CID_MPEG_MFC_BASE + 6) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 7) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA \ + (V4L2_CID_MPEG_MFC_BASE + 8) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA \ + (V4L2_CID_MPEG_MFC_BASE + 9) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA_BOT \ + (V4L2_CID_MPEG_MFC_BASE + 10) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA_BOT \ + (V4L2_CID_MPEG_MFC_BASE + 11) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_GENERATED \ + (V4L2_CID_MPEG_MFC_BASE + 12) +#define V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE \ + (V4L2_CID_MPEG_MFC_BASE + 13) +#define V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS \ + (V4L2_CID_MPEG_MFC_BASE + 14) +#define V4L2_CID_MPEG_MFC51_VIDEO_LUMA_ADDR \ + (V4L2_CID_MPEG_MFC_BASE + 15) +#define V4L2_CID_MPEG_MFC51_VIDEO_CHROMA_ADDR \ + (V4L2_CID_MPEG_MFC_BASE + 16) +#define V4L2_CID_MPEG_MFC51_VIDEO_STREAM_SIZE \ + (V4L2_CID_MPEG_MFC_BASE + 17) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_COUNT \ + (V4L2_CID_MPEG_MFC_BASE + 18) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 19) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_INTERLACE \ + (V4L2_CID_MPEG_MFC_BASE + 20) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE \ + (V4L2_CID_MPEG_MFC_BASE + 21) +#define V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_TIME_RES \ + (V4L2_CID_MPEG_MFC_BASE + 22) +#define V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_FRM_DELTA \ + (V4L2_CID_MPEG_MFC_BASE + 23) +#define V4L2_CID_MPEG_MFC51_VIDEO_H263_RC_FRAME_RATE \ + (V4L2_CID_MPEG_MFC_BASE + 24) +#define V4L2_CID_MPEG_MFC6X_VIDEO_FRAME_DELTA \ + (V4L2_CID_MPEG_MFC_BASE + 25) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA1 \ + (V4L2_CID_MPEG_MFC_BASE + 26) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_LUMA \ + (V4L2_CID_MPEG_MFC_BASE + 27) +#define V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_CHROMA \ + (V4L2_CID_MPEG_MFC_BASE + 28) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_POC \ + (V4L2_CID_MPEG_MFC_BASE + 29) +#define V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG \ + (V4L2_CID_MPEG_MFC_BASE + 30) +#define V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG \ + (V4L2_CID_MPEG_MFC_BASE + 31) +#define V4L2_CID_MPEG_VIDEO_GDC_VOTF \ + (V4L2_CID_MPEG_MFC_BASE + 32) +#define V4L2_CID_MPEG_VIDEO_FRAME_ERROR_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 33) +#define V4L2_CID_MPEG_VIDEO_GOP_CTRL \ + (V4L2_CID_MPEG_MFC_BASE + 35) +#define V4L2_CID_MPEG_VIDEO_PRIORITY \ + (V4L2_CID_MPEG_MFC_BASE + 36) +#define V4L2_CID_MPEG_VIDEO_MAX_IFRAME_SIZE \ + (V4L2_CID_MPEG_MFC_BASE + 37) +#define V4L2_CID_MPEG_VIDEO_CHROMA_QP_OFFSET_CB \ + (V4L2_CID_MPEG_MFC_BASE + 38) +#define V4L2_CID_MPEG_VIDEO_CHROMA_QP_OFFSET_CR \ + (V4L2_CID_MPEG_MFC_BASE + 39) +#define V4L2_CID_MPEG_VIDEO_GET_DISPLAY_DELAY \ + (V4L2_CID_MPEG_MFC_BASE + 40) +#define V4L2_CID_MPEG_VIDEO_DROP_CONTROL \ + (V4L2_CID_MPEG_MFC_BASE + 41) +#define V4L2_CID_MPEG_VIDEO_H264_MVC_VIEW_ID \ + (V4L2_CID_MPEG_MFC_BASE + 42) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS \ + (V4L2_CID_MPEG_MFC_BASE + 43) +#define V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING \ + (V4L2_CID_MPEG_MFC_BASE + 44) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE \ + (V4L2_CID_MPEG_MFC_BASE + 45) +#define V4L2_CID_MPEG_VIDEO_H264_PREPEND_SPSPPS_TO_IDR \ + (V4L2_CID_MPEG_MFC_BASE + 46) +#define V4L2_CID_MPEG_VIDEO_DECODER_IMMEDIATE_DISPLAY \ + (V4L2_CID_MPEG_MFC_BASE + 47) +#define V4L2_CID_MPEG_VIDEO_DECODER_DECODING_TIMESTAMP_MODE \ + (V4L2_CID_MPEG_MFC_BASE + 48) +#define V4L2_CID_MPEG_VIDEO_DECODER_WAIT_DECODING_START \ + (V4L2_CID_MPEG_MFC_BASE + 49) +#define V4L2_CID_MPEG_VIDEO_QOS_RATIO \ + (V4L2_CID_MPEG_MFC_BASE + 50) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT \ + (V4L2_CID_MPEG_MFC_BASE + 51) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH \ + (V4L2_CID_MPEG_MFC_BASE + 52) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT0 \ + (V4L2_CID_MPEG_MFC_BASE + 53) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT1 \ + (V4L2_CID_MPEG_MFC_BASE + 54) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT2 \ + (V4L2_CID_MPEG_MFC_BASE + 55) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT3 \ + (V4L2_CID_MPEG_MFC_BASE + 56) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT4 \ + (V4L2_CID_MPEG_MFC_BASE + 57) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT5 \ + (V4L2_CID_MPEG_MFC_BASE + 58) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT6 \ + (V4L2_CID_MPEG_MFC_BASE + 59) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_VERSION \ + (V4L2_CID_MPEG_MFC_BASE + 60) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_RC_FRAME_RATE \ + (V4L2_CID_MPEG_MFC_BASE + 61) +#define V4L2_CID_MPEG_VIDEO_VP8_MIN_QP \ + (V4L2_CID_MPEG_MFC_BASE + 62) +#define V4L2_CID_MPEG_VIDEO_VP8_MAX_QP \ + (V4L2_CID_MPEG_MFC_BASE + 63) +#define V4L2_CID_MPEG_VIDEO_VP8_I_FRAME_QP \ + (V4L2_CID_MPEG_MFC_BASE + 64) +#define V4L2_CID_MPEG_VIDEO_VP8_P_FRAME_QP \ + (V4L2_CID_MPEG_MFC_BASE + 65) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_OF_PARTITIONS \ + (V4L2_CID_MPEG_MFC_BASE + 66) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_LEVEL \ + (V4L2_CID_MPEG_MFC_BASE + 67) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_SHARPNESS \ + (V4L2_CID_MPEG_MFC_BASE + 68) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_GOLDEN_FRAMESEL \ + (V4L2_CID_MPEG_MFC_BASE + 69) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_GF_REFRESH_PERIOD \ + (V4L2_CID_MPEG_MFC_BASE + 70) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 71) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER0 \ + (V4L2_CID_MPEG_MFC_BASE + 72) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER1 \ + (V4L2_CID_MPEG_MFC_BASE + 73) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER2 \ + (V4L2_CID_MPEG_MFC_BASE + 74) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_REF_NUMBER_FOR_PFRAMES \ + (V4L2_CID_MPEG_MFC_BASE + 75) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_DISABLE_INTRA_MD4X4 \ + (V4L2_CID_MPEG_MFC_BASE + 76) +#define V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_TEMPORAL_LAYER \ + (V4L2_CID_MPEG_MFC_BASE + 77) +#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT \ + (V4L2_CID_MPEG_MFC_BASE + 78) +#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH \ + (V4L2_CID_MPEG_MFC_BASE + 79) +#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT0 \ + (V4L2_CID_MPEG_MFC_BASE + 80) +#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT1 \ + (V4L2_CID_MPEG_MFC_BASE + 81) +#define V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT2 \ + (V4L2_CID_MPEG_MFC_BASE + 82) + +/* ~ 90 : Reserved for using later */ + +#define V4L2_CID_MPEG_MFC_GET_VERSION_INFO \ + (V4L2_CID_MPEG_MFC_BASE + 91) +#define V4L2_CID_MPEG_MFC_GET_EXTRA_BUFFER_SIZE \ + (V4L2_CID_MPEG_MFC_BASE + 92) +#define V4L2_CID_MPEG_MFC_SET_DUAL_DPB_MODE \ + (V4L2_CID_MPEG_MFC_BASE + 93) +#define V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE \ + (V4L2_CID_MPEG_MFC_BASE + 95) +#define V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE \ + (V4L2_CID_MPEG_MFC_BASE + 96) +#define V4L2_CID_MPEG_MFC_GET_EXT_INFO \ + (V4L2_CID_MPEG_MFC_BASE + 97) +#define V4L2_CID_MPEG_MFC_SET_BUF_PROCESS_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 98) +#define V4L2_CID_MPEG_MFC_GET_10BIT_INFO \ + (V4L2_CID_MPEG_MFC_BASE + 99) +#define V4L2_CID_MPEG_MFC_H264_ENABLE_LTR \ + (V4L2_CID_MPEG_MFC_BASE + 100) +#define V4L2_CID_MPEG_MFC_H264_MARK_LTR \ + (V4L2_CID_MPEG_MFC_BASE + 101) +#define V4L2_CID_MPEG_MFC_H264_USE_LTR \ + (V4L2_CID_MPEG_MFC_BASE + 102) +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB_ROW \ + (V4L2_CID_MPEG_MFC_BASE + 103) +#define V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY \ + (V4L2_CID_MPEG_MFC_BASE + 104) +#define V4L2_CID_MPEG_MFC_CONFIG_QP \ + (V4L2_CID_MPEG_MFC_BASE + 105) +#define V4L2_CID_MPEG_MFC_H264_VUI_RESTRICTION_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 106) +#define V4L2_CID_MPEG_MFC_GET_DRIVER_INFO \ + (V4L2_CID_MPEG_MFC_BASE + 107) +#define V4L2_CID_MPEG_MFC_CONFIG_QP_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 108) +#define V4L2_CID_MPEG_MFC_HDR_USER_SHARED_HANDLE \ + (V4L2_CID_MPEG_MFC_BASE + 109) + +/* + * CIDs for HEVC encoding. + * Even though it was merged to mainline, do not use it for HAL code compa= tibility. + * V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (V4L2_CID_MPEG_MFC_BASE + 110) + * V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (V4L2_CID_MPEG_MFC_BASE + 111) + * V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (V4L2_CID_MPEG_MFC_BASE + 112) + * V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (V4L2_CID_MPEG_MFC_BASE + 113) + * V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (V4L2_CID_MPEG_MFC_BASE + 114) + */ +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_QP_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 115) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 116) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER \ + (V4L2_CID_MPEG_MFC_BASE + 117) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_QP \ + (V4L2_CID_MPEG_MFC_BASE + 118) +/* empty number */ +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_RC_FRAME_RATE \ + (V4L2_CID_MPEG_MFC_BASE + 122) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TIER_FLAG \ + (V4L2_CID_MPEG_MFC_BASE + 123) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_PARTITION_DEPTH \ + (V4L2_CID_MPEG_MFC_BASE + 124) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REF_NUMBER_FOR_PFRAMES \ + (V4L2_CID_MPEG_MFC_BASE + 125) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_DISABLE \ + (V4L2_CID_MPEG_MFC_BASE + 126) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_SLICE_BOUNDARY \ + (V4L2_CID_MPEG_MFC_BASE + 127) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 \ + (V4L2_CID_MPEG_MFC_BASE + 128) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_TC_OFFSET_DIV2 \ + (V4L2_CID_MPEG_MFC_BASE + 129) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 130) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_PERIOD \ + (V4L2_CID_MPEG_MFC_BASE + 131) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LOSSLESS_CU_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 132) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_CONST_INTRA_PRED_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 133) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WAVEFRONT_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 134) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LTR_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 135) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_USER_REF \ + (V4L2_CID_MPEG_MFC_BASE + 136) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STORE_REF \ + (V4L2_CID_MPEG_MFC_BASE + 137) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIGN_DATA_HIDING \ + (V4L2_CID_MPEG_MFC_BASE + 138) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_GENERAL_PB_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 139) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TEMPORAL_ID_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 140) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STRONG_SMOTHING_FLAG \ + (V4L2_CID_MPEG_MFC_BASE + 141) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 \ + (V4L2_CID_MPEG_MFC_BASE + 142) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_DARK \ + (V4L2_CID_MPEG_MFC_BASE + 143) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_SMOOTH \ + (V4L2_CID_MPEG_MFC_BASE + 144) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_STATIC \ + (V4L2_CID_MPEG_MFC_BASE + 145) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_ACTIVITY \ + (V4L2_CID_MPEG_MFC_BASE + 146) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_INTRA_PU_SPLIT \ + (V4L2_CID_MPEG_MFC_BASE + 147) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_TMV_PREDICTION \ + (V4L2_CID_MPEG_MFC_BASE + 148) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WITHOUT_STARTCODE_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 149) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_QP_INDEX_CR \ + (V4L2_CID_MPEG_MFC_BASE + 150) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_QP_INDEX_CB \ + (V4L2_CID_MPEG_MFC_BASE + 151) +#define V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD \ + (V4L2_CID_MPEG_MFC_BASE + 152) +#define V4L2_CID_MPEG_VIDEO_HEVC_PREPEND_SPSPPS_TO_IDR \ + (V4L2_CID_MPEG_MFC_BASE + 153) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH \ + (V4L2_CID_MPEG_MFC_BASE + 154) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT0 \ + (V4L2_CID_MPEG_MFC_BASE + 155) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT1 \ + (V4L2_CID_MPEG_MFC_BASE + 156) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT2 \ + (V4L2_CID_MPEG_MFC_BASE + 157) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT3 \ + (V4L2_CID_MPEG_MFC_BASE + 158) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT4 \ + (V4L2_CID_MPEG_MFC_BASE + 159) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT5 \ + (V4L2_CID_MPEG_MFC_BASE + 160) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT6 \ + (V4L2_CID_MPEG_MFC_BASE + 161) +#define V4L2_CID_MPEG_MFC_HEIF_MODE \ + (V4L2_CID_MPEG_MFC_BASE + 162) + +/* CIDs for VP9 encoding. Number gaps are for compatibility */ +#define V4L2_CID_MPEG_VIDEO_VP9_VERSION \ + (V4L2_CID_MPEG_MFC_BASE + 163) +#define V4L2_CID_MPEG_VIDEO_VP9_RC_FRAME_RATE \ + (V4L2_CID_MPEG_MFC_BASE + 164) +#define V4L2_CID_MPEG_VIDEO_VP9_MIN_QP \ + (V4L2_CID_MPEG_MFC_BASE + 165) +#define V4L2_CID_MPEG_VIDEO_VP9_MAX_QP \ + (V4L2_CID_MPEG_MFC_BASE + 166) +#define V4L2_CID_MPEG_VIDEO_VP9_I_FRAME_QP \ + (V4L2_CID_MPEG_MFC_BASE + 167) +#define V4L2_CID_MPEG_VIDEO_VP9_P_FRAME_QP \ + (V4L2_CID_MPEG_MFC_BASE + 168) +#define V4L2_CID_MPEG_VIDEO_VP9_GOLDEN_FRAMESEL \ + (V4L2_CID_MPEG_MFC_BASE + 169) +#define V4L2_CID_MPEG_VIDEO_VP9_GF_REFRESH_PERIOD \ + (V4L2_CID_MPEG_MFC_BASE + 170) +#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHY_QP_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 171) +#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_QP \ + (V4L2_CID_MPEG_MFC_BASE + 172) +#define V4L2_CID_MPEG_VIDEO_VP9_REF_NUMBER_FOR_PFRAMES \ + (V4L2_CID_MPEG_MFC_BASE + 173) +#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER \ + (V4L2_CID_MPEG_MFC_BASE + 174) +#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH \ + (V4L2_CID_MPEG_MFC_BASE + 175) +#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT0 \ + (V4L2_CID_MPEG_MFC_BASE + 176) +#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT1 \ + (V4L2_CID_MPEG_MFC_BASE + 177) +#define V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT2 \ + (V4L2_CID_MPEG_MFC_BASE + 178) +#define V4L2_CID_MPEG_VIDEO_VP9_MAX_PARTITION_DEPTH \ + (V4L2_CID_MPEG_MFC_BASE + 179) +#define V4L2_CID_MPEG_VIDEO_VP9_DISABLE_INTRA_PU_SPLIT \ + (V4L2_CID_MPEG_MFC_BASE + 180) +#define V4L2_CID_MPEG_VIDEO_DISABLE_IVF_HEADER \ + (V4L2_CID_MPEG_MFC_BASE + 181) +/* + * Even though it was merged to mainline, do not use it for HAL code compa= tibility. + * V4L2_CID_MPEG_VIDEO_VP9_LEVEL (V4L2_CID_MPEG_MFC_BASE + 183) + */ +#define V4L2_CID_MPEG_VIDEO_UNCOMP_FMT \ + (V4L2_CID_MPEG_MFC_BASE + 184) +#define V4L2_CID_MPEG_VIDEO_SKIP_LAZY_UNMAP \ + (V4L2_CID_MPEG_MFC_BASE + 185) +#define V4L2_CID_MPEG_VIDEO_MIN_QUALITY \ + (V4L2_CID_MPEG_MFC_BASE + 186) + +/* CIDs for new common interface */ +#define V4L2_CID_MPEG_VIDEO_ROI_CONTROL \ + (V4L2_CID_MPEG_MFC_BASE + 190) +#define V4L2_CID_MPEG_VIDEO_ROI_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 191) +#define V4L2_CID_MPEG_VIDEO_RC_PVC_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 192) +#define V4L2_CID_MPEG_VIDEO_TEMPORAL_SHORTTERM_MAX_LAYER \ + (V4L2_CID_MPEG_MFC_BASE + 193) +#define V4L2_CID_MPEG_VIDEO_BLACK_BAR_DETECT \ + (V4L2_CID_MPEG_MFC_BASE + 194) +#define V4L2_CID_MPEG_MFC_H264_NUM_OF_LTR \ + (V4L2_CID_MPEG_MFC_BASE + 195) +#define V4L2_CID_MPEG_VIDEO_WEIGHTED_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 196) +#define V4L2_CID_MPEG_VIDEO_YSUM \ + (V4L2_CID_MPEG_MFC_BASE + 197) +#define V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA \ + (V4L2_CID_MPEG_MFC_BASE + 198) +#define V4L2_CID_MPEG_VIDEO_HIERARCHICAL_BITRATE_CTRL \ + (V4L2_CID_MPEG_MFC_BASE + 199) +#define V4L2_CID_MPEG_VIDEO_DECODING_ORDER \ + (V4L2_CID_MPEG_MFC_BASE + 200) + +/* QP BOUND interface */ +#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 201) +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 202) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 203) +#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 204) +#define V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 205) +#define V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 206) +#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 207) +#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 208) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 209) +#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 210) +#define V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 211) +#define V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P \ + (V4L2_CID_MPEG_MFC_BASE + 212) +#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B \ + (V4L2_CID_MPEG_MFC_BASE + 213) +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B \ + (V4L2_CID_MPEG_MFC_BASE + 214) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B \ + (V4L2_CID_MPEG_MFC_BASE + 215) +#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B \ + (V4L2_CID_MPEG_MFC_BASE + 216) +#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B \ + (V4L2_CID_MPEG_MFC_BASE + 217) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B \ + (V4L2_CID_MPEG_MFC_BASE + 218) + +/* SEI & VUI PARSING FOR HDR DISPLAY */ +#define V4L2_CID_MPEG_VIDEO_SEI_MAX_PIC_AVERAGE_LIGHT \ + (V4L2_CID_MPEG_MFC_BASE + 219) +#define V4L2_CID_MPEG_VIDEO_SEI_MAX_CONTENT_LIGHT \ + (V4L2_CID_MPEG_MFC_BASE + 220) +#define V4L2_CID_MPEG_VIDEO_SEI_MAX_DISPLAY_LUMINANCE \ + (V4L2_CID_MPEG_MFC_BASE + 221) +#define V4L2_CID_MPEG_VIDEO_SEI_MIN_DISPLAY_LUMINANCE \ + (V4L2_CID_MPEG_MFC_BASE + 222) +#define V4L2_CID_MPEG_VIDEO_MATRIX_COEFFICIENTS \ + (V4L2_CID_MPEG_MFC_BASE + 223) +#define V4L2_CID_MPEG_VIDEO_FORMAT \ + (V4L2_CID_MPEG_MFC_BASE + 224) +#define V4L2_CID_MPEG_VIDEO_SEI_WHITE_POINT \ + (V4L2_CID_MPEG_MFC_BASE + 225) +#define V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_0 \ + (V4L2_CID_MPEG_MFC_BASE + 226) +#define V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_1 \ + (V4L2_CID_MPEG_MFC_BASE + 227) +#define V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_2 \ + (V4L2_CID_MPEG_MFC_BASE + 228) +#define V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG \ + (V4L2_CID_MPEG_MFC_BASE + 229) +#define V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES \ + (V4L2_CID_MPEG_MFC_BASE + 230) +#define V4L2_CID_MPEG_VIDEO_TRANSFER_CHARACTERISTICS \ + (V4L2_CID_MPEG_MFC_BASE + 231) +#define V4L2_CID_MPEG_VIDEO_STATIC_INFO_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 232) +#define V4L2_CID_MPEG_VIDEO_MV_SEARCH_MODE \ + (V4L2_CID_MPEG_MFC_BASE + 233) +#define V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L0 \ + (V4L2_CID_MPEG_MFC_BASE + 234) +#define V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L1 \ + (V4L2_CID_MPEG_MFC_BASE + 235) +#define V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L0 \ + (V4L2_CID_MPEG_MFC_BASE + 236) +#define V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L1 \ + (V4L2_CID_MPEG_MFC_BASE + 237) + +/* CID for AV1 decoding interface */ +#define V4L2_CID_MPEG_MFC_AV1_FILM_GRAIN_USER_SHARED_HANDLE \ + (V4L2_CID_MPEG_MFC_BASE + 240) +#define V4L2_CID_MPEG_MFC_AV1_FILM_GRAIN_PRESENT \ + (V4L2_CID_MPEG_MFC_BASE + 241) + +#define V4L2_CID_MPEG_MFC_HDR10_PLUS_STAT_USER_SHARED_HANDLE \ + (V4L2_CID_MPEG_MFC_BASE + 242) +#define V4L2_CID_MPEG_MFC_HDR10_PLUS_STAT_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 243) + +/* CID for encoding info interface */ +#define V4L2_CID_MPEG_VIDEO_WP_TWO_PASS_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 260) +#define V4L2_CID_MPEG_VIDEO_ADAPTIVE_GOP_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 261) +#define V4L2_CID_MPEG_VIDEO_SUM_SKIP_MB \ + (V4L2_CID_MPEG_MFC_BASE + 262) +#define V4L2_CID_MPEG_VIDEO_SUM_INTRA_MB \ + (V4L2_CID_MPEG_MFC_BASE + 263) +#define V4L2_CID_MPEG_VIDEO_SUM_ZERO_MV_MB \ + (V4L2_CID_MPEG_MFC_BASE + 264) +#define V4L2_CID_MPEG_VIDEO_MV_HOR_RANGE \ + (V4L2_CID_MPEG_MFC_BASE + 265) +#define V4L2_CID_MPEG_VIDEO_MV_VER_RANGE \ + (V4L2_CID_MPEG_MFC_BASE + 266) +#define V4L2_CID_MPEG_MFC_H264_SUB_GOP_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 267) +#define V4L2_CID_MPEG_MFC_H264_SUB_GOP_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 268) +#define V4L2_CID_MPEG_MFC_HEVC_SUB_GOP_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 269) +#define V4L2_CID_MPEG_MFC_HEVC_SUB_GOP_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 270) +#define V4L2_CID_MPEG_MFC_TIMING_INFO_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 271) +#define V4L2_CID_MPEG_MFC_MULTI_VIEW_ENABLE \ + (V4L2_CID_MPEG_MFC_BASE + 272) +#define V4L2_CID_MPEG_MFC_MULTI_VIEW_MAIN_TYPE \ + (V4L2_CID_MPEG_MFC_BASE + 273) +#endif /* __MFC_MEDIA_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_mem.c new file mode 100644 index 000000000000..dfb58ddcbd5d --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_mem.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include +#include +#include +#include +#include +#include + +#include "mfc_mem.h" + +static int mfc_mem_fw_alloc(struct mfc_dev *dev, struct mfc_special_buf *s= pecial_buf) +{ +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + struct page *fw_pages; + unsigned long nr_pages =3D special_buf->size >> PAGE_SHIFT; + int ret; + + special_buf->cma_area =3D dev->device->cma_area; + fw_pages =3D cma_alloc(special_buf->cma_area, nr_pages, 0, false); + if (!fw_pages) + return -ENOMEM; + + special_buf->sgt =3D kmalloc((*special_buf->sgt), GFP_KERNEL); + if (!special_buf->sgt) + goto err_cma_alloc; + + ret =3D sg_alloc_table(special_buf->sgt, 1, GFP_KERNEL); + if (ret) { + mfc_dev_err("Failed to allocate sg_table\n"); + goto err_sg_alloc; + } + + sg_set_page(special_buf->sgt->sgl, fw_pages, special_buf->size, 0); + + special_buf->paddr =3D page_to_phys(fw_pages); + special_buf->vaddr =3D phys_to_virt(special_buf->paddr); + + return 0; + +err_sg_alloc: + kfree(special_buf->sgt); + special_buf->sgt =3D NULL; +err_cma_alloc: + cma_release(special_buf->cma_area, fw_pages, nr_pages); + return -ENOMEM; +#else + special_buf->vaddr =3D dma_alloc_coherent(dev->mem_dev[BANK_L_CTX], + MFC_FW_MEM_SIZE, + &special_buf->daddr, + GFP_KERNEL); + if (!special_buf->vaddr) + return -ENOMEM; + special_buf->paddr =3D dma_to_phys(dev->device, special_buf->daddr); + mfc_dev_info("mapped on 0x%lx with size of 0x%lx paddr 0x%lx daddr 0x%lx\= n", + (unsigned long)special_buf->vaddr, + special_buf->size, + (unsigned long)special_buf->paddr, + (unsigned long)special_buf->daddr); + return 0; +#endif +} + +static void mfc_mem_fw_free(struct mfc_special_buf *special_buf) +{ +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + if (special_buf->sgt) { + cma_release(special_buf->cma_area, phys_to_page(special_buf->paddr), + (special_buf->size >> PAGE_SHIFT)); + sg_free_table(special_buf->sgt); + kfree(special_buf->sgt); + } +#endif + special_buf->sgt =3D NULL; + special_buf->dma_buf =3D NULL; + special_buf->attachment =3D NULL; + special_buf->daddr =3D 0; + special_buf->vaddr =3D NULL; + special_buf->name[0] =3D 0; +} + +static void dma_release(struct dma_buf *dmabuf) +{ + struct dma_priv *priv =3D dmabuf->priv; + + dma_free_coherent(priv->dev, priv->size, priv->vaddr, priv->dma_addr); +} + +static struct sg_table *dma_map(struct dma_buf_attachment *attach, enum dm= a_data_direction dir) +{ + struct dma_priv *priv =3D attach->dmabuf->priv; + struct sg_table *sgt; + int ret; + + sgt =3D kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return ERR_PTR(-ENOMEM); + + ret =3D sg_alloc_table(sgt, 1, GFP_KERNEL); + if (ret) { + kfree(sgt); + return ERR_PTR(ret); + } + + sg_set_page(sgt->sgl, virt_to_page(priv->vaddr), priv->size, + offset_in_page(priv->vaddr)); + sg_dma_address(sgt->sgl) =3D priv->dma_addr; + sg_dma_len(sgt->sgl) =3D priv->size; + + return sgt; +} + +static void dma_unmap(struct dma_buf_attachment *attach, + struct sg_table *sgt, enum dma_data_direction dir) +{ + sg_free_table(sgt); + kfree(sgt); +} + +static int dma_vmap(struct dma_buf *dmabuf, struct iosys_map *map) +{ + struct dma_priv *priv =3D dmabuf->priv; + + if (!priv || !priv->vaddr) + return -EINVAL; + + map->vaddr =3D priv->vaddr; + return 0; +} + +static void dma_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) +{ +} + +static const struct dma_buf_ops dma_ops =3D { + .release =3D dma_release, + .map_dma_buf =3D dma_map, + .unmap_dma_buf =3D dma_unmap, + .vmap =3D dma_vmap, + .vunmap =3D dma_vunmap, +}; + +static struct dma_buf *create_dma_buf(struct mfc_dev *dev, struct mfc_spec= ial_buf *special_buf) +{ + struct dma_priv *priv; + struct dma_buf_export_info exp_info; + + priv =3D devm_kzalloc(dev->mem_dev[BANK_R_CTX], sizeof(*priv), GFP_KERNEL= ); + if (!priv) + goto err_alloc; + + priv->dev =3D dev->mem_dev[BANK_R_CTX]; + priv->size =3D special_buf->size; + + priv->vaddr =3D dma_alloc_coherent(dev->mem_dev[BANK_R_CTX], + priv->size, &priv->dma_addr, GFP_KERNEL); + if (!priv->vaddr) { + kfree(priv); + goto err_alloc; + } + + exp_info.ops =3D &dma_ops; + exp_info.size =3D priv->size; + exp_info.flags =3D O_RDWR; + exp_info.exp_name =3D "ctx_buf"; + exp_info.priv =3D priv; + exp_info.owner =3D THIS_MODULE; + + return dma_buf_export(&exp_info); + +err_alloc: + return ERR_PTR(-ENOMEM); +} + +static int mfc_mem_dma_heap_alloc(struct mfc_dev *dev, + struct mfc_special_buf *special_buf) +{ + int ret =3D 0; + + /* control by DMA heap API */ + special_buf->dma_buf =3D create_dma_buf(dev, special_buf); + if (IS_ERR(special_buf->dma_buf)) { + mfc_dev_err("Failed to allocate buffer (err %ld)\n", + PTR_ERR(special_buf->dma_buf)); + return -EINVAL; + } + + /* control by DMA buf API */ + special_buf->attachment =3D dma_buf_attach(special_buf->dma_buf, + dev->device); + if (IS_ERR(special_buf->attachment)) { + mfc_dev_err("Failed to get dma_buf_attach (err %ld)\n", + PTR_ERR(special_buf->attachment)); + goto err_put_dma_buf; + } + + special_buf->sgt =3D dma_buf_map_attachment_unlocked(special_buf->attachm= ent, + DMA_BIDIRECTIONAL); + if (IS_ERR(special_buf->sgt)) { + mfc_dev_err("Failed to get sgt (err %ld)\n", + PTR_ERR(special_buf->sgt)); + goto err_detach_dma_buf; + } + + special_buf->daddr =3D sg_dma_address(special_buf->sgt->sgl); + if (IS_ERR_VALUE(special_buf->daddr)) { + mfc_dev_err("Failed to get iova (err 0x%p)\n", + &special_buf->daddr); + goto err_unmap_attachment; + } + + special_buf->map_size =3D mfc_mem_get_sg_length(dev, special_buf->sgt); + if (!special_buf->map_size || special_buf->map_size < special_buf->size) { + mfc_dev_err("Failed to get iova map size (sg length: %zu / buf size: %zu= )\n", + special_buf->map_size, special_buf->size); + goto err_unmap_attachment; + } + + if (special_buf->buftype =3D=3D MFCBUF_NORMAL) { + ret =3D dma_buf_vmap_unlocked(special_buf->dma_buf, &special_buf->map); + if (ret) { + mfc_dev_err("Failed to get vaddr\n"); + goto err_unmap_attachment; + } + + if (!special_buf->map.vaddr) { + mfc_dev_err("[ERROR]: special_buf->vaddr =3D map.vaddr: %pa\n", + special_buf->vaddr); + goto err_unmap_attachment; + } + special_buf->vaddr =3D special_buf->map.vaddr; + } + + special_buf->paddr =3D page_to_phys(sg_page(special_buf->sgt->sgl)); + + return 0; +err_unmap_attachment: + if (special_buf->vaddr) { + dma_buf_vunmap_unlocked(special_buf->dma_buf, &special_buf->map); + special_buf->vaddr =3D NULL; + } + dma_buf_unmap_attachment_unlocked(special_buf->attachment, special_buf->s= gt, + DMA_BIDIRECTIONAL); + special_buf->sgt =3D NULL; + special_buf->map_size =3D 0; + special_buf->daddr =3D 0; +err_detach_dma_buf: + dma_buf_detach(special_buf->dma_buf, special_buf->attachment); + special_buf->attachment =3D NULL; +err_put_dma_buf: + dma_buf_put(special_buf->dma_buf); + special_buf->dma_buf =3D NULL; + return -ENOMEM; +} + +static void mfc_mem_dma_heap_free(struct mfc_special_buf *special_buf) +{ + struct iosys_map map =3D IOSYS_MAP_INIT_VADDR(special_buf->vaddr); + + if (special_buf->vaddr) + dma_buf_vunmap_unlocked(special_buf->dma_buf, &map); + if (special_buf->sgt) + dma_buf_unmap_attachment_unlocked(special_buf->attachment, + special_buf->sgt, DMA_BIDIRECTIONAL); + if (special_buf->attachment) + dma_buf_detach(special_buf->dma_buf, special_buf->attachment); + if (special_buf->dma_buf) + dma_buf_put(special_buf->dma_buf); + + special_buf->dma_buf =3D NULL; + special_buf->attachment =3D NULL; + special_buf->sgt =3D NULL; + special_buf->daddr =3D 0; + special_buf->vaddr =3D NULL; + special_buf->map_size =3D 0; + special_buf->name[0] =3D 0; +} + +int mfc_mem_special_buf_alloc(struct mfc_dev *dev, struct mfc_special_buf = *special_buf) +{ + int ret; + + mfc_dev_debug(2, "[MEMINFO] REQUEST:%s buf\n", + special_buf->name); + + switch (special_buf->buftype) { + case MFCBUF_NORMAL_FW: + ret =3D mfc_mem_fw_alloc(dev, special_buf); + break; + case MFCBUF_NORMAL: + ret =3D mfc_mem_dma_heap_alloc(dev, special_buf); + break; + default: + mfc_dev_err("not supported mfc mem type: %d\n", special_buf->buftype); + return -EINVAL; + } + + mfc_dev_debug(2, "[MEMINFO] ALLOC: %s buf size: %zu, map size: %zu, daddr= : 0x%08llx\n", + special_buf->name, + special_buf->size, special_buf->map_size, special_buf->daddr); + + if (ret) + mfc_dev_err("[MEMINFO] Allocating %s failed\n", + special_buf->name); + + return ret; +} + +void mfc_mem_special_buf_free(struct mfc_dev *dev, struct mfc_special_buf = *special_buf) +{ + mfc_dev_debug(2, "[MEMINFO] RELEASE:%s buf\n", + special_buf->name); + + switch (special_buf->buftype) { + case MFCBUF_NORMAL_FW: + dma_free_coherent(dev->device, special_buf->size, + special_buf->vaddr, special_buf->daddr); + mfc_mem_fw_free(special_buf); + break; + case MFCBUF_NORMAL: + mfc_mem_dma_heap_free(special_buf); + break; + default: + break; + } +} + +void mfc_iova_pool_free(struct mfc_dev *dev, struct mfc_special_buf *buf) +{ + /* Project that do not support iova reservation */ + if (!dev->pdata->reserved_start) + return; + + if (!buf->iova) { + mfc_dev_debug(2, "[POOL] There is no reserved iova for %s\n", buf->name); + return; + } + +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + if (buf->map_size) + iommu_unmap(dev->domain, buf->daddr, buf->map_size); +#endif + + gen_pool_free(dev->iova_pool, buf->daddr, buf->map_size); + + mfc_dev_debug(2, "[POOL] FREE: %s iova: %#llx ++ %#zx, %zu/%zu (avail/tot= al KB)\n", + buf->name, buf->daddr, buf->map_size, + gen_pool_avail(dev->iova_pool) / SZ_1K, + gen_pool_size(dev->iova_pool) / SZ_1K); + + buf->daddr =3D buf->iova; + buf->iova =3D 0; +} + +/* + * This iova is allocated for special FW only memory. + * These memories deliver to F/W without 4bit shift even if 36bit DMA is s= upported, + * because it is already less than 1GB(0x4000_0000) address. + * Therefore, it should be noted that the memory below should use + * MFC_CORE_READL(WRITEL) rather than MFC_CORE_DMA_READL(WRITEL). + * - MFC_REG_CONTEXT_MEM_ADDR, MFC0_REG_COMMON_CONTEXT_MEM_ADDR, MFC1_REG_= COMMON_CONTEXT_MEM_ADDR + * - MFC_REG_DBG_BUFFER_ADDR + * - MFC_REG_D_METADATA_BUFFER_ADDR, MFC_REG_E_METADATA_BUFFER_ADDR + * - MFC_REG_SHARED_MEM_ADDR (not used) + */ +int mfc_iova_pool_alloc(struct mfc_dev *dev, struct mfc_special_buf *buf) +{ + size_t size; + + /* Project that do not support iova reservation */ + if (!dev->pdata->reserved_start) + return 0; + + if (!dev->iova_pool) { + mfc_dev_err("there is no iova pool\n"); + return -ENOMEM; + } + + buf->iova =3D buf->daddr; + buf->daddr =3D gen_pool_alloc(dev->iova_pool, buf->map_size); + if (buf->daddr =3D=3D 0) { + buf->daddr =3D buf->iova; + buf->iova =3D 0; + mfc_dev_err("[POOL] failed alloc %s iova. %zu/%zu (avail/total KB)\n", + buf->name, + gen_pool_avail(dev->iova_pool) / SZ_1K, + gen_pool_size(dev->iova_pool) / SZ_1K); + return -ENOMEM; + } + + mfc_dev_debug(2, "[POOL] ALLOC: %s iova: %#llx ++ %#zx, %zu/%zu (avail/to= tal KB)\n", + buf->name, buf->daddr, buf->map_size, + gen_pool_avail(dev->iova_pool) / SZ_1K, + gen_pool_size(dev->iova_pool) / SZ_1K); + + size =3D iommu_map_sg(dev->domain, buf->daddr, buf->sgt->sgl, + buf->sgt->orig_nents, IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); + if (!size || size !=3D buf->map_size) { + mfc_dev_err("[POOL] Failed to map %s iova %#llx (map size: %zu)\n", + buf->name, buf->daddr, size); + mfc_iova_pool_free(dev, buf); + return -ENOMEM; + } + + mfc_dev_debug(2, "[POOL] MAP: %s iova size: %zu(%#zx)\n", + buf->name, buf->map_size, buf->map_size); + + return 0; +} + +int mfc_iova_pool_init(struct mfc_dev *dev) +{ + struct device_node *node =3D dev->device->of_node; + dma_addr_t reserved_base; + const __be32 *prop; + size_t reserved_size; + int n_addr_cells =3D of_n_addr_cells(node); + int n_size_cells =3D of_n_size_cells(node); + int n_all_cells =3D n_addr_cells + n_size_cells; + int i, cnt, ret, found =3D 0; + char name[10]; + + snprintf(name, sizeof(name), "MFC"); + dev->iova_pool =3D devm_gen_pool_create(dev->device, PAGE_SHIFT, -1, name= ); + if (IS_ERR(dev->iova_pool)) { + mfc_dev_err("failed to create MFC IOVA gen pool\n"); + goto err_init; + } + + prop =3D of_get_property(node, "samsung,iommu-reserved-map", &cnt); + if (!prop) { + mfc_dev_err("No reserved F/W dma area\n"); + goto err_init; + } + + cnt /=3D sizeof(unsigned int); + if (cnt % n_all_cells !=3D 0) { + mfc_dev_err("Invalid number(%d) of values\n", cnt); + goto err_init; + } + + for (i =3D 0; i < cnt; i +=3D n_all_cells) { + reserved_base =3D of_read_number(prop + i, n_addr_cells); + reserved_size =3D of_read_number(prop + i + n_addr_cells, n_size_cells); + if (reserved_base =3D=3D dev->pdata->reserved_start) { + found =3D 1; + break; + } + } + + if (!found) { + mfc_dev_err("failed to search for reserved address %#x\n", + dev->pdata->reserved_start); + goto err_init; + } + + ret =3D gen_pool_add(dev->iova_pool, reserved_base, reserved_size, -1); + if (ret) { + mfc_dev_err("failed to set address range of MFC IOVA pool (ret: %d)\n", = ret); + goto err_init; + } + mfc_dev_info("[POOL] Add MFC iova ranges %#llx ++ %#zx\n", + reserved_base, reserved_size); + + return 0; + +err_init: + dev->iova_pool =3D NULL; + return -ENOMEM; +} + +int mfc_iommu_map_firmware(struct mfc_core *core, struct mfc_special_buf *= fw_buf) +{ + struct mfc_dev *dev =3D core->dev; + struct device_node *node =3D core->device->of_node; + dma_addr_t reserved_base; + const __be32 *prop; + + prop =3D of_get_property(node, "samsung,iommu-reserved-map", NULL); + if (!prop) { + mfc_dev_err("No reserved F/W dma area\n"); + return -ENOENT; + } + + if (fw_buf->buftype =3D=3D MFCBUF_NORMAL_FW) { + reserved_base =3D of_read_number(prop, of_n_addr_cells(node)); + mfc_core_info("[F/W] %s: reserved_base is %#llx\n", + fw_buf->name, reserved_base); + } else { + mfc_core_err("Wrong firmware buftype %d\n", fw_buf->buftype); + return -EINVAL; + } + + if (!reserved_base) { + mfc_core_err("There is no %s firmware reserved_base\n", + (fw_buf->buftype =3D=3D MFCBUF_NORMAL_FW) ? "normal" : "drm"); + return -ENOMEM; + } + + fw_buf->map_size =3D iommu_map_sg(core->domain, reserved_base, + fw_buf->sgt->sgl, fw_buf->sgt->orig_nents, + IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); + if (!fw_buf->map_size) { + mfc_core_err("Failed to map iova (err VA: %#llx, PA: %#llx)\n", + reserved_base, fw_buf->paddr); + return -ENOMEM; + } + + fw_buf->daddr =3D reserved_base; + mfc_core_debug(2, "[MEMINFO] FW mapped iova: %#llx (pa: %#llx)\n", + fw_buf->daddr, fw_buf->paddr); + + return 0; +} + +/* DMA memory related helper functions */ +static void mfc_memdev_release(struct device *dev) +{ + of_reserved_mem_device_release(dev); +} + +static struct device *mfc_alloc_memdev(struct device *dev, + const char *name, unsigned int idx) +{ + struct device *child; + int ret; + + child =3D devm_kzalloc(dev, sizeof(*child), GFP_KERNEL); + if (!child) + return NULL; + + device_initialize(child); + dev_set_name(child, "%s:%s", dev_name(dev), name); + child->parent =3D dev; + child->coherent_dma_mask =3D dev->coherent_dma_mask; + child->dma_mask =3D dev->dma_mask; + child->release =3D mfc_memdev_release; + child->dma_parms =3D devm_kzalloc(dev, sizeof(*child->dma_parms), + GFP_KERNEL); + if (!child->dma_parms) + goto err; + /* + * The memdevs are not proper OF platform devices, so in order for them + * to be treated as valid DMA masters we need a bit of a hack to force + * them to inherit the MFC node's DMA configuration. + */ + of_dma_configure(child, dev->of_node, true); + + if (!device_add(child)) { + ret =3D of_reserved_mem_device_init_by_idx(child, dev->of_node, + idx); + if (!ret) + return child; + device_del(child); + } +err: + put_device(child); + return NULL; +} + +int mfc_configure_dma_memory(struct mfc_dev *mfc_dev) +{ + struct device *dev =3D mfc_dev->device; + void *bank2_virt; + dma_addr_t bank2_dma_addr; + unsigned long align_size =3D BIT(17); // MFC_BASE_ALIGN_ORDER; + + /* + * Create and initialize virtual devices for accessing + * reserved memory regions. + */ + mfc_dev->mem_dev[BANK_L_CTX] =3D mfc_alloc_memdev(dev, "left", + BANK_L_CTX); + if (!mfc_dev->mem_dev[BANK_L_CTX]) + return -ENODEV; + + mfc_dev->mem_dev[BANK_R_CTX] =3D mfc_alloc_memdev(dev, "right", + BANK_R_CTX); + if (!mfc_dev->mem_dev[BANK_R_CTX]) { + device_unregister(mfc_dev->mem_dev[BANK_L_CTX]); + return -ENODEV; + } + + /* Allocate memory for firmware and initialize both banks addresses */ + bank2_virt =3D dma_alloc_coherent(mfc_dev->mem_dev[BANK_R_CTX], + align_size, &bank2_dma_addr, GFP_KERNEL); + if (!bank2_virt) { + // mfc_release_firmware(mfc_dev); + device_unregister(mfc_dev->mem_dev[BANK_R_CTX]); + device_unregister(mfc_dev->mem_dev[BANK_L_CTX]); + return -ENOMEM; + } + + dma_free_coherent(mfc_dev->mem_dev[BANK_R_CTX], align_size, bank2_virt, + bank2_dma_addr); + + dma_set_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX], DMA_BIT_MASK(32)); + dma_set_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX], DMA_BIT_MASK(32)); + + return 0; +} + +void mfc_unconfigure_dma_memory(struct mfc_dev *mfc_dev) +{ + device_unregister(mfc_dev->mem_dev[BANK_L_CTX]); + device_unregister(mfc_dev->mem_dev[BANK_R_CTX]); +} + +MODULE_IMPORT_NS("DMA_BUF"); diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_mem.h new file mode 100644 index 000000000000..7b37e5fedbf2 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_mem.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_MEM_H +#define __MFC_MEM_H __FILE__ + +#include +#include +#include + +#include "mfc_common.h" + +/* Offset base used to differentiate between CAPTURE and OUTPUT + * while mmaping + */ +#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2) + +struct dma_priv { + void *vaddr; + dma_addr_t dma_addr; + struct device *dev; + size_t size; +}; + +static inline dma_addr_t mfc_mem_get_daddr_vb(struct vb2_buffer *vb, unsig= ned int n) +{ + struct sg_table *sgt; + dma_addr_t addr =3D 0; + + sgt =3D vb2_dma_sg_plane_desc(vb, n); + addr =3D sg_dma_address(sgt->sgl); + WARN_ON((addr =3D=3D 0) || IS_ERR_VALUE(addr)); + + return addr; +} + +static inline phys_addr_t mfc_mem_get_paddr_vb(struct vb2_buffer *vb) +{ + struct sg_table *sgt; + + sgt =3D vb2_dma_sg_plane_desc(vb, 0); + + return page_to_phys(sg_page(sgt->sgl)); +} + +static inline size_t mfc_mem_get_sg_length(struct mfc_dev *dev, struct sg_= table *sgt) +{ + struct scatterlist *sg; + size_t size =3D 0; + int i; + + for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { + size +=3D sg->length; + mfc_dev_debug(6, "[MEMINFO] [%d] sg length %u, sum %zu\n", + i, sg->length, size); + } + + return size; +} + +int mfc_mem_special_buf_alloc(struct mfc_dev *dev, struct mfc_special_buf = *special_buf); +void mfc_mem_special_buf_free(struct mfc_dev *dev, struct mfc_special_buf = *special_buf); + +void mfc_iova_pool_free(struct mfc_dev *dev, struct mfc_special_buf *buf); +int mfc_iova_pool_alloc(struct mfc_dev *dev, struct mfc_special_buf *buf); +int mfc_iova_pool_init(struct mfc_dev *dev); + +int mfc_iommu_map_firmware(struct mfc_core *core, struct mfc_special_buf *= fw_buf); + +int mfc_configure_dma_memory(struct mfc_dev *mfc_dev); +void mfc_unconfigure_dma_memory(struct mfc_dev *mfc_dev); +#endif /* __MFC_MEM_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_regs.h b/dr= ivers/media/platform/samsung/exynos-mfc/base/mfc_regs.h new file mode 100644 index 000000000000..39f20e5c8877 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_regs.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * mfc_regs.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_REGS_H +#define __MFC_REGS_H __FILE__ + +#include "mfc_regs_mfc.h" +#include "mfc_data_struct.h" + +#define MFC_MIN_BITMASK 32 + +#define MFC_CORE_READL(offset) \ + readl(core->regs_base + (offset)) +#define MFC_CORE_WRITEL(data, offset) \ + writel((data), core->regs_base + (offset)) + +#define MFC_CORE_RAW_READL(offset) \ + __raw_readl(core->regs_base + (offset)) +#define MFC_CORE_RAW_WRITEL(data, offset) \ + __raw_writel((data), core->regs_base + (offset)) + +#define MFC_CORE_DMA_READL(offset) \ +({ \ + dma_addr_t data; \ + typeof(offset) off =3D offset; = \ + if (core->dev->pdata->dma_bit_mask =3D=3D MFC_MIN_BITMASK) \ + data =3D readl(core->regs_base + (off)); \ + else \ + data =3D (dma_addr_t)readl(core->regs_base + (off)) << 4; \ + data; \ +}) + +#define MFC_CORE_DMA_WRITEL(data, offset) \ +({ \ + typeof(offset) off =3D offset; = \ + typeof(data) dat =3D data; \ + if (core->dev->pdata->dma_bit_mask =3D=3D MFC_MIN_BITMASK) \ + writel((dat), core->regs_base + (off)); \ + else \ + writel((dat >> 4), core->regs_base + (off)); \ +}) + +#define MFC_MMU0_READL(offset) readl(core->sysmmu0_base + (offset)) +#define MFC_MMU1_READL(offset) readl(core->sysmmu1_base + (offset)) +#define mfc_get_mmu0_value(offset, shift, mask) ((MFC_MMU0_READL(offset) >= > (shift)) & (mask)) +#define mfc_get_mmu1_value(offset, shift, mask) ((MFC_MMU1_READL(offset) >= > (shift)) & (mask)) + +#define PMU_READL(offset) readl(core->pmu_base + (offset)) +#define PMU_WRITEL(data, offset) writel((data), core->pmu_base + (offset)) + +#endif /* __MFC_REGS_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_sched.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_sched.h new file mode 100644 index 000000000000..5048bf6c1d78 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_sched.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_sched.h File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_SCHED_H +#define __MFC_SCHED_H __FILE__ + +#include "mfc_common.h" + +extern struct mfc_sched_class mfc_sched_prio; + +static inline int mfc_get_prio(struct mfc_core *core, int rt, int prio) +{ + return (rt =3D=3D MFC_NON_RT) ? (core->num_prio + prio) : prio; +} + +static inline void mfc_sched_init_core(struct mfc_core *core) +{ + core->sched =3D &mfc_sched_prio; + core->sched->create_work(core); + core->sched->init_work(core); +} +#endif /* __MFC_SCHED_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h new file mode 100644 index 000000000000..f965a2e440c0 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_utils.h + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_UTILS_H +#define __MFC_UTILS_H __FILE__ + +#include "mfc_common.h" + +static inline void mfc_core_clean_dev_int_flags(struct mfc_core *core) +{ + core->int_condition =3D 0; + core->int_reason =3D 0; + core->int_err =3D 0; +} + +static inline void mfc_clean_core_ctx_int_flags(struct mfc_core_ctx *core_= ctx) +{ + core_ctx->int_condition =3D 0; + core_ctx->int_reason =3D 0; + core_ctx->int_err =3D 0; +} + +static inline void mfc_change_state(struct mfc_core_ctx *core_ctx, enum mf= c_inst_state state) +{ + MFC_TRACE_CORE_CTX("** state : %d -> %d\n", core_ctx->state, state); + mfc_debug(3, "state %d -> %d\n", core_ctx->state, state); + core_ctx->state =3D state; +} + +static inline void mfc_change_op_mode(struct mfc_ctx *ctx, enum mfc_op_mod= e op_mode) +{ + struct mfc_dev *dev =3D ctx->dev; + + MFC_TRACE_RM("** op_mode : %d -> %d\n", ctx->op_mode, op_mode); + MFC_TRACE_CTX("** op_mode : %d -> %d\n", ctx->op_mode, op_mode); + mfc_ctx_debug(3, "op_mode %d -> %d\n", ctx->op_mode, op_mode); + ctx->op_mode =3D op_mode; +} + +static inline void mfc_core_change_state(struct mfc_core *core, enum mfc_c= ore_state state) +{ + MFC_TRACE_CORE("** core state : %d\n", state); + core->state =3D state; +} + +static inline void mfc_core_change_fw_state(struct mfc_core *core, + enum mfc_fw_status state, int set) +{ + enum mfc_fw_status prev_stat; + + prev_stat =3D core->fw.status; + if (set) + core->fw.status |=3D state; + else + core->fw.status &=3D ~state; + MFC_TRACE_CORE("** normal FW status %#x -> %#x (%s: %#x)\n", + prev_stat, core->fw.status, set ? "set" : "clear", state); + mfc_core_debug(2, "[F/W] normal status: %#x -> %#x (%s: %#x)\n", + prev_stat, core->fw.status, set ? "set" : "clear", state); +} + +static inline enum mfc_node_type mfc_get_node_type(struct file *file) +{ + struct video_device *vdev =3D video_devdata(file); + struct mfc_dev *dev; + enum mfc_node_type node_type; + + if (!vdev) { + mfc_pr_err("mfc failed to get video_device\n"); + return MFCNODE_INVALID; + } + dev =3D video_drvdata(file); + + mfc_dev_debug(2, "video_device index: %d\n", vdev->index); + + switch (vdev->index) { + case 0: + node_type =3D MFCNODE_DECODER; + break; + case 1: + node_type =3D MFCNODE_ENCODER; + break; + case 2: + node_type =3D MFCNODE_DECODER_DRM; + break; + case 3: + node_type =3D MFCNODE_ENCODER_DRM; + break; + case 4: + node_type =3D MFCNODE_ENCODER_OTF; + break; + case 5: + node_type =3D MFCNODE_ENCODER_OTF_DRM; + break; + default: + node_type =3D MFCNODE_INVALID; + break; + } + + return node_type; +} + +static inline int mfc_is_decoder_node(enum mfc_node_type node) +{ + if (node =3D=3D MFCNODE_DECODER) + return 1; + + return 0; +} + +/* Meerkat interval */ +#define MEERKAT_TICK_INTERVAL 1000 +/* After how many executions meerkat should assume lock up */ +#define MEERKAT_TICK_CNT_TO_START_MEERKAT 10 + +/* MFC idle checker interval */ +#define MFCIDLE_TICK_INTERVAL 1500 + +void mfc_core_idle_checker(struct timer_list *t); + +static inline void mfc_core_idle_checker_start_tick(struct mfc_core *core) +{ + mod_timer(&core->mfc_idle_timer, jiffies + + msecs_to_jiffies(MFCIDLE_TICK_INTERVAL)); + + atomic_set(&core->hw_run_bits, 0); + atomic_set(&core->dev->queued_bits, 0); +} + +static inline void mfc_core_idle_update_hw_run(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + unsigned long flags; + int bits; + + spin_lock_irqsave(&core->dev->idle_bits_lock, flags); + + bits =3D atomic_read(&core->hw_run_bits); + atomic_set(&core->hw_run_bits, (bits | BIT(ctx->num))); + + spin_unlock_irqrestore(&core->dev->idle_bits_lock, flags); +} + +static inline void mfc_core_change_idle_mode(struct mfc_core *core, + enum mfc_idle_mode idle_mode) +{ + MFC_TRACE_CORE("** idle mode : %d\n", idle_mode); + core->idle_mode =3D idle_mode; + + if (core->idle_mode =3D=3D MFC_IDLE_MODE_NONE) + mfc_core_idle_checker_start_tick(core); +} + +static inline void mfc_ctx_change_idle_mode(struct mfc_ctx *ctx, + enum mfc_idle_mode idle_mode) +{ + MFC_TRACE_CTX("**[c:%d] idle mode : %d\n", ctx->num, idle_mode); + ctx->idle_mode =3D idle_mode; +} +#endif /* __MFC_UTILS_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 0550A25A2A1 for ; Tue, 30 Sep 2025 03:55:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204547; cv=none; b=hKdduQ1WsTTWlrmkpk6V3N/UEFFAJVJ3QXsf5z4+a4oHWpN9aorkjB87g3GoJrdi/rkH8o/k+SBK3DyQpqjKWXDAd+hRV97+jwgmhQ9o3PF6/NxZGIedaj/LQx7rMNK/smcgDHRAHUt71ACokUJp8zYICcN9vbksmVmmEqyVhaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204547; c=relaxed/simple; bh=oSpX/oMsK1Qwl1QRUKu8cE7KsSylalIwGvsqe7NRFds=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=E5OPe6pRoTXiAfl+SV72z7ecPBWxqNm4Gcp5to+jfcifO6vyam/1l2ngO+ooObRdgndt/QUBK+dWzv5ckyCC12DkLKDqa3Zbeb+paq+XNoRNKJfd54SlquW17jIUDP+zBF2wMUmZLwL9uwIWlG2Pl3z9cCi77R/0jM1e2TGtZiE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=izOP9FGU; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="izOP9FGU" Received: from epcas5p2.samsung.com (unknown [182.195.41.40]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035542epoutp02559341830ca7b0b9f7542112bc52d635~p8zX0IG312366023660epoutp02d for ; Tue, 30 Sep 2025 03:55:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035542epoutp02559341830ca7b0b9f7542112bc52d635~p8zX0IG312366023660epoutp02d DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204542; bh=P8Qh4CW2BFkICzdjo78AipBn/sVMMTrhSgSCULYFzEY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=izOP9FGUpthRH9snp8YjYK/KvuaN/T2sGCmimIhWaqjT9/Wl8dj4AHt4TBtCdRJSP vcNcINBrNFql0mvZwW4+drUojSjuoN4P5GCcUZtJBq3X21TayJsLerYXMGMnMZylXA b7fFqMOvuiU+Dyt83fSdxZo7KAZi24spSEdSu0jk= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPS id 20250930035541epcas5p2d68d44fe2eb1a6c1a14de342d579566a~p8zXC0suL2331123311epcas5p2I; Tue, 30 Sep 2025 03:55:41 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.93]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPNX4hzGz2SSKh; Tue, 30 Sep 2025 03:55:40 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035540epcas5p356f8403ee4d973701a74cf96f6e9c19b~p8zVfGh0E0048900489epcas5p3c; Tue, 30 Sep 2025 03:55:40 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035535epsmtip164bc5085be34f0b361946b2b18080022~p8zRuewU52882428824epsmtip1Q; Tue, 30 Sep 2025 03:55:35 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 06/29] media: mfc: Add MFC core hardware register and debugfs APIs Date: Tue, 30 Sep 2025 09:33:25 +0530 Message-Id: <20250930040348.3702923-7-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035540epcas5p356f8403ee4d973701a74cf96f6e9c19b X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035540epcas5p356f8403ee4d973701a74cf96f6e9c19b References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce mfc_core_hw_reg_api.h with inline helpers for interrupt handling, firmware status polling, pending checks, power control (MFC on/off), and RISC start/stop. Add mfc_debugfs.h exposing mfc_init_debugfs / mfc_deinit_debugfs for kernel debugfs integration. Provide mfc_core_reg_api.h containing version=E2=80=91query macros and interrupt=E2=80=91status getters, plus debug=E2=80=91control stubs. Include necessary header dependencies. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/mfc_core_hw_reg_api.c | 122 +++++++++++ .../samsung/exynos-mfc/mfc_core_hw_reg_api.h | 144 +++++++++++++ .../platform/samsung/exynos-mfc/mfc_core_pm.c | 184 +++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_core_pm.h | 33 +++ .../samsung/exynos-mfc/mfc_core_reg_api.c | 44 ++++ .../samsung/exynos-mfc/mfc_core_reg_api.h | 46 +++++ .../samsung/exynos-mfc/mfc_core_sync.c | 190 ++++++++++++++++++ .../samsung/exynos-mfc/mfc_core_sync.h | 25 +++ .../platform/samsung/exynos-mfc/mfc_debugfs.c | 189 +++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_debugfs.h | 20 ++ 10 files changed, 997 insertions(+) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_r= eg_api.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_r= eg_api.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_= api.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_= api.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_sync= .c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_sync= .h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.h diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_reg_api.= c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_reg_api.c new file mode 100644 index 000000000000..b4401ea2b476 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_reg_api.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_hw_reg_api.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_hw_reg_api.h" + +/* Reset the device */ +void mfc_core_reg_clear(struct mfc_core *core) +{ + int i; + + /* Zero Initialization of MFC registers */ + MFC_CORE_WRITEL(0, MFC_REG_RISC2HOST_CMD); + MFC_CORE_WRITEL(0, MFC_REG_HOST2RISC_CMD); + MFC_CORE_WRITEL(0, MFC_REG_FW_VERSION); + + for (i =3D 0; i < MFC_REG_CLEAR_COUNT; i++) + MFC_CORE_WRITEL(0, MFC_REG_CLEAR_BEGIN + (i * 4)); +} + +void mfc_core_reset_mfc(struct mfc_core *core) +{ + mfc_core_debug_enter(); + + MFC_CORE_WRITEL(0x1FFF, MFC_REG_MFC_RESET); + MFC_CORE_WRITEL(0, MFC_REG_MFC_RESET); + + mfc_core_debug_leave(); +} + +void mfc_core_set_risc_base_addr(struct mfc_core *core) +{ + struct mfc_special_buf *fw_buf; + + fw_buf =3D &core->fw_buf; + + MFC_CORE_DMA_WRITEL(fw_buf->daddr, + MFC_REG_RISC_NONSECURE_BASE_ADDR); + + mfc_core_debug(2, "[MEMINFO][F/W] Base Address : %#x\n", + (u32)fw_buf->daddr); + MFC_TRACE_CORE("F/W Base Address : %#x\n", + (u32)fw_buf->daddr); +} + +void mfc_core_cmd_host2risc(struct mfc_core *core, int cmd) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[core->curr_core_ctx]; + struct mfc_ctx *ctx =3D core_ctx->ctx; + int ret =3D 0; + + mfc_debug(1, "[c:%d] Issue the command: %d%s\n", core->curr_core_ctx, + cmd, core->cache_flush_flag ? " with cache flush" : ""); + MFC_TRACE_CORE_CTX("%s %d, (dev:0x%lx, bits:%lx, owned:%d, wl:%d, trans:%= d, opmode: %d)\n", + ">> CMD :", cmd, + core->hwlock.dev, core->hwlock.bits, + core->hwlock.owned_by_irq, core->hwlock.wl_count, + core->hwlock.transfer_owner, ctx->op_mode); + MFC_TRACE_LOG_CORE("C%d", cmd); + + if (core->cache_flush_flag) { + MFC_TRACE_CORE_CTX(">> CMD : 12 in FW\n"); + MFC_TRACE_LOG_CORE("C12FW"); + } + + /* Reset RISC2HOST command except nal q stop command */ + if (cmd !=3D MFC_REG_H2R_CMD_STOP_QUEUE) + MFC_CORE_WRITEL(0x0, MFC_REG_RISC2HOST_CMD); + + if (cmd !=3D MFC_REG_H2R_CMD_NAL_QUEUE && + cmd !=3D MFC_REG_H2R_CMD_NAL_LL && + cmd !=3D MFC_REG_H2R_CMD_STOP_QUEUE) { + /* TODO: change to core */ + if (cmd !=3D MFC_REG_H2R_CMD_NAL_ABORT) { + /* Check the fw status */ + ret =3D mfc_core_wait_fw_status(core); + if (ret !=3D 0) + mfc_err("Failed to wait firmware status\n"); + } + } + + if (core->dev->debugfs.dbg_enable && core->dbg_info_buf.dma_buf) { + /* For FW debugging */ + mfc_core_dbg_set_addr(core); + mfc_core_dbg_enable(core); + } + + core->last_cmd =3D cmd; + core->last_cmd_time =3D ktime_to_timespec64(ktime_get()); + + /* Record if the command incurs cache flush */ + core->last_cmd_has_cache_flush =3D + (cmd =3D=3D MFC_REG_H2R_CMD_CACHE_FLUSH || core->cache_flush_flag) ? 1 := 0; + + if (core->cache_flush_flag) + cmd |=3D BIT(MFC_REG_H2R_CACHE_FLUSH_FLAG); + core->cache_flush_flag =3D 0; + + /* Issue the command */ + MFC_CORE_WRITEL(cmd, MFC_REG_HOST2RISC_CMD); + MFC_CORE_WRITEL(0x1, MFC_REG_HOST2RISC_INT); +} + +/* Check whether HW interrupt has occurred or not */ +int mfc_core_check_risc2host(struct mfc_core *core) +{ + if (mfc_core_get_pwr_ref_cnt(core) && mfc_core_get_clk_ref_cnt(core)) { + if (MFC_CORE_READL(MFC_REG_RISC2HOST_INT)) + return MFC_CORE_READL(MFC_REG_RISC2HOST_CMD); + else + return 0; + } + + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_reg_api.= h b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_reg_api.h new file mode 100644 index 000000000000..5a7bba8f54c3 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hw_reg_api.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_hw_reg_api.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_HW_REG_API_H +#define __MFC_CORE_HW_REG_API_H __FILE__ + +#include "mfc_core_reg_api.h" + +#define mfc_core_get_int_reason() (MFC_CORE_READL(MFC_REG_RISC2HOST_CMD) \ + & MFC_REG_RISC2HOST_CMD_MASK) + +#define mfc_core_clear_int() \ + do { \ + MFC_CORE_WRITEL(0, MFC_REG_RISC2HOST_CMD); \ + MFC_CORE_WRITEL(0, MFC_REG_RISC2HOST_INT); \ + } while (0) + +#define mfc_core_clear_int_only() MFC_CORE_WRITEL(0, MFC_REG_RISC2HOST_INT) + +static inline int mfc_core_wait_fw_status(struct mfc_core *core) +{ + struct mfc_dev *dev =3D core->dev; + unsigned int status; + unsigned long timeout; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->wait_fw_status)) { + status =3D MFC_CORE_READL(MFC_REG_FIRMWARE_STATUS_INFO); + if (status & 0x1) + return 0; + + timeout =3D jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT); + do { + if (time_after(jiffies, timeout)) { + mfc_core_err("Timeout while waiting MFC F/W done\n"); + return -EIO; + } + status =3D MFC_CORE_READL(MFC_REG_FIRMWARE_STATUS_INFO); + } while ((status & 0x1) =3D=3D 0); + } + + return 0; +} + +static inline int mfc_core_wait_pending(struct mfc_core *core) +{ + unsigned int status; + unsigned long timeout; + + /* Check F/W wait status */ + timeout =3D jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT); + do { + if (time_after(jiffies, timeout)) { + mfc_core_err("Timeout while waiting MFC F/W done\n"); + return -EIO; + } + status =3D MFC_CORE_READL(MFC_REG_FIRMWARE_STATUS_INFO); + } while ((status & 0x1) =3D=3D 0); + + /* Check H/W pending status */ + timeout =3D jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT); + do { + if (time_after(jiffies, timeout)) { + mfc_core_err("Timeout while pendng clear\n"); + mfc_core_err("MFC access pending R: %#x, BUS: %#x\n", + MFC_CORE_READL(MFC_REG_MFC_RPEND), + MFC_CORE_READL(MFC_REG_MFC_BUS_STATUS)); + return -EIO; + } + status =3D MFC_CORE_READL(MFC_REG_MFC_RPEND); + } while (status !=3D 0); + + MFC_TRACE_CORE("** pending wait done\n"); + + return 0; +} + +static inline void mfc_core_mfc_off(struct mfc_core *core) +{ + if (core->dev->pdata->support_hwacg =3D=3D MFC_HWACG_DRV_CTRL) { + mfc_core_debug(2, "MFC_OFF 1(off)\n"); + MFC_TRACE_CORE(">> MFC OFF 1(off)\n"); + MFC_CORE_WRITEL(0x1, MFC_REG_MFC_OFF); + } +} + +static inline void mfc_core_mfc_always_off(struct mfc_core *core) +{ + mfc_core_debug(2, "MFC_OFF 1(off)\n"); + MFC_TRACE_CORE(">> MFC OFF 1(off)\n"); + MFC_CORE_WRITEL(0x1, MFC_REG_MFC_OFF); +} + +static inline void mfc_core_mfc_on(struct mfc_core *core) +{ + MFC_CORE_WRITEL(0x0, MFC_REG_MFC_OFF); + mfc_core_debug(2, "MFC_OFF 0(on)\n"); + MFC_TRACE_CORE(">> MFC OFF 0(on)\n"); +} + +static inline void mfc_core_risc_on(struct mfc_core *core) +{ + mfc_core_clean_dev_int_flags(core); + mfc_core_mfc_on(core); + + MFC_CORE_WRITEL(0x1, MFC_REG_RISC_ON); + mfc_core_debug(1, "RISC_ON\n"); + MFC_TRACE_CORE(">> RISC ON\n"); +} + +static inline void mfc_core_risc_off(struct mfc_core *core) +{ + unsigned int status; + unsigned long timeout; + + timeout =3D jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT); + /* Check pending status */ + do { + if (time_after(jiffies, timeout)) { + mfc_core_err("Timeout while pendng clear\n"); + mfc_core_err("MFC access pending state: %#x\n", status); + mfc_core_err("MFC access pending R: %#x, W: %#x\n", + MFC_CORE_READL(MFC_REG_MFC_RPEND), + MFC_CORE_READL(MFC_REG_MFC_WPEND)); + break; + } + status =3D MFC_CORE_READL(MFC_REG_MFC_BUS_STATUS); + } while (status !=3D 0); + + MFC_CORE_WRITEL(0x0, MFC_REG_RISC_ON); +} + +void mfc_core_reg_clear(struct mfc_core *core); +void mfc_core_reset_mfc(struct mfc_core *core); +void mfc_core_set_risc_base_addr(struct mfc_core *core); +void mfc_core_cmd_host2risc(struct mfc_core *core, int cmd); +int mfc_core_check_risc2host(struct mfc_core *core); +#endif /* __MFC_CORE_HW_REG_API_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_core_pm.c new file mode 100644 index 000000000000..def7ac2a2007 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_pm.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include + +#include "mfc_core_pm.h" +#include "mfc_core_sync.h" + +#include "mfc_core_hw_reg_api.h" + +void mfc_core_pm_init(struct mfc_core *core) +{ + spin_lock_init(&core->pm.clklock); + atomic_set(&core->pm.pwr_ref, 0); + atomic_set(&core->pm.protect_ref, 0); + atomic_set(&core->clk_ref, 0); + + core->pm.device =3D core->device; + core->pm.clock_on_steps =3D 0; + core->pm.clock_off_steps =3D 0; + pm_runtime_enable(core->pm.device); +} + +void mfc_core_pm_final(struct mfc_core *core) +{ + pm_runtime_disable(core->pm.device); +} + +int mfc_core_pm_clock_on(struct mfc_core *core, bool qos_update) +{ + int ret =3D 0; + int state; + + if (core->dev->pdata->support_hwacg =3D=3D MFC_HWACG_HWFW_CTRL) + return ret; + + core->pm.clock_on_steps =3D 1; + state =3D mfc_core_get_clk_ref_cnt(core); + + core->pm.clock_on_steps |=3D BIT(1); + if (core->pm.base_type !=3D MFCBUF_INVALID) { + /* + * There is no place to set core->pm.base_type, + * so it is always MFCBUF_INVALID now. + * When necessary later, you can set the bse_type. + */ + core->pm.clock_on_steps |=3D BIT(2); + ret =3D mfc_core_wait_pending(core); + if (ret !=3D 0) { + mfc_core_err("pending wait failed (%d)\n", ret); + return ret; + } + core->pm.clock_on_steps |=3D BIT(3); + mfc_core_set_risc_base_addr(core); + } + + core->pm.clock_on_steps |=3D BIT(4); + if (!IS_ERR(core->pm.clock)) { + ret =3D clk_enable(core->pm.clock); + if (ret < 0) + mfc_core_err("clk_enable failed (%d)\n", ret); + } + + core->pm.clock_on_steps |=3D BIT(5); + state =3D atomic_inc_return(&core->clk_ref); + + if (!core->dev->multi_core_inst_bits) + mfc_core_mfc_on(core); + + mfc_core_debug(2, "clk_ref =3D %d\n", state); + MFC_TRACE_LOG_CORE("clk_ref =3D %d", state); + + return 0; +} + +void mfc_core_pm_clock_off(struct mfc_core *core, bool qos_update) +{ + int state; + + if (core->dev->pdata->support_hwacg =3D=3D MFC_HWACG_HWFW_CTRL) + return; + + core->pm.clock_off_steps =3D 1; + state =3D atomic_dec_return(&core->clk_ref); + if (state < 0) { + mfc_core_info("MFC clock is already disabled (%d)\n", state); + atomic_set(&core->clk_ref, 0); + core->pm.clock_off_steps |=3D BIT(2); + MFC_TRACE_CORE("** clock_off already: ref state(%d)\n", + mfc_core_get_clk_ref_cnt(core)); + } else { + core->pm.clock_off_steps |=3D BIT(3); + if (!IS_ERR(core->pm.clock)) { + clk_disable(core->pm.clock); + core->pm.clock_off_steps |=3D BIT(4); + } + } + + state =3D mfc_core_get_clk_ref_cnt(core); + + if (!core->dev->multi_core_inst_bits && !state) + mfc_core_mfc_off(core); + + core->pm.clock_off_steps |=3D BIT(5); + + mfc_core_debug(2, "clk_ref =3D %d\n", state); + MFC_TRACE_LOG_CORE("clk_ref =3D %d", state); +} + +int mfc_core_pm_power_on(struct mfc_core *core) +{ + int ret; + + MFC_TRACE_CORE("++ Power on\n"); + ret =3D pm_runtime_get_sync(core->pm.device); + if (ret < 0) { + mfc_core_err("Failed to get power: ret(%d)\n", ret); + goto err_power_on; + } + +#if (IS_ENABLED(CONFIG_COMMON_CLK_SAMSUNG)) + core->pm.clock =3D clk_get(core->device, "aclk_mfc"); + if (IS_ERR(core->pm.clock)) { + mfc_core_err("failed to get parent clock: ret(%d)\n", ret); + } else { + ret =3D clk_prepare(core->pm.clock); + if (ret) { + mfc_core_err("clk_prepare() failed: ret(%d)\n", ret); + clk_put(core->pm.clock); + } + } +#endif + + atomic_inc(&core->pm.pwr_ref); + + MFC_TRACE_CORE("-- Power on: ret(%d)\n", ret); + MFC_TRACE_LOG_CORE("p+%d", mfc_core_get_pwr_ref_cnt(core)); + + return 0; + +err_power_on: + return ret; +} + +int mfc_core_pm_power_off(struct mfc_core *core) +{ + int state; + int ret; + + MFC_TRACE_CORE("++ Power off\n"); + + state =3D mfc_core_get_clk_ref_cnt(core); + if (state > 0 && core->dev->pdata->support_hwacg !=3D MFC_HWACG_HWFW_CTRL= ) { + mfc_core_info("MFC clock is still enabled (%d)\n", state); + mfc_core_pm_clock_off(core, 0); + } + + if (!IS_ERR(core->pm.clock)) { + clk_unprepare(core->pm.clock); + clk_put(core->pm.clock); + } + mfc_core_mfc_always_off(core); + + ret =3D pm_runtime_put_sync(core->pm.device); + if (ret < 0) { + mfc_core_err("Failed to put power: ret(%d)\n", ret); + return ret; + } + + atomic_dec(&core->pm.pwr_ref); + + MFC_TRACE_CORE("-- Power off: ret(%d)\n", ret); + MFC_TRACE_LOG_CORE("p-%d", mfc_core_get_pwr_ref_cnt(core)); + + return ret; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.h b/driv= ers/media/platform/samsung/exynos-mfc/mfc_core_pm.h new file mode 100644 index 000000000000..f0b6159ce91a --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_pm.h File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_PM_H +#define __MFC_CORE_PM_H __FILE__ + +#include + +#include "base/mfc_common.h" + +static inline void mfc_core_pm_clock_get(struct mfc_core *core) +{ + /* This should be called after clock was enabled by mfc_core_pm_clock_on(= ) */ + if (core->continue_clock_on) + mfc_core_info("MFC frequency : %ld\n", clk_get_rate(core->pm.clock)); +} + +void mfc_core_pm_init(struct mfc_core *core); +void mfc_core_pm_final(struct mfc_core *core); +int mfc_core_pm_clock_on(struct mfc_core *core, bool qos_update); +void mfc_core_pm_clock_off(struct mfc_core *core, bool qos_update); +int mfc_core_pm_power_on(struct mfc_core *core); +int mfc_core_pm_power_off(struct mfc_core *core); + +#endif /* __MFC_CORE_PM_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c new file mode 100644 index 000000000000..ec6699dbd451 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_reg_api.c File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include + +#include "mfc_core_reg_api.h" + +void mfc_core_dbg_enable(struct mfc_core *core) +{ + unsigned int reg; + + mfc_core_debug(2, "MFC debug info enable\n"); + reg =3D MFC_CORE_READL(MFC_REG_DBG_INFO_ENABLE); + reg |=3D BIT(MFC_REG_DBG_INFO_ENABLE_SHIFT); + MFC_CORE_WRITEL(reg, MFC_REG_DBG_INFO_ENABLE); +} + +void mfc_core_dbg_disable(struct mfc_core *core) +{ + unsigned int reg; + + mfc_core_debug(2, "MFC debug info disable\n"); + reg =3D MFC_CORE_READL(MFC_REG_DBG_INFO_ENABLE); + reg &=3D ~BIT(MFC_REG_DBG_INFO_ENABLE_SHIFT); + MFC_CORE_WRITEL(reg, MFC_REG_DBG_INFO_ENABLE); +} + +void mfc_core_dbg_set_addr(struct mfc_core *core) +{ + struct mfc_ctx_buf_size *buf_size =3D core->dev->variant->buf_size->ctx_b= uf; + + memset((void *)core->dbg_info_buf.vaddr, 0, buf_size->dbg_info_buf); + + MFC_CORE_WRITEL(core->dbg_info_buf.daddr, MFC_REG_DBG_BUFFER_ADDR); + MFC_CORE_WRITEL(buf_size->dbg_info_buf, MFC_REG_DBG_BUFFER_SIZE); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h new file mode 100644 index 000000000000..a23d25edce5d --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_reg_api.h File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_REG_API_H +#define __MFC_CORE_REG_API_H __FILE__ + +#include "base/mfc_regs.h" +#include "base/mfc_utils.h" + +/* version */ +#define mfc_core_get_fimv_info() ((MFC_CORE_READL(MFC_REG_FW_VERSION) \ + >> MFC_REG_FW_VER_INFO_SHFT) \ + & MFC_REG_FW_VER_INFO_MASK) +#define mfc_core_get_fw_ver_year() ((MFC_CORE_READL(MFC_REG_FW_VERSION) \ + >> MFC_REG_FW_VER_YEAR_SHFT) \ + & MFC_REG_FW_VER_YEAR_MASK) +#define mfc_core_get_fw_ver_month() ((MFC_CORE_READL(MFC_REG_FW_VERSION) \ + >> MFC_REG_FW_VER_MONTH_SHFT) \ + & MFC_REG_FW_VER_MONTH_MASK) +#define mfc_core_get_fw_ver_date() ((MFC_CORE_READL(MFC_REG_FW_VERSION) \ + >> MFC_REG_FW_VER_DATE_SHFT) \ + & MFC_REG_FW_VER_DATE_MASK) +#define mfc_core_get_fw_ver_all() ((MFC_CORE_READL(MFC_REG_FW_VERSION) \ + >> MFC_REG_FW_VER_ALL_SHFT) \ + & MFC_REG_FW_VER_ALL_MASK) +#define mfc_core_get_mfc_version() ((MFC_CORE_READL(MFC_REG_MFC_VERSION) \ + >> MFC_REG_MFC_VER_SHFT) \ + & MFC_REG_MFC_VER_MASK) + +/* kind of interrupt */ +#define mfc_core_get_int_err() MFC_CORE_READL(MFC_REG_ERROR_CODE) + +#define mfc_core_get_inst_no() MFC_CORE_READL(MFC_REG_RET_INSTANCE_ID) + +void mfc_core_dbg_enable(struct mfc_core *core); +void mfc_core_dbg_disable(struct mfc_core *core); +void mfc_core_dbg_set_addr(struct mfc_core *core); +#endif /* __MFC_CORE_REG_API_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c b/dr= ivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c new file mode 100644 index 000000000000..de13cdb9c99a --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_sync.c File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_sync.h" + +#include "mfc_core_hw_reg_api.h" + +#define R2H_BIT(x) ({ \ + typeof(x) _x =3D (x); \ + (_x > 0) ? BIT(_x - 1) : 0; \ +}) + +static inline unsigned int __mfc_r2h_bit_mask(int cmd) +{ + unsigned int mask =3D R2H_BIT(cmd); + + if (cmd =3D=3D MFC_REG_R2H_CMD_FRAME_DONE_RET) + mask |=3D (R2H_BIT(MFC_REG_R2H_CMD_FIELD_DONE_RET) | + R2H_BIT(MFC_REG_R2H_CMD_COMPLETE_SEQ_RET) | + R2H_BIT(MFC_REG_R2H_CMD_SLICE_DONE_RET) | + R2H_BIT(MFC_REG_R2H_CMD_INIT_BUFFERS_RET) | + R2H_BIT(MFC_REG_R2H_CMD_NAL_ABORT_RET)); + /* FIXME: Temporal mask for S3D SEI processing */ + else if (cmd =3D=3D MFC_REG_R2H_CMD_INIT_BUFFERS_RET) + mask |=3D (R2H_BIT(MFC_REG_R2H_CMD_FIELD_DONE_RET) | + R2H_BIT(MFC_REG_R2H_CMD_SLICE_DONE_RET) | + R2H_BIT(MFC_REG_R2H_CMD_FRAME_DONE_RET)); + + return (mask |=3D R2H_BIT(MFC_REG_R2H_CMD_ERR_RET)); +} + +void mfc_get_corelock_ctx(struct mfc_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->corelock.lock, flags); + mfc_ctx_debug(3, "[CORELOCK] get_corelock: cnt %d\n", + ctx->corelock.cnt); + + ctx->corelock.cnt++; + + spin_unlock_irqrestore(&ctx->corelock.lock, flags); +} + +void mfc_release_corelock_ctx(struct mfc_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->corelock.lock, flags); + + ctx->corelock.cnt--; + if (ctx->corelock.cnt =3D=3D 0) + wake_up(&ctx->corelock.wq); + else if (ctx->corelock.cnt < 0) + mfc_ctx_err("[CORELOCK] wrong corelock cnt %d\n", ctx->corelock.cnt); + + mfc_ctx_debug(3, "[CORELOCK] release_corelock: cnt %d\n", + ctx->corelock.cnt); + spin_unlock_irqrestore(&ctx->corelock.lock, flags); +} + +#define wait_condition(x, c) ({ \ + typeof(x) __x =3D (x); \ + __x->int_condition && (R2H_BIT(__x->int_reason) & __mfc_r2h_bit_mask(c));= \ +}) + +#define is_err_cond(x) ({ \ + typeof(x) __x =3D (x); \ + __x->int_condition && (__x->int_reason =3D=3D MFC_REG_R2H_CMD_ERR_RET); = \ +}) + +/* + * Return value description + * 0: waked up before timeout + * 1: failed to get the response for the command before timeout + */ +int mfc_wait_for_done_core(struct mfc_core *core, int command) +{ + int ret; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_info("[MSR] Couldn't run HW. It's Error state\n"); + return 0; + } + + ret =3D wait_event_timeout(core->cmd_wq, + wait_condition(core, command), + msecs_to_jiffies(MFC_INT_TIMEOUT)); + if (ret =3D=3D 0) { + mfc_core_err("Interrupt (core->int_reason:%d, command:%d) timed out\n", + core->int_reason, command); + if (mfc_core_check_risc2host(core)) { + ret =3D wait_event_timeout(core->cmd_wq, + wait_condition(core, command), + msecs_to_jiffies(MFC_INT_TIMEOUT * + MFC_INT_TIMEOUT_CNT)); + if (ret =3D=3D 0) { + mfc_core_err("Timeout: MFC driver waited for upward of %dmsec\n", + 3 * MFC_INT_TIMEOUT); + } else { + goto wait_done; + } + } + return 1; + } + +wait_done: + if (is_err_cond(core)) { + mfc_core_err("Finished (core->int_reason:%d, command: %d)\n", + core->int_reason, command); + mfc_core_err("But error (core->int_err:%d)\n", core->int_err); + return -1; + } + + mfc_core_debug(2, "Finished waiting (core->int_reason:%d, command: %d)\n", + core->int_reason, command); + return 0; +} + +/* + * Return value description + * 0: waked up before timeout + * 1: failed to get the response for the command before timeout + * -1: got the error response for the command before timeout + */ +int mfc_wait_for_done_core_ctx(struct mfc_core_ctx *core_ctx, int command) +{ + struct mfc_core *core =3D core_ctx->core; + int ret; + unsigned int timeout =3D MFC_INT_TIMEOUT; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_info("[MSR] Couldn't run HW. It's Error state\n"); + return 0; + } + + if (command =3D=3D MFC_REG_R2H_CMD_CLOSE_INSTANCE_RET) + timeout =3D MFC_INT_SHORT_TIMEOUT; + + ret =3D wait_event_timeout(core_ctx->cmd_wq, + wait_condition(core_ctx, command), + msecs_to_jiffies(timeout)); + if (ret =3D=3D 0) { + mfc_err("Interrupt (core_ctx->int_reason:%d, command:%d) timed out\n", + core_ctx->int_reason, command); + if (mfc_core_check_risc2host(core)) { + ret =3D wait_event_timeout(core_ctx->cmd_wq, + wait_condition(core_ctx, command), + msecs_to_jiffies + (MFC_INT_TIMEOUT * MFC_INT_TIMEOUT_CNT)); + if (ret =3D=3D 0) { + mfc_err("Timeout: MFC driver waited for upward of %dmsec\n", + 3 * MFC_INT_TIMEOUT); + } else { + goto wait_done; + } + } + return 1; + } + +wait_done: + if (is_err_cond(core_ctx)) { + mfc_err("Finished (core_ctx->int_reason:%d, command: %d)\n", + core_ctx->int_reason, command); + mfc_err("But error (core_ctx->int_err:%d)\n", core_ctx->int_err); + return -1; + } + + mfc_debug(2, "Finished waiting (core_ctx->int_reason:%d, command: %d)\n", + core_ctx->int_reason, command); + return 0; +} + +/* Wake up device wait_queue */ +void mfc_wake_up_core(struct mfc_core *core, unsigned int reason, + unsigned int err) +{ + core->int_condition =3D 1; + core->int_reason =3D reason; + core->int_err =3D err; + wake_up(&core->cmd_wq); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.h b/dr= ivers/media/platform/samsung/exynos-mfc/mfc_core_sync.h new file mode 100644 index 000000000000..2c19819048de --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_sync.h File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_SYNC_H +#define __MFC_CORE_SYNC_H __FILE__ + +#include "base/mfc_common.h" + +void mfc_get_corelock_ctx(struct mfc_ctx *ctx); +void mfc_release_corelock_ctx(struct mfc_ctx *ctx); + +int mfc_wait_for_done_core(struct mfc_core *core, int command); +int mfc_wait_for_done_core_ctx(struct mfc_core_ctx *core_ctx, int command); +void mfc_wake_up_core(struct mfc_core *core, unsigned int reason, + unsigned int err); +int mfc_core_get_new_ctx(struct mfc_core *core); +#endif /* __MFC_CORE_SYNC_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_debugfs.c new file mode 100644 index 000000000000..5baa76a6b405 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_debugfs.c File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include +#include + +#include "mfc_debugfs.h" + +static int __mfc_info_show(struct seq_file *s, void *unused) +{ + struct mfc_dev *dev =3D s->private; + struct mfc_core *core =3D NULL; + struct mfc_core_ctx *core_ctx =3D NULL; + int i, j; + + seq_puts(s, ">>> MFC common device information\n"); + seq_printf(s, " [DEBUG MODE] dt: %s sysfs: %s\n", + dev->pdata->debug_mode ? "enabled" : "disabled", + dev->debugfs.debug_mode_en ? "enabled" : "disabled"); + + seq_printf(s, " [LOWMEM] is_low_mem: %d\n", IS_LOW_MEM); + + for (j =3D 0; j < dev->num_core; j++) { + core =3D dev->core[j]; + if (!core) { + mfc_dev_debug(2, "There is no core[%d]\n", j); + continue; + } + seq_printf(s, ">>> MFC core-%d device information\n", j); + seq_printf(s, " [VERSION] H/W: v%x, F/W: %06x(%c, normal: %#x), DRV: %d\= n", + core->core_pdata->ip_ver, core->fw.date, core->fw.fimv_info, + core->fw.status, MFC_DRIVER_INFO); + seq_printf(s, " [PM] power: %d, clock: %d, clk_get %s, QoS level: %d\n", + mfc_core_get_pwr_ref_cnt(core), + mfc_core_get_clk_ref_cnt(core), + IS_ERR(core->pm.clock) ? "failed" : "succeeded", + atomic_read(&core->qos_req_cur) - 1); + seq_printf(s, " [CTX] num_inst: %d, curr_ctx: %d\n", + core->num_inst, + core->curr_core_ctx); + seq_printf(s, " [HWLOCK] bits: %#lx, dev: %#lx, owned_by_irq =3D %d, wl_= count =3D %d\n", + core->hwlock.bits, core->hwlock.dev, + core->hwlock.owned_by_irq, + core->hwlock.wl_count); + seq_printf(s, " >>> MFC core-%d instance information\n", j); + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + core_ctx =3D core->core_ctx[i]; + if (core_ctx) { + seq_printf(s, " [CORECTX:%d] state: %d\n", + i, core_ctx->state); + } + } + } + + return 0; +} + +static int __mfc_debug_info_show(struct seq_file *s, void *unused) +{ + seq_puts(s, ">> MFC debug information\n"); + + seq_puts(s, "-----SFR dump options (bit setting)\n"); + seq_puts(s, "ex) echo 0xff > /d/mfc/sfr_dump (all dump mode)\n"); + seq_puts(s, "1 BIT(0): dec SEQ_START\n"); + seq_puts(s, "2 BIT(1): dec INIT_BUFS\n"); + seq_puts(s, "4 BIT(2): dec first NAL_START\n"); + seq_puts(s, "8 BIT(3): enc SEQ_START\n"); + seq_puts(s, "16 BIT(4): enc INIT_BUFS\n"); + seq_puts(s, "32 BIT(5): enc first NAL_START\n"); + seq_puts(s, "64 BIT(6): ERR interrupt\n"); + seq_puts(s, "128 BIT(7): WARN interrupt\n"); + seq_puts(s, "256 BIT(8): dec NAL_START\n"); + seq_puts(s, "512 BIT(9): dec FRAME_DONE\n"); + seq_puts(s, "1024 BIT(10): enc NAL_START\n"); + seq_puts(s, "2048 BIT(11): enc FRAME_DONE\n"); + seq_puts(s, "0x1000 BIT(12): MOVE_INSTANCE_RET\n"); + seq_puts(s, "0x2000 BIT(13): Unknown unterrupt\n"); + seq_puts(s, "0x8000 BIT(15): dec SEQ_DONE\n"); + seq_puts(s, "0x10000 BIT(16): dec INIT_BUF_DONE\n"); + seq_puts(s, "0x20000 BIT(17): dec first FRAME_DONE\n"); + seq_puts(s, "0x40000 BIT(18): enc SEQ_DONE\n"); + seq_puts(s, "0x80000 BIT(19): enc INIT_BUF_DONE\n"); + seq_puts(s, "0x100000 BIT(20): enc first FRAME_DONE\n"); + seq_puts(s, "0x20000000 BIT(29): Dump decoder CRC\n"); + seq_puts(s, "0x40000000 BIT(30): Dump firmware\n"); + seq_puts(s, "0x80000000 BIT(31): Dump all info\n"); + + seq_puts(s, "-----Performance boost options (bit setting)\n"); + seq_puts(s, "1 BIT(0): DVFS (INT/MFC/MIF)\n"); + seq_puts(s, "2 BIT(1): MO value\n"); + seq_puts(s, "4 BIT(2): CPU frequency\n"); + + seq_puts(s, "-----Feature options (bit setting)\n"); + seq_puts(s, "ex) echo 1 > /d/mfc/feture_option\n"); + seq_puts(s, "1 BIT(0): recon SBWC disable\n"); + seq_puts(s, "2 BIT(1): decoding order\n"); + seq_puts(s, "4 BIT(2): meerkat disable\n"); + seq_puts(s, "8 BIT(3): OTF path test enable\n"); + seq_puts(s, "16 BIT(4): multi core disable\n"); + seq_puts(s, "32 BIT(5): force multi core enable\n"); + seq_puts(s, "64 BIT(6): black bar enable\n"); + seq_puts(s, "128 BIT(7): when dec and enc, SBWC enable\n"); + seq_puts(s, "256 BIT(8): sync minlock with clock disable\n"); + seq_puts(s, "512 BIT(9): dynamic weight disable (use fixed weight)\n"); + seq_puts(s, "1024 BIT(10): when high fps, SBWC enable\n"); + seq_puts(s, "2048 BIT(11): film grain disable\n"); + seq_puts(s, "0x4000 BIT(14): enable MSR mode once\n"); + + seq_puts(s, "-----Logging options (bit setting)\n"); + seq_puts(s, "ex) echo 7 > /d/mfc/logging_option (all logging option)\n"); + seq_puts(s, "1 BIT(0): kernel printk\n"); + seq_puts(s, "2 BIT(1): memlog printf\n"); + seq_puts(s, "4 BIT(2): memlog sfr dump\n"); + + seq_puts(s, "-----Scheduler type\n"); + seq_puts(s, "ex) echo 1 > /d/mfc/sched_type\n"); + seq_puts(s, "1 BIT(0): Round-robin scheduler\n"); + seq_puts(s, "2 BIT(1): PBS (Priority Based Scheduler)\n"); + + return 0; +} + +static int __mfc_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, __mfc_info_show, inode->i_private); +} + +static int __mfc_debug_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, __mfc_debug_info_show, inode->i_private); +} + +static const struct file_operations mfc_info_fops =3D { + .open =3D __mfc_info_open, + .read =3D seq_read, + .llseek =3D seq_lseek, + .release =3D single_release, +}; + +static const struct file_operations debug_info_fops =3D { + .open =3D __mfc_debug_info_open, + .read =3D seq_read, + .llseek =3D seq_lseek, + .release =3D single_release, +}; + +void mfc_init_debugfs(struct mfc_dev *dev) +{ + struct mfc_debugfs *debugfs =3D &dev->debugfs; + + debugfs->root =3D debugfs_create_dir("mfc", NULL); + if (!debugfs->root) { + mfc_dev_err("debugfs: failed to create root directory\n"); + return; + } + + dev->debugfs.logging_option =3D MFC_DEFAULT_LOGGING_OPTION; + + debugfs_create_file("mfc_info", 0444, debugfs->root, dev, &mfc_info_fops); + debugfs_create_file("debug_info", 0444, debugfs->root, dev, &debug_info_f= ops); + + debugfs_create_u32("debug", 0644, debugfs->root, &dev->debugfs.debug_leve= l); + debugfs_create_u32("debug_ts", 0644, debugfs->root, &dev->debugfs.debug_t= s); + debugfs_create_u32("debug_mode_en", 0644, debugfs->root, &dev->debugfs.de= bug_mode_en); + debugfs_create_u32("dbg_enable", 0644, debugfs->root, &dev->debugfs.dbg_e= nable); + + debugfs_create_u32("sfr_dump", 0644, debugfs->root, &dev->debugfs.sfr_dum= p); + + debugfs_create_u32("feature_option", 0644, debugfs->root, &dev->debugfs.f= eature_option); + debugfs_create_u32("logging_option", 0644, debugfs->root, &dev->debugfs.l= ogging_option); + debugfs_create_u32("sched_perf_disable", 0644, + debugfs->root, &dev->debugfs.sched_perf_disable); + debugfs_create_u32("sched_type", 0644, debugfs->root, &dev->debugfs.sched= _type); +} + +void mfc_deinit_debugfs(struct mfc_dev *dev) +{ + struct mfc_debugfs *debugfs =3D &dev->debugfs; + + debugfs_remove_recursive(debugfs->root); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.h b/driv= ers/media/platform/samsung/exynos-mfc/mfc_debugfs.h new file mode 100644 index 000000000000..bbefd046587b --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_debugfs.h File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_DEBUGFS_H +#define __MFC_DEBUGFS_H __FILE__ + +#include "base/mfc_common.h" + +void mfc_init_debugfs(struct mfc_dev *dev); +void mfc_deinit_debugfs(struct mfc_dev *dev); + +#endif /* __MFC_DEBUGFS_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 5CB782641FB for ; Tue, 30 Sep 2025 03:55:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204555; cv=none; b=Ox78Qa6Tr7sosImDgZXp3tIz1Ztga4YyO0dmvYSziTIuHuZoTh8yBHmNZgvYX10do1CtoX02W5hlM5VfAOxlO3FrD423WejllYHqDrMDZESCmWO04j2PhB+nh8PAv/kkTRQ1pf0AUOr3YhUoe6BQTp+/9ZV7lCCZPvno8c1O1hk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204555; c=relaxed/simple; bh=R2RkIT6srYKyxZbLUeJ5/Yj/Rk2mIpJs5FFU+imvIDA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=k+iOiG7hYRNtvR3Xf/HI+0B7b4BWyVqYb6FywMfl7zVWHidNITK6sokYtK5k+9K8P4XecIMDQ7Cl9+DPYLVa5XwNxSI3FKQavwNIcuilCGql/ENSMtkou/aMR5IhPRbmBdGJan1H+lZbj41SSUOd30ZVL9u0kD2ut1knkWtz7Dw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=Q4w8qIys; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="Q4w8qIys" Received: from epcas5p1.samsung.com (unknown [182.195.41.39]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035549epoutp024690dda227db66fa4c0e09c9b6f157c9~p8zellKlg2611626116epoutp02R for ; Tue, 30 Sep 2025 03:55:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035549epoutp024690dda227db66fa4c0e09c9b6f157c9~p8zellKlg2611626116epoutp02R DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204549; bh=bpej8MLmB7L59W41wU3Kn4I7jm3vAl8REcpYHgaZwlw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Q4w8qIysQII7bGxHMSKxu3kYUJ1GUz760JFxTAnIHEYHAPBWWW1GCIp/t7/vkaKW5 m9JDlWAb1zrC3KXn0OnizmR6jyulfSsKtcNc/LousdQJGANDQVbuZNgEk2EJ0m/f6r BmC1du3BIQS75I7V0GmRNtkrDeSzWmssO30dSsHE= Received: from epsnrtp03.localdomain (unknown [182.195.42.155]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035548epcas5p46dfa08c01a201a23c2633d63bc46e30b~p8zdg597R2781827818epcas5p4g; Tue, 30 Sep 2025 03:55:48 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.89]) by epsnrtp03.localdomain (Postfix) with ESMTP id 4cbPNg4gsQz3hhT3; Tue, 30 Sep 2025 03:55:47 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035547epcas5p11f579ca8f8a83767cbb2b632fa9cd6f7~p8zcEEX2V2443624436epcas5p1o; Tue, 30 Sep 2025 03:55:47 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035540epsmtip1f725bce14fa9813bc4427b5983b76b6e~p8zWFMOT52908429084epsmtip1o; Tue, 30 Sep 2025 03:55:40 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 07/29] media: mfc: Add MFC core command, hwlock, ISR and run functionalities Date: Tue, 30 Sep 2025 09:33:26 +0530 Message-Id: <20250930040348.3702923-8-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035547epcas5p11f579ca8f8a83767cbb2b632fa9cd6f7 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035547epcas5p11f579ca8f8a83767cbb2b632fa9cd6f7 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce functions that define the core command interface, hardware=E2=80= =91lock management, interrupt handling, and run=E2=80=91time control for the MFC dr= iver. mfc_core_cmd.h API for core initialization, power management, instance lifecycle (open/close/abort) and cache flushing. mfc_core_hwlock.h APIs for initializing, acquiring and releasing hardware locks for both device and context structures. mfc_core_isr.h ISR prototypes for top=E2=80=91half and threaded interrupt handling. mfc_core_run.h Functions for cache flush, hardware initialization, de=E2=80=91initialization, and power state transitions (sleep/wakeup). All files reference the common core structures via base/mfc_common.h. This patch prepares the interface layer for upcoming implementation changes in the MFC driver. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/mfc_core_cmd.c | 158 ++++++++ .../samsung/exynos-mfc/mfc_core_cmd.h | 26 ++ .../samsung/exynos-mfc/mfc_core_hwlock.c | 336 ++++++++++++++++++ .../samsung/exynos-mfc/mfc_core_hwlock.h | 72 ++++ .../samsung/exynos-mfc/mfc_core_intlock.c | 98 +++++ .../samsung/exynos-mfc/mfc_core_intlock.h | 20 ++ .../samsung/exynos-mfc/mfc_core_isr.c | 124 +++++++ .../samsung/exynos-mfc/mfc_core_isr.h | 22 ++ .../samsung/exynos-mfc/mfc_core_run.c | 265 ++++++++++++++ .../samsung/exynos-mfc/mfc_core_run.h | 26 ++ 10 files changed, 1147 insertions(+) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlo= ck.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlo= ck.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_intl= ock.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_intl= ock.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c new file mode 100644 index 000000000000..5be9fd086a93 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_cmd.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_cmd.h" +#include "mfc_core_intlock.h" + +#include "mfc_core_hw_reg_api.h" + +#include "base/mfc_utils.h" +#include "base/mfc_buf.h" + +void mfc_core_cmd_sys_init(struct mfc_core *core, + enum mfc_buf_usage_type buf_type) +{ + struct mfc_dev *dev =3D core->dev; + struct mfc_ctx_buf_size *buf_size; + struct mfc_special_buf *ctx_buf; + + mfc_core_debug_enter(); + + mfc_core_clean_dev_int_flags(core); + + buf_size =3D dev->variant->buf_size->ctx_buf; + ctx_buf =3D &core->common_ctx_buf; + MFC_CORE_WRITEL(ctx_buf->daddr, MFC_REG_CONTEXT_MEM_ADDR); + MFC_CORE_WRITEL(buf_size->dev_ctx, MFC_REG_CONTEXT_MEM_SIZE); + + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_SYS_INIT); + + mfc_core_debug_leave(); +} + +void mfc_core_cmd_sleep(struct mfc_core *core) +{ + mfc_core_debug_enter(); + + mfc_core_clean_dev_int_flags(core); + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_SLEEP); + + mfc_core_debug_leave(); +} + +void mfc_core_cmd_wakeup(struct mfc_core *core) +{ + mfc_core_debug_enter(); + + mfc_core_clean_dev_int_flags(core); + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_WAKEUP); + + mfc_core_debug_leave(); +} + +/* Open a new instance and get its number */ +void mfc_core_cmd_open_inst(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + unsigned int reg; + + mfc_debug_enter(); + + /* Preparing decoding - getting instance number */ + mfc_debug(2, "Getting instance number\n"); + mfc_clean_core_ctx_int_flags(core_ctx); + + reg =3D MFC_CORE_READL(MFC_REG_CODEC_CONTROL); + /* Clear OTF_CONTROL[2:1] & OTF_DEBUG[3] */ + reg &=3D ~(0x7 << 1); + + MFC_CORE_WRITEL(reg, MFC_REG_CODEC_CONTROL); + + mfc_debug(2, "Requested codec mode: %d\n", ctx->codec_mode); + reg =3D ctx->codec_mode & MFC_REG_CODEC_TYPE_MASK; + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->mem_clear)) { + reg |=3D BIT(MFC_REG_CLEAR_CTX_MEM_SHIFT); + mfc_debug(2, "Enable to clear context memory: %#x\n", reg); + } + MFC_CORE_WRITEL(reg, MFC_REG_CODEC_TYPE); + + MFC_CORE_WRITEL(core_ctx->instance_ctx_buf.daddr, MFC_REG_CONTEXT_MEM_ADD= R); + MFC_CORE_WRITEL(core_ctx->instance_ctx_buf.size, MFC_REG_CONTEXT_MEM_SIZE= ); + + if (dev->debugfs.feature_option & MFC_OPTION_SET_MULTI_CORE_FORCE) { + reg =3D MFC_CORE_READL(MFC_REG_DBG_INFO_ENABLE); + reg |=3D BIT(MFC_REG_DBG_INFO_TWO_MFC_FORCING_SHIFT); + MFC_CORE_WRITEL(reg, MFC_REG_DBG_INFO_ENABLE); + mfc_info("[2CORE] Forcely enable multi core mode %#x\n", + MFC_CORE_READL(MFC_REG_DBG_INFO_ENABLE)); + } + + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_OPEN_INSTANCE); + + mfc_debug_leave(); +} + +/* Close instance */ +int mfc_core_cmd_close_inst(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + + mfc_debug_enter(); + + /* Closing decoding instance */ + mfc_debug(2, "Returning instance number\n"); + mfc_clean_core_ctx_int_flags(core_ctx); + if (core_ctx->state =3D=3D MFCINST_FREE) { + mfc_err("ctx already free status\n"); + return -EINVAL; + } + + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_CLOSE_INSTANCE); + + mfc_debug_leave(); + + return 0; +} + +void mfc_core_cmd_abort_inst(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + + mfc_clean_core_ctx_int_flags(core_ctx); + + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_NAL_ABORT); +} + +void mfc_core_cmd_cache_flush(struct mfc_core *core) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[core->curr_core_ctx]; + struct mfc_ctx *ctx =3D core_ctx->ctx; + u32 reg =3D 0; + + mutex_lock(&ctx->op_mode_mutex); + + reg =3D MFC_CORE_READL(MFC_REG_D_NAL_START_OPTIONS); + reg &=3D ~BIT(MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + if (IS_MULTI_MODE(ctx) || ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2) + reg |=3D BIT(MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + else + reg |=3D (0 << MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + MFC_CORE_WRITEL(reg, MFC_REG_D_NAL_START_OPTIONS); + mfc_debug(3, "NAL_START_OPTIONS: %#x, op_mode: %d\n", reg, ctx->op_mode); + mutex_unlock(&ctx->op_mode_mutex); + + mfc_core_clean_dev_int_flags(core); + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_CACHE_FLUSH); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h new file mode 100644 index 000000000000..86b82d63f3b5 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_cmd.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_CMD_H +#define __MFC_CORE_CMD_H __FILE__ + +#include "base/mfc_common.h" + +void mfc_core_cmd_sys_init(struct mfc_core *core, + enum mfc_buf_usage_type buf_type); +void mfc_core_cmd_sleep(struct mfc_core *core); +void mfc_core_cmd_wakeup(struct mfc_core *core); + +void mfc_core_cmd_open_inst(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_cmd_close_inst(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_core_cmd_abort_inst(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_core_cmd_cache_flush(struct mfc_core *core); +#endif /* __MFC_CORE_CMD_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c b/= drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c new file mode 100644 index 000000000000..4de836543e82 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_hwlock.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_rm.h" + +#include "mfc_core_hwlock.h" +#include "mfc_core_run.h" +#include "mfc_core_pm.h" +#include "mfc_core_sync.h" + +#include "mfc_core_cmd.h" +#include "mfc_core_hw_reg_api.h" + +#include "base/mfc_utils.h" +#include "base/mfc_sched.h" + +static inline void __mfc_print_hwlock(struct mfc_core *core) +{ + mfc_core_debug(3, "%s%lx, %sx%lx, %s %d, %s %d, %s %d\n", + "hwlock.dev =3D 0x", core->hwlock.dev, + "bits =3D 0x", core->hwlock.bits, + "owned_by_irq =3D", core->hwlock.owned_by_irq, + "wl_count =3D", core->hwlock.wl_count, + "transfer_owner =3D", core->hwlock.transfer_owner); +} + +void mfc_core_init_hwlock(struct mfc_core *core) +{ + unsigned long flags; + + spin_lock_init(&core->hwlock.lock); + spin_lock_irqsave(&core->hwlock.lock, flags); + + INIT_LIST_HEAD(&core->hwlock.waiting_list); + core->hwlock.wl_count =3D 0; + core->hwlock.bits =3D 0; + core->hwlock.dev =3D 0; + core->hwlock.owned_by_irq =3D 0; + core->hwlock.transfer_owner =3D 0; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); +} + +static void __mfc_remove_listable_wq_core(struct mfc_core *core) +{ + struct mfc_listable_wq *listable_wq; + unsigned long flags; + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + list_for_each_entry(listable_wq, &core->hwlock.waiting_list, list) { + if (!listable_wq->core) + continue; + + mfc_core_debug(2, "Found dev and will delete it!\n"); + + list_del(&listable_wq->list); + core->hwlock.wl_count--; + + break; + } + + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); +} + +static void __mfc_remove_listable_wq_ctx(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_listable_wq *listable_wq; + unsigned long flags; + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + list_for_each_entry(listable_wq, &core->hwlock.waiting_list, list) { + if (!listable_wq->core_ctx) + continue; + + if (listable_wq->core_ctx->num =3D=3D core_ctx->num) { + mfc_debug(2, "Found ctx and will delete it (%d)!\n", core_ctx->num); + + list_del(&listable_wq->list); + core->hwlock.wl_count--; + break; + } + } + + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); +} + +/* + * Return value description + * 0: succeeded to get hwlock + * -EIO: failed to get hwlock (time out) + */ +int mfc_core_get_hwlock_dev(struct mfc_core *core) +{ + int ret =3D 0; + unsigned long flags; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_info("[MSR] Couldn't lock HW. It's Error state\n"); + return 0; + } + + if (core->shutdown) { + mfc_core_info("Couldn't lock HW. Shutdown was called\n"); + return -EINVAL; + } + + mutex_lock(&core->hwlock_wq.wait_mutex); + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + if (core->hwlock.bits !=3D 0 || core->hwlock.dev !=3D 0) { + list_add_tail(&core->hwlock_wq.list, &core->hwlock.waiting_list); + core->hwlock.wl_count++; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_core_debug(2, "Waiting for hwlock to be released\n"); + + ret =3D wait_event_timeout + (core->hwlock_wq.wait_queue, + (core->hwlock.transfer_owner =3D=3D 1 && + (core->hwlock.dev =3D=3D 1)), + msecs_to_jiffies(MFC_HWLOCK_TIMEOUT)); + + core->hwlock.transfer_owner =3D 0; + __mfc_remove_listable_wq_core(core); + if (!ret) { + mfc_core_err("Woken up but timed out\n"); + __mfc_print_hwlock(core); + mutex_unlock(&core->hwlock_wq.wait_mutex); + return -EIO; + } + mfc_core_debug(2, "Woken up and got hwlock\n"); + __mfc_print_hwlock(core); + mutex_unlock(&core->hwlock_wq.wait_mutex); + } else { + core->hwlock.bits =3D 0; + core->hwlock.dev =3D 1; + core->hwlock.owned_by_irq =3D 0; + + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + mutex_unlock(&core->hwlock_wq.wait_mutex); + } + + return 0; +} + +/* + * Return value description + * 0: succeeded to get hwlock + * -EIO: failed to get hwlock (time out) + */ +int mfc_core_get_hwlock_ctx(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + int ret =3D 0; + unsigned long flags; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_info("[MSR] Couldn't lock HW. It's Error state\n"); + return 0; + } + + if (core->shutdown) { + mfc_info("Couldn't lock HW. Shutdown was called\n"); + return -EINVAL; + } + + mutex_lock(&core_ctx->hwlock_wq.wait_mutex); + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + if (core->hwlock.bits !=3D 0 || core->hwlock.dev !=3D 0) { + list_add_tail(&core_ctx->hwlock_wq.list, &core->hwlock.waiting_list); + core->hwlock.wl_count++; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_debug(2, "core_ctx[%d] Waiting for hwlock to be released\n", + core_ctx->num); + + ret =3D wait_event_timeout + (core_ctx->hwlock_wq.wait_queue, + (core->hwlock.transfer_owner =3D=3D 1 && + test_bit(core_ctx->num, &core->hwlock.bits)), + msecs_to_jiffies(MFC_HWLOCK_TIMEOUT)); + + core->hwlock.transfer_owner =3D 0; + __mfc_remove_listable_wq_ctx(core_ctx); + if (!ret) { + mfc_err("Woken up but timed out\n"); + __mfc_print_hwlock(core); + mutex_unlock(&core_ctx->hwlock_wq.wait_mutex); + return -EIO; + } + mfc_debug(2, "Woken up and got hwlock\n"); + __mfc_print_hwlock(core); + mutex_unlock(&core_ctx->hwlock_wq.wait_mutex); + } else { + core->hwlock.bits =3D 0; + core->hwlock.dev =3D 0; + set_bit(core_ctx->num, &core->hwlock.bits); + core->hwlock.owned_by_irq =3D 0; + + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + mutex_unlock(&core_ctx->hwlock_wq.wait_mutex); + } + return 0; +} + +static void __mfc_release_hwlock(struct mfc_core *core) +{ + struct mfc_listable_wq *listable_wq; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_debug(2, "[MSR] Couldn't wakeup module. It's Error state\n"); + } else if (core->shutdown) { + mfc_core_debug(2, "Couldn't wakeup module. Shutdown was called\n"); + } else if (list_empty(&core->hwlock.waiting_list)) { + mfc_core_debug(2, "No waiting module\n"); + } else { + mfc_core_debug(2, "There is a waiting module\n"); + listable_wq =3D list_entry(core->hwlock.waiting_list.next, + struct mfc_listable_wq, list); + list_del(&listable_wq->list); + core->hwlock.wl_count--; + + if (listable_wq->core) { + mfc_core_debug(2, "Waking up core\n"); + core->hwlock.dev =3D 1; + } else { + mfc_core_debug(2, "Waking up another ctx\n"); + set_bit(listable_wq->core_ctx->num, &core->hwlock.bits); + } + + core->hwlock.transfer_owner =3D 1; + + wake_up(&listable_wq->wait_queue); + } + + __mfc_print_hwlock(core); +} + +void mfc_core_release_hwlock_dev(struct mfc_core *core) +{ + unsigned long flags; + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + core->hwlock.dev =3D 0; + core->hwlock.owned_by_irq =3D 0; + + __mfc_release_hwlock(core); + + spin_unlock_irqrestore(&core->hwlock.lock, flags); +} + +void mfc_core_release_hwlock_ctx(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + unsigned long flags; + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + clear_bit(core_ctx->num, &core->hwlock.bits); + core->hwlock.owned_by_irq =3D 0; + + __mfc_release_hwlock(core); + + spin_unlock_irqrestore(&core->hwlock.lock, flags); +} + +/* Run an operation on hardware */ +int mfc_core_just_run(struct mfc_core *core, int new_ctx_index) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[new_ctx_index]; + struct mfc_ctx *ctx =3D core_ctx->ctx; + unsigned long flags; + unsigned int ret =3D 0; + int prio; + + mfc_core_idle_update_hw_run(core, ctx); + + if (core->state =3D=3D MFCCORE_ERROR || core_ctx->state =3D=3D MFCINST_ER= ROR) { + mfc_info("[MSR] Couldn't run HW. It's Error state\n"); + return 0; + } + + if (core_ctx->state =3D=3D MFCINST_RUNNING) + mfc_clean_core_ctx_int_flags(core_ctx); + + mfc_debug(2, "New context: %d\n", new_ctx_index); + core->curr_core_ctx =3D core_ctx->num; + if (core->sched_type =3D=3D MFC_SCHED_PRIO) { + spin_lock_irqsave(&core->prio_work_lock, flags); + prio =3D mfc_get_prio(core, ctx->rt, ctx->prio); + spin_unlock_irqrestore(&core->prio_work_lock, flags); + + core->last_core_ctx[prio] =3D core_ctx->num; + core->next_ctx_idx =3D -1; + } + + mfc_debug(2, "core_ctx->state =3D %d\n", core_ctx->state); + /* Last frame has already been sent to MFC + * Now obtaining frames from MFC buffer + */ + + mfc_debug(2, "continue_clock_on =3D %d\n", core->continue_clock_on); + if (!core->continue_clock_on) + mfc_core_pm_clock_on(core, 1); + else + core->continue_clock_on =3D false; + + return ret; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h b/= drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h new file mode 100644 index 000000000000..35f34f306d7d --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_hwlock.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_HWLOCK_H +#define __MFC_CORE_HWLOCK_H __FILE__ + +#include "base/mfc_common.h" + +static inline void mfc_core_init_listable_wq_dev(struct mfc_core *core) +{ + if (!core) { + mfc_pr_err("no mfc core device to run\n"); + return; + } + + INIT_LIST_HEAD(&core->hwlock_wq.list); + init_waitqueue_head(&core->hwlock_wq.wait_queue); + mutex_init(&core->hwlock_wq.wait_mutex); + core->hwlock_wq.core_ctx =3D NULL; + core->hwlock_wq.core =3D core; +} + +static inline void mfc_core_init_listable_wq_ctx(struct mfc_core_ctx *core= _ctx) +{ + if (!core_ctx) { + mfc_pr_err("no mfc core context to run\n"); + return; + } + + INIT_LIST_HEAD(&core_ctx->hwlock_wq.list); + init_waitqueue_head(&core_ctx->hwlock_wq.wait_queue); + mutex_init(&core_ctx->hwlock_wq.wait_mutex); + core_ctx->hwlock_wq.core_ctx =3D core_ctx; + core_ctx->hwlock_wq.core =3D NULL; +} + +static inline void mfc_core_destroy_listable_wq_core(struct mfc_core *core) +{ + if (!core) { + mfc_pr_err("no mfc core device to run\n"); + return; + } + + mutex_destroy(&core->hwlock_wq.wait_mutex); +} + +static inline void mfc_core_destroy_listable_wq_ctx(struct mfc_core_ctx *c= ore_ctx) +{ + if (!core_ctx) { + mfc_pr_err("no mfc core context to run\n"); + return; + } + + mutex_destroy(&core_ctx->hwlock_wq.wait_mutex); +} + +void mfc_core_init_hwlock(struct mfc_core *core); + +int mfc_core_get_hwlock_dev(struct mfc_core *core); +int mfc_core_get_hwlock_ctx(struct mfc_core_ctx *core_ctx); + +void mfc_core_release_hwlock_dev(struct mfc_core *core); +void mfc_core_release_hwlock_ctx(struct mfc_core_ctx *core_ctx); +int mfc_core_just_run(struct mfc_core *core, int new_ctx_index); +#endif /* __MFC_CORE_HWLOCK_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_intlock.c b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_intlock.c new file mode 100644 index 000000000000..facb7255c400 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_intlock.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_intlock.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_intlock.h" +#include "mfc_core_isr.h" + +void mfc_clear_core_intlock(struct mfc_ctx *ctx) +{ + mutex_lock(&ctx->intlock.core_mutex); + + ctx->intlock.bits =3D 0; + + mutex_unlock(&ctx->intlock.core_mutex); +} + +int mfc_get_core_intlock(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_dev *dev =3D core->dev; + struct mfc_ctx *ctx =3D core_ctx->ctx; + + if (!(IS_TWO_MODE2(ctx) && core_ctx->state =3D=3D MFCINST_RUNNING)) + return 0; + + mutex_lock(&ctx->intlock.core_mutex); + + if (ctx->intlock.lock) { + mfc_debug(2, "[2CORE] previous interrupt isn't handled yet\n"); + set_bit(core->id, &ctx->intlock.pending); + mutex_unlock(&ctx->intlock.core_mutex); + return -1; + } + + /* + * 1) First interrupt case, should be core0. + * 2) Previous interrupt number should be different with current core. + */ + if ((!ctx->intlock.bits && core->id !=3D 0) || + ctx->intlock.bits & BIT(core->id)) { + mfc_debug(2, "[2CORE] interrupt reverse, MFC-%d isr should be delayed ha= ndled\n", + core->id); + MFC_TRACE_RM("[c:%d] MFC-%d ISR reverse\n", ctx->num, core->id); + set_bit(core->id, &ctx->intlock.pending); + mutex_unlock(&ctx->intlock.core_mutex); + return -1; + } + + ctx->intlock.lock =3D 1; + ctx->intlock.bits =3D 0; + set_bit(core->id, &ctx->intlock.bits); + mfc_debug(3, "[2CORE] get core int lock: %#08lx\n", ctx->intlock.bits); + + mutex_unlock(&ctx->intlock.core_mutex); + + return 0; +} + +void mfc_release_core_intlock(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_dev *dev =3D core->dev; + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_core *pending_core =3D NULL; + int i; + + mutex_lock(&ctx->intlock.core_mutex); + + if (!ctx->intlock.lock) { + mfc_debug(4, "[2CORE] have been didn't get intlock\n"); + mutex_unlock(&ctx->intlock.core_mutex); + return; + } + + ctx->intlock.lock =3D 0; + mfc_debug(3, "[2CORE] release core int lock\n"); + + for (i =3D 0; i < dev->num_core; i++) { + if (ctx->intlock.pending & BIT(i)) { + pending_core =3D dev->core[i]; + clear_bit(i, &ctx->intlock.pending); + mfc_debug(2, "[2CORE] interrupt pending clear\n"); + MFC_TRACE_RM("[c:%d] MFC-%d ISR delayed handle\n", ctx->num, core->id); + } + } + + mutex_unlock(&ctx->intlock.core_mutex); + + if (pending_core) + mfc_core_irq(pending_core->irq, pending_core); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_intlock.h b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_intlock.h new file mode 100644 index 000000000000..e1e2e9bdaabb --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_intlock.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_intlock.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_INTLOCK_H +#define __MFC_CORE_INTLOCK_H __FILE__ + +#include "base/mfc_common.h" + +void mfc_clear_core_intlock(struct mfc_ctx *ctx); +int mfc_get_core_intlock(struct mfc_core_ctx *core_ctx); +void mfc_release_core_intlock(struct mfc_core_ctx *core_ctx); + +#endif /* __MFC_CORE_INTLOCK_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_isr.c new file mode 100644 index 000000000000..4c6f531ffc02 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_isr.h" + +#include "mfc_core_hwlock.h" +#include "mfc_core_intlock.h" +#include "mfc_core_pm.h" + +#include "mfc_core_hw_reg_api.h" +#include "mfc_core_reg_api.h" + +#include "base/mfc_utils.h" + +static inline int __mfc_core_is_err_condition(unsigned int err) +{ + switch (err) { + case MFC_REG_ERR_NO_AVAILABLE_DPB: + case MFC_REG_ERR_INSUFFICIENT_DPB_SIZE: + case MFC_REG_ERR_INSUFFICIENT_NUM_DPB: + case MFC_REG_ERR_INSUFFICIENT_MV_BUF_SIZE: + case MFC_REG_ERR_INSUFFICIENT_SCRATCH_BUF_SIZE: + case MFC_REG_ERR_UNDEFINED_EXCEPTION: + return 1; + default: + return 0; + } +} + +irqreturn_t mfc_core_top_half_irq(int irq, void *priv) +{ + struct mfc_core *core =3D priv; + struct mfc_core_ctx *core_ctx; + unsigned int err; + unsigned int reason; + + core_ctx =3D core->core_ctx[core->curr_core_ctx]; + if (!core_ctx) { + mfc_core_err("no mfc context to run\n"); + return IRQ_WAKE_THREAD; + } + + reason =3D mfc_core_get_int_reason(); + err =3D mfc_core_get_int_err(); + + core->last_int =3D reason; + core->last_int_time =3D ktime_to_timespec64(ktime_get()); + + mfc_debug(2, "[c:%d] Int reason: %d (err: %d, warn: %d)\n", + core->curr_core_ctx, reason, mfc_get_err(err), mfc_get_warn(err)); + MFC_TRACE_CORE_CTX("<< INT(top): %d\n", reason); + MFC_TRACE_LOG_CORE("I%d", reason); + + return IRQ_WAKE_THREAD; +} + +static int __mfc_irq_dev(struct mfc_core *core, unsigned int reason, unsig= ned int err) +{ + switch (reason) { + case MFC_REG_R2H_CMD_CACHE_FLUSH_RET: + case MFC_REG_R2H_CMD_SYS_INIT_RET: + case MFC_REG_R2H_CMD_FW_STATUS_RET: + case MFC_REG_R2H_CMD_SLEEP_RET: + case MFC_REG_R2H_CMD_WAKEUP_RET: + mfc_core_clear_int(); + mfc_wake_up_core(core, reason, err); + return 0; + } + + return 1; +} + +/* Interrupt processing */ +irqreturn_t mfc_core_irq(int irq, void *priv) +{ + struct mfc_core *core =3D priv; + struct mfc_core_ctx *core_ctx; + struct mfc_ctx *ctx; + unsigned int reason; + unsigned int err; + int ret =3D -1; + + mfc_core_debug_enter(); + + if (mfc_core_get_pwr_ref_cnt(core) =3D=3D 0) { + mfc_core_err("no mfc power on\n"); + goto irq_end; + } + + /* Get the reason of interrupt and the error code */ + reason =3D mfc_core_get_int_reason(); + err =3D mfc_core_get_int_err(); + mfc_core_debug(1, "[c:%d] Int reason: %d (err: %d, warn: %d)\n", + core->curr_core_ctx, reason, + mfc_get_err(err), mfc_get_warn(err)); + MFC_TRACE_CORE("<< INT: %d (err: %d)\n", reason, err); + + core->preempt_core_ctx =3D MFC_NO_INSTANCE_SET; + + if (core->dev->debugfs.dbg_enable && reason !=3D MFC_REG_R2H_CMD_QUEUE_DO= NE_RET) + mfc_core_dbg_disable(core); + + if (__mfc_core_is_err_condition(err)) { + mfc_err("Interrupt Error Value %d", err); + WARN_ON(1); + } + + ret =3D __mfc_irq_dev(core, reason, err); + if (!ret) + goto irq_end; + mfc_ctx_info("not implemented context irq ctx"); + +irq_end: + mfc_core_debug_leave(); + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_isr.h new file mode 100644 index 000000000000..046b20e6d4c2 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_isr.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_ISR_H +#define __MFC_CORE_ISR_H __FILE__ + +#include + +#include "mfc_rm.h" + +#include "base/mfc_utils.h" + +irqreturn_t mfc_core_top_half_irq(int irq, void *priv); +irqreturn_t mfc_core_irq(int irq, void *priv); +#endif /* __MFC_CORE_ISR_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_run.c new file mode 100644 index 000000000000..fd7ebe95e715 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_run.c File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_run.h" + +#include "mfc_core_pm.h" +#include "mfc_core_sync.h" + +#include "mfc_core_cmd.h" +#include "mfc_core_hw_reg_api.h" + +#include "base/mfc_utils.h" +#include "base/mfc_mem.h" + +void mfc_core_run_cache_flush(struct mfc_core *core, + enum mfc_do_cache_flush do_cache_flush, + int reg_clear) +{ + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_info("[MSR] Couldn't lock HW. It's Error state\n"); + return; + } + + if (do_cache_flush =3D=3D MFC_CACHEFLUSH) { + mfc_core_cmd_cache_flush(core); + if (mfc_wait_for_done_core(core, MFC_REG_R2H_CMD_CACHE_FLUSH_RET)) { + mfc_core_err("Failed to CACHE_FLUSH\n"); + core->logging_data->cause |=3D BIT(MFC_CAUSE_FAIL_CACHE_FLUSH); + } + } else if (do_cache_flush =3D=3D MFC_NO_CACHEFLUSH) { + mfc_core_debug(2, "F/W has already done cache flush with prediction\n"); + } + + /* When init_hw(), reg_clear is required between cache flush and (un)prot= ection */ + if (reg_clear) { + mfc_core_reg_clear(core); + mfc_core_debug(2, "Done register clear\n"); + } +} + +/* Initialize hardware */ +int mfc_core_run_init_hw(struct mfc_core *core) +{ + enum mfc_buf_usage_type buf_type; + int fw_ver; + int ret =3D 0; + + mfc_core_debug(2, "F/W initialize start\n"); + + /* 0. MFC reset */ + ret =3D mfc_core_pm_clock_on(core, 0); + if (ret) { + mfc_core_err("Failed to enable clock before reset(%d)\n", ret); + return ret; + } + + mfc_core_pm_clock_get(core); + + mfc_core_run_cache_flush(core, MFC_NO_CACHEFLUSH, 1); + + mfc_core_reset_mfc(core); + mfc_core_debug(2, "Done MFC reset\n"); + + /* 1. Set DRAM base Addr */ + mfc_core_set_risc_base_addr(core); + + /* 2. Release reset signal to the RISC */ + mfc_core_risc_on(core); + + mfc_core_debug(2, "Will now wait for completion of firmware transfer\n"); + if (mfc_wait_for_done_core(core, MFC_REG_R2H_CMD_FW_STATUS_RET)) { + mfc_core_err("Failed to RISC_ON\n"); + mfc_core_clean_dev_int_flags(core); + ret =3D -EIO; + goto err_init_hw; + } + + /* 3. Initialize firmware */ + mfc_core_cmd_sys_init(core, buf_type); + + mfc_core_debug(2, "Ok, now will write a command to init the system\n"); + if (mfc_wait_for_done_core(core, MFC_REG_R2H_CMD_SYS_INIT_RET)) { + mfc_core_err("Failed to SYS_INIT\n"); + mfc_core_clean_dev_int_flags(core); + ret =3D -EIO; + goto err_init_hw; + } + + core->int_condition =3D 0; + if (core->int_err !=3D 0 || core->int_reason !=3D MFC_REG_R2H_CMD_SYS_INI= T_RET) { + /* Failure. */ + mfc_core_err("Failed to init firmware - error: %d, int: %d\n", + core->int_err, core->int_reason); + ret =3D -EIO; + goto err_init_hw; + } + + core->fw.fimv_info =3D mfc_core_get_fimv_info(); + if (core->fw.fimv_info !=3D 'D' && core->fw.fimv_info !=3D 'E') + core->fw.fimv_info =3D 'N'; + + mfc_core_info("[F/W] MFC v%x, %02xyy %02xmm %02xdd (%c)\n", + core->core_pdata->ip_ver, + mfc_core_get_fw_ver_year(), + mfc_core_get_fw_ver_month(), + mfc_core_get_fw_ver_date(), + core->fw.fimv_info); + + core->fw.date =3D mfc_core_get_fw_ver_all(); + /* Check MFC version and F/W version */ + fw_ver =3D mfc_core_get_mfc_version(); + if ((fw_ver & MFC_REG_MFC_VER_MAJOR_MASK) !=3D core->core_pdata->ip_ver) { + mfc_core_err("Invalid F/W version(0x%x) for MFC H/W(0x%x)\n", + fw_ver, core->core_pdata->ip_ver); + ret =3D -EIO; + goto err_init_hw; + } + core->dev->fw_changed_info =3D (fw_ver & MFC_REG_MFC_VER_MINOR_MASK); + + mfc_core_change_fw_state(core, MFC_FW_INITIALIZED, 1); + +err_init_hw: + mfc_core_pm_clock_off(core, 0); + mfc_core_debug_leave(); + + return ret; +} + +/* Deinitialize hardware */ +void mfc_core_run_deinit_hw(struct mfc_core *core) +{ + int ret; + + mfc_core_debug(2, "mfc deinit start\n"); + + ret =3D mfc_core_pm_clock_on(core, 0); + if (ret) { + mfc_core_err("Failed to enable clock before reset(%d)\n", ret); + return; + } + + mfc_core_mfc_off(core); + + mfc_core_pm_clock_off(core, 0); + + mfc_core_debug(2, "mfc deinit completed\n"); +} + +int mfc_core_run_sleep(struct mfc_core *core) +{ + struct mfc_core_ctx *core_ctx; + int i; + + mfc_core_debug_enter(); + + core_ctx =3D core->core_ctx[core->curr_core_ctx]; + if (!core_ctx) { + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (core->core_ctx[i]) { + core_ctx =3D core->core_ctx[i]; + break; + } + } + + if (!core_ctx) { + mfc_core_err("no mfc context to run\n"); + return -EINVAL; + } + mfc_info("ctx is changed %d -> %d\n", + core->curr_core_ctx, core_ctx->num); + + core->curr_core_ctx =3D core_ctx->num; + } + + mfc_core_pm_clock_on(core, 0); + mfc_core_cmd_sleep(core); + + if (mfc_wait_for_done_core(core, MFC_REG_R2H_CMD_SLEEP_RET)) { + mfc_err("Failed to SLEEP\n"); + core->logging_data->cause |=3D BIT(MFC_CAUSE_FAIL_SLEEP); + return -EBUSY; + } + + core->int_condition =3D 0; + if (core->int_err !=3D 0 || core->int_reason !=3D MFC_REG_R2H_CMD_SLEEP_R= ET) { + /* Failure. */ + mfc_err("Failed to sleep - error: %d, int: %d\n", + core->int_err, core->int_reason); + return -EBUSY; + } + + core->sleep =3D 1; + + mfc_core_mfc_always_off(core); + mfc_core_pm_clock_off(core, 0); + + mfc_core_debug_leave(); + + return 0; +} + +int mfc_core_run_wakeup(struct mfc_core *core) +{ + int ret =3D 0; + + mfc_core_debug_enter(); + + /* 0. MFC reset */ + ret =3D mfc_core_pm_clock_on(core, 0); + if (ret) { + mfc_core_err("Failed to enable clock before reset(%d)\n", ret); + return ret; + } + mfc_core_reg_clear(core); + mfc_core_debug(2, "Done register clear\n"); + + mfc_core_reset_mfc(core); + mfc_core_debug(2, "Done MFC reset\n"); + + /* 1. Set DRAM base Addr */ + mfc_core_set_risc_base_addr(core); + + /* 2. Release reset signal to the RISC */ + mfc_core_risc_on(core); + + mfc_core_debug(2, "Will now wait for completion of firmware transfer\n"); + if (mfc_wait_for_done_core(core, MFC_REG_R2H_CMD_FW_STATUS_RET)) { + mfc_core_err("Failed to RISC_ON\n"); + core->logging_data->cause |=3D BIT(MFC_CAUSE_FAIL_RISC_ON); + return -EBUSY; + } + + mfc_core_debug(2, "Ok, now will write a command to wakeup the system\n"); + mfc_core_cmd_wakeup(core); + + mfc_core_debug(2, "Will now wait for completion of firmware wake up\n"); + if (mfc_wait_for_done_core(core, MFC_REG_R2H_CMD_WAKEUP_RET)) { + mfc_core_err("Failed to WAKEUP\n"); + core->logging_data->cause |=3D BIT(MFC_CAUSE_FAIL_WAKEUP); + return -EBUSY; + } + + core->int_condition =3D 0; + if (core->int_err !=3D 0 || core->int_reason !=3D MFC_REG_R2H_CMD_WAKEUP_= RET) { + /* Failure. */ + mfc_core_err("Failed to wakeup - error: %d, int: %d\n", + core->int_err, core->int_reason); + } + + core->sleep =3D 0; + + mfc_core_pm_clock_off(core, 0); + + mfc_core_debug_leave(); + + return ret; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_run.h new file mode 100644 index 000000000000..3d243dc18e15 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_run.h + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_CORE_RUN_H +#define __MFC_CORE_RUN_H __FILE__ + +#include "base/mfc_common.h" + +void mfc_core_run_cache_flush(struct mfc_core *core, + enum mfc_do_cache_flush do_cache_flush, + int reg_clear); + +int mfc_core_run_init_hw(struct mfc_core *core); +void mfc_core_run_deinit_hw(struct mfc_core *core); + +int mfc_core_run_sleep(struct mfc_core *core); +int mfc_core_run_wakeup(struct mfc_core *core); +#endif /* __MFC_CORE_RUN_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 394C4266B6F for ; Tue, 30 Sep 2025 03:55:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204559; cv=none; b=cDmJVr3wsy1ML6R0YE2v9xA/uV0vtHJ2BTV44CD9peVNb12fGBjoib37jVZHxO/SGvqDQNO9Q0X7aevRXGnej8pEOSYAkNNmFdFD9rr2K2VASu/Utq0fngAc/lIC3G6+H9vXF0fJKep/ljnJa85n90foVvoTTm3/kfyNC0W6PbI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204559; c=relaxed/simple; bh=sbQCLL9XamLq6bMQWsAAcIqsgE+MQVBXEThkrDzI3fM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=Edwy1fwifyIW79M7IKimDalRFI9mUnN5LVIP1hSasTxxuEkQMQLtTOqnQngPvnsxD+WSFHTDJ6KA8Qcayocziqk8BNYUEx5KyzMbu+s2rWMfCEXmWRfLEvVP6wRE80YOmkBLzVdTDfabw/IxOkfaOf/6b+D7r5GDxn9NMiqnZFs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=Qe5plXuN; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="Qe5plXuN" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035553epoutp02ec1e18ec031496f697bf487b3bec47a9~p8ziDClzR2603326033epoutp02a for ; Tue, 30 Sep 2025 03:55:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035553epoutp02ec1e18ec031496f697bf487b3bec47a9~p8ziDClzR2603326033epoutp02a DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204553; bh=pD71bFh5A20eGx+67CxEL8hTCmGXdoSx521MaAxw2CQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Qe5plXuNV07ketMeAggjAzy/W6U7I0eYLzhNNlIsFL4Kjf8Qhh7ZbjWtsGZYIZjfi PIy7n0oHgDdniRzLfcxkcFdHvyQuYGO2DdYJWT+UtR4/7coCmWtSZckoM8IjLFUwD1 gKhiVC59/GSvpNKRj+FLgYbofPI2g8GtkzTm2gqs= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPS id 20250930035553epcas5p2321074b61e2ef9126cd7ada7993abe13~p8zhmncjh2437424374epcas5p2T; Tue, 30 Sep 2025 03:55:53 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.93]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPNl6qSbz6B9mD; Tue, 30 Sep 2025 03:55:51 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPA id 20250930035551epcas5p4ee7cb5af08eadb2f5ed6e5eaa06a60a9~p8zf9QgRe2781827818epcas5p4s; Tue, 30 Sep 2025 03:55:51 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035547epsmtip18704e0a45bb73bbc1d5fc52bc9dab4a6~p8zcn17e62931929319epsmtip1M; Tue, 30 Sep 2025 03:55:47 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 08/29] =?UTF-8?q?media:=20mfc:=20Add=20Exynos=E2=80=91MFC?= =?UTF-8?q?=20driver=20probe=20support?= Date: Tue, 30 Sep 2025 09:33:27 +0530 Message-Id: <20250930040348.3702923-9-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035551epcas5p4ee7cb5af08eadb2f5ed6e5eaa06a60a9 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035551epcas5p4ee7cb5af08eadb2f5ed6e5eaa06a60a9 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce a new Kconfig entry VIDEO_EXYNOS_MFC for the Samsung Exynos MFC driver that supports firmware version=E2=80=AF13 and later. Extend the top=E2=80=91level Samsung platform Kconfig to disable the legacy S5P=E2=80=91MFC driver when its firmware version is >=E2=80=AFv12 and to se= lect the new Exynos=E2=80=91MFC driver only when VIDEO_SAMSUNG_S5P_MFC is not enable= d. Add exynos-mfc Kconfig and Makefile for probe functionality and creation of decoder and encoder device files by registering the driver object exynos_mfc.o and other relevant source files. Provide header files mfc_core_ops.h and mfc_rm.h containing core operation prototypes, resource=E2=80=91manager helpers, and core=E2=80=91selection utilities. Add a configurable option MFC_USE_COREDUMP to enable core=E2=80=91dump support for debugging MFC errors. These changes bring support for newer Exynos=E2=80=91based MFC hardware, cleanly separate it from the legacy S5P=E2=80=91MFC driver, and lay the groundwork for future feature development and debugging. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- drivers/media/platform/samsung/Kconfig | 7 + drivers/media/platform/samsung/Makefile | 1 + .../media/platform/samsung/exynos-mfc/Kconfig | 34 + .../platform/samsung/exynos-mfc/Makefile | 24 + .../media/platform/samsung/exynos-mfc/mfc.c | 725 ++++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_core.c | 530 +++++++++++++ .../samsung/exynos-mfc/mfc_core_ops.c | 289 +++++++ .../samsung/exynos-mfc/mfc_core_ops.h | 16 + .../samsung/exynos-mfc/mfc_core_sched_prio.c | 100 +++ .../platform/samsung/exynos-mfc/mfc_rm.c | 142 ++++ .../platform/samsung/exynos-mfc/mfc_rm.h | 56 ++ 11 files changed, 1924 insertions(+) create mode 100644 drivers/media/platform/samsung/exynos-mfc/Kconfig create mode 100644 drivers/media/platform/samsung/exynos-mfc/Makefile create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_sche= d_prio.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_rm.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_rm.h diff --git a/drivers/media/platform/samsung/Kconfig b/drivers/media/platfor= m/samsung/Kconfig index 0e34c5fc1dfc..13790b004f14 100644 --- a/drivers/media/platform/samsung/Kconfig +++ b/drivers/media/platform/samsung/Kconfig @@ -7,4 +7,11 @@ source "drivers/media/platform/samsung/exynos4-is/Kconfig" source "drivers/media/platform/samsung/s3c-camif/Kconfig" source "drivers/media/platform/samsung/s5p-g2d/Kconfig" source "drivers/media/platform/samsung/s5p-jpeg/Kconfig" + +comment "Disable S5P-MFC (FW <=3D v12) to select Exynos-MFC (FW >=3D v13)" + source "drivers/media/platform/samsung/s5p-mfc/Kconfig" + +if !VIDEO_SAMSUNG_S5P_MFC && !(VIDEO_SAMSUNG_S5P_MFC =3D m) +source "drivers/media/platform/samsung/exynos-mfc/Kconfig" +endif diff --git a/drivers/media/platform/samsung/Makefile b/drivers/media/platfo= rm/samsung/Makefile index 21fea3330e4b..afda9713d966 100644 --- a/drivers/media/platform/samsung/Makefile +++ b/drivers/media/platform/samsung/Makefile @@ -5,3 +5,4 @@ obj-y +=3D s3c-camif/ obj-y +=3D s5p-g2d/ obj-y +=3D s5p-jpeg/ obj-y +=3D s5p-mfc/ +obj-y +=3D exynos-mfc/ diff --git a/drivers/media/platform/samsung/exynos-mfc/Kconfig b/drivers/me= dia/platform/samsung/exynos-mfc/Kconfig new file mode 100644 index 000000000000..8b0212100994 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/Kconfig @@ -0,0 +1,34 @@ +config VIDEO_EXYNOS_MFC + tristate "Samsung Exynos MFC Driver (FW v13+)" + default n + depends on !VIDEO_SAMSUNG_S5P_MFC + select DMABUF_HEAPS + select DMABUF_HEAPS_SYSTEM + select DMABUF_HEAPS_CMA if !EXYNOS_IOMMU + select VIDEOBUF2_CORE + select VIDEOBUF2_DMA_SG + help + Enables the Exynos Multi=E2=80=91Format Codec (MFC) driver for the V4L2 + subsystem, providing hardware=E2=80=91accelerated video decoding and + encoding on Samsung Exynos SoCs that ship with firmware version 13 + or newer. The driver offloads video processing from the CPU, + improving performance and reducing power consumption for a wide + range of codecs and picture formats. It integrates with the + DMABUF=E2=80=91HEAPS and videobuf2 frameworks for efficient buffer manag= ement + and should be selected only when the legacy VIDEO_SAMSUNG_S5P_MFC + driver is not present. Disable this option on platforms lacking + the required firmware version or when you prefer the older driver. + +config MFC_USE_COREDUMP + bool "MFC COREDUMP support" + default n + depends on VIDEO_EXYNOS_MFC + help + Enables generation of a core dump when the Exynos MFC driver + encounters a fatal error or hardware fault. The dump captures + internal driver state, register contents, and relevant buffer + information, providing valuable context for post=E2=80=91mortem analysis. + This aids developers and integrators in diagnosing codec crashes + and improving driver stability without requiring a full system + reboot. Use this option only when debugging is needed, as it + may increase memory usage and expose sensitive data. diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile new file mode 100644 index 000000000000..bee95b16ac7c --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -0,0 +1,24 @@ +obj-$(CONFIG_VIDEO_EXYNOS_MFC) :=3D exynos_mfc.o +#Header include +ccflags-y +=3D -I$(srctree)/$(src) + +#Dev interface layer +exynos_mfc-y +=3D mfc.o +#Dev control layer +exynos_mfc-y +=3D mfc_rm.o mfc_debugfs.o +#Core interface layer +exynos_mfc-y +=3D mfc_core.o mfc_core_ops.o mfc_core_isr.o +#Core control layer +exynos_mfc-y +=3D mfc_core_hwlock.o mfc_core_intlock.o mfc_core_run.o +exynos_mfc-y +=3D mfc_core_pm.o +exynos_mfc-y +=3D mfc_core_sync.o mfc_core_sched_prio.o +#Core HW access layer +exynos_mfc-y +=3D mfc_core_cmd.o +exynos_mfc-y +=3D mfc_core_hw_reg_api.o mfc_core_reg_api.o +#Plugin interface layer +#Plugin control layer +#Plugin HW access layer +#Common base layer +exynos_mfc-y +=3D base/mfc_buf.o base/mfc_mem.o +#Tracing +# exynos_mfc-y +=3D trace/mfc_trace_points.o diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc.c b/drivers/medi= a/platform/samsung/exynos-mfc/mfc.c new file mode 100644 index 000000000000..b5b66083cc8b --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc.c @@ -0,0 +1,725 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mfc_rm.h" +#include "mfc_debugfs.h" + +#include "mfc_core_hwlock.h" +#include "mfc_core_run.h" + +#include "mfc_core_hw_reg_api.h" + +#include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" + +#define MFC_NAME "exynos-mfc" +#define MFC_DEC_NAME "exynos-mfc-dec" +#define MFC_ENC_NAME "exynos-mfc-enc" + +struct _mfc_trace g_mfc_trace[MFC_TRACE_COUNT_MAX]; +struct _mfc_trace g_mfc_trace_rm[MFC_TRACE_COUNT_MAX]; +struct _mfc_trace g_mfc_trace_longterm[MFC_TRACE_COUNT_MAX]; + +/* Open an MFC node */ +static int mfc_open(struct file *file) +{ + struct mfc_ctx *ctx =3D NULL; + struct mfc_dev *dev =3D video_drvdata(file); + int i, ret =3D 0; + enum mfc_node_type node; + struct video_device *vdev =3D NULL; + + if (!dev) { + mfc_pr_err("no mfc device to run\n"); + goto err_no_device; + } + + mfc_dev_debug(2, "mfc driver open called\n"); + + if (mutex_lock_interruptible(&dev->mfc_mutex)) + return -ERESTARTSYS; + + node =3D mfc_get_node_type(file); + if (node =3D=3D MFCNODE_INVALID) { + mfc_dev_err("cannot specify node type\n"); + ret =3D -ENOENT; + goto err_node_type; + } + + dev->num_inst++; /* It is guarded by mfc_mutex in vfd */ + + /* Allocate memory for context */ + ctx =3D kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret =3D -ENOMEM; + goto err_ctx_alloc; + } + + switch (node) { + case MFCNODE_DECODER: + vdev =3D dev->vfd_dec; + break; + case MFCNODE_ENCODER: + vdev =3D dev->vfd_enc; + break; + default: + mfc_dev_err("Invalid node(%d)\n", node); + break; + } + + if (!vdev) + goto err_vdev; + + v4l2_fh_init(&ctx->fh, vdev); + file->private_data =3D &ctx->fh; + v4l2_fh_add(&ctx->fh, file); + + ctx->dev =3D dev; + + /* Get context number */ + ctx->num =3D 0; + while (dev->ctx[ctx->num]) { + ctx->num++; + if (ctx->num >=3D MFC_NUM_CONTEXTS) { + mfc_ctx_err("Too many open contexts\n"); + mfc_ctx_err("Print information to check if there was an error or not\n"= ); + ret =3D -EBUSY; + goto err_ctx_num; + } + } + + spin_lock_init(&ctx->corelock.lock); + mutex_init(&ctx->intlock.core_mutex); + mutex_init(&ctx->op_mode_mutex); + init_waitqueue_head(&ctx->corelock.wq); + + mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE); + + if (mfc_is_decoder_node(node)) { + ctx->type =3D MFCINST_DECODER; + dev->num_dec_inst++; + } else { + ctx->type =3D MFCINST_ENCODER; + dev->num_enc_inst++; + } + + if (dev->num_inst =3D=3D 1) { + /* all of the ctx list */ + INIT_LIST_HEAD(&dev->ctx_list); + spin_lock_init(&dev->ctx_list_lock); + /* idle mode */ + spin_lock_init(&dev->idle_bits_lock); + } + + mfc_ctx_info + ("NORMAL %s instance is opened [%d]\n", + ctx->type =3D=3D MFCINST_DECODER ? "Decoder" : "Encoder", + dev->num_inst); + + /* Mark context as idle */ + dev->ctx[ctx->num] =3D ctx; + for (i =3D 0; i < MFC_NUM_CORE; i++) + ctx->op_core_num[i] =3D MFC_CORE_INVALID; + + ret =3D mfc_rm_instance_init(dev, ctx); + if (ret) { + mfc_ctx_err("rm_instance_init failed\n"); + goto err_inst_init; + } + + mfc_ctx_info + ("MFC open completed [%d] version =3D %d\n", + dev->num_inst, MFC_DRIVER_INFO); + MFC_TRACE_CTX_LT + ("[INFO] %s opened (ctx:%d, total:%d)\n", + mfc_is_decoder_node(node) ? "DEC" : "ENC", ctx->num, dev->num_inst); + + mutex_unlock(&dev->mfc_mutex); + return ret; + + /* Deinit when failure occurred */ +err_inst_init: + if (mfc_is_decoder_node(node)) + dev->num_dec_inst--; + else + dev->num_enc_inst--; + dev->ctx[ctx->num] =3D 0; + +err_ctx_num: + v4l2_fh_del(&ctx->fh, file); + v4l2_fh_exit(&ctx->fh); + +err_vdev: + kfree(ctx); + +err_ctx_alloc: + dev->num_inst--; + +err_node_type: + mfc_dev_err + ("MFC driver open is failed [%d]\n", + dev->num_inst); + mutex_unlock(&dev->mfc_mutex); + +err_no_device: + + return ret; +} + +/* Release MFC context */ +static int mfc_release(struct file *file) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + int ret =3D 0; + unsigned long flags; + + mutex_lock(&dev->mfc_mutex); + + mfc_ctx_info + ("%s instance release is called [%d]\n", + ctx->type =3D=3D MFCINST_DECODER ? "Decoder" : "Encoder", + dev->num_inst); + + MFC_TRACE_CTX_LT("[INFO] release is called (ctx:%d, total:%d)\n", ctx->nu= m, dev->num_inst); + + /* Free resources */ + v4l2_fh_del(&ctx->fh, file); + v4l2_fh_exit(&ctx->fh); + + /* + * mfc_release() can be called without a streamoff + * when the application is forcibly terminated. + * At that time, stop_streaming() is called by vb2_queue_release. + * So, we need to performed stop_streaming + * before instance de-init(CLOSE_INSTANCE). + */ + + ret =3D mfc_rm_instance_deinit(dev, ctx); + if (ret) { + mfc_dev_err("failed to rm_instance_deinit\n"); + goto end_release; + } + + dev->num_inst--; + + if (ctx->type =3D=3D MFCINST_DECODER) + dev->num_dec_inst--; + else if (ctx->type =3D=3D MFCINST_ENCODER) + dev->num_enc_inst--; + + MFC_TRACE_CTX_LT("[INFO] Release finished (ctx:%d, total:%d)\n", ctx->num= , dev->num_inst); + + spin_lock_irqsave(&dev->ctx_list_lock, flags); + dev->ctx[ctx->num] =3D NULL; + kfree(ctx); + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + + mfc_dev_info("mfc driver release finished [%d]\n", dev->num_inst); + +end_release: + mutex_unlock(&dev->mfc_mutex); + return ret; +} + +/* v4l2 ops */ +static const struct v4l2_file_operations mfc_fops =3D { + .owner =3D THIS_MODULE, + .open =3D mfc_open, + .release =3D mfc_release, + .poll =3D NULL, + .unlocked_ioctl =3D video_ioctl2, +}; + +static void __mfc_parse_dt_resource(struct mfc_dev *dev, struct device_nod= e *np) +{ + struct device_node *np_resource; + struct device_node *np_tmp; + int idx =3D 0; + struct mfc_resource *resource; + unsigned int codec_mode; + + /* Initialization */ + for (idx =3D 0; idx < MFC_MAX_CODEC_TYPE; idx++) { + resource =3D &dev->pdata->mfc_resource[idx]; + resource->op_core_type =3D MFC_OP_CORE_NOT_FIXED; + } + + np_resource =3D of_get_child_by_name(np, "mfc_resource"); + if (!np_resource) { + dev_err(dev->device, "there is no mfc_resource\n"); + return; + } + + /* Parse resource information */ + for_each_child_of_node(np_resource, np_tmp) { + idx =3D 0; + of_property_read_u32_index(np_tmp, "info", idx++, &codec_mode); + resource =3D &dev->pdata->mfc_resource[codec_mode]; + of_property_read_u32_index(np_tmp, "info", idx++, (u32 *)&resource->op_c= ore_type); + } +} + +static int __mfc_parse_dt(struct device_node *np, struct mfc_dev *mfc) +{ + struct mfc_platdata *pdata =3D mfc->pdata; + + if (!np) { + dev_err(mfc->device, "there is no device node\n"); + return -EINVAL; + } + + /* MFC FW memory size */ + of_property_read_u32(np, "fw_mem_size", &pdata->fw_mem_size); + + /* MFC DVA reservation start address and DMA bit mask */ + of_property_read_u32(np, "reserved_start", &pdata->reserved_start); + of_property_read_u32(np, "dma_bit_mask", &pdata->dma_bit_mask); + + /* MFC version */ + of_property_read_u32(np, "ip_ver", &pdata->ip_ver); + + /* MFC firmware name */ + of_property_read_string(np, "fw_name", &pdata->fw_name); + + /* Debug mode */ + of_property_read_u32(np, "debug_mode", &pdata->debug_mode); + + /* Resource of standard */ + __mfc_parse_dt_resource(mfc, np); + + of_property_read_u32_array + (np, "mem_clear", + &pdata->mem_clear.support, 2); + of_property_read_u32_array + (np, "wait_fw_status", + &pdata->wait_fw_status.support, 2); + + /* Scheduler */ + of_property_read_u32(np, "scheduler", &pdata->scheduler); + of_property_read_u32(np, "pbs_num_prio", &pdata->pbs_num_prio); + + return 0; +} + +static void *__mfc_get_drv_data(struct platform_device *pdev); + +static struct video_device *__mfc_video_device_register + (struct mfc_dev *dev, char *name, int node_num) +{ + struct video_device *vfd; + int ret =3D 0; + + vfd =3D video_device_alloc(); + if (!vfd) { + v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); + return NULL; + } + strscpy_pad(vfd->name, name, sizeof(vfd->name)); + vfd->fops =3D &mfc_fops; + vfd->minor =3D -1; + vfd->release =3D video_device_release; + + vfd->lock =3D &dev->mfc_mutex; + vfd->v4l2_dev =3D &dev->v4l2_dev; + vfd->vfl_dir =3D VFL_DIR_M2M; + set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); + vfd->device_caps =3D V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_VIDEO_OUTPUT_MPLANE + | V4L2_CAP_STREAMING; + + ret =3D video_register_device(vfd, VFL_TYPE_VIDEO, node_num); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "Failed to register video device /dev/video%d\n", + node_num); + video_device_release(vfd); + return NULL; + } + v4l2_info + (&dev->v4l2_dev, "video device registered as /dev/video%d\n", + vfd->num); + video_set_drvdata(vfd, dev); + + return vfd; +} + +/* MFC probe function */ +static int mfc_probe(struct platform_device *pdev) +{ + struct device *device =3D &pdev->dev; + struct device_node *np =3D device->of_node; + struct mfc_dev *dev; + int ret =3D -ENOENT; + + dev_info(&pdev->dev, "%s is called\n", __func__); + + dev =3D devm_kzalloc(&pdev->dev, sizeof(struct mfc_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* empty device for CPU cache flush with dma_sync_* API */ + dev->cache_op_dev =3D devm_kzalloc(&pdev->dev, sizeof(struct device), GFP= _KERNEL); + device_initialize(dev->cache_op_dev); + dma_coerce_mask_and_coherent(dev->cache_op_dev, DMA_BIT_MASK(36)); + + dev->device =3D &pdev->dev; + dev->variant =3D __mfc_get_drv_data(pdev); + platform_set_drvdata(pdev, dev); + + dev->pdata =3D devm_kzalloc(&pdev->dev, sizeof(struct mfc_platdata), GFP_= KERNEL); + if (!dev->pdata) { + ret =3D -ENOMEM; + goto err_res_mem; + } + + ret =3D __mfc_parse_dt(dev->device->of_node, dev); + if (ret) + goto err_res_mem; + + mfc_init_debugfs(dev); + + atomic_set(&dev->trace_ref, 0); + atomic_set(&dev->trace_ref_longterm, 0); + dev->mfc_trace =3D g_mfc_trace; + dev->mfc_trace_rm =3D g_mfc_trace_rm; + dev->mfc_trace_longterm =3D g_mfc_trace_longterm; + + if (dev->pdata->dma_bit_mask < MFC_MIN_BITMASK) + dev->pdata->dma_bit_mask =3D MFC_MIN_BITMASK; + dma_set_mask(&pdev->dev, DMA_BIT_MASK(dev->pdata->dma_bit_mask)); + + mutex_init(&dev->mfc_mutex); + + ret =3D v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + goto err_v4l2_dev; + + /* decoder */ + dev->vfd_dec =3D __mfc_video_device_register + (dev, MFC_DEC_NAME, + EXYNOS_VIDEONODE_MFC_DEC); + if (!dev->vfd_dec) { + ret =3D -ENOMEM; + goto alloc_vdev_dec; + } + + /* encoder */ + dev->vfd_enc =3D __mfc_video_device_register + (dev, MFC_ENC_NAME, + EXYNOS_VIDEONODE_MFC_ENC); + if (!dev->vfd_enc) { + ret =3D -ENOMEM; + goto alloc_vdev_enc; + } + /* end of node setting*/ + + /* for DVA reservation */ + if (dev->pdata->reserved_start) { + dev->domain =3D iommu_get_domain_for_dev(dev->device); + ret =3D mfc_iova_pool_init(dev); + if (ret) { + mfc_dev_err("Failed to reserve memory (%d)\n", ret); + goto err_iova_reserve; + } + } + + ret =3D mfc_configure_dma_memory(dev); + if (ret) { + dev_err(&pdev->dev, "failed to configure DMA memory\n"); + goto err_iova_reserve; + } + __platform_driver_register(&mfc_core_driver, THIS_MODULE); + of_platform_populate(np, NULL, NULL, device); + + dev_info(&pdev->dev, "%s is completed\n", __func__); + + return 0; + +/* Deinit MFC if probe had failed */ +err_iova_reserve: + video_unregister_device(dev->vfd_enc); +alloc_vdev_enc: + video_unregister_device(dev->vfd_dec); +alloc_vdev_dec: + v4l2_device_unregister(&dev->v4l2_dev); +err_v4l2_dev: + mutex_destroy(&dev->mfc_mutex); +err_res_mem: + return ret; +} + +/* Remove the driver */ +static void mfc_remove(struct platform_device *pdev) +{ + struct mfc_dev *dev =3D platform_get_drvdata(pdev); + + mfc_dev_info("++MFC remove\n"); + + platform_driver_unregister(&mfc_core_driver); + mfc_unconfigure_dma_memory(dev); + of_reserved_mem_device_release(dev->device); + + mfc_deinit_debugfs(dev); + video_unregister_device(dev->vfd_enc); + video_unregister_device(dev->vfd_dec); + v4l2_device_unregister(&dev->v4l2_dev); + + mfc_dev_info("--MFC remove\n"); +} + +static void mfc_shutdown(struct platform_device *pdev) +{ + struct platform_driver *pcoredrv =3D &mfc_core_driver; + struct mfc_dev *dev =3D platform_get_drvdata(pdev); + struct mfc_core *core; + int i; + + for (i =3D 0; i < dev->num_core; i++) { + core =3D dev->core[i]; + if (!core) { + mfc_dev_debug(2, "There is no core[%d]\n", i); + continue; + } + + if (!core->shutdown) { + mfc_core_info("%s core shutdown was not performed\n", core->name); + pcoredrv->shutdown(to_platform_device(core->device)); + } + } + + mfc_dev_info("MFC shutdown is completed\n"); +} + +#if IS_ENABLED(CONFIG_PM_SLEEP) +static int mfc_suspend(struct device *device) +{ + struct mfc_dev *dev =3D platform_get_drvdata(to_platform_device(device)); + struct mfc_core *core[MFC_NUM_CORE]; + int i, ret; + + if (!dev) { + dev_err(device, "no mfc device to run\n"); + return -EINVAL; + } + + for (i =3D 0; i < dev->num_core; i++) { + core[i] =3D dev->core[i]; + if (!core[i]) { + dev_err(device, "no mfc core%d device to run\n", i); + return -EINVAL; + } + + if (core[i]->state =3D=3D MFCCORE_ERROR) { + dev_info(device, "[MSR] Couldn't sleep. It's Error state\n"); + return 0; + } + } + + /* + * Multi core mode instance can send sleep command + * when there are no H/W operation both two core. + */ + for (i =3D 0; i < dev->num_core; i++) { + core[i] =3D dev->core[i]; + if (!core[i]) { + dev_err(device, "no mfc core%d device to run\n", i); + return -EINVAL; + } + + if (core[i]->num_inst =3D=3D 0) { + core[i] =3D NULL; + continue; + } + + mfc_dev_info("MFC%d will suspend\n", i); + + ret =3D mfc_core_get_hwlock_dev(core[i]); + if (ret < 0) { + mfc_dev_err("Failed to get hwlock for MFC%d\n", i); + mfc_dev_err + ("dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n", + core[i]->hwlock.dev, core[i]->hwlock.bits, + core[i]->hwlock.owned_by_irq, + core[i]->hwlock.wl_count, + core[i]->hwlock.transfer_owner); + return -EBUSY; + } + + if (!mfc_core_get_pwr_ref_cnt(core[i])) { + mfc_dev_info("MFC%d power has not been turned on yet\n", i); + mfc_core_release_hwlock_dev(core[i]); + core[i] =3D NULL; + continue; + } + } + + for (i =3D 0; i < dev->num_core; i++) { + if (core[i]) { + ret =3D mfc_core_run_sleep(core[i]); + if (ret) { + mfc_dev_err("Failed core_run_sleep for MFC%d\n", i); + return -EFAULT; + } + + mfc_core_release_hwlock_dev(core[i]); + + mfc_dev_info("MFC%d suspend is completed\n", i); + } + } + + return 0; +} + +static int mfc_resume(struct device *device) +{ + struct mfc_dev *dev =3D platform_get_drvdata(to_platform_device(device)); + struct mfc_core *core; + int i, ret; + + if (!dev) { + dev_err(device, "no mfc device to run\n"); + return -EINVAL; + } + + for (i =3D 0; i < dev->num_core; i++) { + core =3D dev->core[i]; + if (!core) { + dev_err(device, "no mfc core%d device to run\n", i); + return -EINVAL; + } + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_info("[MSR] Couldn't wakeup. It's Error state\n"); + return 0; + } + } + + for (i =3D 0; i < dev->num_core; i++) { + core =3D dev->core[i]; + if (!core) { + dev_err(device, "no mfc core%d device to run\n", i); + return -EINVAL; + } + + if (core->num_inst =3D=3D 0) + continue; + + mfc_dev_info("MFC%d will resume\n", i); + + ret =3D mfc_core_get_hwlock_dev(core); + if (ret < 0) { + mfc_dev_err("Failed to get hwlock for MFC%d\n", i); + mfc_dev_err + ("dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n", + core->hwlock.dev, + core->hwlock.bits, + core->hwlock.owned_by_irq, + core->hwlock.wl_count, + core->hwlock.transfer_owner); + return -EBUSY; + } + + ret =3D mfc_core_run_wakeup(core); + if (ret) { + mfc_dev_err("Failed core_run_wakeup for MFC%d\n", i); + return -EFAULT; + } + + mfc_core_release_hwlock_dev(core); + + mfc_dev_info("MFC%d resume is completed\n", i); + } + + return 0; +} +#endif + +/* Power management */ +static const struct dev_pm_ops mfc_pm_ops =3D { + SET_SYSTEM_SLEEP_PM_OPS(mfc_suspend, mfc_resume) +}; + +struct mfc_ctx_buf_size mfc_ctx_buf_size =3D { + .dev_ctx =3D PAGE_ALIGN(0x7800), /* 30KB */ + .dbg_info_buf =3D PAGE_ALIGN(0x1000), /* 4KB for DEBUG INFO */ +}; + +struct mfc_buf_size mfc_buf_size =3D { + .firmware_code =3D PAGE_ALIGN(SZ_1M), /* 1MB */ + .ctx_buf =3D &mfc_ctx_buf_size, +}; + +static struct mfc_variant mfc_drvdata =3D { + .buf_size =3D &mfc_buf_size, + .num_entities =3D 2, +}; + +static const struct of_device_id exynos_mfc_match[] =3D { + { + .compatible =3D "samsung,exynos-mfc", + .data =3D &mfc_drvdata, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_mfc_match); + +static void *__mfc_get_drv_data(struct platform_device *pdev) +{ + struct mfc_variant *driver_data =3D NULL; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match =3D of_match_node + (of_match_ptr(exynos_mfc_match), + pdev->dev.of_node); + if (match) + driver_data =3D (struct mfc_variant *)match->data; + } else { + driver_data =3D (struct mfc_variant *) + platform_get_device_id(pdev)->driver_data; + } + return driver_data; +} + +static struct platform_driver mfc_driver =3D { + .probe =3D mfc_probe, + .remove =3D mfc_remove, + .shutdown =3D mfc_shutdown, + .driver =3D { + .name =3D MFC_NAME, + .owner =3D THIS_MODULE, + .pm =3D &mfc_pm_ops, + .of_match_table =3D exynos_mfc_match, + .suppress_bind_attrs =3D true, + }, +}; + +module_platform_driver(mfc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kamil Debski "); diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core.c b/drivers= /media/platform/samsung/exynos-mfc/mfc_core.c new file mode 100644 index 000000000000..4f5cf459c36f --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) +#include +#endif + +#include "mfc_core_ops.h" +#include "mfc_core_isr.h" +#include "mfc_debugfs.h" + +#include "mfc_core_hwlock.h" +#include "mfc_core_run.h" + +#include "mfc_core_pm.h" + +#include "mfc_core_hw_reg_api.h" + +#include "base/mfc_sched.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" + +#define MFC_CORE_NAME "mfc-core" + +struct _mfc_trace_logging g_mfc_core_trace_logging[MFC_TRACE_LOG_COUNT_MAX= ]; + +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) +static int mfc_core_sysmmu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, + int data, void *param) +{ + struct mfc_core *core =3D (struct mfc_core *)param; + struct mfc_core_platdata *pdata =3D core->core_pdata; + unsigned int fault_status, fault_info1, fault_info2 =3D 0; + + if (mfc_core_get_pwr_ref_cnt(core) =3D=3D 0) { + mfc_core_info("This is not a MFC issue(not executed)\n"); + return 0; + } + + /* MMU Fault Status: If no page fault has occurred, it has a value of 0 */ + if (pdata->fault_status) + fault_status =3D pdata->fault_status + (data * 0x1000); + else + fault_status =3D MFC_MMU_INTERRUPT_STATUS; + /* MMU_FAULT_INFO1_VM[31:0]: It has AXID value. */ + if (pdata->fault_info1) + fault_info1 =3D pdata->fault_info1 + (data * 0x1000); + else + fault_info1 =3D MFC_MMU_FAULT_TRANS_INFO; + /* MMU_FAULT_INFO2_VM[31:0]: It has PMMU ID[31:24], MASTER ID[11:7], STRE= AM ID[6:0] */ + if (pdata->fault_info2) + fault_info2 =3D pdata->fault_info2 + (data * 0x1000); + + if (core->has_2sysmmu) { + if ((!MFC_MMU0_READL(fault_status)) && + (!MFC_MMU1_READL(fault_status))) { + mfc_core_info + ("This is not a MFC issue(fault status is zero)\n"); + return 0; + } + } else { + if (MFC_MMU0_READL(fault_status) =3D=3D 0) { + mfc_core_info + ("This is not a MFC issue(fault status is zero)\n"); + return 0; + } + } + + /* If sysmmu is used with other IPs, it should be checked whether it's an= MFC fault */ + if (pdata->share_sysmmu) { + if (mfc_get_mmu0_value(fault_info1, 0, pdata->axid_mask) !=3D + pdata->mfc_axid) { + mfc_core_err("This is not a MFC page fault\n"); + return 0; + } + } + + if (MFC_MMU0_READL(fault_status)) { + /* + * Since it has become complicated to distinguish + * read or write of page fault in IP driver, + * the cause of page fault in logging data is unified into + * the page fault. + */ + core->logging_data->cause |=3D BIT(MFC_CAUSE_0PAGE_FAULT); + core->logging_data->fault_status =3D MFC_MMU0_READL(fault_status); + core->logging_data->fault_trans_info =3D + MFC_MMU0_READL(fault_info1); + } + + if (core->has_2sysmmu) { + if (MFC_MMU1_READL(fault_status)) { + core->logging_data->cause |=3D + BIT(MFC_CAUSE_1PAGE_FAULT); + core->logging_data->fault_status =3D + MFC_MMU1_READL(fault_status); + core->logging_data->fault_trans_info =3D + MFC_MMU1_READL(fault_info1); + } + } + core->logging_data->fault_addr =3D iova; + + mfc_core_err + ("MFC-%d SysMMU PAGE FAULT at %#lx, AxID: %#x, fault_status: %#x\n", + core->id, iova, core->logging_data->fault_trans_info, + core->logging_data->fault_status); + MFC_TRACE_CORE("MFC-%d SysMMU PAGE FAULT at %#lx (AxID: %#x)\n", + core->id, iova, core->logging_data->fault_trans_info); + + /* + * if return 0, sysmmu occurs kernel panic for debugging + * if -EAGAIN, sysmmu doesn't occur kernel panic (but need async-fault in= dt). + */ + return 0; +} +#endif + +static int __mfc_core_parse_dt(struct device_node *np, struct mfc_core *co= re) +{ + struct mfc_core_platdata *pdata =3D core->core_pdata; + + if (!np) { + mfc_pr_err("there is no device node\n"); + return -EINVAL; + } + + /* MFC version */ + of_property_read_u32(np, "ip_ver", &pdata->ip_ver); + + /* Sysmmu check */ + of_property_read_u32(np, "share_sysmmu", &pdata->share_sysmmu); + of_property_read_u32(np, "fault_status", &pdata->fault_status); + of_property_read_u32(np, "fault_info1", &pdata->fault_info1); + of_property_read_u32(np, "axid_mask", &pdata->axid_mask); + of_property_read_u32(np, "mfc_axid", &pdata->mfc_axid); + of_property_read_u32(np, "tsmux_axid", &pdata->tsmux_axid); + of_property_read_u32(np, "fault_info2", &pdata->fault_info2); + of_property_read_u32(np, "pmmuid_shift", &pdata->pmmuid_shift); + of_property_read_u32(np, "pmmuid_mask", &pdata->pmmuid_mask); + of_property_read_u32(np, "tsmux_pmmuid", &pdata->tsmux_pmmuid); + of_property_read_u32(np, "masterid_shift", &pdata->masterid_shift); + of_property_read_u32(np, "masterid_mask", &pdata->masterid_mask); + of_property_read_u32(np, "tsmux_masterid", &pdata->tsmux_masterid); + + return 0; +} + +static int __mfc_core_register_resource(struct platform_device *pdev, + struct mfc_core *core) +{ + struct device_node *np =3D core->device->of_node; + struct device_node *iommu; + struct device_node *pmu; + struct resource *res; + int ret; + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get memory region resource\n"); + return -ENOENT; + } + core->mfc_mem =3D + request_mem_region(res->start, resource_size(res), pdev->name); + if (!core->mfc_mem) { + dev_err(&pdev->dev, "failed to get memory region\n"); + return -ENOENT; + } + core->regs_base =3D + ioremap(core->mfc_mem->start, resource_size(core->mfc_mem)); + if (!core->regs_base) { + dev_err(&pdev->dev, "failed to ioremap address region\n"); + goto err_ioremap; + } + + iommu =3D of_get_child_by_name(np, "iommu"); + if (!iommu) { + dev_err(&pdev->dev, "failed to get iommu node\n"); + goto err_ioremap_mmu0; + } + + core->sysmmu0_base =3D of_iomap(iommu, 0); + if (!core->sysmmu0_base) { + dev_err(&pdev->dev, + "failed to ioremap sysmmu0 address region\n"); + goto err_ioremap_mmu0; + } +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + // sysmmu bypass settings for sysmmu0_base + writel(0x00, core->sysmmu0_base + 0x00); +#endif + + core->sysmmu1_base =3D of_iomap(iommu, 1); + if (!core->sysmmu1_base) { + dev_dbg(&pdev->dev, "there is only one MFC sysmmu\n"); + } else { + core->has_2sysmmu =3D 1; +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + // sysmmu bypass settings for sysmmu1_base + writel(0x00, core->sysmmu1_base + 0x00); +#endif + } + + pmu =3D of_get_child_by_name(np, "pmu"); + if (pmu) { + core->pmu_base =3D of_iomap(pmu, 0); + if (!core->pmu_base) { + core->has_pmu =3D 0; + dev_err(&pdev->dev, + "failed to iomap pmu address region\n"); + goto err_ioremap_pmu; + } + core->has_pmu =3D 1; + } + + core->irq =3D platform_get_irq(pdev, 0); + if (core->irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + goto err_res_irq; + } + + ret =3D + devm_request_threaded_irq(&pdev->dev, core->irq, + mfc_core_top_half_irq, mfc_core_irq, + IRQF_ONESHOT, pdev->name, core); + if (ret) { + dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); + goto err_res_irq; + } + + return 0; + + err_res_irq: + if (core->has_pmu) + iounmap(core->pmu_base); + err_ioremap_pmu: + if (core->has_2sysmmu) { + if (core->sysmmu1_base) + iounmap(core->sysmmu1_base); + } + if (core->sysmmu0_base) + iounmap(core->sysmmu0_base); + err_ioremap_mmu0: + iounmap(core->regs_base); + err_ioremap: + release_mem_region(core->mfc_mem->start, resource_size(core->mfc_mem)); + return -ENOENT; +} + +/* MFC probe function */ +static int mfc_core_probe(struct platform_device *pdev) +{ + struct mfc_core *core; + struct mfc_dev *dev; + int ret =3D -ENOENT; + + dev_info(&pdev->dev, "%s is called\n", __func__); + + core =3D devm_kzalloc(&pdev->dev, sizeof(struct mfc_core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->device =3D &pdev->dev; + + /* set core id */ + of_property_read_u32(core->device->of_node, "id", &core->id); + snprintf(core->name, sizeof(core->name), "MFC%d", core->id); + + /* register core to dev */ + dev =3D dev_get_drvdata(pdev->dev.parent); + dev->core[core->id] =3D core; + dev->num_core++; + dev->num_subsystem++; + core->dev =3D dev; + core->core_ops =3D mfc_get_core_ops(); + + core->core_pdata =3D devm_kzalloc(&pdev->dev, + sizeof(struct mfc_core_platdata), + GFP_KERNEL); + if (!core->core_pdata) { + ret =3D -ENOMEM; + goto err_pm; + } + + ret =3D __mfc_core_parse_dt(core->device->of_node, core); + if (ret) + goto err_pm; + + atomic_set(&core->trace_ref_log, 0); + core->mfc_trace_logging =3D g_mfc_core_trace_logging; + + mfc_core_pm_init(core); + ret =3D __mfc_core_register_resource(pdev, core); + if (ret) + goto err_res_mem; + init_waitqueue_head(&core->cmd_wq); + mfc_core_init_listable_wq_dev(core); + + platform_set_drvdata(pdev, core); + + mfc_core_init_hwlock(core); + + mfc_sched_init_core(core); + + spin_lock_init(&core->batch_lock); + +#if IS_ENABLED(CONFIG_SAMSUNG_IOMMU) + ret =3D samsung_iommu_register_fault_handler(core->device, + mfc_core_sysmmu_fault_handler, + core); + if (ret) { + dev_err(&pdev->dev, + "failed to register sysmmu fault handler %d\n", ret); + ret =3D -EPROBE_DEFER; + goto err_sysmmu_fault_handler; + } +#endif + /* set async suspend/resume */ + device_enable_async_suspend(core->device); + + mfc_alloc_firmware(core); + + core->logging_data =3D devm_kzalloc(&pdev->dev, sizeof(struct mfc_debug), + GFP_KERNEL); + if (!core->logging_data) { + ret =3D -ENOMEM; + goto err_alloc_debug; + } +#if IS_ENABLED(CONFIG_MFC_USE_COREDUMP) + core->dbg_info.size =3D MFC_DUMP_BUF_SIZE; + core->dbg_info.addr =3D vmalloc(core->dbg_info.size); + if (!core->dbg_info.addr) + dev_err(&pdev->dev, "failed to alloc for debug buffer\n"); +#endif +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + samsung_sysmmu_activate(core->device); +#endif + mfc_core_info("%s is completed\n", core->name); + + return 0; + + err_alloc_debug: +#if IS_ENABLED(CONFIG_SAMSUNG_IOMMU) + samsung_iommu_unregister_fault_handler(&pdev->dev); + err_sysmmu_fault_handler: +#endif + if (core->has_2sysmmu) + iounmap(core->sysmmu1_base); + iounmap(core->sysmmu0_base); + iounmap(core->regs_base); + release_mem_region(core->mfc_mem->start, resource_size(core->mfc_mem)); + err_res_mem: + mfc_core_pm_final(core); + err_pm: + return ret; +} + +/* Remove the driver */ +static void mfc_core_remove(struct platform_device *pdev) +{ + struct mfc_core *core =3D platform_get_drvdata(pdev); + + mfc_core_info("++%s remove\n", core->name); + + if (core->dbg_info.addr) + vfree(core->dbg_info.addr); +#if IS_ENABLED(CONFIG_SAMSUNG_IOMMU) + samsung_iommu_unregister_fault_handler(&pdev->dev); +#endif + mfc_core_destroy_listable_wq_core(core); + + mfc_release_common_context(core); + mfc_release_firmware(core); + core->fw.status =3D 0; + + if (core->has_2sysmmu) + if (core->sysmmu1_base) + iounmap(core->sysmmu1_base); + if (core->sysmmu0_base) + iounmap(core->sysmmu0_base); + iounmap(core->regs_base); + release_mem_region(core->mfc_mem->start, resource_size(core->mfc_mem)); + mfc_core_pm_final(core); + + mfc_core_info("--%s remove\n", core->name); +} + +static void mfc_core_shutdown(struct platform_device *pdev) +{ + struct mfc_core *core =3D platform_get_drvdata(pdev); + struct mfc_dev *dev =3D core->dev; + int ret; + + mfc_core_info("%s core shutdown is called\n", core->name); + if (core->shutdown) { + mfc_core_info("%s core shutdown was already performed\n", + core->name); + return; + } + + mutex_lock(&dev->mfc_mutex); + if (!mfc_core_get_pwr_ref_cnt(core)) { + core->shutdown =3D 1; + mfc_core_info("MFC is not running\n"); + mutex_unlock(&dev->mfc_mutex); + return; + } + + ret =3D mfc_core_get_hwlock_dev(core); + if (ret < 0) + mfc_core_err("Failed to get hwlock\n"); + + mfc_core_risc_off(core); + core->sched->clear_all_work(core); + mfc_core_release_hwlock_dev(core); + + core->shutdown =3D 1; + mutex_unlock(&dev->mfc_mutex); + + mfc_core_info("%s core shutdown completed\n", core->name); +} + +#if IS_ENABLED(CONFIG_PM_SLEEP) +static int mfc_core_suspend(struct device *device) +{ + struct mfc_core *core =3D + platform_get_drvdata(to_platform_device(device)); + + if (!core) { + dev_err(device, "no mfc device to run\n"); + return -EINVAL; + } + + mfc_core_debug(2, "MFC suspend will be handled by main driver\n"); + + return 0; +} + +static int mfc_core_resume(struct device *device) +{ + struct mfc_core *core =3D + platform_get_drvdata(to_platform_device(device)); + + if (!core) { + dev_err(device, "no mfc core to run\n"); + return -EINVAL; + } + + mfc_core_debug(2, "MFC resume will be handled by main driver\n"); + + return 0; +} +#endif + +#if IS_ENABLED(CONFIG_PM) +static int mfc_core_runtime_suspend(struct device *device) +{ + struct mfc_core *core =3D + platform_get_drvdata(to_platform_device(device)); + + mfc_core_debug(3, "mfc runtime suspend\n"); + + return 0; +} + +static int mfc_core_runtime_idle(struct device *dev) +{ + return 0; +} + +static int mfc_core_runtime_resume(struct device *device) +{ + struct mfc_core *core =3D + platform_get_drvdata(to_platform_device(device)); + + mfc_core_debug(3, "mfc runtime resume\n"); + + return 0; +} +#endif + +/* Power management */ +static const struct dev_pm_ops mfc_core_pm_ops =3D { + SET_SYSTEM_SLEEP_PM_OPS(mfc_core_suspend, mfc_core_resume) + SET_RUNTIME_PM_OPS(mfc_core_runtime_suspend, + mfc_core_runtime_resume, + mfc_core_runtime_idle) +}; + +static const struct of_device_id exynos_mfc_core_match[] =3D { + { + .compatible =3D "samsung,exynos-mfc-core", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, exynos_mfc_core_match); + +struct platform_driver mfc_core_driver =3D { + .probe =3D mfc_core_probe, + .remove =3D mfc_core_remove, + .shutdown =3D mfc_core_shutdown, + .driver =3D { + .name =3D MFC_CORE_NAME, + .owner =3D THIS_MODULE, + .pm =3D &mfc_core_pm_ops, + .of_match_table =3D of_match_ptr(exynos_mfc_core_match), + }, +}; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_ops.c new file mode 100644 index 000000000000..d9200bba1bb5 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_ops.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_ops.h" + +#include "mfc_core_hwlock.h" +#include "mfc_core_run.h" +#include "mfc_core_pm.h" +#include "mfc_core_sync.h" + +#include "mfc_core_hw_reg_api.h" + +#include "base/mfc_sched.h" +#include "base/mfc_buf.h" +#include "base/mfc_utils.h" +#include "base/mfc_mem.h" + +static void __mfc_core_init(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D core->dev; + + /* set MFC idle timer */ + atomic_set(&core->hw_run_bits, 0); + mfc_core_change_idle_mode(core, MFC_IDLE_MODE_NONE); + + if (!dev->fw_date) + dev->fw_date =3D core->fw.date; + else if (dev->fw_date > core->fw.date) + dev->fw_date =3D core->fw.date; +} + +static int __mfc_wait_close_inst(struct mfc_core *core, struct mfc_ctx *ct= x) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret =3D 0; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_info("[MSR] Couldn't close inst. It's Error state\n"); + return 0; + } + + if (core_ctx->state <=3D MFCINST_INIT) { + mfc_debug(2, "mfc instance didn't opend or already closed\n"); + return 0; + } + + mfc_clean_core_ctx_int_flags(core_ctx); + mfc_change_state(core_ctx, MFCINST_RETURN_INST); + core->sched->set_work(core, core_ctx); + + /* To issue the command 'CLOSE_INSTANCE' */ + if (mfc_core_just_run(core, ctx->num)) { + mfc_err("failed to run MFC, state: %d\n", core_ctx->state); + MFC_TRACE_CTX_LT("[ERR][Release] failed to run MFC, state: %d\n", core_c= tx->state); + return -EIO; + } + + /* Wait until instance is returned or timeout occurred */ + ret =3D mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_CLOSE_INSTAN= CE_RET); + if (ret =3D=3D 1) { + mfc_err("failed to wait CLOSE_INSTANCE(timeout)\n"); + + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_CLOSE_INSTANCE_= RET)) { + mfc_err("waited once more but failed to wait CLOSE_INSTANCE\n"); + core->logging_data->cause |=3D BIT(MFC_CAUSE_FAIL_CLOSE_INST); + } + } else if (ret =3D=3D -1) { + mfc_err("failed to wait CLOSE_INSTANCE(err)\n"); + } + + return 0; +} + +static int __mfc_core_deinit(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret =3D 0; + + core->sched->clear_work(core, core_ctx); + + ret =3D __mfc_wait_close_inst(core, ctx); + if (ret) { + mfc_err("Failed to close instance\n"); + return ret; + } + + core->num_inst--; + + /* Last normal instance */ + if (core->num_inst =3D=3D 0) { + core->curr_core_ctx =3D ctx->num; + mfc_core_pm_clock_on(core, 0); + mfc_core_run_cache_flush(core, MFC_CACHEFLUSH, 0); + mfc_core_pm_clock_off(core, 0); + + mfc_core_change_fw_state(core, MFC_FW_INITIALIZED, 0); + + mfc_core_change_fw_state(core, MFC_FW_LOADED, 0); + } + + if (core->num_inst =3D=3D 0) { + mfc_core_run_deinit_hw(core); + + mfc_debug(2, "power off\n"); + mfc_core_pm_power_off(core); + + if (core->dev->debugfs.dbg_enable) + mfc_release_dbg_info_buffer(core); + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_change_state(core, MFCCORE_INIT); + mfc_info("[MSR] MFC-%d will be reset\n", core->id); + } + } + + return 0; +} + +static int __mfc_core_instance_init(struct mfc_core *core, struct mfc_ctx = *ctx) +{ + struct mfc_core_ctx *core_ctx =3D NULL; + int ret =3D 0; + enum mfc_fw_status fw_status; + struct mfc_special_buf *fw_buf; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_ctx_err("MFC-%d is ERROR state\n", core->id); + return -EBUSY; + } + + core->num_inst++; + + /* Allocate memory for core context */ + core_ctx =3D kzalloc(sizeof(*core_ctx), GFP_KERNEL); + if (!core_ctx) { + ret =3D -ENOMEM; + goto err_init_inst; + } + + core_ctx->core =3D core; + core_ctx->ctx =3D ctx; + core_ctx->num =3D ctx->num; + core_ctx->inst_no =3D MFC_NO_INSTANCE_SET; + core->core_ctx[core_ctx->num] =3D core_ctx; + + init_waitqueue_head(&core_ctx->cmd_wq); + mfc_core_init_listable_wq_ctx(core_ctx); + core->sched->clear_work(core, core_ctx); + + if (core->num_inst =3D=3D 1) { + mfc_debug(2, "it is first instance in to core-%d\n", core->id); + + mfc_debug(2, "power on\n"); + ret =3D mfc_core_pm_power_on(core); + if (ret) { + mfc_err("Failed block power on, ret=3D%d\n", ret); + goto err_power_on; + } + + if (core->dev->debugfs.dbg_enable) + mfc_alloc_dbg_info_buffer(core); + } + + /* Load and verify the FW */ + + fw_buf =3D &core->fw_buf; + fw_status =3D core->fw.status; + + if (!(fw_status & MFC_FW_LOADED)) { + ret =3D mfc_request_load_firmware(core, fw_buf); + if (ret) + goto err_fw_load; + } + + if (!(fw_status & MFC_FW_INITIALIZED)) { + core->curr_core_ctx =3D ctx->num; + core->preempt_core_ctx =3D MFC_NO_INSTANCE_SET; + + ret =3D mfc_core_run_init_hw(core); + if (ret) + goto err_init_hw; + } + + if (core->num_inst =3D=3D 1) + __mfc_core_init(core, ctx); + + return 0; + +err_init_hw: + mfc_core_change_fw_state(core, MFC_FW_LOADED, 0); + +err_fw_load: + if (core->dev->debugfs.dbg_enable) + mfc_release_dbg_info_buffer(core); + if (core->num_inst =3D=3D 1) { + if (mfc_core_get_pwr_ref_cnt(core)) + mfc_core_pm_power_off(core); + } +err_power_on: + core->core_ctx[ctx->num] =3D 0; + kfree(core->core_ctx[ctx->num]); + +err_init_inst: + core->num_inst--; + + return ret; +} + +static int mfc_core_instance_init(struct mfc_core *core, struct mfc_ctx *c= tx) +{ + int ret =3D 0; + + mfc_core_debug_enter(); + + ret =3D mfc_core_get_hwlock_dev(core); + if (ret < 0) { + mfc_core_err("Failed to get hwlock\n"); + mfc_core_err("dev.hwlock.dev =3D 0x%lx, bits =3D 0x%lx, owned_by_irq =3D= %d, wl_count =3D %d, transfer_owner =3D %d\n", + core->hwlock.dev, core->hwlock.bits, core->hwlock.owned_by_irq, + core->hwlock.wl_count, core->hwlock.transfer_owner); + return ret; + } + + ret =3D __mfc_core_instance_init(core, ctx); + if (ret) + mfc_core_err("Failed to core instance init\n"); + + mfc_core_release_hwlock_dev(core); + + mfc_core_debug_leave(); + + return ret; +} + +static int mfc_core_instance_deinit(struct mfc_core *core, struct mfc_ctx = *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret =3D 0; + + if (!core_ctx) { + mfc_core_err("There is no instance\n"); + return -EINVAL; + } + + core->sched->clear_work(core, core_ctx); + + ret =3D mfc_core_get_hwlock_ctx(core_ctx); + if (ret < 0) { + mfc_err("Failed to get hwlock\n"); + MFC_TRACE_CTX_LT("[ERR][Release] failed to get hwlock (shutdown: %d)\n", + core->shutdown); + return -EBUSY; + } + + /* If instance was initialised then return instance and free reosurces */ + ret =3D __mfc_core_deinit(core, ctx); + if (ret) + goto err_release_try; + + mfc_core_release_hwlock_ctx(core_ctx); + mfc_core_destroy_listable_wq_ctx(core_ctx); + + core->core_ctx[core_ctx->num] =3D 0; + kfree(core_ctx); + + return 0; + +err_release_try: + mfc_core_release_hwlock_ctx(core_ctx); + return ret; +} + +static const struct mfc_core_ops mfc_core_ops =3D { + .instance_init =3D mfc_core_instance_init, + .instance_deinit =3D mfc_core_instance_deinit, +}; + +const struct mfc_core_ops *mfc_get_core_ops(void) +{ + return &mfc_core_ops; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_ops.h new file mode 100644 index 000000000000..80647b3b165a --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_ops.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ +#ifndef __MFC_CORE_OPS_H +#define __MFC_CORE_OPS_H __FILE__ + +#include "base/mfc_common.h" + +const struct mfc_core_ops *mfc_get_core_ops(void); +#endif /* __MFC_CORE_OPS_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sched_prio.= c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sched_prio.c new file mode 100644 index 000000000000..a2f69a064d3d --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sched_prio.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_core_sched_prio.c File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_hwlock.h" +#include "mfc_core_sync.h" + +#include "base/mfc_sched.h" +#include "base/mfc_utils.h" + +static inline void __mfc_print_workbits(struct mfc_core *core, int prio, i= nt num) +{ + int i; + + mfc_core_debug(4, "[PRIO][c:%d] prio %d\n", num, prio); + for (i =3D 0; i < core->total_num_prio; i++) + mfc_core_debug(4, "[PRIO] MFC-%d P[%d] bits %08lx\n", + core->id, i, core->prio_work_bits[i]); +} + +static inline void __mfc_clear_all_prio_bits(struct mfc_core *core) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&core->prio_work_lock, flags); + for (i =3D 0; i < core->total_num_prio; i++) + core->prio_work_bits[i] =3D 0; + spin_unlock_irqrestore(&core->prio_work_lock, flags); +} + +static void mfc_create_work_prio(struct mfc_core *core) +{ + int num_prio =3D core->dev->pdata->pbs_num_prio; + + spin_lock_init(&core->prio_work_lock); + + core->sched_type =3D MFC_SCHED_PRIO; + core->num_prio =3D num_prio ? num_prio : 1; + core->total_num_prio =3D core->num_prio * 2 + 2; + + __mfc_clear_all_prio_bits(core); +} + +static void mfc_init_work_prio(struct mfc_core *core) +{ + core->sched_type =3D MFC_SCHED_PRIO; + __mfc_clear_all_prio_bits(core); + mfc_core_debug(2, "[SCHED][PRIO] Scheduler type is PBS\n"); +} + +static void mfc_clear_all_work_prio(struct mfc_core *core) +{ + __mfc_clear_all_prio_bits(core); +} + +static void mfc_set_work_prio(struct mfc_core *core, struct mfc_core_ctx *= core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + unsigned long flags; + int p; + + spin_lock_irqsave(&core->prio_work_lock, flags); + + p =3D mfc_get_prio(core, ctx->rt, ctx->prio); + __set_bit(core_ctx->num, &core->prio_work_bits[p]); + __mfc_print_workbits(core, p, core_ctx->num); + + spin_unlock_irqrestore(&core->prio_work_lock, flags); +} + +static void mfc_clear_work_prio(struct mfc_core *core, struct mfc_core_ctx= *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + unsigned long flags; + int p; + + spin_lock_irqsave(&core->prio_work_lock, flags); + + p =3D mfc_get_prio(core, ctx->rt, ctx->prio); + __clear_bit(core_ctx->num, &core->prio_work_bits[p]); + __mfc_print_workbits(core, p, core_ctx->num); + + spin_unlock_irqrestore(&core->prio_work_lock, flags); +} + +struct mfc_sched_class mfc_sched_prio =3D { + .create_work =3D mfc_create_work_prio, + .init_work =3D mfc_init_work_prio, + .clear_all_work =3D mfc_clear_all_work_prio, + .set_work =3D mfc_set_work_prio, + .clear_work =3D mfc_clear_work_prio, +}; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c b/drivers/m= edia/platform/samsung/exynos-mfc/mfc_rm.c new file mode 100644 index 000000000000..a25a05642cf2 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_rm.c File + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_rm.h" + +#include "mfc_core_hwlock.h" +#include "mfc_core_intlock.h" +#include "mfc_core_hw_reg_api.h" +#include "mfc_core_pm.h" + +#include "base/mfc_buf.h" +#include "base/mfc_utils.h" +#include "base/mfc_mem.h" + +int mfc_rm_instance_init(struct mfc_dev *dev, struct mfc_ctx *ctx) +{ + struct mfc_core *core; + int i, ret; + + mfc_ctx_debug_enter(); + + mfc_get_corelock_ctx(ctx); + + /* + * The FW memory for all cores is allocated in advance. + * (Only once at first time) + * Because FW base address should be the lowest address + * than all DVA that FW approaches. + */ + for (i =3D 0; i < dev->num_core; i++) { + core =3D dev->core[i]; + if (!core) { + mfc_ctx_err("[RM] There is no MFC-%d\n", i); + continue; + } + + if (!(core->fw.status & MFC_FW_ALLOC)) { + ret =3D mfc_alloc_firmware(core); + if (ret) + goto err_inst_init; + } + + if (!(core->fw.status & MFC_CTX_ALLOC)) { + ret =3D mfc_alloc_common_context(core); + if (ret) + goto err_inst_init; + } + } + + mfc_change_op_mode(ctx, MFC_OP_SINGLE); + ctx->op_core_type =3D MFC_OP_CORE_NOT_FIXED; + if (ctx->type =3D=3D MFCINST_DECODER) + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_DEC_DEFAULT_CORE; + else + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_ENC_DEFAULT_CORE; + + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no main core\n"); + ret =3D -EINVAL; + goto err_inst_init; + } + + mfc_ctx_debug(2, "[RM] init instance core-%d\n", + ctx->op_core_num[MFC_CORE_MAIN]); + ret =3D core->core_ops->instance_init(core, ctx); + if (ret) { + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_CORE_INVALID; + mfc_ctx_err("[RM] Failed to init\n"); + } + +err_inst_init: + mfc_release_corelock_ctx(ctx); + + mfc_ctx_debug_leave(); + + return ret; +} + +int mfc_rm_instance_deinit(struct mfc_dev *dev, struct mfc_ctx *ctx) +{ + struct mfc_core *core =3D NULL, *subcore; + int i, ret =3D 0; + + mfc_ctx_debug_enter(); + + mfc_get_corelock_ctx(ctx); + + /* reset original stream mode */ + mutex_lock(&ctx->op_mode_mutex); + if (IS_SWITCH_SINGLE_MODE(ctx)) { + mfc_rm_set_core_num(ctx, MFC_DEC_DEFAULT_CORE); + mfc_change_op_mode(ctx, ctx->stream_op_mode); + } + mutex_unlock(&ctx->op_mode_mutex); + + if (IS_TWO_MODE1(ctx)) { + subcore =3D mfc_get_sub_core(dev, ctx); + if (!subcore) + mfc_ctx_err("[RM] There is no sub core for clock off\n"); + else + mfc_core_pm_clock_off(subcore, 0); + } + + for (i =3D (MFC_CORE_TYPE_NUM - 1); i >=3D 0; i--) { + if (ctx->op_core_num[i] =3D=3D MFC_CORE_INVALID) + continue; + + core =3D dev->core[ctx->op_core_num[i]]; + if (!core) { + mfc_ctx_err("[RM] There is no core[%d]\n", + ctx->op_core_num[i]); + ret =3D -EINVAL; + goto err_inst_deinit; + } + + mfc_core_debug(2, "[RM] core%d will be deinit, ctx[%d]\n", + i, ctx->num); + ret =3D core->core_ops->instance_deinit(core, ctx); + if (ret) + mfc_core_err("[RM] Failed to deinit\n"); + } + + clear_bit(ctx->num, &dev->multi_core_inst_bits); + mfc_change_op_mode(ctx, MFC_OP_SINGLE); + ctx->op_core_type =3D MFC_OP_CORE_NOT_FIXED; + +err_inst_deinit: + mfc_release_corelock_ctx(ctx); + + mfc_ctx_debug_leave(); + + return ret; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h b/drivers/m= edia/platform/samsung/exynos-mfc/mfc_rm.h new file mode 100644 index 000000000000..8f9e7494057e --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_rm.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_RM_H +#define __MFC_RM_H __FILE__ + +#include "mfc_core_sync.h" + +#define MFC_RM_LOAD_DELETE 0 +#define MFC_RM_LOAD_ADD 1 +#define MFC_RM_LOAD_DELETE_UPDATE 2 + +static inline struct mfc_core *mfc_get_main_core(struct mfc_dev *dev, + struct mfc_ctx *ctx) +{ + if (ctx->op_core_num[MFC_CORE_MAIN] =3D=3D MFC_CORE_INVALID) + return NULL; + + return dev->core[ctx->op_core_num[MFC_CORE_MAIN]]; +} + +static inline struct mfc_core *mfc_get_sub_core(struct mfc_dev *dev, + struct mfc_ctx *ctx) +{ + if (ctx->op_core_num[MFC_CORE_SUB] =3D=3D MFC_CORE_INVALID) + return NULL; + + return dev->core[ctx->op_core_num[MFC_CORE_SUB]]; +} + +static inline void mfc_rm_set_core_num(struct mfc_ctx *ctx, int main_core_= num) +{ + ctx->op_core_num[MFC_CORE_MAIN] =3D main_core_num; + + if (main_core_num =3D=3D MFC_DEC_DEFAULT_CORE) + ctx->op_core_num[MFC_CORE_SUB] =3D MFC_SURPLUS_CORE; + else + ctx->op_core_num[MFC_CORE_SUB] =3D MFC_DEC_DEFAULT_CORE; + + mfc_ctx_debug(2, "[RM] main core %d, sub core %d\n", + ctx->op_core_num[MFC_CORE_MAIN], + ctx->op_core_num[MFC_CORE_SUB]); +} + +/* core ops */ +int mfc_rm_instance_init(struct mfc_dev *dev, struct mfc_ctx *ctx); +int mfc_rm_instance_deinit(struct mfc_dev *dev, struct mfc_ctx *ctx); +#endif /* __MFC_RM_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 7471224CEE8 for ; Tue, 30 Sep 2025 03:56:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204564; cv=none; b=MTXjeSpbqMO4l+pAm5W57xZ6BK0pXO0eMWdhEiP6HJMvZJdEMSBOD1iHuVJqsHTlzcIuNKZgYRbUI9bAFO4uyQoGNVvQhZuB7L2c0ztq9so85TfMmzH1MB+RBSvmS27Dfk+XumGCi0I3gS6d2AYd4Izkll7xbs9NebwNBaSa0Jw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204564; c=relaxed/simple; bh=Am5/2bFOpxd6fg7eUXfGB5nSzGmXmMVZoHrCMNKOzy4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=lZp092s5iqUFA9HfXlo4g2WfEXvnZdH6dJRHYHX4Hm2wq7ib3WYXvUw90Zv+25Noh8b2im6fPaIwPx189PnsXJuGgbokDzaH8ZGeWT9uN0rGZrVvpWb5qDSrwFTIV8V6TP4d7wIaV5UodhmpV19S6l/G20FWXMjv7Et6CRyCmH0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=bwKPpVWC; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="bwKPpVWC" Received: from epcas5p2.samsung.com (unknown [182.195.41.40]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035559epoutp0458dd02e638fc807aa81a1ef80313b32a~p8zoEWH0j2001720017epoutp04P for ; Tue, 30 Sep 2025 03:55:59 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035559epoutp0458dd02e638fc807aa81a1ef80313b32a~p8zoEWH0j2001720017epoutp04P DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204560; bh=fsLERXRDyCNEbx47LNmWUKpYJ8hnbQ5zgukn3NjaXDE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bwKPpVWC+wivdYROWHtesymjGoIStzcQzuzRfrzFP1VoC3k9jx7vhjPpwF+db1d9/ XCsWA1oTHxqJfKpSmY3q+1fZmsv+eU1iT/EIGlgUUVJXHsttDuQx96wAesgJH9eP7R jpcSCzF1yeBePyi/gdnT7EnEUVVaFHVsET3qiBK0= Received: from epsnrtp03.localdomain (unknown [182.195.42.155]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035559epcas5p42b42e0b13edc7de117b553724cb92261~p8znkYwHP2781827818epcas5p4T; Tue, 30 Sep 2025 03:55:59 +0000 (GMT) Received: from epcas5p2.samsung.com (unknown [182.195.38.90]) by epsnrtp03.localdomain (Postfix) with ESMTP id 4cbPNt2NNtz3hhTB; Tue, 30 Sep 2025 03:55:58 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPA id 20250930035557epcas5p2efa9fa41298b8a9aabb46e5fac70d49e~p8zl-U8Rm0060500605epcas5p2a; Tue, 30 Sep 2025 03:55:57 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035551epsmtip10b3f862702187955e4e92c4cc8153c6e~p8zgNeRAr2931929319epsmtip1N; Tue, 30 Sep 2025 03:55:51 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 09/29] =?UTF-8?q?media:=20mfc:=20Add=20bus=E2=80=91devfreq?= =?UTF-8?q?,=20QoS,=20multi=E2=80=91view=20and=20control=20infrastructure?= Date: Tue, 30 Sep 2025 09:33:28 +0530 Message-Id: <20250930040348.3702923-10-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035557epcas5p2efa9fa41298b8a9aabb46e5fac70d49e X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035557epcas5p2efa9fa41298b8a9aabb46e5fac70d49e References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Optional bus=E2=80=91devfreq support (CONFIG_MFC_USE_BUS_DEVFREQ). - New control framework (mfc_ctrl_type, mfc_ctrl_mode, ops structs). - QoS and per=E2=80=91context/core power=E2=80=91clock handling. - Multi=E2=80=91view / depth=E2=80=91map support with related buffers and c= onstants. - Added helper macros (mfc_macros.h) and extra pixel formats. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_data_struct.h | 659 +++++++++++++++++- .../samsung/exynos-mfc/base/mfc_format.h | 132 ++++ .../samsung/exynos-mfc/base/mfc_macros.h | 95 +++ 3 files changed, 882 insertions(+), 4 deletions(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_form= at.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_macr= os.h diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct= .h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h index 59fef39095d2..34b4b13b4f01 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h @@ -12,6 +12,13 @@ #ifndef __MFC_DATA_STRUCT_H #define __MFC_DATA_STRUCT_H __FILE__ =20 +#if IS_ENABLED(CONFIG_EXYNOS_PM_QOS) || IS_ENABLED(CONFIG_EXYNOS_PM_QOS_MO= DULE) +#define CONFIG_MFC_USE_BUS_DEVFREQ +#endif + +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ +#include +#endif #include #include #include @@ -24,6 +31,7 @@ #include "mfc_media.h" =20 /* DEBUGFS */ +#define MFC_DEFAULT_MEMLOG_LEVEL 0x7 #define MFC_DEFAULT_LOGGING_OPTION 0x7 =20 #define MFC_NUM_CORE 2 @@ -36,6 +44,9 @@ #define MFC_SFR_LOGGING_COUNT_SET1 28 #define MFC_SFR_LOGGING_COUNT_SET2 32 #define MFC_LOGGING_DATA_SIZE 950 +#define MFC_MAX_DEFAULT_PARAM 100 +#define MFC_NUM_EXTRA_DPB 5 +#define MFC_MAX_MB_TABLE 30 /* the number of priority is 2N(num of OPP) + 2 */ #define MFC_MAX_PRIO 12 /* The number of display DRC max frames that can occur continuously in NAL= _Q */ @@ -60,6 +71,14 @@ #define MFC_FMT_FLAG_MULTI_VIEW 0x0010 #define MFC_FMT_FLAG_DEPTH_MAP 0x0020 =20 +/* MFC Multi-View */ +#define MFC_NUM_FD_DEPTH_MAP 1 +#define MFC_NUM_MULTI_VIEW 2 +#define MFC_NUM_FD_SUB_VIEW_META 1 + +/* MFC meminfo */ +#define MFC_MEMINFO_MAX_NUM 10 + #define MFC_NUM_SPECIAL_BUF_NAME 25 =20 #define BANK_L_CTX 0 @@ -102,10 +121,59 @@ enum mfc_inst_type { enum mfc_inst_state { MFCINST_FREE =3D 0, MFCINST_INIT =3D 100, + MFCINST_GOT_INST, + MFCINST_HEAD_PARSED, + MFCINST_RUNNING_BUF_FULL, MFCINST_RUNNING, + MFCINST_FINISHING, MFCINST_RETURN_INST, MFCINST_ERROR, - MFCINST_ABORT + MFCINST_ABORT, + MFCINST_RES_CHANGE_INIT, + MFCINST_RES_CHANGE_FLUSH, + MFCINST_RES_CHANGE_FLUSH_FINISHED, + MFCINST_RES_CHANGE_END, + MFCINST_FINISHED, + MFCINST_ABORT_INST, + MFCINST_DPB_FLUSHING, + MFCINST_MOVE_INST, + MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_YET, +}; + +enum mfc_inst_state_query { + EQUAL =3D 0, + BIGGER, + SMALLER, + EQUAL_BIGGER, + EQUAL_SMALLER, + EQUAL_OR, +}; + +/** + * enum mfc_queue_state - The state of buffer queue. + */ +enum mfc_queue_state { + QUEUE_FREE =3D 0, + QUEUE_BUFS_REQUESTED, + QUEUE_BUFS_QUERIED, + QUEUE_BUFS_MMAPED, +}; + +enum mfc_dec_wait_state { + WAIT_NONE =3D 0, + WAIT_G_FMT =3D BIT(0), + WAIT_STOP =3D BIT(1), +}; + +/** + * enum mfc_check_state - The state for user notification + */ +enum mfc_check_state { + MFCSTATE_PROCESSING =3D 0, + MFCSTATE_DEC_RES_DETECT, + MFCSTATE_DEC_TERMINATING, + MFCSTATE_ENC_NO_OUTPUT, + MFCSTATE_DEC_S3D_REALLOC, }; =20 enum mfc_buf_usage_type { @@ -116,6 +184,54 @@ enum mfc_buf_usage_type { MFCBUF_DRM_FW, }; =20 +enum mfc_buf_process_type { + MFCBUFPROC_DEFAULT =3D 0x0, + MFCBUFPROC_COPY =3D BIT(0), + MFCBUFPROC_SHARE =3D BIT(1), + MFCBUFPROC_META =3D BIT(2), + MFCBUFPROC_ANBSHARE =3D BIT(3), + MFCBUFPROC_ANBSHARE_NV12L =3D BIT(4), +}; + +enum mfc_ctrl_type { + MFC_CTRL_TYPE_GET_SRC =3D 0x1, + MFC_CTRL_TYPE_GET_DST =3D 0x2, + MFC_CTRL_TYPE_SET_SRC =3D 0x4, + MFC_CTRL_TYPE_SET_DST =3D 0x8, +}; + +enum mfc_ctrl_mode { + MFC_CTRL_MODE_NONE =3D 0x0, + MFC_CTRL_MODE_SFR =3D 0x1, +}; + +enum mfc_mb_flag { + /* Driver set to user when DST DQbuf */ + MFC_FLAG_HDR_CONTENT_LIGHT =3D 0, + MFC_FLAG_HDR_DISPLAY_COLOUR =3D 1, + MFC_FLAG_HDR_MAXTIX_COEFF =3D 2, + MFC_FLAG_HDR_COLOUR_DESC =3D 3, + MFC_FLAG_HDR_VIDEO_SIGNAL_TYPE =3D 4, + MFC_FLAG_BLACKBAR_DETECT =3D 5, + MFC_FLAG_HDR_PLUS =3D 6, + MFC_FLAG_DISP_RES_CHANGE =3D 7, + MFC_FLAG_UNCOMP =3D 8, + MFC_FLAG_FRAMERATE_CH =3D 9, + MFC_FLAG_SYNC_FRAME =3D 10, + MFC_FLAG_AV1_FILM_GRAIN =3D 11, + MFC_FLAG_MULTIFRAME =3D 12, + MFC_FLAG_RIGHT_IS_MAIN_VIEW =3D 13, + /* Driver set to user when SRC DQbuf */ + MFC_FLAG_CONSUMED_ONLY =3D 15, + /* User set to driver when SRC Qbuf */ + MFC_FLAG_ENC_SRC_VOTF =3D 26, + MFC_FLAG_ENC_SRC_FAKE =3D 27, + MFC_FLAG_ENC_SRC_UNCOMP =3D 28, + MFC_FLAG_CSD =3D 29, + MFC_FLAG_EMPTY_DATA =3D 30, + MFC_FLAG_LAST_FRAME =3D 31, +}; + enum mfc_frame_error_type { MFC_ERR_FRAME_NO_ERR =3D 0, MFC_ERR_FRAME_CONCEALMENT =3D 1, @@ -152,6 +268,31 @@ enum mfc_debug_cause { MFC_CAUSE_FAIL_DPB_FLUSH =3D 12, MFC_CAUSE_FAIL_CACHE_FLUSH =3D 13, MFC_CAUSE_FAIL_MOVE_INST =3D 14, + /* last information */ + MFC_LAST_INFO_BLACK_BAR =3D 26, + MFC_LAST_INFO_NAL_QUEUE =3D 27, + MFC_LAST_INFO_CLOCK =3D 28, + MFC_LAST_INFO_POWER =3D 29, + MFC_LAST_INFO_SHUTDOWN =3D 30, + MFC_LAST_INFO_DRM =3D 31, +}; + +enum mfc_request_work { + MFC_WORK_BUTLER =3D 0x1, + MFC_WORK_TRY =3D 0x2, +}; + +enum mfc_qos_control { + MFC_QOS_ON =3D 0x1, + MFC_QOS_OFF =3D 0x2, + MFC_QOS_TRIGGER =3D 0x3, +}; + +enum mfc_ts_type { + MFC_TS_SRC =3D 0x1, + MFC_TS_DST_Q =3D 0x2, + MFC_TS_SRC_Q =3D 0x3, + MFC_TS_DST_DQ =3D 0x4, }; =20 enum mfc_core_type { @@ -200,6 +341,13 @@ enum mfc_real_time { MFC_RT_UNDEFINED =3D 4, }; =20 +enum mfc_hwapg_status { + MFC_HWAPG_CLEAR =3D 0, + MFC_HWAPG_ENABLE =3D 1, + MFC_HWAPG_HOST_SET =3D 2, + MFC_HWAPG_HOST_CLEAR =3D 3, +}; + enum mfc_sched_type { MFC_SCHED_RR =3D 0, MFC_SCHED_PRIO =3D 1, @@ -236,6 +384,7 @@ struct mfc_debug { u8 power_cnt; u8 clock_cnt; /* for decoder only */ + u64 dynamic_used; u32 last_src_addr; u32 last_dst_addr[MFC_MAX_PLANES]; /* total logging data */ @@ -268,6 +417,7 @@ struct mfc_buf { unsigned char *vir_addr[MFC_MAX_PLANES]; u32 flag; unsigned long i_ino; + int select_view; }; =20 struct mfc_buf_queue { @@ -275,6 +425,12 @@ struct mfc_buf_queue { unsigned int count; }; =20 +struct mfc_bits { + unsigned long bits; + /* protection */ + spinlock_t lock; +}; + struct mfc_hwlock { struct list_head waiting_list; unsigned int wl_count; @@ -307,9 +463,11 @@ struct mfc_core_intlock { =20 struct mfc_core_lock { int cnt; + int migrate; /* protection */ spinlock_t lock; wait_queue_head_t wq; + wait_queue_head_t migrate_wq; }; =20 struct mfc_pm { @@ -344,6 +502,7 @@ struct mfc_fw { =20 struct mfc_ctx_buf_size { size_t dev_ctx; + size_t h264_dec_ctx; size_t dbg_info_buf; }; =20 @@ -487,6 +646,7 @@ struct mfc_debugfs { unsigned int sfr_dump; unsigned int logging_option; unsigned int feature_option; + unsigned int core_balance; unsigned int sched_perf_disable; unsigned int sched_type; }; @@ -524,6 +684,74 @@ struct mfc_mem { size_t size; }; =20 +enum mfc_meminfo_type { + MFC_MEMINFO_FW =3D 0, + MFC_MEMINFO_INTERNAL =3D 1, + MFC_MEMINFO_INPUT =3D 2, + MFC_MEMINFO_OUTPUT =3D 3, + MFC_MEMINFO_CTX_ALL =3D 4, + MFC_MEMINFO_CTX_MAX =3D 5, + MFC_MEMINFO_DEV_ALL =3D 6, +}; + +struct mfc_meminfo { + enum mfc_meminfo_type type; + const char *name; + unsigned int count; + size_t size; + size_t total; +}; + +struct mfc_bw_data { + unsigned int peak; + unsigned int read; + unsigned int write; +}; + +struct mfc_bw_info { + struct mfc_bw_data bw_dec_h264; +}; + +/* + * threshold_mb - threshold of total MB(macroblock) count + * Total MB count can be calculated by + * (MB of width) * (MB of height) * fps + */ +struct mfc_qos { + unsigned int threshold_mb; + unsigned int freq_mfc; + unsigned int freq_int; + unsigned int freq_mif; + unsigned int mo_value; + unsigned int time_fw; + unsigned int bts_scen_idx; + const char *name; +}; + +struct mfc_qos_boost { + unsigned int num_cluster; + unsigned int num_cpu[MAX_NUM_CLUSTER]; + unsigned int freq_mfc; + unsigned int freq_int; + unsigned int freq_mif; + unsigned int freq_cluster[MAX_NUM_CLUSTER]; + unsigned int bts_scen_idx; + const char *name; +}; + +struct mfc_qos_ctrl { + unsigned int idx; + unsigned int table_type; + unsigned int mfc_freq; +}; + +struct mfc_qos_weight { + unsigned int weight_h264_hevc; + unsigned int weight_3plane; + unsigned int weight_num_of_tile; + unsigned int weight_mbaff; +}; + struct mfc_feature { unsigned int support; unsigned int version; @@ -537,25 +765,58 @@ struct mfc_resource { struct mfc_platdata { /* Debug mode */ unsigned int debug_mode; + unsigned int stride_align; + unsigned int stride_type; + unsigned int stream_buf_limit; + unsigned int support_8K_cavlc; + /* Formats */ + unsigned int support_10bit; + unsigned int support_422; + /* Resolution */ + unsigned int support_check_res; + + /* error type for sync_point display */ + unsigned int display_err_type; + /* output buffer Q framerate */ + unsigned int display_framerate; =20 /* Resource */ struct mfc_resource mfc_resource[MFC_MAX_CODEC_TYPE]; /* Features */ + struct mfc_feature skype; + struct mfc_feature black_bar; + struct mfc_feature color_aspect_dec; + struct mfc_feature static_info_dec; + struct mfc_feature vp9_stride_align; struct mfc_feature mem_clear; struct mfc_feature wait_fw_status; + struct mfc_feature hevc_pic_output_flag; + + struct mfc_bw_info mfc_bw_info; + unsigned int dynamic_weight; + struct mfc_qos_weight qos_weight; =20 const char *fw_name; unsigned int fw_mem_size; unsigned int reserved_start; unsigned int dma_bit_mask; unsigned int ip_ver; + int num_mfc_freq; + unsigned int mfc_freqs[MAX_NUM_MFC_FREQ]; + unsigned int core_balance; unsigned int iova_threshold; + unsigned int idle_clk_ctrl; + unsigned int qos_ctrl_level; =20 unsigned int memlog_size; + unsigned int memlog_sfr_size; + + unsigned int reg_h264_loop_filter_disable; =20 unsigned int scheduler; unsigned int pbs_num_prio; enum mfc_hwacg_type support_hwacg; + unsigned int support_mv_hevc; }; =20 struct mfc_core_platdata { @@ -575,6 +836,36 @@ struct mfc_core_platdata { unsigned int masterid_shift; unsigned int masterid_mask; unsigned int tsmux_masterid; + /* QoS */ + unsigned int num_default_qos_steps; + unsigned int max_mb; + unsigned int max_hw_mb; + unsigned int mfc_freq_control; + unsigned int mo_control; + unsigned int bw_control; + unsigned int pm_qos_id; + unsigned int mfc_bw_index; + struct mfc_qos *default_qos_table; + struct mfc_qos *encoder_qos_table; + struct mfc_qos_boost *qos_boost_table; +}; + +struct mfc_perf { + void __iomem *regs_base0; + void __iomem *regs_base1; + + struct timespec64 begin; + struct timespec64 end; + + int new_start; + int count; + int drv_margin; + unsigned int latency; +}; + +struct mfc_dump_ops { + void (*dump_info_context)(struct mfc_dev *dev); + void (*dump_and_stop_debug_mode)(struct mfc_dev *dev); }; =20 struct mfc_dev_memlog { @@ -618,14 +909,17 @@ struct mfc_dev { =20 /* protection */ struct mutex mfc_mutex; + /* protection */ + struct mutex mfc_migrate_mutex; =20 struct mfc_ctx *ctx[MFC_NUM_CONTEXTS]; struct mfc_ctx *move_ctx[MFC_NUM_CONTEXTS]; dma_addr_t dma_base[BANK_NUM]; - + int move_ctx_cnt; struct list_head ctx_list; /* protection */ spinlock_t ctx_list_lock; + unsigned int core_balance; =20 atomic_t queued_bits; /* protection */ @@ -641,14 +935,45 @@ struct mfc_dev { =20 /* Debugfs and dump */ struct mfc_debugfs debugfs; + struct mfc_dump_ops *dump_ops; + + /* Instance migration worker */ + struct workqueue_struct *migration_wq; + struct work_struct migration_work; + + /* Butler */ + struct workqueue_struct *butler_wq; + struct work_struct butler_work; + + /* Lazy unmap disable */ + int skip_lazy_unmap; + + int tmu_fps; + + int max_kbps; + + /* Reg test */ + char *reg_buf; + unsigned int *reg_val; + unsigned int reg_cnt; =20 + struct mfc_meminfo meminfo[MFC_MEMINFO_DEV_ALL + 1]; struct mfc_dev_memlog memlog; =20 + const struct mfc_votf_ops *votf_ops; }; =20 struct mfc_core_ops { int (*instance_init)(struct mfc_core *core, struct mfc_ctx *ctx); int (*instance_deinit)(struct mfc_core *core, struct mfc_ctx *ctx); + int (*instance_open)(struct mfc_core *core, struct mfc_ctx *ctx); + void (*instance_cache_flush)(struct mfc_core *core, struct mfc_ctx *ctx); + int (*instance_move_to)(struct mfc_core *core, struct mfc_ctx *ctx); + int (*instance_move_from)(struct mfc_core *core, struct mfc_ctx *ctx); + int (*request_work)(struct mfc_core *core, enum mfc_request_work work, st= ruct mfc_ctx *ctx); + /* for DEC */ + void (*instance_dpb_flush)(struct mfc_core *core, struct mfc_ctx *ctx); + int (*instance_init_buf)(struct mfc_core *core, struct mfc_ctx *ctx); }; =20 struct dump_info { @@ -693,6 +1018,7 @@ struct mfc_core { /* Power and Clock */ atomic_t clk_ref; struct mfc_pm pm; + struct mfc_perf perf; bool continue_clock_on; bool sleep; bool shutdown; @@ -725,13 +1051,17 @@ struct mfc_core { int next_ctx_idx; =20 /* HW lock */ + struct mfc_bits work_bits; struct mfc_hwlock hwlock; struct mfc_listable_wq hwlock_wq; wait_queue_head_t cmd_wq; =20 + struct mfc_core_dump_ops *dump_ops; + /* batch mode */ int batch_enable; int batch_index; + enum mfc_hwapg_status hwapg_status; /* protection */ spinlock_t batch_lock; =20 @@ -748,10 +1078,20 @@ struct mfc_core { int cache_flush_flag; int last_cmd_has_cache_flush; =20 + /* Butler */ + struct workqueue_struct *butler_wq; + struct work_struct butler_work; + /* QoS */ struct list_head qos_queue; atomic_t qos_req_cur; - +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ + struct exynos_pm_qos_request qos_req_mfc_noidle; + struct exynos_pm_qos_request qos_req_mfc; + struct exynos_pm_qos_request qos_req_int; + struct exynos_pm_qos_request qos_req_mif; + struct freq_qos_request qos_req_cluster[MAX_NUM_CLUSTER]; +#endif /* protection */ struct mutex qos_mutex; int mfc_freq_by_bps; @@ -760,6 +1100,14 @@ struct mfc_core { unsigned long total_mb; unsigned int cpu_boost_enable; =20 + /* QoS control depending on MFC H/W run */ + struct workqueue_struct *qos_ctrl_wq; + struct work_struct qos_ctrl_work; + struct mfc_qos_ctrl qos_ctrl[MAX_NUM_QOS_DYNAMIC]; + unsigned int qos_ctrl_last_idx; + atomic_t qos_ctrl_cnt; + /* protection */ + struct mutex pm_qos_mutex; /* Logging trace data */ atomic_t trace_ref_log; struct _mfc_trace_logging *mfc_trace_logging; @@ -773,6 +1121,10 @@ struct mfc_core { struct dump_info dbg_info; =20 /* Debug */ + char *reg_buf; + unsigned int *reg_val; + unsigned int reg_cnt; + struct mfc_meminfo meminfo[MFC_MEMINFO_DEV_ALL + 1]; struct mfc_core_memlog memlog; }; =20 @@ -783,6 +1135,7 @@ struct mfc_ctx_ctrl_val { =20 struct mfc_ctx_ctrl { struct list_head list; + enum mfc_ctrl_type type; unsigned int id; unsigned int addr; struct mfc_ctx_ctrl_val set; @@ -792,6 +1145,7 @@ struct mfc_ctx_ctrl { struct mfc_buf_ctrl { struct list_head list; unsigned int id; + enum mfc_ctrl_type type; int has_new; int val; unsigned int old_val; /* only for MFC_CTRL_TYPE_SET */ @@ -808,6 +1162,7 @@ struct mfc_buf_ctrl { }; =20 struct mfc_ctrl_cfg { + enum mfc_ctrl_type type; unsigned int id; unsigned int is_volatile; /* only for MFC_CTRL_TYPE_SET */ unsigned int mode; @@ -819,6 +1174,46 @@ struct mfc_ctrl_cfg { unsigned int flag_shft; /* only for MFC_CTRL_TYPE_SET */ }; =20 +/* per buffer control */ +extern struct mfc_ctrls_ops mfc_ctrls_ops; +struct mfc_ctrls_ops { + void (*cleanup_ctx_ctrls)(struct mfc_ctx *ctx); + int (*init_ctx_ctrls)(struct mfc_ctx *ctx); + void (*reset_buf_ctrls)(struct list_head *head); + int (*cleanup_buf_ctrls)(struct mfc_ctx *ctx, enum mfc_ctrl_type type, un= signed int index); + int (*init_buf_ctrls)(struct mfc_ctx *ctx, enum mfc_ctrl_type type, unsig= ned int index); + void (*to_buf_ctrls)(struct mfc_ctx *ctx, struct list_head *head); + void (*to_ctx_ctrls)(struct mfc_ctx *ctx, struct list_head *head); + int (*get_buf_ctrl_val)(struct mfc_ctx *ctx, struct list_head *head, unsi= gned int id); + void (*update_buf_val)(struct mfc_ctx *ctx, struct list_head *head, + unsigned int id, int value); +}; + +extern struct mfc_bufs_ops mfc_bufs_ops; +struct mfc_bufs_ops { + int (*core_set_buf_ctrls)(struct mfc_core *core, + struct mfc_ctx *ctx, struct list_head *head); + int (*core_get_buf_ctrls)(struct mfc_core *core, + struct mfc_ctx *ctx, struct list_head *head); + int (*core_recover_buf_ctrls)(struct mfc_core *core, + struct mfc_ctx *ctx, struct list_head *head); + int (*core_restore_buf_ctrls)(struct mfc_ctx *ctx, struct list_head *head= ); +}; + +struct stored_dpb_info { + int fd[MFC_MAX_PLANES]; +}; + +struct dec_dpb_ref_info { + int index; + struct stored_dpb_info dpb[MFC_MAX_BUFFERS]; +}; + +struct temporal_layer_info { + unsigned int temporal_layer_count; + unsigned int temporal_layer_bitrate[VIDEO_MAX_TEMPORAL_LAYERS]; +}; + struct mfc_user_shared_handle { int fd; struct dma_buf *dma_buf; @@ -835,6 +1230,35 @@ struct mfc_raw_info { unsigned int total_plane_size; }; =20 +struct mfc_timestamp { + struct list_head list; + struct timespec64 timestamp; + int index; + int interval; +}; + +struct mfc_ts_control { + struct mfc_timestamp ts_array[MAX_TIME_INDEX]; + int ts_interval_array[MAX_TIME_INDEX]; + struct list_head ts_list; + int ts_count; + int ts_is_full; + int ts_last_interval; + /* protection */ + spinlock_t ts_lock; +}; + +struct mfc_bitrate { + struct list_head list; + int bytesused; +}; + +struct mfc_mb_control { + struct list_head list; + unsigned long mb_per_sec; + unsigned int fps; +}; + struct dpb_table { dma_addr_t addr[MFC_MAX_PLANES]; phys_addr_t paddr; @@ -849,6 +1273,97 @@ struct dpb_table { struct sg_table *sgt[MFC_MAX_PLANES]; }; =20 +struct disp_drc_info { + int disp_res_change; + int push_idx; + int pop_idx; + int width[MFC_MAX_DRC_FRAME]; + int height[MFC_MAX_DRC_FRAME]; +}; + +struct mfc_dec { + int total_dpb_count; + + unsigned int src_buf_size; + + int loop_filter_mpeg4; + int display_delay; + int immediate_display; + int slice_enable; + int mv_count; + int idr_decoding; + int is_interlaced; + int is_mbaff; + int is_dts_mode; + int inter_res_change; + struct disp_drc_info disp_drc; + + int crc_enable; + unsigned int *crc; + int crc_idx; + + unsigned int consumed; + + int sei_parse; + + int cr_left, cr_right, cr_top, cr_bot; + + int detect_black_bar; + bool black_bar_updated; + struct v4l2_rect black_bar; + + /* For dynamic DPB */ + int is_dynamic_dpb; + int is_dpb_full; + int display_index; + unsigned long queued_dpb; + unsigned long dynamic_set; + unsigned long dynamic_used; + + int is_multiframe; + int has_multiframe; + int is_multiple_show; + + unsigned int num_of_tile_over_4; + unsigned int super64_bframe; + + unsigned int color_range; + unsigned int color_space; + + unsigned int decoding_order; + unsigned int frame_display_delay; + + struct mfc_fmt *uncomp_fmt; + + /* for Internal DPB */ + struct mfc_special_buf internal_dpb[MFC_MAX_DPBS]; + unsigned long plugin_used; + + /* for Dynamic DPB */ + struct dpb_table dpb[MFC_MAX_DPBS]; + /* protection */ + struct mutex dpb_mutex; + unsigned long dpb_table_used; + struct dec_dpb_ref_info *ref_info; + struct stored_dpb_info ref_buf[MFC_MAX_BUFFERS]; + int refcnt; + int last_dpb_max_index; + struct mfc_user_shared_handle sh_handle_dpb; + + /* for debugging about black bar detection */ + void *frame_vaddr[3][30]; + dma_addr_t frame_daddr[3][30]; + int index[3][30]; + int fd[3][30]; + unsigned int frame_size[3][30]; + unsigned char frame_cnt; +}; + +struct mfc_resolution { + int width; + int height; +}; + struct mfc_fmt { char *name; u32 fourcc; @@ -868,18 +1383,65 @@ enum mfc_multi_view_buf_idx { MFC_MV_BUF_IDX_MAX, }; =20 +struct mfc_multi_view_buf_info { + int offset; + int num_fd; +}; + /** * struct mfc_ctx - This struct contains the instance context */ struct mfc_ctx { struct mfc_dev *dev; + struct mfc_dec *dec_priv; =20 int num; int prio; int user_prio; enum mfc_real_time rt; =20 + struct mfc_fmt *src_fmt; + struct mfc_fmt *dst_fmt; + + struct mfc_buf_queue src_buf_ready_queue; + struct mfc_buf_queue dst_buf_queue; + struct mfc_buf_queue err_buf_queue; + struct mfc_buf_queue ref_buf_queue; /* Encoder only */ + spinlock_t buf_queue_lock; + enum mfc_inst_type type; + int subcore_inst_no; + + int img_width; + int img_height; + int crop_width; + int crop_height; + int crop_left; + int crop_top; + int mb_width; + int mb_height; + int dpb_count; + int rgb_bpp; + + int min_dpb_size[3]; + int min_dpb_size_2bits[3]; + + int bytesperline[3]; + struct mfc_raw_info raw_buf; + + int num_fd_frame; + struct mfc_multi_view_buf_info view_buf_info[MFC_MV_BUF_IDX_MAX]; + int select_view; + int select_view_irq; + + enum mfc_queue_state capture_state; + enum mfc_queue_state output_state; + + DECLARE_BITMAP(src_ctrls_avail, MFC_MAX_BUFFERS); + DECLARE_BITMAP(dst_ctrls_avail, MFC_MAX_BUFFERS); + + unsigned int sequence; + int stored_tag; =20 /* operation mode */ int op_core_num[MFC_NUM_CORE]; @@ -888,7 +1450,9 @@ struct mfc_ctx { enum mfc_op_mode op_mode; enum mfc_op_core_type op_core_type; struct mfc_core_lock corelock; - + int serial_src_index; + int curr_src_index; + int cmd_counter; /* protection */ struct mutex op_mode_mutex; int last_op_core; @@ -900,10 +1464,20 @@ struct mfc_ctx { int codec_mode; __u32 pix_format; =20 + /* Profile information */ + int is_422; + int bit_depth_luma; + int bit_depth_chroma; + + /* for AV1 Annex B */ + int is_av1_annex_b; + int is_heif_mode; =20 int is_dpb_realloc; + enum mfc_dec_wait_state wait_state; /* protection */ + struct mutex drc_wait_mutex; int clear_work_bit; =20 /* Extra Buffers */ @@ -911,8 +1485,25 @@ struct mfc_ctx { struct mfc_special_buf mv_buf; =20 unsigned long framerate; + unsigned long last_framerate; + unsigned long operating_framerate; + unsigned long dst_q_framerate; + unsigned long dst_dq_framerate; + unsigned long src_q_framerate; + unsigned long max_framerate; + unsigned int qos_ratio; + bool update_framerate; + bool update_bitrate; + bool check_src_ts_full; + bool ktime_used; + + struct mfc_ts_control src_ts; + struct mfc_ts_control dst_q_ts; + struct mfc_ts_control src_q_ts; + struct mfc_ts_control dst_dq_ts; =20 /* bitrate control for QoS*/ + struct mfc_bitrate bitrate_array[MAX_TIME_INDEX]; struct list_head bitrate_list; int bitrate_index; int bitrate_is_full; @@ -922,6 +1513,7 @@ struct mfc_ctx { unsigned long weighted_mb; struct list_head list; =20 + unsigned int *mfc_qos_portion; int disp_ratio; =20 int buf_process_type; @@ -939,11 +1531,35 @@ struct mfc_ctx { /* QoS idle */ enum mfc_idle_mode idle_mode; =20 + /* Lazy unmap disable */ + int skip_lazy_unmap; + /* external structure */ struct v4l2_fh fh; struct vb2_queue vq_src; struct vb2_queue vq_dst; =20 + /* per buffer controls */ + struct mfc_ctrls_ops *c_ops; + struct mfc_bufs_ops *b_ops; + struct list_head ctrls; + struct list_head src_ctrls[MFC_MAX_BUFFERS]; + struct list_head dst_ctrls[MFC_MAX_BUFFERS]; + + /* Extra Buffers size */ + size_t mv_size; + size_t scratch_buf_size; + size_t loopfilter_luma_size; + size_t loopfilter_chroma_size; + + /* mem info */ + struct mfc_buf_queue meminfo_inbuf_q; + struct mfc_buf_queue meminfo_outbuf_q; + /* protection */ + spinlock_t meminfo_queue_lock; + struct mfc_meminfo meminfo[MFC_MEMINFO_MAX_NUM]; + size_t meminfo_size[MFC_MEMINFO_CTX_MAX + 1]; + u32 ready_to_be_multi_view_enable; u32 multi_view_enable; u32 left_view_id; @@ -963,11 +1579,35 @@ struct mfc_core_ctx { unsigned int int_err; bool check_dump; =20 + struct mfc_buf_queue src_buf_queue; + struct mfc_buf_queue dst_buf_queue; + /* protection */ + spinlock_t buf_queue_lock; + unsigned long dynamic_set; + enum mfc_inst_state state; enum mfc_inst_state prev_state; =20 + /* QoS */ + struct list_head qos_list; + + /* MB control for QoS */ + struct mfc_mb_control mb_table[MFC_MAX_MB_TABLE]; + struct list_head mb_list; + int mb_index; + int mb_is_full; + int dynamic_weight_level; + unsigned int dynamic_weight_started; + int mb_update_time; + unsigned int avg_runtime; + unsigned long mb_not_coded_time; + /* Extra Buffers */ + int codec_buffer_allocated; + int scratch_buffer_allocated; + struct mfc_special_buf codec_buf; struct mfc_special_buf instance_ctx_buf; + struct mfc_special_buf scratch_buf; =20 /* wait queue */ wait_queue_head_t cmd_wq; @@ -979,7 +1619,18 @@ struct mfc_sched_class { void (*create_work)(struct mfc_core *core); void (*init_work)(struct mfc_core *core); void (*clear_all_work)(struct mfc_core *core); + void (*queue_work)(struct mfc_core *core); + int (*is_work)(struct mfc_core *core); + int (*pick_next_work)(struct mfc_core *core); + int (*get_next_work)(struct mfc_core *core); + void (*set_work)(struct mfc_core *core, struct mfc_core_ctx *core_ctx); void (*clear_work)(struct mfc_core *core, struct mfc_core_ctx *core_ctx); + int (*enqueue_work)(struct mfc_core *core, struct mfc_core_ctx *core_ctx); + int (*enqueue_otf_work)(struct mfc_core *core, struct mfc_core_ctx *core_= ctx, bool flag); + int (*dequeue_work)(struct mfc_core *core, struct mfc_core_ctx *core_ctx); + void (*yield_work)(struct mfc_core *core, struct mfc_core_ctx *core_ctx); + int (*change_prio_work)(struct mfc_core *core, struct mfc_ctx *ctx, + int cur_rt, int cur_prio, int new_rt, int new_prio); }; #endif /* __MFC_DATA_STRUCT_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h new file mode 100644 index 000000000000..3307c2eeaebb --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_format.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_FORMAT_H +#define __MFC_FORMAT_H __FILE__ + +#define MFC_NUM_FORMATS ARRAY_SIZE(mfc_formats) + +static struct mfc_fmt mfc_formats[] =3D { + { + .name =3D "Unknown", + .fourcc =3D 0, + .codec_mode =3D 0, + .type =3D 0, + .num_planes =3D 0, + .mem_planes =3D 0, + }, + { + .name =3D "YUV420M", + .fourcc =3D V4L2_PIX_FMT_YUV420M, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME, + .num_planes =3D 3, + .mem_planes =3D 3, + }, + { + .name =3D "YVU420M", + .fourcc =3D V4L2_PIX_FMT_YVU420M, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME, + .num_planes =3D 3, + .mem_planes =3D 3, + }, + { + .name =3D "NV12M", + .fourcc =3D V4L2_PIX_FMT_NV12M, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME, + .num_planes =3D 2, + .mem_planes =3D 2, + }, + { + .name =3D "NV21M", + .fourcc =3D V4L2_PIX_FMT_NV21M, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME, + .num_planes =3D 2, + .mem_planes =3D 2, + }, + { + .name =3D "NV16M", + .fourcc =3D V4L2_PIX_FMT_NV16M, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_422, + .num_planes =3D 2, + .mem_planes =3D 2, + }, + { + .name =3D "NV61M", + .fourcc =3D V4L2_PIX_FMT_NV61M, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_422, + .num_planes =3D 2, + .mem_planes =3D 2, + }, + { + .name =3D "RGB888 24bpp", + .fourcc =3D V4L2_PIX_FMT_RGB24, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_RGB, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "RGB565 16bpp", + .fourcc =3D V4L2_PIX_FMT_RGB565, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_RGB, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "RGBX8888 32bpp", + .fourcc =3D V4L2_PIX_FMT_RGB32X, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_RGB, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "BGRA8888 32bpp", + .fourcc =3D V4L2_PIX_FMT_BGR32, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_RGB, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "ARGB8888 32bpp", + .fourcc =3D V4L2_PIX_FMT_ARGB32, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_RGB, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "RGB8888 32bpp", + .fourcc =3D V4L2_PIX_FMT_RGB32, + .codec_mode =3D MFC_FORMATS_NO_CODEC, + .type =3D MFC_FMT_FRAME | MFC_FMT_RGB, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC H264", + .fourcc =3D V4L2_PIX_FMT_H264, + .codec_mode =3D MFC_REG_CODEC_H264_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, +}; + +#endif /* __MFC_FORMAT_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_macros.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_macros.h new file mode 100644 index 000000000000..a41686214988 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_macros.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_macros.h file + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_MACROS_H +#define __MFC_MACROS_H __FILE__ + +/* helper macros */ +#ifndef __ALIGN_UP +#define __ALIGN_UP(x, a) ({ \ + typeof(a) _a =3D (a); \ + (((x) + (_a - 1)) & ~(_a - 1)); \ +}) +#endif + +#define WIDTH_MB(x_size) (((x_size) + 15) / 16) +#define HEIGHT_MB(y_size) (((y_size) + 15) / 16) + +/* + * Note that lcu_width and lcu_height are defined as follows : + * lcu_width =3D (frame_width + lcu_size - 1)/lcu_size + * lcu_height =3D (frame_height + lcu_size - 1)/lcu_size. + * (lcu_size is 32(encoder) or 64(decoder)) + * + * Note that ctb_width and ctb_height are defined as follows : + * ctb_width =3D (frame_width + ctb_size - 1)/ctb_size + * ctb_height =3D (frame_hegiht + ctb_size - 1)/ctb_size + * (ctb_size is 128(AV1 decoder)) + * + */ +#define DEC_LCU_WIDTH(x_size) (((x_size) + 63) / 64) +#define ENC_LCU_WIDTH(x_size) (((x_size) + 31) / 32) +#define DEC_LCU_HEIGHT(y_size) (((y_size) + 63) / 64) +#define ENC_LCU_HEIGHT(y_size) (((y_size) + 31) / 32) + +#define DEC_CTB_WIDTH(x_size) (((x_size) + 127) / 128) +#define DEC_CTB_HEIGHT(y_size) (((y_size) + 127) / 128) + +#define STREAM_BUF_ALIGN SZ_512 +#define MFC_LINEAR_BUF_SIZE SZ_256 + +#define DEC_STATIC_BUFFER_SIZE 20480 +/* STATIC buffer for AV1 will be aligned by 32 */ +#define DEC_AV1_STATIC_BUFFER_SIZE(x_size, y_size) \ + __ALIGN_UP((440192 + (DEC_LCU_WIDTH(x_size) * DEC_LCU_HEIGHT(y_size) * SZ= _8K)), SZ_32) + +#define DEC_MV_SIZE_MB(x, y) (WIDTH_MB(x) \ + * (((HEIGHT_MB(y) + SZ_1) / SZ_2) * SZ_2) * SZ_64 + SZ_1K) +#define DEC_HEVC_MV_SIZE(x, y) (DEC_LCU_WIDTH(x) * DEC_LCU_HEIGHT(y) * SZ_= 256 + SZ_512) +#define DEC_AV1_MV_SIZE(x, y) ((DEC_CTB_WIDTH(x) * DEC_CTB_HEIGHT(y) * 153= 6) * 10) + +#define ENC_LUMA_DPB_SIZE(x, y) ((((x) + 63) / 64) * 64 * (((y) + 31) / = 32) * 32 + 64) +#define ENC_CHROMA_DPB_SIZE(x, y) ((((x) + 63) / 64) * 64 * (((((y) + 31) = / 32) * 32) / 2) + 64) + +#define ENC_V100_H264_ME_SIZE(x, y) \ + ({ \ + typeof(x) __x =3D (x); \ + typeof(y) __y =3D (y); \ + ((((__x) + 3) * ((__y) + 3) * 8) + (((((__x) * (__y)) + 63) / 64) * 32) = + \ + ((((__y) * 64) + 2304) * ((__x) + 7) / 8)); \ + }) +#define ENC_V100_MPEG4_ME_SIZE(x, y) \ + ({ \ + typeof(x) __x =3D (x); \ + typeof(y) __y =3D (y); \ + ((((__x) + 3) * ((__y) + 3) * 8) + (((((__x) * (__y)) + 127) / 128) * 16= ) + \ + ((((__y) * 64) + 2304) * ((__x) + 7) / 8)); \ + }) +#define ENC_V100_VP8_ME_SIZE(x, y) \ + ({ \ + typeof(x) __x =3D (x); \ + typeof(y) __y =3D (y); \ + ((((__x) + 3) * ((__y) + 3) * 8) + ((((__y) * 64) + 2304) * ((__x) + 7) = / 8)); \ + }) +#define ENC_V100_VP9_ME_SIZE(x, y) \ + ({ \ + typeof(x) __x =3D (x); \ + typeof(y) __y =3D (y); \ + (((((__x) * 2) + 3) * (((__y) * 2) + 3) * 128) + \ + ((((__y) * 256) + 2304) * ((__x) + 1) / 2)); \ + }) +#define ENC_V100_HEVC_ME_SIZE(x, y) \ + ({ \ + typeof(x) __x =3D (x); \ + typeof(y) __y =3D (y); \ + ((((__x) + 3) * ((__y) + 3) * 32) + ((((__y) * 128) + 2304) * ((__x) + 3= ) / 4)); \ + }) + +#endif /* __MFC_MACROS_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 A6F0126CE34 for ; Tue, 30 Sep 2025 03:56:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204569; cv=none; b=sYk6KuW18tFW/RohMPCAyOiSdTH/IzjoyErdzSOrCphySeJ6PTEWEB1DPCbnFTKw9XUYkjRawbtlPkxeCS9+KMKP4cKAyviC2BReAXdoiL9gymuCYk4lo16dD/K1+ncl4VGoQT1TTS5Uk2KuxFUqDmUtCjwH+Q5iUx3HE0sHk0M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204569; c=relaxed/simple; bh=6prEJZPfuCLUpU018v8UiSFdTWAYG4Jl53YoIC5OYLY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=KCCgCcWK+DV+cPRP/OKWK6uo37/4IRiq9rRulrPqN0mXuzpzfusfYSIC9gmJYH6yo/F7KDWoMwT/TcWFAUpnhvMedLNlTgAEkazl4qJP3EZuapSe7RDZEKWU5YnZan8bfY708pgvyVlszst9CtrKpnqeGYTDTF1cAfdnkZ6DVC0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=YyRprlGf; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="YyRprlGf" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035602epoutp02afa93db0be7adac53420cd66f3b303cd~p8zq0t_Qn2636326363epoutp02O for ; Tue, 30 Sep 2025 03:56:02 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035602epoutp02afa93db0be7adac53420cd66f3b303cd~p8zq0t_Qn2636326363epoutp02O DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204562; bh=GmReH713XOv6Ei1nlAtnwGxy+kOekFmsGqxXzzq0lxc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YyRprlGfbzoopo4+KrO2i9+9KppjqbMnnphAlza4Xw+lXymy9HgP2P5CRbuKZdGbM 2LhMWwbdkq6tiIShbJMCOWV08qCiNegXLZpUt7Bsq4edsoqdS3HhU4doCmpVoVv0/c b0ALlDrTh/G+sJu7C42/6B3JjacDVvsiMFQI59P4= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035602epcas5p475adcdf4b6659ed51bd0cfa2dd161803~p8zqRl3xE0216002160epcas5p4V; Tue, 30 Sep 2025 03:56:02 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.95]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPNx28NJz6B9mN; Tue, 30 Sep 2025 03:56:01 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035600epcas5p13782313e204588986f44514d005ccf70~p8zotN_7-1532115321epcas5p1D; Tue, 30 Sep 2025 03:56:00 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035558epsmtip1f59f65f14ef1374c26bd2a80caafd9ce~p8zmO2dKz2908429084epsmtip1r; Tue, 30 Sep 2025 03:55:57 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 10/29] =?UTF-8?q?media:=20mfc:=20Add=20buffer=E2=80=91queu?= =?UTF-8?q?e=20and=20IOVMM=20support?= Date: Tue, 30 Sep 2025 09:33:29 +0530 Message-Id: <20250930040348.3702923-11-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035600epcas5p13782313e204588986f44514d005ccf70 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035600epcas5p13782313e204588986f44514d005ccf70 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Implement full buffer queue and DPB management (mfc_queue). - Extended mfc_common.h with runtime/boost macros and helper calls. - Added IOVMM helpers ; DPB allocation now uses IOVMM. - Introduced macros for MMCache timeout, boost timing, resolution thresholds, default runtime, and codec limits. - Refined debug and error handling throughout. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 1 + .../samsung/exynos-mfc/base/mfc_common.h | 174 +++- .../samsung/exynos-mfc/base/mfc_mem.c | 190 ++++ .../samsung/exynos-mfc/base/mfc_mem.h | 31 + .../samsung/exynos-mfc/base/mfc_queue.c | 930 ++++++++++++++++++ .../samsung/exynos-mfc/base/mfc_queue.h | 156 +++ .../samsung/exynos-mfc/base/mfc_utils.h | 41 + 7 files changed, 1522 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_queu= e.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_queu= e.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index bee95b16ac7c..5353289fa810 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -19,6 +19,7 @@ exynos_mfc-y +=3D mfc_core_hw_reg_api.o mfc_core_reg_api.o #Plugin control layer #Plugin HW access layer #Common base layer +exynos_mfc-y +=3D base/mfc_queue.o exynos_mfc-y +=3D base/mfc_buf.o base/mfc_mem.o #Tracing # exynos_mfc-y +=3D trace/mfc_trace_points.o diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h index 4a1ec714fbb5..de22c28d1625 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h @@ -20,6 +20,7 @@ #include #include "mfc_data_struct.h" #include "mfc_regs.h" +#include "mfc_macros.h" #include "mfc_debug.h" #include "mfc_media.h" =20 @@ -51,9 +52,20 @@ #define MFC_HWLOCK_TIMEOUT 12000 /* Busy wait timeout */ #define MFC_BW_TIMEOUT 500 - +/* MMCache invalidation timeout */ +#define MMCACHE_INVAL_TIMEOUT 1000 /* Interrupt timeout count*/ #define MFC_INT_TIMEOUT_CNT 3 +/* The initial skip frame for boost */ +#define MFC_INITIAL_SKIP_FRAME 60 +/* The MAX I frame interval for boosting is 2000ms(2sec) */ +#define MFC_BOOST_TIME (2000) +/* The boost mode is maintained at least 20msec */ +#define MFC_BOOST_OFF_TIME ((MFC_BOOST_TIME * NSEC_PER_MSEC) - (20 * NSEC= _PER_MSEC)) +/* The boost speed is multiplied by the framerate */ +#define MFC_BOOST_SPEED (8) + +#define MFC_DEFAULT_RUNTIME (33000) =20 /* * This value guarantees 299.4msec ~ 2.25sec according to MFC clock (668MH= z ~ 89MHz) @@ -63,6 +75,10 @@ /* 250ms is the mfc firmware timeout value*/ #define MFC_TIMEOUT_VALUE_IN_MSEC 250 =20 +#define NUM_MPEG4_LF_BUF 2 + +#define FRAME_RATE_RESOLUTION 10000 + #define DEFAULT_TAG (0xE05) #define IGNORE_TAG (0xD5C) /* ex) encoder DRC */ #define HEADER_TAG (0xC5D) @@ -70,6 +86,11 @@ =20 #define MFC_NO_INSTANCE_SET -1 =20 +#define MFC_ENC_CAP_PLANE_COUNT 1 +#define MFC_ENC_OUT_PLANE_COUNT 2 + +#define MFC_NAME_LEN 16 + #define STUFF_BYTE 4 #define MFC_EXTRA_DPB 5 #define MFC_ALL_AVAILABLE_DPB 0xffffffffffffffff @@ -82,12 +103,30 @@ #define mfc_get_warn(x) (((x) >> MFC_REG_WARN_STATUS_SHIFT) \ & MFC_REG_WARN_STATUS_MASK) =20 +/* MFC conceal color is black */ +#define MFC_CONCEAL_COLOR 0x8020000 + #define vb_to_mfc_buf(x) \ container_of(x, struct mfc_buf, vb.vb2_buf) =20 #define fh_to_mfc_ctx(x) \ container_of(x, struct mfc_ctx, fh) =20 +#define call_bop(ctx, op, args...) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->b_ops->op) ? (_ctx->b_ops->op(args)) : 0); \ +}) + +#define call_cop(ctx, op, args...) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->c_ops->op) ? (_ctx->c_ops->op(args)) : 0); \ +}) + +#define MFC_CTRL_TYPE_GET (MFC_CTRL_TYPE_GET_SRC | MFC_CTRL_TYPE_GET_DST) +#define MFC_CTRL_TYPE_SET (MFC_CTRL_TYPE_SET_SRC | MFC_CTRL_TYPE_SET_DST) +#define MFC_CTRL_TYPE_SRC (MFC_CTRL_TYPE_SET_SRC | MFC_CTRL_TYPE_GET_SRC) +#define MFC_CTRL_TYPE_DST (MFC_CTRL_TYPE_SET_DST | MFC_CTRL_TYPE_GET_DST) + #define MFC_FMT_STREAM BIT(0) #define MFC_FMT_FRAME BIT(1) #define MFC_FMT_10BIT BIT(2) @@ -112,6 +151,132 @@ (_n =3D=3D EXYNOS_VIDEONODE_MFC_ENC_OTF_DRM)); \ }) =20 +/* Decoder codec mode check */ +#define IS_H264_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_H264_DEC) + +#define ON_RES_CHANGE(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->state >=3D MFCINST_RES_CHANGE_INIT) && \ + (_ctx->state < MFCINST_RES_CHANGE_END)); \ +}) +#define ON_RUNNING(ctx) ((ctx)->state =3D=3D MFCINST_RUNNING) +#define NEED_TO_DPB_FLUSH(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->state =3D=3D MFCINST_FINISHING) || (_ctx->state =3D=3D MFCINST_RU= NNING) || \ + (_ctx->state =3D=3D MFCINST_RES_CHANGE_END)); \ +}) +#define NEED_TO_WAIT_NAL_ABORT(ctx) ((ctx)->state =3D=3D MFCINST_ABORT_INS= T) +#define NEED_TO_GET_CROP(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->state =3D=3D MFCINST_HEAD_PARSED) || (_ctx->state =3D=3D MFCINST_= RUNNING) || \ + (_ctx->state =3D=3D MFCINST_FINISHING)); \ +}) +#define IS_NO_ERROR(err) ({ \ + typeof(err) _err =3D (err); \ + (_err =3D=3D 0 || (mfc_get_warn(_err) =3D=3D MFC_REG_ERR_SYNC_POINT_NOT_R= ECEIVED)); \ +}) +#define CODEC_HAS_IDR(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_H264_DEC(_ctx)); \ +}) + +/* + * levels with maximum property values + * level Maximum frame size in MB (MB width x MB height) + * LV5 22,080 + * LV5.1 36,864 + * LV6 139,264 + */ + +#define LV51_MB_MIN (22080) +#define LV51_MB_MAX (36864) +#define LV60_MB_MAX (139264) + +#define IS_LV51_MB(mb) ({ \ + typeof(mb) _mb =3D (mb); \ + ((_mb > LV51_MB_MIN) && (_mb <=3D LV51_MB_MAX)); \ +}) +#define IS_LV60_MB(mb) ({ \ + typeof(mb) _mb =3D (mb); \ + ((_mb > LV51_MB_MAX) && (_mb <=3D LV60_MB_MAX)); \ +}) + +/* 8K resolution */ +#define MFC_8K_RES (7680 * 4320) +#define IS_8K_RES(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->crop_width * _ctx->crop_height) >=3D MFC_8K_RES); \ +}) +/* For max h/w performance */ +#define IS_8K_PERF(ctx) ({ \ + typeof(ctx) __ctx =3D (ctx); \ + ((__ctx->crop_width * __ctx->crop_height) >=3D (MFC_8K_RES / 2)); \ +}) +#define UNDER_8K_RES(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->crop_width * _ctx->crop_height) < MFC_8K_RES); \ +}) + +/* 4K resolution */ +/* VP9 needs 64 alignment because of min MB(64x64) */ +#define MFC_4K_RES (4096 * 2160) +#define MFC_4K_RES_64ALIGN (4096 * 2176) +#define UNDER_4K_RES(ctx) ({ \ + typeof(ctx) __ctx =3D (ctx); \ + ((__ctx->crop_width * __ctx->crop_height) < MFC_4K_RES_64ALIGN); \ +}) +#define IS_MULTI_MODE_RES(ctx) ({ \ + typeof(ctx) __ctx =3D (ctx); \ + ((__ctx->img_width * __ctx->img_height) >=3D (2 * MFC_4K_RES)); \ +}) + +/* UHD resolution */ +#define MFC_UHD_RES (3840 * 2160) +#define OVER_UHD_RES(ctx) ({ \ + typeof(ctx) __ctx =3D (ctx); \ + ((__ctx->crop_width * __ctx->crop_height) >=3D MFC_UHD_RES); \ +}) +#define UNDER_UHD_RES(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->crop_width * _ctx->crop_height) <=3D MFC_4K_RES_64ALIGN); \ +}) + +/* FHD resolution */ +#define MFC_FHD_RES (1920 * 1088) +#define MFC_1080P_RES (1920 * 1080) +#define MFC_FHD_RES_MB (((1920 + 15) / 16) * ((1088 + 15) / 16)) +#define UNDER_FHD_RES(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((_ctx->crop_width * _ctx->crop_height) <=3D MFC_FHD_RES); \ +}) +#define UNDER_1080P_RES(ctx) ({ \ + typeof(ctx) __ctx =3D (ctx); \ + (((__ctx)->crop_width * (__ctx)->crop_height) < MFC_1080P_RES); \ +}) + +/* HD resolution */ +#define MFC_HD_RES_MB (((1280 + 15) / 16) * ((720 + 15) / 16)) + +/* + * If it is a resolution that requires max performance, + * the performance degradation option is not available. + * (2-ref, B frame) + */ +#define IS_MFC_MAX_PERF(ctx, fps) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_8K_PERF(_ctx) || (OVER_UHD_RES(_ctx) && ((fps) > 60))); \ +}) +#define IS_MFC_HEAVY_PERF(ctx, fps) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + typeof(fps) _fps =3D (fps); \ + (IS_8K_PERF(_ctx) || (OVER_UHD_RES(_ctx) && (_fps >=3D 60)) || \ + (_fps >=3D 240)); \ +}) + +#define IS_BLACKBAR_OFF(ctx) ((ctx)->crop_height > 2160) + +#define IS_SINGLE_FD(ctx, fmt) ((!(ctx)->rgb_bpp) && ((fmt)->mem_planes = =3D=3D 1)) + #define IS_MULTI_CORE_DEVICE(dev) ((dev)->num_core > 1) #define IS_SINGLE_MODE(ctx) ((ctx)->op_mode =3D=3D MFC_OP_SINGLE) #define IS_TWO_MODE1(ctx) ((ctx)->op_mode =3D=3D MFC_OP_TWO_MODE1) @@ -134,6 +299,13 @@ (_ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2)); \ }) =20 +#define IS_MODE_SWITCHING(ctx) ((ctx)->op_mode =3D=3D MFC_OP_SWITCHING) + +#define IS_NO_INFOLOG(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (_ctx->dec_priv && _ctx->dec_priv->is_multiframe); \ +}) + /* Extra information for Decoder */ #define DEC_SET_DUAL_DPB BIT(0) #define DEC_SET_DYNAMIC_DPB BIT(1) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_mem.c index dfb58ddcbd5d..17cc1d793cbc 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c @@ -334,6 +334,196 @@ void mfc_mem_special_buf_free(struct mfc_dev *dev, st= ruct mfc_special_buf *speci } } =20 +void mfc_put_iovmm(struct mfc_ctx *ctx, struct dpb_table *dpb, int num_pla= nes, int index) +{ + int i; + + MFC_TRACE_CTX("DPB[%d] fd: %d addr: %#llx put(%d)\n", + index, dpb[index].fd[0], dpb[index].addr[0], dpb[index].mapcnt); + + for (i =3D 0; i < num_planes; i++) { + if (dpb[index].addr[i]) + mfc_ctx_debug(2, "[IOVMM] index %d buf[%d] fd: %d addr: %#llx\n", + index, i, dpb[index].fd[i], dpb[index].addr[i]); + if (dpb[index].sgt[i]) + dma_buf_unmap_attachment_unlocked(dpb[index].attach[i], dpb[index].sgt[= i], + DMA_BIDIRECTIONAL); + if (dpb[index].attach[i]) + dma_buf_detach(dpb[index].dmabufs[i], dpb[index].attach[i]); + if (dpb[index].dmabufs[i]) + dma_buf_put(dpb[index].dmabufs[i]); + + dpb[index].fd[i] =3D -1; + dpb[index].addr[i] =3D 0; + dpb[index].attach[i] =3D NULL; + dpb[index].dmabufs[i] =3D NULL; + dpb[index].sgt[i] =3D NULL; + } + + dpb[index].new_fd =3D -1; + dpb[index].mapcnt--; + mfc_ctx_debug(2, "[IOVMM] index %d mapcnt %d\n", index, dpb[index].mapcnt= ); + + if (dpb[index].mapcnt !=3D 0) { + mfc_ctx_err("[IOVMM] DPB[%d] %#llx invalid mapcnt %d\n", + index, dpb[index].addr[0], dpb[index].mapcnt); + dpb[index].mapcnt =3D 0; + } +} + +void mfc_get_iovmm(struct mfc_ctx *ctx, struct vb2_buffer *vb, struct dpb_= table *dpb) +{ + struct mfc_dev *dev =3D ctx->dev; + int i, mem_get_count =3D 0; + struct mfc_buf *mfc_buf =3D vb_to_mfc_buf(vb); + int index =3D mfc_buf->dpb_index; + int sub_view_index, offset; + + if (dpb[index].mapcnt !=3D 0) + mfc_ctx_err("[IOVMM] DPB[%d] %#llx invalid mapcnt %d\n", + index, dpb[index].addr[0], dpb[index].mapcnt); + + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) { + mem_get_count++; + + dpb[index].fd[i] =3D vb->planes[i].m.fd; + + dpb[index].dmabufs[i] =3D dma_buf_get(vb->planes[i].m.fd); + if (IS_ERR(dpb[index].dmabufs[i])) { + mfc_ctx_err("[IOVMM] Failed to dma_buf_get (err %ld)\n", + PTR_ERR(dpb[index].dmabufs[i])); + dpb[index].dmabufs[i] =3D NULL; + goto err_iovmm; + } + + dpb[index].attach[i] =3D dma_buf_attach(dpb[index].dmabufs[i], dev->devi= ce); + if (IS_ERR(dpb[index].attach[i])) { + mfc_ctx_err("[IOVMM] Failed to get dma_buf_attach (err %ld)\n", + PTR_ERR(dpb[index].attach[i])); + dpb[index].attach[i] =3D NULL; + goto err_iovmm; + } + + dpb[index].sgt[i] =3D + dma_buf_map_attachment_unlocked(dpb[index].attach[i], + DMA_BIDIRECTIONAL); + if (IS_ERR(dpb[index].sgt[i])) { + mfc_ctx_err("[IOVMM] Failed to get sgt (err %ld)\n", + PTR_ERR(dpb[index].sgt[i])); + dpb[index].sgt[i] =3D NULL; + goto err_iovmm; + } + + dpb[index].addr[i] =3D sg_dma_address(dpb[index].sgt[i]->sgl); + if (IS_ERR_VALUE(dpb[index].addr[i])) { + mfc_ctx_err("[IOVMM] Failed to get iova (err 0x%p)\n", + &dpb[index].addr[i]); + dpb[index].addr[i] =3D 0; + goto err_iovmm; + } + + mfc_ctx_debug(2, "[IOVMM] index %d buf[%d] fd: %d addr: %#llx\n", + index, i, dpb[index].fd[i], dpb[index].addr[i]); + } +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + dpb[index].paddr =3D page_to_phys(sg_page(dpb[index].sgt[0]->sgl)); +#else + dpb[index].paddr =3D dpb[index].sgt[0]->sgl->dma_address; +#endif + mfc_ctx_debug(2, "[DPB] dpb index [%d][%d] paddr %#llx daddr %#llx\n", + mfc_buf->vb.vb2_buf.index, + index, dpb[index].paddr, dpb[index].addr[0]); + + dpb[index].mapcnt++; + mfc_ctx_debug(2, "[IOVMM] index %d mapcnt %d\n", index, dpb[index].mapcnt= ); + MFC_TRACE_CTX("DPB[%d] fd: %d addr: %#llx get(%d)\n", + index, dpb[index].fd[0], dpb[index].addr[0], dpb[index].mapcnt); + + /* In multi_view_enable, sub_view should be mapped together. + * Lower 32 bits are used for main_view. Upper 32 bits are used for sub_v= iew. + */ + if (ctx->multi_view_enable) { + sub_view_index =3D index + MFC_MAX_DPBS / 2; + if (dpb[sub_view_index].mapcnt !=3D 0) + mfc_ctx_err("[IOVMM] DPB[%d] %#llx invalid mapcnt %d\n", + sub_view_index, dpb[sub_view_index].addr[0], + dpb[sub_view_index].mapcnt); + + offset =3D ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1].offset; + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) { + mem_get_count++; + + dpb[sub_view_index].fd[i] =3D vb->planes[offset + i].m.fd; + + dpb[sub_view_index].dmabufs[i] =3D dma_buf_get(vb->planes[offset + i].m= .fd); + if (IS_ERR(dpb[sub_view_index].dmabufs[i])) { + mfc_ctx_err("[IOVMM] Failed to dma_buf_get (err %ld)\n", + PTR_ERR(dpb[sub_view_index].dmabufs[i])); + dpb[sub_view_index].dmabufs[i] =3D NULL; + goto err_iovmm_sub; + } + + dpb[sub_view_index].attach[i] =3D + dma_buf_attach(dpb[sub_view_index].dmabufs[i], dev->device); + if (IS_ERR(dpb[sub_view_index].attach[i])) { + mfc_ctx_err("[IOVMM] Failed to get dma_buf_attach (err %ld)\n", + PTR_ERR(dpb[sub_view_index].attach[i])); + dpb[sub_view_index].attach[i] =3D NULL; + goto err_iovmm_sub; + } + + dpb[sub_view_index].sgt[i] =3D + dma_buf_map_attachment_unlocked(dpb[sub_view_index].attach[i], + DMA_BIDIRECTIONAL); + if (IS_ERR(dpb[sub_view_index].sgt[i])) { + mfc_ctx_err("[IOVMM] Failed to get sgt (err %ld)\n", + PTR_ERR(dpb[sub_view_index].sgt[i])); + dpb[sub_view_index].sgt[i] =3D NULL; + goto err_iovmm_sub; + } + + dpb[sub_view_index].addr[i] =3D + sg_dma_address(dpb[sub_view_index].sgt[i]->sgl); + if (IS_ERR_VALUE(dpb[sub_view_index].addr[i])) { + mfc_ctx_err("[IOVMM] Failed to get iova (err 0x%p)\n", + &dpb[sub_view_index].addr[i]); + dpb[sub_view_index].addr[i] =3D 0; + goto err_iovmm_sub; + } + + mfc_ctx_debug(2, "[IOVMM] sub_view_index %d buf[%d] fd: %d addr: %#llx\= n", + sub_view_index, i, dpb[sub_view_index].fd[i], + dpb[sub_view_index].addr[i]); + } +#if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) + dpb[sub_view_index].paddr =3D page_to_phys(sg_page(dpb[sub_view_index].s= gt[0]->sgl)); +#else + dpb[sub_view_index].paddr =3D dpb[sub_view_index].sgt[0]->sgl->dma_addre= ss; +#endif + mfc_ctx_debug(2, "[DPB] dpb index [%d][%d] paddr %#llx daddr %#llx\n", + mfc_buf->vb.vb2_buf.index, + sub_view_index, dpb[sub_view_index].paddr, + dpb[sub_view_index].addr[0]); + + dpb[sub_view_index].mapcnt++; + mfc_ctx_debug(2, "[IOVMM] index %d mapcnt %d\n", index, dpb[sub_view_ind= ex].mapcnt); + MFC_TRACE_CTX("DPB[%d] fd: %d addr: %#llx get(%d)\n", + sub_view_index, dpb[sub_view_index].fd[0], + dpb[sub_view_index].addr[0], dpb[sub_view_index].mapcnt); + } + + return; + +err_iovmm: + dpb[index].mapcnt++; + mfc_put_iovmm(ctx, dpb, mem_get_count, index); + return; +err_iovmm_sub: + dpb[sub_view_index].mapcnt++; + mfc_put_iovmm(ctx, dpb, mem_get_count, index); + mfc_put_iovmm(ctx, dpb, mem_get_count, sub_view_index); +} + void mfc_iova_pool_free(struct mfc_dev *dev, struct mfc_special_buf *buf) { /* Project that do not support iova reservation */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_mem.h index 7b37e5fedbf2..3deeb0d611a0 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h @@ -66,9 +66,40 @@ static inline size_t mfc_mem_get_sg_length(struct mfc_de= v *dev, struct sg_table return size; } =20 +static inline void mfc_print_dpb_table(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *mfc_buf =3D NULL; + unsigned long flags; + int i, found =3D 0; + + mfc_ctx_debug(3, "[DPB] dynamic_used: %#lx, queued: %#lx, table_used: %#l= x\n", + dec->dynamic_used, dec->queued_dpb, dec->dpb_table_used); + for (i =3D 0; i < MFC_MAX_DPBS; i++) { + found =3D 0; + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + list_for_each_entry(mfc_buf, &ctx->dst_buf_queue.head, list) { + if (i =3D=3D mfc_buf->dpb_index) { + found =3D 1; + break; + } + } + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + mfc_ctx_debug(3, "[%d] dpb [%d] %#010llx %#010llx %#010llx fd %d(%d) (%s= , %s, %s)\n", + i, found ? mfc_buf->vb.vb2_buf.index : -1, + dec->dpb[i].addr[0], dec->dpb[i].addr[1], + dec->dpb[i].paddr, dec->dpb[i].fd[0], dec->dpb[i].new_fd, + dec->dpb[i].mapcnt ? "map" : "unmap", + dec->dpb[i].ref ? "ref" : "free", + dec->dpb[i].queued ? "Q" : "DQ"); + } +} int mfc_mem_special_buf_alloc(struct mfc_dev *dev, struct mfc_special_buf = *special_buf); void mfc_mem_special_buf_free(struct mfc_dev *dev, struct mfc_special_buf = *special_buf); =20 +void mfc_put_iovmm(struct mfc_ctx *ctx, struct dpb_table *dpb, int num_pla= nes, int index); +void mfc_get_iovmm(struct mfc_ctx *ctx, struct vb2_buffer *vb, struct dpb_= table *dpb); void mfc_iova_pool_free(struct mfc_dev *dev, struct mfc_special_buf *buf); int mfc_iova_pool_alloc(struct mfc_dev *dev, struct mfc_special_buf *buf); int mfc_iova_pool_init(struct mfc_dev *dev); diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c new file mode 100644 index 000000000000..f56e800c55f0 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c @@ -0,0 +1,930 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_queue.c + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_queue.h" + +#include "mfc_utils.h" +#include "mfc_mem.h" + +void mfc_add_tail_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + struct mfc_buf *mfc_buf) +{ + unsigned long flags; + + if (!mfc_buf) { + mfc_ctx_err("mfc_buf is NULL!\n"); + return; + } + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + mfc_buf->used =3D 0; + list_add_tail(&mfc_buf->list, &queue->head); + queue->count++; + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); +} + +struct mfc_buf *mfc_get_buf_no_used(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + enum mfc_queue_used_type used) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&queue->head)) { + mfc_ctx_debug(2, "queue is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; + } + + list_for_each_entry(mfc_buf, &queue->head, list) { + if (mfc_buf->used) + continue; + + if (used =3D=3D MFC_BUF_RESET_USED || used =3D=3D MFC_BUF_SET_USED) + mfc_buf->used =3D used; + + mfc_ctx_debug(2, "addr[0]: 0x%08llx\n", mfc_buf->addr[0][0]); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; +} + +struct mfc_buf *mfc_get_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + enum mfc_queue_used_type used) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&queue->head)) { + mfc_ctx_debug(2, "queue is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; + } + + mfc_buf =3D list_entry(queue->head.next, struct mfc_buf, list); + + if (used =3D=3D MFC_BUF_RESET_USED || used =3D=3D MFC_BUF_SET_USED) + mfc_buf->used =3D used; + + mfc_ctx_debug(2, "addr[0]: 0x%08llx\n", mfc_buf->addr[0][0]); + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; +} + +struct mfc_buf *mfc_get_del_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + enum mfc_queue_used_type used) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&queue->head)) { + mfc_ctx_debug(2, "queue is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; + } + + mfc_buf =3D list_entry(queue->head.next, struct mfc_buf, list); + + if (used =3D=3D MFC_BUF_RESET_USED || used =3D=3D MFC_BUF_SET_USED) + mfc_buf->used =3D used; + + mfc_ctx_debug(2, "addr[0]: 0x%08llx\n", mfc_buf->addr[0][0]); + + list_del(&mfc_buf->list); + queue->count--; + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; +} + +struct mfc_buf *mfc_get_del_if_consumed(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + unsigned int consumed, + unsigned int min_bytes, + int error, + int *deleted) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + unsigned int remained, strm_size; + bool exceed =3D false; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&queue->head)) { + mfc_ctx_debug(2, "queue is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; + } + + mfc_buf =3D list_entry(queue->head.next, struct mfc_buf, list); + + mfc_ctx_debug(2, "addr[0]: 0x%08llx\n", mfc_buf->addr[0][0]); + + strm_size =3D mfc_dec_get_strm_size(ctx, mfc_buf); + if (strm_size >=3D consumed) { + remained =3D strm_size - consumed; + } else { + remained =3D 0; + exceed =3D true; + mfc_ctx_err("[MULTIFRAME] consumed (%d) exceeded the strm_size (%d)\n", + consumed, strm_size); + } + + if (consumed > 0 && remained > min_bytes && + IS_NO_ERROR(error) && !exceed) { + /* do not delete from queue */ + *deleted =3D 0; + } else { + list_del(&mfc_buf->list); + queue->count--; + + *deleted =3D 1; + } + + mfc_ctx_debug(2, "[MULTIFRAME] strm %d, consumed %d, remained %d, deleted= %d, error %d, exceed %d\n", + strm_size, consumed, remained, *deleted, error, exceed); + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; +} + +struct mfc_buf *mfc_get_move_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *to_queue, + struct mfc_buf_queue *from_queue, + enum mfc_queue_used_type used, + enum mfc_queue_top_type top) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&from_queue->head)) { + mfc_ctx_debug(2, "from_queue is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; + } + + mfc_buf =3D list_entry(from_queue->head.next, struct mfc_buf, list); + + if (used =3D=3D MFC_BUF_RESET_USED || used =3D=3D MFC_BUF_SET_USED) + mfc_buf->used =3D used; + + mfc_ctx_debug(2, "addr[0]: 0x%08llx\n", mfc_buf->addr[0][0]); + + list_del(&mfc_buf->list); + from_queue->count--; + + if (top =3D=3D MFC_QUEUE_ADD_TOP) + list_add(&mfc_buf->list, &to_queue->head); + else + list_add_tail(&mfc_buf->list, &to_queue->head); + + to_queue->count++; + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; +} + +struct mfc_buf *mfc_get_move_buf_used(struct mfc_ctx *ctx, + struct mfc_buf_queue *to_queue, + struct mfc_buf_queue *from_queue) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&from_queue->head)) { + mfc_ctx_debug(2, "from_queue is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; + } + + mfc_buf =3D list_entry(from_queue->head.next, struct mfc_buf, list); + + if (mfc_buf->used) { + mfc_ctx_debug(2, "addr[0]: 0x%08llx\n", mfc_buf->addr[0][0]); + + list_del(&mfc_buf->list); + from_queue->count--; + + list_add_tail(&mfc_buf->list, &to_queue->head); + to_queue->count++; + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; +} + +struct mfc_buf *mfc_find_first_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + dma_addr_t addr) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + dma_addr_t mb_addr; + int i; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&queue->head)) { + mfc_ctx_debug(2, "queue is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + + mfc_ctx_debug(4, "Looking for this address: 0x%08llx\n", addr); + mfc_buf =3D list_entry(queue->head.next, struct mfc_buf, list); + if (mfc_buf->num_valid_bufs > 0) { + for (i =3D 0; i < mfc_buf->num_valid_bufs; i++) { + mb_addr =3D mfc_buf->addr[i][0]; + mfc_ctx_debug(4, "[BUFCON] batch[%d] addr[0]: 0x%08llx\n", i, mb_addr); + if (addr =3D=3D mb_addr) { + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + } + } else { + mb_addr =3D mfc_buf->addr[0][0]; + mfc_ctx_debug(4, "addr[0]: 0x%08llx\n", mb_addr); + + if (addr =3D=3D mb_addr) { + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + } + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; +} + +struct mfc_buf *mfc_find_buf(struct mfc_ctx *ctx, struct mfc_buf_queue *qu= eue, dma_addr_t addr) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + dma_addr_t mb_addr; + int i; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + mfc_ctx_debug(4, "Looking for this address: 0x%08llx\n", addr); + list_for_each_entry(mfc_buf, &queue->head, list) { + if (mfc_buf->num_valid_bufs > 0) { + for (i =3D 0; i < mfc_buf->num_valid_bufs; i++) { + mb_addr =3D mfc_buf->addr[i][0]; + mfc_ctx_debug(4, "[BUFCON] batch[%d] addr[0]: 0x%08llx\n", + i, mb_addr); + if (addr =3D=3D mb_addr) { + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + } + } else { + mb_addr =3D mfc_buf->addr[0][0]; + mfc_ctx_debug(4, "addr[0]: 0x%08llx\n", mb_addr); + + if (addr =3D=3D mb_addr) { + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + } + } + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; +} + +struct mfc_buf *mfc_find_del_buf(struct mfc_ctx *ctx, struct mfc_buf_queue= *queue, dma_addr_t addr) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + dma_addr_t mb_addr; + int found =3D 0, i; + int index_view; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + mfc_ctx_debug(4, "Looking for this address: 0x%08llx\n", addr); + list_for_each_entry(mfc_buf, &queue->head, list) { + if (mfc_buf->num_valid_bufs > 0) { + for (i =3D 0; i < mfc_buf->num_valid_bufs; i++) { + mb_addr =3D mfc_buf->addr[i][0]; + mfc_ctx_debug(4, "batch buf[%d] plane[0] addr: 0x%08llx\n", + i, mb_addr); + + if (addr =3D=3D mb_addr) { + found =3D 1; + break; + } + } + + if (found) + break; + } else { + /* + * In case of not Multi-View, Main view will be selected always. + */ + if (ctx->type =3D=3D MFCINST_DECODER) + index_view =3D ctx->select_view_irq =3D=3D MFC_VIEW_ID_MAIN ? + MFC_MV_BUF_IDX_VIEW0 : MFC_MV_BUF_IDX_VIEW1; + else + index_view =3D ctx->select_view =3D=3D MFC_VIEW_ID_MAIN ? + MFC_MV_BUF_IDX_VIEW0 : MFC_MV_BUF_IDX_VIEW1; + mb_addr =3D mfc_buf->addr[index_view][0]; + mfc_ctx_debug(4, "addr[0]: 0x%08llx\n", mb_addr); + + if (addr =3D=3D mb_addr) { + found =3D 1; + break; + } + } + } + + if (found =3D=3D 1) { + list_del(&mfc_buf->list); + queue->count--; + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return NULL; +} + +void mfc_move_buf_all(struct mfc_ctx *ctx, struct mfc_buf_queue *to_queue, + struct mfc_buf_queue *from_queue, enum mfc_queue_top_type top) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (top =3D=3D MFC_QUEUE_ADD_TOP) { + while (!list_empty(&from_queue->head)) { + mfc_buf =3D list_entry(from_queue->head.prev, struct mfc_buf, list); + + list_del(&mfc_buf->list); + from_queue->count--; + + list_add(&mfc_buf->list, &to_queue->head); + to_queue->count++; + } + } else { + while (!list_empty(&from_queue->head)) { + mfc_buf =3D list_entry(from_queue->head.next, struct mfc_buf, list); + + list_del(&mfc_buf->list); + from_queue->count--; + + list_add_tail(&mfc_buf->list, &to_queue->head); + to_queue->count++; + } + } + + INIT_LIST_HEAD(&from_queue->head); + from_queue->count =3D 0; + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); +} + +void mfc_return_buf_to_ready_queue(struct mfc_ctx *ctx, + struct mfc_buf_queue *maincore_queue, + struct mfc_buf_queue *subcore_queue) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_buf_queue *to_queue =3D &ctx->src_buf_ready_queue; + struct mfc_buf_queue *first_queue, *second_queue; + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + int maincore_src_index =3D -1, subcore_src_index =3D -1; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (list_empty(&maincore_queue->head) && list_empty(&subcore_queue->head)= ) { + mfc_ctx_debug(2, "all src queue of core_ctx is empty\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return; + } + + /* Search the last distributed src index */ + if (!list_empty(&maincore_queue->head)) { + mfc_buf =3D list_entry(maincore_queue->head.prev, struct mfc_buf, list); + maincore_src_index =3D mfc_buf->src_index; + mfc_ctx_debug(4, "maincore src last buf src index: %d\n", maincore_src_i= ndex); + } + if (!list_empty(&subcore_queue->head)) { + mfc_buf =3D list_entry(subcore_queue->head.prev, struct mfc_buf, list); + subcore_src_index =3D mfc_buf->src_index; + mfc_ctx_debug(4, "subcore src last buf src index: %d\n", subcore_src_ind= ex); + } + + MFC_TRACE_RM("[c:%d] last src index maincore %d subcore %d\n", + ctx->num, maincore_src_index, subcore_src_index); + + /* Select the core_ctx->src_buf_queue to take out first */ + if (maincore_src_index > subcore_src_index) { + first_queue =3D maincore_queue; + second_queue =3D subcore_queue; + mfc_ctx_debug(2, "last src index (maincore:%d, subcore:%d) move first ma= incore\n", + maincore_src_index, subcore_src_index); + } else { + first_queue =3D subcore_queue; + second_queue =3D maincore_queue; + mfc_ctx_debug(2, "last src index (maincore:%d, subcore:%d) move first su= bcore\n", + maincore_src_index, subcore_src_index); + } + + /* Src index is sequentially returned to ready_queue */ + while (1) { + if (!list_empty(&first_queue->head)) { + mfc_buf =3D list_entry(first_queue->head.prev, struct mfc_buf, list); + + list_del(&mfc_buf->list); + first_queue->count--; + + list_add(&mfc_buf->list, &to_queue->head); + to_queue->count++; + mfc_ctx_debug(2, "first queue src[%d] move\n", mfc_buf->src_index); + MFC_TRACE_RM("[c:%d] first queue src[%d] move\n", ctx->num, + mfc_buf->src_index); + } + + if (!list_empty(&second_queue->head)) { + mfc_buf =3D list_entry(second_queue->head.prev, struct mfc_buf, list); + + list_del(&mfc_buf->list); + second_queue->count--; + + list_add(&mfc_buf->list, &to_queue->head); + to_queue->count++; + mfc_ctx_debug(2, "second queue src[%d] move\n", mfc_buf->src_index); + MFC_TRACE_RM("[c:%d] second queue src[%d] move\n", ctx->num, + mfc_buf->src_index); + } + + if (list_empty(&first_queue->head) && list_empty(&second_queue->head)) { + mfc_ctx_debug(2, "all src of core_ctx return to ready_queue\n"); + mfc_ctx_debug(2, "ready %d maincore %d subcore %d\n", + to_queue->count, maincore_queue->count, + subcore_queue->count); + MFC_TRACE_RM("[c:%d] all src return to ready\n", ctx->num); + MFC_TRACE_RM("[c:%d] ready %d maincore %d subcore %d\n", ctx->num, + to_queue->count, maincore_queue->count, + subcore_queue->count); + INIT_LIST_HEAD(&first_queue->head); + first_queue->count =3D 0; + INIT_LIST_HEAD(&second_queue->head); + second_queue->count =3D 0; + break; + } + } + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); +} + +void mfc_cleanup_queue(spinlock_t *plock, struct mfc_buf_queue *queue) +{ + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + int i; + + spin_lock_irqsave(plock, flags); + + while (!list_empty(&queue->head)) { + mfc_buf =3D list_entry(queue->head.next, struct mfc_buf, list); + + for (i =3D 0; i < mfc_buf->vb.vb2_buf.num_planes; i++) + vb2_set_plane_payload(&mfc_buf->vb.vb2_buf, i, 0); + + vb2_buffer_done(&mfc_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&mfc_buf->list); + queue->count--; + } + + INIT_LIST_HEAD(&queue->head); + queue->count =3D 0; + + spin_unlock_irqrestore(plock, flags); +} + +void mfc_print_dpb_queue(struct mfc_core_ctx *core_ctx, struct mfc_dec *de= c) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_buf *mfc_buf =3D NULL; + + mfc_debug(2, "[DPB] src: %d(ready: %d), dst %d, used %#lx, queued %#lx, s= et %#lx(dec: %#lx)\n", + core_ctx->src_buf_queue.count, + ctx->src_buf_ready_queue.count, + ctx->dst_buf_queue.count, + dec->dynamic_used, dec->queued_dpb, + core_ctx->dynamic_set, dec->dynamic_set); + + if (!list_empty(&ctx->dst_buf_queue.head)) + list_for_each_entry(mfc_buf, &ctx->dst_buf_queue.head, list) + mfc_debug(2, "[DPB] dst[%d][%d] %#llx used: %d\n", + mfc_buf->vb.vb2_buf.index, + mfc_buf->dpb_index, + mfc_buf->addr[0][0], mfc_buf->used); +} + +void mfc_print_dpb_queue_with_lock(struct mfc_core_ctx *core_ctx, struct m= fc_dec *dec) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + unsigned long flags; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + mfc_print_dpb_queue(core_ctx, dec); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); +} + +int mfc_get_available_dpb_count(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *mfc_buf =3D NULL; + unsigned long flags, used_flag_count; + int cnt =3D 0; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + if (ctx->dst_buf_queue.count =3D=3D 0) { + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return 0; + } + + list_for_each_entry(mfc_buf, &ctx->dst_buf_queue.head, list) { + if (IS_TWO_MODE2(ctx) && (dec->dynamic_set & (1UL << mfc_buf->dpb_index)= )) { + mfc_debug(2, "[DPB] dst index %d already set\n", + mfc_buf->dpb_index); + continue; + } + if ((dec->dynamic_used & (1UL << mfc_buf->dpb_index)) =3D=3D 0) { + mfc_debug(2, "[DPB] There is available dpb(index:%d, used:%#lx)\n", + mfc_buf->dpb_index, dec->dynamic_used); + cnt++; + } + } + + if (cnt) { + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return cnt; + } + + used_flag_count =3D hweight64(dec->dynamic_used); + if (used_flag_count >=3D ctx->dpb_count) { + mfc_debug(2, "[DPB] All DPB(%ld) of min count are referencing\n", + used_flag_count); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return 1; + } + + mfc_debug(2, "[DPB] There is no available DPB\n"); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + + return 0; +} + +/* Try to search non-referenced DPB on dst-queue */ +struct mfc_buf *mfc_search_for_dpb(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned long flags, used_flag_count; + struct mfc_buf *mfc_buf =3D NULL; + int dpb_index; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + list_for_each_entry(mfc_buf, &ctx->dst_buf_queue.head, list) { + dpb_index =3D mfc_buf->dpb_index; + /* sub_view was mapped together(at qbuf). + * At the time, dpb_index of sub_view is dpb_index of main_view + MFC_MA= X_DPBS / 2 + */ + if (ctx->multi_view_enable && ctx->select_view =3D=3D MFC_VIEW_ID_SUB) + dpb_index +=3D (MFC_MAX_DPBS / 2); + if (IS_TWO_MODE2(ctx) && (dec->dynamic_set & (1UL << dpb_index))) { + mfc_debug(2, "[DPB] dst index %d already set\n", + dpb_index); + continue; + } + if ((dec->dynamic_used & (1UL << dpb_index)) =3D=3D 0) { + mfc_buf->used =3D 1; + dec->dynamic_set =3D 1UL << dpb_index; + core_ctx->dynamic_set =3D 1UL << dpb_index; + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + } + + /* + * Bumping process + * In case of H.264/HEVC codec, + * all of the queued buffers can be referenced by F/W. + * At that time, we should set the any DPB to F/W, + * F/W will returns display only buffer whether if reference or not. + * In this way the reference can be released by circulating. + */ + used_flag_count =3D hweight64(dec->dynamic_used); + if (used_flag_count >=3D ctx->dpb_count) { + mfc_buf =3D list_entry(ctx->dst_buf_queue.head.next, struct mfc_buf, lis= t); + mfc_buf->used =3D 1; + dpb_index =3D mfc_buf->dpb_index; + if (ctx->multi_view_enable && ctx->select_view =3D=3D MFC_VIEW_ID_SUB) + dpb_index +=3D (MFC_MAX_DPBS / 2); + mfc_debug(2, "[DPB] All DPB(%ld) of min count are referencing. select bu= f[%d][%d]\n", + used_flag_count, mfc_buf->vb.vb2_buf.index, dpb_index); + dec->dynamic_set =3D 1UL << dpb_index; + core_ctx->dynamic_set =3D 1UL << dpb_index; + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return mfc_buf; + } + + /* + * 1) ctx->dst_buf_queue.count >=3D (ctx->dpb_count + MFC_EXTRA_DPB): All= DPBs queued in DRV + * 2) ctx->dst_buf_queue.count =3D=3D 0: All DPBs dequeued to user + * we will wait + */ + mfc_debug(2, "[DPB] All enqueued DPBs are referencing or there's no DPB i= n DRV (in %d/total %d)\n", + ctx->dst_buf_queue.count, ctx->dpb_count + MFC_EXTRA_DPB); + if (!(dec->queued_dpb & ~dec->dynamic_used)) + mfc_debug(2, "[DPB] All enqueued DPBs are referencing\n"); + + mfc_print_dpb_queue(core_ctx, dec); + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + + mutex_lock(&dec->dpb_mutex); + mfc_print_dpb_table(ctx); + mutex_unlock(&dec->dpb_mutex); + + return NULL; +} + +static int __mfc_assign_dpb_index(struct mfc_ctx *ctx, struct mfc_buf *mfc= _buf) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned long used; + int index =3D -1; + int max_dpb =3D MFC_MAX_DPBS; + int i, dpb_table_used; + + /* When multi_view_enable(MV-HEVC), driver has to limit assign dpb_index + * as the half of MAX_DPB. Because, upper half is for sub_view + */ + if (ctx->multi_view_enable) + max_dpb =3D MFC_MAX_DPBS / 2; + + /* case 1: dpb has same address with vb index */ + if (mfc_buf->paddr =3D=3D dec->dpb[mfc_buf->vb.vb2_buf.index].paddr && in= dex < max_dpb) { + mfc_ctx_debug(2, "[DPB] vb index [%d] %#llx has same address\n", + mfc_buf->vb.vb2_buf.index, mfc_buf->paddr); + index =3D mfc_buf->vb.vb2_buf.index; + return index; + } + + /* case 2: dpb has same address with referenced buffer */ + used =3D dec->dynamic_used; + if (used) { + for (i =3D __ffs(used); i < max_dpb;) { + if (mfc_buf->paddr =3D=3D dec->dpb[i].paddr) { + mfc_ctx_debug(2, "[DPB] index [%d][%d] %#llx is referenced\n", + mfc_buf->vb.vb2_buf.index, i, mfc_buf->paddr); + index =3D i; + return index; + } + used &=3D ~(1UL << i); + if (used =3D=3D 0) + break; + i =3D __ffs(used); + } + } + + /* case 3: allocate new dpb index */ + dpb_table_used =3D dec->dpb_table_used; + /* When multi_view_enable(MV-HEVC), upper half bits should be 1 not to be= assigned. */ + if (ctx->multi_view_enable) + dpb_table_used =3D dec->dpb_table_used | 0xFFFFFFFF00000000; + if (dpb_table_used =3D=3D ~0UL) { + mfc_ctx_err("[DPB] index is full\n"); + return 0; + } + index =3D __ffs(~dpb_table_used); + + return index; +} + +static void __mfc_update_base_addr_dpb(struct mfc_ctx *ctx, struct mfc_buf= *buf, + int index) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_raw_info *raw; + size_t offset =3D 0; + int i; + + if (ctx->dst_fmt->mem_planes =3D=3D 1) { + raw =3D &ctx->raw_buf; + for (i =3D 0; i < ctx->dst_fmt->num_planes; i++) { + buf->addr[0][i] =3D dec->dpb[index].addr[0] + offset; + offset +=3D raw->plane_size[i]; + } + } else { + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) + buf->addr[0][i] =3D dec->dpb[index].addr[i]; + } + + mfc_ctx_debug(2, "[DPB] index [%d] daddr plane[0] %#llx [1] %#llx [2] %#l= lx\n", + index, buf->addr[0][0], buf->addr[0][1], buf->addr[0][2]); +} + +static int __mfc_update_dpb_fd(struct mfc_ctx *ctx, struct vb2_buffer *vb,= int index) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + + if (dec->dpb[index].queued) { + mfc_ctx_err("[REFINFO] DPB[%d] is already queued\n", index); + return -EINVAL; + } + + if (dec->dpb[index].fd[0] =3D=3D vb->planes[0].m.fd) + mfc_ctx_debug(3, "[REFINFO] same dma_buf has same fd\n"); + + if (dec->dpb[index].new_fd !=3D -1) { + dec->ref_buf[dec->refcnt].fd[0] =3D dec->dpb[index].fd[0]; + dec->refcnt++; + dec->dpb[index].fd[0] =3D dec->dpb[index].new_fd; + } + + dec->dpb[index].new_fd =3D vb->planes[0].m.fd; + mfc_ctx_debug(3, "[REFINFO] index %d update fd: %d -> %d after release\n", + vb->index, dec->dpb[index].fd[0], + dec->dpb[index].new_fd); + + return 0; +} + +static void __mfc_store_dynamic_dpb(struct mfc_ctx *ctx, struct mfc_dec *d= ec, struct vb2_buffer *vb) +{ + struct mfc_buf *mfc_buf; + unsigned long flags; + int index, plane, ret =3D 0; + + mfc_buf =3D vb_to_mfc_buf(vb); + mfc_buf->used =3D 0; + + mutex_lock(&dec->dpb_mutex); + mfc_buf->dpb_index =3D __mfc_assign_dpb_index(ctx, mfc_buf); + mfc_ctx_debug(2, "[DPB] DPB vb_index %d -> dpb_index %d addr %#llx (used:= %#lx)\n", + vb->index, mfc_buf->dpb_index, mfc_buf->addr[0][0], dec->dynamic_u= sed); + + index =3D mfc_buf->dpb_index; + if (index > dec->last_dpb_max_index) { + mfc_ctx_debug(3, "[DPB] last dpb max index update %d -> %d\n", + dec->last_dpb_max_index, index); + if (index > (dec->last_dpb_max_index + 1)) { + mfc_ctx_err("[DPB] wrong dpb max index: %d max: %d\n", + index, dec->last_dpb_max_index); + MFC_TRACE_CTX("wrong dpb max index: %d max: %d\n", + index, dec->last_dpb_max_index); + } + dec->last_dpb_max_index =3D index; + } + + if (!dec->dpb[index].mapcnt) { + mfc_get_iovmm(ctx, vb, dec->dpb); + } else { + if (dec->dpb[index].paddr =3D=3D mfc_buf->paddr) { + mfc_ctx_debug(2, "[DPB] DPB[%d] is same %#llx(used: %#lx)\n", + index, dec->dpb[index].paddr, dec->dynamic_used); + ret =3D __mfc_update_dpb_fd(ctx, vb, index); + } else { + mfc_ctx_err("[DPB] wrong assign dpb index\n"); + } + } + + if (ret) { + /* + * Handling exception case that + * the queued buffer is same with already queued buffer's paddr. + * Driver cannot use that buffer so moves it to err_buf_queue + * and will dequeue in ISR. + */ + mutex_unlock(&dec->dpb_mutex); + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + list_add_tail(&mfc_buf->list, &ctx->err_buf_queue.head); + ctx->err_buf_queue.count++; + mfc_ctx_debug(2, "[DPB] DPB[%d][%d] fd: %d will be not used %#llx %s %s = (%d)\n", + mfc_buf->vb.vb2_buf.index, index, + mfc_buf->vb.planes[0].m.fd, mfc_buf->addr[0][0], + (dec->dynamic_used & (1UL << index)) ? "ref" : "no-ref", + dec->dpb[index].queued ? "q" : "dq", + ctx->err_buf_queue.count); + MFC_TRACE_CTX("unused DPB[%d][%d] fd: %d %#llx %s %s (%d)\n", + mfc_buf->vb.vb2_buf.index, index, + mfc_buf->vb.planes[0].m.fd, mfc_buf->addr[0][0], + (dec->dynamic_used & (1UL << index)) ? "ref" : "no-ref", + dec->dpb[index].queued ? "q" : "dq", + ctx->err_buf_queue.count); + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); + return; + } + + __mfc_update_base_addr_dpb(ctx, mfc_buf, index); + + dec->dpb[index].size =3D 0; + for (plane =3D 0; plane < vb->num_planes; ++plane) + dec->dpb[index].size +=3D vb->planes[plane].length; + + dec->dpb[index].queued =3D 1; + dec->dpb_table_used |=3D (1UL << index); + mutex_unlock(&dec->dpb_mutex); + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + list_add_tail(&mfc_buf->list, &ctx->dst_buf_queue.head); + ctx->dst_buf_queue.count++; + set_bit(index, &dec->queued_dpb); + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); +} + +/* Add dst buffer in dst_buf_queue */ +void mfc_store_dpb(struct mfc_ctx *ctx, struct vb2_buffer *vb) +{ + struct mfc_dec *dec; + + dec =3D ctx->dec_priv; + if (!dec) { + mfc_ctx_err("[DPB] no mfc decoder to run\n"); + return; + } + + __mfc_store_dynamic_dpb(ctx, dec, vb); +} + +void mfc_dec_drc_find_del_buf(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core *core =3D core_ctx->core; + struct mfc_buf *dst_mb; + int i; + + if (!dec || dec->disp_drc.disp_res_change) + return; + + dst_mb =3D mfc_get_del_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USE= D); + if (!dst_mb) + return; + + mfc_info("[DRC] already stopped and dqbuf with DRC\n"); + i =3D dst_mb->vb.vb2_buf.index; + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_PFRAME; + + mfc_clear_mb_flag(dst_mb); + dst_mb->vb.flags &=3D ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_ERROR); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[i]) < 0) + mfc_err("[DRC] failed in core_get_buf_ctrls\n"); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[i], + V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, + MFC_REG_DEC_STATUS_DECODING_EMPTY); + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[i], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, UNUSED_TAG); + + ctx->wait_state =3D WAIT_G_FMT | WAIT_STOP; + mfc_debug(2, "[DRC] Decoding waiting again! : %d\n", ctx->wait_state); + MFC_TRACE_CTX("[DRC] wait again\n"); + vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_queue.h new file mode 100644 index 000000000000..055e7a23a527 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_queue.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_QUEUE_H +#define __MFC_QUEUE_H __FILE__ + +#include "mfc_common.h" + +/** + * enum mfc_queue_used_type + */ +enum mfc_queue_used_type { + MFC_BUF_NO_TOUCH_USED =3D -1, + MFC_BUF_RESET_USED =3D 0, + MFC_BUF_SET_USED =3D 1, +}; + +/** + * enum mfc_queue_top_type + */ +enum mfc_queue_top_type { + MFC_QUEUE_ADD_BOTTOM =3D 0, + MFC_QUEUE_ADD_TOP =3D 1, +}; + +static inline unsigned int mfc_get_queue_count(spinlock_t *plock, struct m= fc_buf_queue *queue) +{ + unsigned long flags; + unsigned int ret =3D 0; + + spin_lock_irqsave(plock, flags); + ret =3D queue->count; + spin_unlock_irqrestore(plock, flags); + + return ret; +} + +static inline int mfc_is_queue_count_same(spinlock_t *plock, + struct mfc_buf_queue *queue, + unsigned int value) +{ + unsigned long flags; + int ret =3D 0; + + spin_lock_irqsave(plock, flags); + if (queue->count =3D=3D value) + ret =3D 1; + spin_unlock_irqrestore(plock, flags); + + return ret; +} + +static inline int mfc_is_queue_count_greater(spinlock_t *plock, + struct mfc_buf_queue *queue, + unsigned int value) +{ + unsigned long flags; + int ret =3D 0; + + spin_lock_irqsave(plock, flags); + if (queue->count > value) + ret =3D 1; + spin_unlock_irqrestore(plock, flags); + + return ret; +} + +static inline void mfc_init_queue(struct mfc_buf_queue *queue) +{ + INIT_LIST_HEAD(&queue->head); + queue->count =3D 0; +} + +static inline void mfc_create_queue(struct mfc_buf_queue *queue) +{ + mfc_init_queue(queue); +} + +static inline void mfc_delete_queue(struct mfc_buf_queue *queue) +{ + mfc_init_queue(queue); +} + +void mfc_add_tail_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + struct mfc_buf *mfc_buf); + +struct mfc_buf *mfc_get_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + enum mfc_queue_used_type used); + +struct mfc_buf *mfc_get_buf_no_used(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + enum mfc_queue_used_type used); + +struct mfc_buf *mfc_get_del_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + enum mfc_queue_used_type used); + +struct mfc_buf *mfc_get_del_if_consumed(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + unsigned int consumed, + unsigned int min_bytes, + int err, + int *deleted); + +struct mfc_buf *mfc_get_move_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *to_queue, + struct mfc_buf_queue *from_queue, + enum mfc_queue_used_type used, + enum mfc_queue_top_type top); + +struct mfc_buf *mfc_get_move_buf_used(struct mfc_ctx *ctx, + struct mfc_buf_queue *to_queue, + struct mfc_buf_queue *from_queue); + +struct mfc_buf *mfc_find_first_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + dma_addr_t addr); + +struct mfc_buf *mfc_find_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + dma_addr_t addr); + +struct mfc_buf *mfc_find_del_buf(struct mfc_ctx *ctx, + struct mfc_buf_queue *queue, + dma_addr_t addr); + +void mfc_move_buf_all(struct mfc_ctx *ctx, + struct mfc_buf_queue *to_queue, + struct mfc_buf_queue *from_queue, + enum mfc_queue_top_type top); + +void mfc_return_buf_to_ready_queue(struct mfc_ctx *ctx, + struct mfc_buf_queue *maincore_queue, + struct mfc_buf_queue *subcore_queue); + +void mfc_cleanup_queue(spinlock_t *plock, struct mfc_buf_queue *queue); + +void mfc_print_dpb_queue(struct mfc_core_ctx *core_ctx, struct mfc_dec *de= c); +void mfc_print_dpb_queue_with_lock(struct mfc_core_ctx *core_ctx, struct m= fc_dec *dec); +int mfc_get_available_dpb_count(struct mfc_core_ctx *core_ctx); +struct mfc_buf *mfc_search_for_dpb(struct mfc_core_ctx *core_ctx); + +void mfc_store_dpb(struct mfc_ctx *ctx, struct vb2_buffer *vb); + +void mfc_dec_drc_find_del_buf(struct mfc_core_ctx *core_ctx); +#endif /* __MFC_QUEUE_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h index f965a2e440c0..320dc96a40ed 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h @@ -116,6 +116,47 @@ static inline int mfc_is_decoder_node(enum mfc_node_ty= pe node) return 0; } =20 +static inline void mfc_clear_mb_flag(struct mfc_buf *mfc_buf) +{ + mfc_buf->flag =3D 0; +} + +static inline u32 mfc_dec_get_strm_size(struct mfc_ctx *ctx, struct mfc_bu= f *src_mb) +{ + struct vb2_plane *vb_plane; + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned int strm_size; + + /* + * struct v4l2_plane data_offset is included in bytesused. + * So the size of image is bytesused - data_offset from start of the plan= e. + * And the dec->consumed is cumulate-decoded size. + */ + vb_plane =3D &src_mb->vb.vb2_buf.planes[0]; + if (!vb_plane->bytesused) { + strm_size =3D 0; + } else if (vb_plane->bytesused > vb_plane->data_offset) { + strm_size =3D vb_plane->bytesused - vb_plane->data_offset; + } else { + strm_size =3D vb_plane->bytesused; + mfc_ctx_info("[STREAM] invalid offset (bytesused %d, data_offset: %d)\n", + vb_plane->bytesused, vb_plane->data_offset); + } + + if (dec->consumed) { + if (strm_size > dec->consumed) { + strm_size -=3D dec->consumed; + } else { + mfc_ctx_info("[STREAM] invalid consumed (strm_size: %d, consumed: %d)", + strm_size, dec->consumed); + } + } + + mfc_ctx_debug(2, "[STREAM] strm_size: %d (bytesused %d, data_offset %d, c= onsumed %d)\n", + strm_size, vb_plane->bytesused, vb_plane->data_offset, dec->consum= ed); + + return strm_size; +} /* Meerkat interval */ #define MEERKAT_TICK_INTERVAL 1000 /* After how many executions meerkat should assume lock up */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 1D2C926FA67 for ; Tue, 30 Sep 2025 03:56:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204575; cv=none; b=Mjcozr8j2ntTHyayRQSGY3TyqDp8JjpE5AqscLJk1Zu+zgwpG1IK8Et0uywuXkvqeHX/+cpOuTo8wLnSCrReaG4EbmC/h2rrEvYHw/4k0DX7NAdmgRsfEv1FtDMzh2oUS0aT9tsJI+VFlsgY+ZVDzBUbQYygOs+A1330M9U2ewg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204575; c=relaxed/simple; bh=QTUpFZhSOMfbsZb08haIzFlANBynnP9xncPA4KLbVgs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=Ei90TUTQP4xpIcmsqbOMLiQTr8gZgsp8BTzkqJmbFDIKkUvvqT6+QW+RYzo0GAD7Bu3OH/liLo9GECjnvzHhuimbQMcWs/80yTkJEd0+ZuoRO78jCC/iC1aYAtlXKxaNCJfrTrcAOrt+fW65ldUFFQ7QOLnUFJhd6HaIFPL2Few= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=U1/m1IjA; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="U1/m1IjA" Received: from epcas5p1.samsung.com (unknown [182.195.41.39]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035609epoutp02651c1bded9d62bbccc4e1059122ee41d~p8zw68wc52603026030epoutp02n for ; Tue, 30 Sep 2025 03:56:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035609epoutp02651c1bded9d62bbccc4e1059122ee41d~p8zw68wc52603026030epoutp02n DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204569; bh=1W11SVDf7mSdU0NAMCcHXIjep9ea1d8AcmsqRXISRQQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=U1/m1IjAI4DIcv4h6+zFdaQRRfvHsfUqxGWL8Mq2aKcA0XNc+ZOm3QmL3P2Lm8MRG 4EzOYlvrN+BrBoMVeC22aBgqKgfL6TE9UduTeNsdnidgr7CmlSEOn4VMBJL6dmmSpU pntyxvkPZfrR86z6DurT+p5t/pi1hxoaBrJCNkq8= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPS id 20250930035608epcas5p3346a9c3759e43a5329f5c2692c011366~p8zwVr79x3089530895epcas5p3w; Tue, 30 Sep 2025 03:56:08 +0000 (GMT) Received: from epcas5p4.samsung.com (unknown [182.195.38.86]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPP25Fd5z2SSKh; Tue, 30 Sep 2025 03:56:06 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035606epcas5p3c38c2ba8148bd90a56415f544c5072f1~p8zt3VL6W0048900489epcas5p30; Tue, 30 Sep 2025 03:56:06 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035600epsmtip1ad42e54e76d70164f1da07e92729229e~p8zo8oThG2885328853epsmtip1T; Tue, 30 Sep 2025 03:56:00 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 11/29] =?UTF-8?q?media:=20mfc:=20Add=20rate=E2=80=91calcul?= =?UTF-8?q?ation=20framework=20and=20memory=20utilities?= Date: Tue, 30 Sep 2025 09:33:30 +0530 Message-Id: <20250930040348.3702923-12-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035606epcas5p3c38c2ba8148bd90a56415f544c5072f1 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035606epcas5p3c38c2ba8148bd90a56415f544c5072f1 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Implement runtime rate=E2=80=91calculation (timestamp handling, framerate= /BPS estimation, QoS adjustments, performance checks) with new helper APIs. - Add utility layer for format validation, resolution/stride/size computation, DPB size, address mapping, and view=E2=80=91buffer bookkeepi= ng. - Export functions used by decoder/encoder paths for dynamic QoS and memory handling. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 2 +- .../samsung/exynos-mfc/base/mfc_mem.c | 187 ++++++ .../samsung/exynos-mfc/base/mfc_mem.h | 44 ++ .../exynos-mfc/base/mfc_rate_calculate.c | 612 ++++++++++++++++++ .../exynos-mfc/base/mfc_rate_calculate.h | 100 +++ .../samsung/exynos-mfc/base/mfc_utils.c | 284 ++++++++ .../samsung/exynos-mfc/base/mfc_utils.h | 214 +++++- 7 files changed, 1441 insertions(+), 2 deletions(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_rate= _calculate.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_rate= _calculate.h create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_util= s.c diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index 5353289fa810..bd5f80953bab 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -19,7 +19,7 @@ exynos_mfc-y +=3D mfc_core_hw_reg_api.o mfc_core_reg_api.o #Plugin control layer #Plugin HW access layer #Common base layer -exynos_mfc-y +=3D base/mfc_queue.o +exynos_mfc-y +=3D base/mfc_rate_calculate.o base/mfc_queue.o base/mfc_util= s.o exynos_mfc-y +=3D base/mfc_buf.o base/mfc_mem.o #Tracing # exynos_mfc-y +=3D trace/mfc_trace_points.o diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_mem.c index 17cc1d793cbc..c99c1c081b0e 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.c @@ -18,6 +18,70 @@ =20 #include "mfc_mem.h" =20 +struct vb2_mem_ops *mfc_mem_ops(void) +{ + return (struct vb2_mem_ops *)&vb2_dma_sg_memops; +} + +int mfc_mem_get_user_shared_handle(struct mfc_ctx *ctx, + struct mfc_user_shared_handle *handle, char *name) +{ + struct iosys_map map =3D IOSYS_MAP_INIT_VADDR(NULL); + int ret =3D 0; + + handle->dma_buf =3D dma_buf_get(handle->fd); + if (IS_ERR(handle->dma_buf)) { + mfc_ctx_err("[MEMINFO][SH][%s] Failed to import fd\n", name); + ret =3D PTR_ERR(handle->dma_buf); + goto import_dma_fail; + } + + if (handle->dma_buf->size < handle->data_size) { + mfc_ctx_err("[MEMINFO][SH][%s] User-provided dma_buf size(%ld) is smalle= r than required size(%ld)\n", + name, handle->dma_buf->size, handle->data_size); + ret =3D -EINVAL; + goto dma_buf_size_fail; + } + ret =3D dma_buf_vmap_unlocked(handle->dma_buf, &map); + if (ret) { + mfc_ctx_err("[MEMINFO][SH][%s] Failed to get kernel virtual address\n", = name); + ret =3D -EINVAL; + goto map_kernel_fail; + } + + handle->vaddr =3D map.vaddr; + mfc_ctx_debug(2, "[MEMINFO][SH][%s] shared handle fd: %d, vaddr: 0x%p, bu= f size: %zu, data size: %zu\n", + name, handle->fd, handle->vaddr, + handle->dma_buf->size, handle->data_size); + + return 0; + +map_kernel_fail: + handle->vaddr =3D NULL; +dma_buf_size_fail: + dma_buf_put(handle->dma_buf); +import_dma_fail: + handle->dma_buf =3D NULL; + handle->fd =3D -1; + return ret; +} + +void mfc_mem_cleanup_user_shared_handle(struct mfc_ctx *ctx, + struct mfc_user_shared_handle *handle) +{ + struct iosys_map map =3D IOSYS_MAP_INIT_VADDR(handle->vaddr); + + if (handle->vaddr) + dma_buf_vunmap_unlocked(handle->dma_buf, &map); + if (handle->dma_buf) + dma_buf_put(handle->dma_buf); + + handle->data_size =3D 0; + handle->dma_buf =3D NULL; + handle->vaddr =3D NULL; + handle->fd =3D -1; +} + static int mfc_mem_fw_alloc(struct mfc_dev *dev, struct mfc_special_buf *s= pecial_buf) { #if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) @@ -334,6 +398,26 @@ void mfc_mem_special_buf_free(struct mfc_dev *dev, str= uct mfc_special_buf *speci } } =20 +void mfc_bufcon_put_daddr(struct mfc_ctx *ctx, struct mfc_buf *mfc_buf, in= t plane) +{ + int i; + + for (i =3D 0; i < mfc_buf->num_valid_bufs; i++) { + if (mfc_buf->addr[i][plane]) { + mfc_ctx_debug(4, "[BUFCON] put batch buf addr[%d][%d]: 0x%08llx\n", + i, plane, mfc_buf->addr[i][plane]); + } + if (mfc_buf->attachments[i][plane]) + dma_buf_detach(mfc_buf->dmabufs[i][plane], mfc_buf->attachments[i][plan= e]); + if (mfc_buf->dmabufs[i][plane]) + dma_buf_put(mfc_buf->dmabufs[i][plane]); + + mfc_buf->addr[i][plane] =3D 0; + mfc_buf->attachments[i][plane] =3D NULL; + mfc_buf->dmabufs[i][plane] =3D NULL; + } +} + void mfc_put_iovmm(struct mfc_ctx *ctx, struct dpb_table *dpb, int num_pla= nes, int index) { int i; @@ -524,6 +608,81 @@ void mfc_get_iovmm(struct mfc_ctx *ctx, struct vb2_buf= fer *vb, struct dpb_table mfc_put_iovmm(ctx, dpb, mem_get_count, sub_view_index); } =20 +void mfc_init_dpb_table(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + int index, plane; + + mutex_lock(&dec->dpb_mutex); + for (index =3D 0; index < MFC_MAX_DPBS; index++) { + for (plane =3D 0; plane < MFC_MAX_PLANES; plane++) { + dec->dpb[index].fd[plane] =3D -1; + dec->dpb[index].addr[plane] =3D 0; + dec->dpb[index].attach[plane] =3D NULL; + dec->dpb[index].dmabufs[plane] =3D NULL; + } + dec->dpb[index].new_fd =3D -1; + dec->dpb[index].mapcnt =3D 0; + dec->dpb[index].queued =3D 0; + } + mutex_unlock(&dec->dpb_mutex); +} + +void mfc_cleanup_iovmm(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + int i; + + mutex_lock(&dec->dpb_mutex); + + for (i =3D 0; i < MFC_MAX_DPBS; i++) { + dec->dpb[i].paddr =3D 0; + dec->dpb[i].ref =3D 0; + if (dec->dpb[i].mapcnt =3D=3D 0) { + continue; + } else if (dec->dpb[i].mapcnt =3D=3D 1) { + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, i); + } else { + mfc_ctx_err("[IOVMM] DPB[%d] %#llx invalid mapcnt %d\n", + i, dec->dpb[i].addr[0], dec->dpb[i].mapcnt); + MFC_TRACE_CTX("DPB[%d] %#llx invalid mapcnt %d\n", + i, dec->dpb[i].addr[0], dec->dpb[i].mapcnt); + } + } + + mutex_unlock(&dec->dpb_mutex); +} + +void mfc_cleanup_iovmm_except_used(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + int i; + + mutex_lock(&dec->dpb_mutex); + + for (i =3D 0; i < MFC_MAX_DPBS; i++) { + if (dec->dynamic_used & (1UL << i)) { + continue; + } else { + dec->dpb[i].paddr =3D 0; + dec->dpb[i].ref =3D 0; + if (dec->dpb[i].mapcnt =3D=3D 0) { + continue; + } else if (dec->dpb[i].mapcnt =3D=3D 1) { + dec->dpb_table_used &=3D ~(1UL << i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, i); + } else { + mfc_ctx_err("[IOVMM] DPB[%d] %#llx invalid mapcnt %d\n", + i, dec->dpb[i].addr[0], dec->dpb[i].mapcnt); + MFC_TRACE_CTX("DPB[%d] %#llx invalid mapcnt %d\n", + i, dec->dpb[i].addr[0], dec->dpb[i].mapcnt); + } + } + } + + mutex_unlock(&dec->dpb_mutex); +} + void mfc_iova_pool_free(struct mfc_dev *dev, struct mfc_special_buf *buf) { /* Project that do not support iova reservation */ @@ -712,6 +871,34 @@ int mfc_iommu_map_firmware(struct mfc_core *core, stru= ct mfc_special_buf *fw_buf return 0; } =20 +void mfc_check_iova(struct mfc_dev *dev) +{ + struct mfc_platdata *pdata =3D dev->pdata; + struct mfc_ctx *ctx; + unsigned long total_iova =3D 0; + + if (!pdata->iova_threshold) + return; + + /* + * The number of extra dpb is 8 + * OMX: extra buffer 5, platform buffer 3 + * Codec2: platform buffer 8 + */ + list_for_each_entry(ctx, &dev->ctx_list, list) + total_iova +=3D (ctx->raw_buf.total_plane_size * + (ctx->dpb_count + MFC_EXTRA_DPB + 3)) / SZ_1K; + + if (total_iova > (pdata->iova_threshold * SZ_1K)) + dev->skip_lazy_unmap =3D 1; + else + dev->skip_lazy_unmap =3D 0; + + mfc_dev_debug(2, "[LAZY_UNMAP] Now the IOVA for DPB is %lu/%uMB, LAZY_UNM= AP %s\n", + total_iova / SZ_1K, pdata->iova_threshold, + dev->skip_lazy_unmap ? "disable" : "enable"); +} + /* DMA memory related helper functions */ static void mfc_memdev_release(struct device *dev) { diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_mem.h index 3deeb0d611a0..3bd40dd0a0ed 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_mem.h @@ -66,6 +66,35 @@ static inline size_t mfc_mem_get_sg_length(struct mfc_de= v *dev, struct sg_table return size; } =20 +static inline void mfc_mem_buf_prepare(struct vb2_buffer *vb, int stream) +{ + int i; + enum dma_data_direction dir; + struct dma_buf *dbuf; + + dir =3D V4L2_TYPE_IS_OUTPUT(vb->type) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE; + + for (i =3D 0; i < vb->num_planes; i++) { + dbuf =3D vb->planes[i].dbuf; + dma_buf_end_cpu_access(dbuf, dir); + } +} + +static inline void mfc_mem_buf_finish(struct vb2_buffer *vb, int stream) +{ + int i; + struct dma_buf *dbuf; + + if (V4L2_TYPE_IS_OUTPUT(vb->type)) + return; + + for (i =3D 0; i < vb->num_planes; i++) { + dbuf =3D vb->planes[i].dbuf; + dma_buf_begin_cpu_access(dbuf, DMA_FROM_DEVICE); + } +} + static inline void mfc_print_dpb_table(struct mfc_ctx *ctx) { struct mfc_dec *dec =3D ctx->dec_priv; @@ -95,16 +124,31 @@ static inline void mfc_print_dpb_table(struct mfc_ctx = *ctx) dec->dpb[i].queued ? "Q" : "DQ"); } } + +struct vb2_mem_ops *mfc_mem_ops(void); + +int mfc_mem_get_user_shared_handle(struct mfc_ctx *ctx, + struct mfc_user_shared_handle *handle, char *name); +void mfc_mem_cleanup_user_shared_handle(struct mfc_ctx *ctx, + struct mfc_user_shared_handle *handle); + int mfc_mem_special_buf_alloc(struct mfc_dev *dev, struct mfc_special_buf = *special_buf); void mfc_mem_special_buf_free(struct mfc_dev *dev, struct mfc_special_buf = *special_buf); =20 +void mfc_bufcon_put_daddr(struct mfc_ctx *ctx, struct mfc_buf *mfc_buf, in= t plane); + void mfc_put_iovmm(struct mfc_ctx *ctx, struct dpb_table *dpb, int num_pla= nes, int index); void mfc_get_iovmm(struct mfc_ctx *ctx, struct vb2_buffer *vb, struct dpb_= table *dpb); +void mfc_init_dpb_table(struct mfc_ctx *ctx); +void mfc_cleanup_iovmm(struct mfc_ctx *ctx); +void mfc_cleanup_iovmm_except_used(struct mfc_ctx *ctx); + void mfc_iova_pool_free(struct mfc_dev *dev, struct mfc_special_buf *buf); int mfc_iova_pool_alloc(struct mfc_dev *dev, struct mfc_special_buf *buf); int mfc_iova_pool_init(struct mfc_dev *dev); =20 int mfc_iommu_map_firmware(struct mfc_core *core, struct mfc_special_buf *= fw_buf); +void mfc_check_iova(struct mfc_dev *dev); =20 int mfc_configure_dma_memory(struct mfc_dev *mfc_dev); void mfc_unconfigure_dma_memory(struct mfc_dev *mfc_dev); diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calcul= ate.c b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.c new file mode 100644 index 000000000000..94a555c900d7 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_rate_calculate.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include +#include + +#include "mfc_utils.h" + +#define COL_FRAME_RATE 0 +#define COL_FRAME_INTERVAL 1 + +#define MFC_MAX_INTERVAL (2 * USEC_PER_SEC) + +/* + * A framerate table determines framerate by the interval(us) of each fram= e. + * Framerate is not accurate, just rough value to separate overload sectio= n. + * Base line of each section are selected from middle value. + * 25fps(40000us), 40fps(25000us), 80fps(12500us) + * 144fps(6940us), 205fps(4860us), 320fps(3125us) + * + * interval(us) | 0 3125 4860 6940 12500 25000 40000 + * framerate | 480fps | 240fps | 180fps | 120fps | 60fp= s | 30fps | + * 24fps + */ +static unsigned long framerate_table[][2] =3D { + { 24000, 40000 }, + { 30000, 25000 }, + { 60000, 12500 }, + { 120000, 6940 }, + { 180000, 4860 }, + { 240000, 3125 }, + { 480000, 0 }, +}; + +/* + * display_framerate_table determines framerate by the queued interval. + * It supports 30fps, 60fps, 120fps as display framerate. + * Base line of each section is selected from middle value. + * 25fps(40000us), 40fps(25000us), 80fps(12500us) + * + * interval(us) | 12500 25000 40000 + * disp framerate | 120fps | 60fps | 30fps | 24fps + */ +static unsigned long display_framerate_table[][2] =3D { + { 24000, 40000 }, + { 30000, 25000 }, + { 60000, 12500 }, + { 120000, 0 }, +}; + +inline unsigned long mfc_rate_timespec64_diff(struct timespec64 *to, + struct timespec64 *from) +{ + unsigned long interval_nsec =3D (to->tv_sec * NSEC_PER_SEC + to->tv_nsec) + - (from->tv_sec * NSEC_PER_SEC + from->tv_nsec); + + if (interval_nsec <=3D 0) + interval_nsec =3D MFC_MAX_INTERVAL * 1000; + + return interval_nsec / 1000; +} + +static int __mfc_rate_ts_sort(const void *p0, const void *p1) +{ + const int *t0 =3D p0, *t1 =3D p1; + + /* ascending sort */ + if (*t0 < *t1) + return -1; + else if (*t0 > *t1) + return 1; + + return 0; +} + +static int __mfc_rate_get_ts_min_interval(struct mfc_ctx *ctx, struct mfc_= ts_control *ts) +{ + int tmp[MAX_TIME_INDEX]; + + if (!ts->ts_is_full) + return 0; + + memcpy(&tmp[0], &ts->ts_interval_array[0], MAX_TIME_INDEX * sizeof(int)); + sort(tmp, MAX_TIME_INDEX, sizeof(int), __mfc_rate_ts_sort, NULL); + + return tmp[0]; +} + +static int __mfc_rate_get_ts_interval(struct mfc_ctx *ctx, struct mfc_ts_c= ontrol *ts, int type) +{ + int tmp[MAX_TIME_INDEX]; + int n, i, val =3D 0, sum =3D 0; + + n =3D ts->ts_is_full ? MAX_TIME_INDEX : ts->ts_count; + + memcpy(&tmp[0], &ts->ts_interval_array[0], n * sizeof(int)); + sort(tmp, n, sizeof(int), __mfc_rate_ts_sort, NULL); + + if (type =3D=3D MFC_TS_SRC) { + /* apply median filter for selecting ts interval */ + val =3D (n <=3D 2) ? tmp[0] : tmp[n / 2]; + } else if ((type =3D=3D MFC_TS_DST_Q) || (type =3D=3D MFC_TS_SRC_Q) || (t= ype =3D=3D MFC_TS_DST_DQ)) { + /* apply average for selecting ts interval except min,max */ + if (n < 3) + return 0; + for (i =3D 1; i < (n - 1); i++) + sum +=3D tmp[i]; + val =3D sum / (n - 2); + } else { + mfc_ctx_err("[TS] Wrong timestamp type %d\n", type); + } + + if (ctx->dev->debugfs.debug_ts =3D=3D 1) { + mfc_ctx_info("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D[%s%s%s][TS] int= erval (sort)=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n", + (type & 0x1) ? "SRC" : "DST", (type & 0x2) ? "_Q" : "", + (type & 0x4) ? "_DQ" : ""); + for (i =3D 0; i < n; i++) + mfc_ctx_info("[%s%s%s][TS] interval [%d] =3D %d\n", + (type & 0x1) ? "SRC" : "DST", + (type & 0x2) ? "_Q" : "", + (type & 0x4) ? "_DQ" : "", i, tmp[i]); + mfc_ctx_info("[%s%s%s][TS] get interval %d\n", + (type & 0x1) ? "SRC" : "DST", + (type & 0x2) ? "_Q" : "", + (type & 0x4) ? "_DQ" : "", val); + } + + return val; +} + +static unsigned long __mfc_rate_get_framerate_by_interval(int interval, in= t type) +{ + unsigned long (*table)[2]; + unsigned long i; + int size; + + /* if the interval is too big (2sec), framerate set to 0 */ + if (interval > MFC_MAX_INTERVAL || interval <=3D 0) + return 0; + + if (type =3D=3D MFC_TS_SRC || type =3D=3D MFC_TS_SRC_Q || type =3D=3D MFC= _TS_DST_DQ) { + table =3D framerate_table; + size =3D ARRAY_SIZE(framerate_table); + } else if (type =3D=3D MFC_TS_DST_Q) { + table =3D display_framerate_table; + size =3D ARRAY_SIZE(display_framerate_table); + } else { + return 0; + } + + for (i =3D 0; i < size; i++) { + if (interval > table[i][COL_FRAME_INTERVAL]) + return table[i][COL_FRAME_RATE]; + } + + return 0; +} + +int mfc_rate_get_bps_section_by_bps(struct mfc_dev *dev, int kbps, int max= _kbps) +{ + struct mfc_platdata *pdata =3D dev->pdata; + int i, max_freq_idx; + int freq_ratio, bps_interval, prev_interval =3D 0; + + max_freq_idx =3D pdata->num_mfc_freq - 1; + if (kbps > max_kbps) { + mfc_dev_debug(4, "[BPS] overspec bps %d > %d\n", kbps, max_kbps); + return max_freq_idx; + } + + for (i =3D 0; i < pdata->num_mfc_freq; i++) { + freq_ratio =3D pdata->mfc_freqs[i] * 100 / pdata->mfc_freqs[max_freq_idx= ]; + bps_interval =3D max_kbps * freq_ratio / 100; + mfc_dev_debug(4, "[BPS] MFC freq lv%d, %uKHz covered: %d ~ %dkbps (now: = %dkbps)\n", + i, pdata->mfc_freqs[i], prev_interval, bps_interval, kbps); + if (kbps <=3D bps_interval) { + mfc_dev_debug(3, "[BPS] MFC freq lv%d, %uKHz is needed: %d ~ %dkbps\n", + i, pdata->mfc_freqs[i], + prev_interval, bps_interval); + return i; + } + prev_interval =3D bps_interval; + } + + /* Not changed the MFC freq according to BPS */ + return 0; +} + +/* Return the minimum interval between previous and next entry */ +static int __mfc_rate_get_interval(struct list_head *head, struct list_hea= d *entry) +{ + unsigned long prev_interval =3D MFC_MAX_INTERVAL, next_interval =3D MFC_M= AX_INTERVAL; + struct mfc_timestamp *prev_ts, *next_ts, *curr_ts; + int ret =3D 0; + + curr_ts =3D list_entry(entry, struct mfc_timestamp, list); + + if (entry->prev !=3D head) { + prev_ts =3D list_entry(entry->prev, struct mfc_timestamp, list); + prev_interval =3D mfc_rate_timespec64_diff(&curr_ts->timestamp, + &prev_ts->timestamp); + if (prev_interval > MFC_MAX_INTERVAL) + prev_interval =3D MFC_MAX_INTERVAL; + } + + if (entry->next !=3D head) { + next_ts =3D list_entry(entry->next, struct mfc_timestamp, list); + next_interval =3D mfc_rate_timespec64_diff(&next_ts->timestamp, + &curr_ts->timestamp); + if (next_interval > MFC_MAX_INTERVAL) + next_interval =3D MFC_MAX_INTERVAL; + } + + ret =3D (prev_interval < next_interval) ? prev_interval : next_interval; + return ret; +} + +static int __mfc_rate_add_timestamp(struct mfc_ctx *ctx, struct mfc_ts_con= trol *ts, + struct timespec64 *time, struct list_head *head) +{ + int replace_entry =3D 0; + struct mfc_timestamp *curr_ts =3D &ts->ts_array[ts->ts_count]; + struct mfc_timestamp *adj_ts =3D NULL; + + if (ts->ts_is_full) { + /* Replace the entry if list of array[ts_count] is same as entry */ + if (&curr_ts->list =3D=3D head) + replace_entry =3D 1; + else + list_del(&curr_ts->list); + } + + memcpy(&curr_ts->timestamp, time, sizeof(*time)); + if (!replace_entry) + list_add(&curr_ts->list, head); + curr_ts->interval =3D __mfc_rate_get_interval(&ts->ts_list, &curr_ts->lis= t); + curr_ts->index =3D ts->ts_count; + + ts->ts_interval_array[ts->ts_count] =3D curr_ts->interval; + ts->ts_count++; + + if (ts->ts_count =3D=3D MAX_TIME_INDEX) { + ts->ts_is_full =3D 1; + ts->ts_count %=3D MAX_TIME_INDEX; + } + + /* + * When timestamp is updated, the interval of adjacent timestamp can be c= hanged. + * So, update this value of prev and next in list. + */ + if (curr_ts->list.next !=3D &ts->ts_list) { + adj_ts =3D list_entry(curr_ts->list.next, struct mfc_timestamp, list); + adj_ts->interval =3D __mfc_rate_get_interval(&ts->ts_list, &adj_ts->list= ); + ts->ts_interval_array[adj_ts->index] =3D adj_ts->interval; + } + if (curr_ts->list.prev !=3D &ts->ts_list) { + adj_ts =3D list_entry(curr_ts->list.prev, struct mfc_timestamp, list); + adj_ts->interval =3D __mfc_rate_get_interval(&ts->ts_list, &adj_ts->list= ); + ts->ts_interval_array[adj_ts->index] =3D adj_ts->interval; + } + + return 0; +} + +void mfc_rate_reset_ts_list(struct mfc_ts_control *ts) +{ + struct mfc_timestamp *temp_ts =3D NULL; + unsigned long flags; + + spin_lock_irqsave(&ts->ts_lock, flags); + + /* empty the timestamp queue */ + while (!list_empty(&ts->ts_list)) { + temp_ts =3D list_entry((&ts->ts_list)->next, struct mfc_timestamp, list); + list_del(&temp_ts->list); + } + + ts->ts_count =3D 0; + ts->ts_is_full =3D 0; + + spin_unlock_irqrestore(&ts->ts_lock, flags); +} + +static unsigned long __mfc_rate_get_fps_by_timestamp(struct mfc_ctx *ctx, + struct mfc_ts_control *ts, + struct timespec64 *time, int type) +{ + struct mfc_timestamp *temp_ts; + int found; + int index =3D 0; + int ts_is_full =3D ts->ts_is_full; + int interval =3D MFC_MAX_INTERVAL; + int min_interval =3D 0; + int time_diff; + unsigned long framerate; + unsigned long flags; + u64 current_time; + + spin_lock_irqsave(&ts->ts_lock, flags); + if (list_empty(&ts->ts_list)) { + __mfc_rate_add_timestamp(ctx, ts, time, &ts->ts_list); + spin_unlock_irqrestore(&ts->ts_lock, flags); + return __mfc_rate_get_framerate_by_interval(0, type); + } + found =3D 0; + list_for_each_entry_reverse(temp_ts, &ts->ts_list, list) { + time_diff =3D __mfc_timespec64_compare(time, &temp_ts->timestamp); + if (time_diff =3D=3D 0) { + /* Do not add if same timestamp already exists */ + found =3D 1; + break; + } else if (time_diff > 0) { + /* Add this after temp_ts */ + __mfc_rate_add_timestamp(ctx, ts, time, &temp_ts->list); + found =3D 1; + break; + } + } + + if (!found) /* Add this at first entry */ + __mfc_rate_add_timestamp(ctx, ts, time, &ts->ts_list); + spin_unlock_irqrestore(&ts->ts_lock, flags); + + interval =3D __mfc_rate_get_ts_interval(ctx, ts, type); + framerate =3D __mfc_rate_get_framerate_by_interval(interval, type); + + if (ctx->dev->debugfs.debug_ts =3D=3D 1) { + spin_lock_irqsave(&ts->ts_lock, flags); + /* Debug info */ + mfc_ctx_info("=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D[%s%s%s= ][TS]=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n", + (type & 0x1) ? "SRC" : "DST", (type & 0x2) ? "_Q" : "", + (type & 0x4) ? "_DQ" : ""); + mfc_ctx_info("[%s%s%s][TS] New timestamp =3D %lld.%06ld, count =3D %d\n", + (type & 0x1) ? "SRC" : "DST", + (type & 0x2) ? "_Q" : "", + (type & 0x4) ? "_DQ" : "", + time->tv_sec, time->tv_nsec, ts->ts_count); + + index =3D 0; + list_for_each_entry(temp_ts, &ts->ts_list, list) { + mfc_ctx_info("[%s%s][TS] [%d] timestamp [i:%d]: %lld.%06ld\n", + (type & 0x1) ? "SRC" : "DST", + (type & 0x2) ? "_Q" : "", + index, temp_ts->index, + temp_ts->timestamp.tv_sec, + temp_ts->timestamp.tv_nsec); + index++; + } + mfc_ctx_info("[%s%s%s][TS] Min interval =3D %d, It is %ld fps\n", + (type & 0x1) ? "SRC" : "DST", + (type & 0x2) ? "_Q" : "", + (type & 0x4) ? "_DQ" : "", + interval, framerate); + spin_unlock_irqrestore(&ts->ts_lock, flags); + } + + if (!ts->ts_is_full) { + if (ctx->dev->debugfs.debug_ts =3D=3D 1) + mfc_ctx_info("[TS] ts doesn't full, keep %ld fps\n", ctx->framerate); + return ctx->framerate; + } + + if (!ts_is_full && type =3D=3D MFC_TS_SRC) { + min_interval =3D __mfc_rate_get_ts_min_interval(ctx, ts); + if (min_interval) + ctx->max_framerate =3D + __mfc_rate_get_framerate_by_interval(min_interval, type); + else + ctx->max_framerate =3D 0; + + current_time =3D ktime_get_ns() / NSEC_PER_SEC; + if (current_time =3D=3D time->tv_sec) + ctx->ktime_used =3D TRUE; + } + + return framerate; +} + +static int __mfc_rate_get_bps_section(struct mfc_ctx *ctx, u32 bytesused) +{ + struct mfc_dev *dev =3D ctx->dev; + struct list_head *head =3D &ctx->bitrate_list; + struct mfc_bitrate *temp_bitrate; + struct mfc_bitrate *new_bitrate =3D &ctx->bitrate_array[ctx->bitrate_inde= x]; + int max_kbps; + unsigned long sum_size =3D 0, avg_kbits, fps; + int count =3D 0; + + if (ctx->bitrate_is_full) { + temp_bitrate =3D list_entry(head->next, struct mfc_bitrate, list); + list_del(&temp_bitrate->list); + } + + new_bitrate->bytesused =3D bytesused; + list_add_tail(&new_bitrate->list, head); + + list_for_each_entry(temp_bitrate, head, list) { + mfc_ctx_debug(4, "[BPS][%d] strm_size %d\n", count, temp_bitrate->bytesu= sed); + sum_size +=3D temp_bitrate->bytesused; + count++; + } + + if (count =3D=3D 0) { + mfc_ctx_err("[BPS] There is no list for bps\n"); + return ctx->last_bps_section; + } + + ctx->bitrate_index++; + if (ctx->bitrate_index =3D=3D MAX_TIME_INDEX) { + ctx->bitrate_is_full =3D 1; + ctx->bitrate_index %=3D MAX_TIME_INDEX; + } + + /* + * When there is a value of ts_is_full, + * we can trust fps(trusted fps calculated by timestamp diff). + * When fps information becomes reliable, + * we will start QoS handling by obtaining bps section. + */ + if (!ctx->src_ts.ts_is_full) + return 0; + + if (IS_MULTI_MODE(ctx)) + fps =3D ctx->last_framerate / 1000 / dev->num_core; + else + fps =3D ctx->last_framerate / 1000; + avg_kbits =3D ((sum_size * BITS_PER_BYTE) / count) / SZ_1K; + ctx->kbps =3D (int)(avg_kbits * fps); + max_kbps =3D dev->pdata->mfc_resource[ctx->codec_mode].max_kbps; + mfc_ctx_debug(3, "[BPS] %d kbps, average %lu Kbits per frame\n", ctx->kbp= s, avg_kbits); + + return mfc_rate_get_bps_section_by_bps(dev, ctx->kbps, max_kbps); +} + +void mfc_rate_update_bitrate(struct mfc_ctx *ctx, u32 bytesused) +{ + int bps_section; + + /* bitrate is updated */ + bps_section =3D __mfc_rate_get_bps_section(ctx, bytesused); + if (ctx->last_bps_section !=3D bps_section) { + mfc_ctx_debug(2, "[BPS] bps section changed: %d -> %d\n", + ctx->last_bps_section, bps_section); + ctx->last_bps_section =3D bps_section; + ctx->update_bitrate =3D true; + } +} + +void mfc_rate_update_framerate(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + unsigned long framerate; + + /* 2) when src timestamp isn't full, only check operating framerate by us= er */ + if (!ctx->src_ts.ts_is_full) { + if (ctx->operating_framerate && ctx->operating_framerate > ctx->framerat= e) { + mfc_ctx_debug(2, "[QoS] operating fps changed: %ld\n", + ctx->operating_framerate); + mfc_rate_set_framerate(ctx, ctx->operating_framerate); + ctx->update_framerate =3D true; + return; + } + } else { + /* 3) get src framerate */ + framerate =3D ctx->last_framerate; + + /* 4) check display framerate */ + if (dev->pdata->display_framerate && + ctx->dst_q_ts.ts_is_full && ctx->dst_q_framerate > framerate) { + framerate =3D ctx->dst_q_framerate; + if (framerate !=3D ctx->framerate) + mfc_ctx_debug(2, "[QoS] display fps %ld\n", framerate); + } + + /* 5) check operating framerate by user */ + if (ctx->operating_framerate && ctx->operating_framerate > framerate) { + framerate =3D ctx->operating_framerate; + if (framerate !=3D ctx->framerate) + mfc_ctx_debug(2, "[QoS] operating fps %ld\n", framerate); + } + + /* 6) check non-real-time and undefined mode */ + if (ctx->rt =3D=3D MFC_NON_RT || ctx->rt =3D=3D MFC_RT_UNDEFINED) { + if (framerate < ctx->src_q_framerate) { + framerate =3D ctx->src_q_framerate; + if (framerate !=3D ctx->framerate) + mfc_ctx_debug(2, + "[QoS][PRIO] (default) NRT src_q fps %ld\n", + framerate); + } + } + + if (ctx->operating_framerate =3D=3D ctx->framerate && !ctx->check_src_ts= _full) { + mfc_ctx_debug(2, "[QoS] src ts is full, update framerate for load balan= cing\n"); + ctx->check_src_ts_full =3D true; + ctx->update_framerate =3D true; + } + + if (framerate && framerate !=3D ctx->framerate) { + mfc_ctx_debug(2, "[QoS] fps changed: %ld -> %ld, qos ratio: %d\n", + ctx->framerate, framerate, ctx->qos_ratio); + mfc_rate_set_framerate(ctx, framerate); + ctx->update_framerate =3D true; + } + } +} + +void mfc_rate_update_last_framerate(struct mfc_ctx *ctx, u64 timestamp) +{ + struct timespec64 time; + + time.tv_sec =3D timestamp / NSEC_PER_SEC; + time.tv_nsec =3D (timestamp - (time.tv_sec * NSEC_PER_SEC)); + + ctx->last_framerate =3D __mfc_rate_get_fps_by_timestamp(ctx, &ctx->src_ts= , &time, MFC_TS_SRC); + if (ctx->last_framerate > MFC_MAX_FPS) + ctx->last_framerate =3D MFC_MAX_FPS; + + if (ctx->src_ts.ts_is_full) + ctx->last_framerate =3D (ctx->qos_ratio * ctx->last_framerate) / 100; +} + +void mfc_rate_update_bufq_framerate(struct mfc_ctx *ctx, int type) +{ + struct timespec64 time; + u64 timestamp; + + timestamp =3D ktime_get_ns(); + time.tv_sec =3D timestamp / NSEC_PER_SEC; + time.tv_nsec =3D timestamp - (time.tv_sec * NSEC_PER_SEC); + + if (type =3D=3D MFC_TS_DST_Q) { + ctx->dst_q_framerate =3D __mfc_rate_get_fps_by_timestamp + (ctx, &ctx->dst_q_ts, &time, MFC_TS_DST_Q); + mfc_ctx_debug(5, "[QoS] dst_q_framerate =3D %lu\n", ctx->dst_q_framerate= ); + } + if (type =3D=3D MFC_TS_DST_DQ) { + ctx->dst_dq_framerate =3D __mfc_rate_get_fps_by_timestamp + (ctx, &ctx->dst_dq_ts, &time, MFC_TS_DST_DQ); + mfc_ctx_debug(5, "[QoS] dst_dq_framerate =3D %lu\n", ctx->dst_dq_framera= te); + } + if (type =3D=3D MFC_TS_SRC_Q) { + ctx->src_q_framerate =3D __mfc_rate_get_fps_by_timestamp + (ctx, &ctx->src_q_ts, &time, MFC_TS_SRC_Q); + mfc_ctx_debug(5, "[QoS] src_q_framerate =3D %lu\n", ctx->src_q_framerate= ); + } +} + +void mfc_rate_reset_bufq_framerate(struct mfc_ctx *ctx) +{ + ctx->dst_q_framerate =3D 0; + ctx->dst_dq_framerate =3D 0; + ctx->src_q_framerate =3D 0; + + mfc_rate_reset_ts_list(&ctx->dst_q_ts); + mfc_rate_reset_ts_list(&ctx->src_q_ts); +} + +int mfc_rate_check_perf_ctx(struct mfc_ctx *ctx, int max_runtime) +{ + struct mfc_ts_control *ts; + struct timespec64 start_time, curr_time; + u64 timestamp, interval; + int op_fps, fps; + unsigned long flags; + + if (ctx->dev->debugfs.sched_perf_disable) + return 0; + + ts =3D &ctx->dst_dq_ts; + if (!ts->ts_is_full) + return 0; + + op_fps =3D ctx->operating_framerate; + if (op_fps =3D=3D 0) { + op_fps =3D ctx->src_q_framerate; + mfc_ctx_debug(2, "[PRIO][rt %d] use fps: %d\n", ctx->rt, op_fps); + } + + /* Calculate interval from start to current time */ + timestamp =3D ktime_get_ns(); + curr_time.tv_sec =3D timestamp / NSEC_PER_SEC; + curr_time.tv_nsec =3D timestamp - (curr_time.tv_sec * NSEC_PER_SEC); + + spin_lock_irqsave(&ts->ts_lock, flags); + start_time =3D ts->ts_array[ts->ts_count].timestamp; + spin_unlock_irqrestore(&ts->ts_lock, flags); + + interval =3D mfc_rate_timespec64_diff(&curr_time, &start_time); + interval +=3D max_runtime; + + fps =3D (((MAX_TIME_INDEX - 1) * 1000) / (interval / 1000)) * 1000; + mfc_ctx_debug(2, "[PRIO][rt %d] st %lld.%06ld, curr %lld.%06ld, interval = %lld, fps %d\n", + ctx->rt, start_time.tv_sec, start_time.tv_nsec, + curr_time.tv_sec, curr_time.tv_nsec, interval, fps); + + if (fps > op_fps) { + mfc_ctx_debug(2, "[PRIO] PERF enough fps: %d, op_fps: %d\n", fps, op_fps= ); + return 1; + } + + mfc_ctx_debug(2, "[PRIO] PERF insufficient fps: %d, op_fps: %d\n", fps, o= p_fps); + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calcul= ate.h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.h new file mode 100644 index 000000000000..2452e6ee56dd --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_rate_calculate.h + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_RATE_CALCULATE_H +#define __MFC_RATE_CALCULATE_H __FILE__ + +#include "mfc_common.h" + +#define MFC_MIN_FPS (30000) +#define MFC_MAX_FPS (480000) +#define DEC_DEFAULT_FPS (480000) +#define ENC_DEFAULT_FPS (480000) +#define ENC_DEFAULT_CAM_CAPTURE_FPS (60000) + +inline unsigned long mfc_rate_timespec64_diff(struct timespec64 *to, + struct timespec64 *from); +void mfc_rate_reset_ts_list(struct mfc_ts_control *ts); +int mfc_rate_get_bps_section_by_bps(struct mfc_dev *dev, int kbps, int max= _kbps); +void mfc_rate_update_bitrate(struct mfc_ctx *ctx, u32 bytesused); +void mfc_rate_update_framerate(struct mfc_ctx *ctx); +void mfc_rate_update_last_framerate(struct mfc_ctx *ctx, u64 timestamp); +void mfc_rate_update_bufq_framerate(struct mfc_ctx *ctx, int type); +void mfc_rate_reset_bufq_framerate(struct mfc_ctx *ctx); +int mfc_rate_check_perf_ctx(struct mfc_ctx *ctx, int max_runtime); + +static inline int __mfc_timespec64_compare(const struct timespec64 *lhs, + const struct timespec64 *rhs) +{ + if (lhs->tv_sec < rhs->tv_sec) + return -1; + if (lhs->tv_sec > rhs->tv_sec) + return 1; + return lhs->tv_nsec - rhs->tv_nsec; +} + +static inline void mfc_rate_reset_framerate(struct mfc_ctx *ctx) +{ + if (ctx->type =3D=3D MFCINST_DECODER) + ctx->framerate =3D DEC_DEFAULT_FPS; + + mfc_ctx_debug(3, "[QoS] reset ctx->framrate: %lu\n", ctx->framerate); +} + +static inline void mfc_rate_reset_last_framerate(struct mfc_ctx *ctx) +{ + ctx->last_framerate =3D 0; +} + +static inline void mfc_rate_set_framerate(struct mfc_ctx *ctx, int rate) +{ + ctx->framerate =3D rate; + + mfc_ctx_debug(3, "[QoS] set ctx->framerate: %lu\n", ctx->framerate); +} + +static inline int mfc_rate_get_framerate(struct mfc_ctx *ctx) +{ + unsigned long framerate =3D ctx->last_framerate; + + if (!framerate) + framerate =3D ctx->framerate; + + if (ctx->operating_framerate > framerate) + return ctx->operating_framerate; + else + return framerate; +} + +static inline unsigned long mfc_rate_get_rt_framerate(struct mfc_ctx *ctx,= enum mfc_real_time rt) +{ + unsigned long framerate; + + framerate =3D ctx->operating_framerate; + + if (rt =3D=3D MFC_RT_UNDEFINED || rt =3D=3D MFC_NON_RT) { + framerate =3D ctx->framerate; + } else { + if (ctx->src_ts.ts_is_full) + framerate =3D mfc_rate_get_framerate(ctx); + } + + if (framerate =3D=3D 0) + framerate =3D MFC_MIN_FPS; + else if (framerate > MFC_MAX_FPS) + framerate =3D MFC_MAX_FPS; + + mfc_ctx_debug(3, "[QoS] rt framerate: %lu\n", framerate); + + return framerate; +} + +#endif /* __MFC_RATE_CALCULATE_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c new file mode 100644 index 000000000000..b0698b2bb0c0 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_utils.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_utils.h" +#include "mfc_mem.h" +#include "mfc_queue.h" + +static struct mfc_resolution mfc_res[] =3D { + { + .width =3D 0, + .height =3D 0, + }, + { + .width =3D 1920, + .height =3D 1088, + }, + { + .width =3D 4096, + .height =3D 2176, + }, + { + .width =3D 8192, + .height =3D 4352, + }, +}; + +int mfc_check_vb_with_fmt(struct mfc_fmt *fmt, struct vb2_buffer *vb) +{ + struct mfc_ctx *ctx =3D vb->vb2_queue->drv_priv; + int mem_planes; + + if (!fmt) + return -EINVAL; + + if (fmt->type & MFC_FMT_FRAME) + mem_planes =3D ctx->num_fd_frame; + else + mem_planes =3D fmt->mem_planes; + + if (mem_planes !=3D vb->num_planes) { + mfc_ctx_err("plane number is different (%d !=3D %d)\n", + mem_planes, vb->num_planes); + return -EINVAL; + } + + return 0; +} + +int mfc_check_resolution(struct mfc_ctx *ctx) +{ + int check_res =3D ctx->dev->pdata->support_check_res; + int max_width, max_height; + + if (!check_res) + return 0; + + max_width =3D mfc_res[check_res].width; + max_height =3D mfc_res[check_res].height; + + if ((ctx->crop_width > max_width && ctx->crop_height > max_height) || + (ctx->crop_width > max_height && ctx->crop_height > max_width)) { + mfc_ctx_err("Resolution is too big (%dx%d > %dx%d or %dx%d)\n", + ctx->crop_width, ctx->crop_height, + max_width, max_height, max_height, max_width); + return -EINVAL; + } + + return 0; +} + +static void __mfc_set_dec_stride(struct mfc_ctx *ctx, + struct mfc_raw_info *raw, + struct mfc_fmt *fmt) +{ + int stride_align, y_stride; + + stride_align =3D ctx->dev->pdata->stride_align; + y_stride =3D ALIGN(ctx->img_width, stride_align); + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + raw->stride[0] =3D y_stride; + raw->stride[1] =3D ALIGN(y_stride >> 1, stride_align); + raw->stride[2] =3D ALIGN(y_stride >> 1, stride_align); + break; + case V4L2_PIX_FMT_NV12MT_16X16: + case V4L2_PIX_FMT_NV12MT: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + raw->stride[0] =3D y_stride; + raw->stride[1] =3D y_stride; + raw->stride[2] =3D 0; + break; + default: + mfc_ctx_err("Invalid pixelformat : %s\n", fmt->name); + break; + } +} + +void mfc_set_linear_stride_size(struct mfc_ctx *ctx, struct mfc_raw_info *= raw, struct mfc_fmt *fmt) +{ + /* + * Decoder: Use stride alignment value defiend in DT. + * (Largest limitation among SoC IPs) + * Encoder: Use the stride value that the user set when s_fmt. + */ + if (ctx->type =3D=3D MFCINST_DECODER) + __mfc_set_dec_stride(ctx, raw, fmt); +} + +void mfc_dec_calc_dpb_size(struct mfc_ctx *ctx, struct mfc_raw_info *raw, = struct mfc_fmt *fmt) +{ + int i; + int extra =3D MFC_LINEAR_BUF_SIZE; + int check_min_dpb_size =3D 1; + + mfc_set_linear_stride_size(ctx, raw, fmt); + + raw->total_plane_size =3D 0; + + for (i =3D 0; i < raw->num_planes; i++) { + raw->plane_size[i] =3D 0; + raw->plane_size_2bits[i] =3D 0; + } + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + raw->plane_size[0] =3D raw->stride[0] * ALIGN(ctx->img_height, 16) + ext= ra; + raw->plane_size[1] =3D raw->stride[1] * ALIGN(ctx->img_height, 16) / 2 += extra; + break; + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + raw->plane_size[0] =3D raw->stride[0] * ALIGN(ctx->img_height, 16) + ext= ra; + raw->plane_size[1] =3D raw->stride[1] * ALIGN(ctx->img_height, 16) / 2 += extra; + raw->plane_size[2] =3D raw->stride[2] * ALIGN(ctx->img_height, 16) / 2 += extra; + break; + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + raw->plane_size[0] =3D raw->stride[0] * ALIGN(ctx->img_height, 16) + ext= ra; + raw->plane_size[1] =3D raw->stride[1] * ALIGN(ctx->img_height, 16) + ext= ra; + break; + default: + mfc_ctx_err("Invalid pixelformat : %s\n", fmt->name); + break; + } + + /* + * The min DPB size returned by firmware may be larger than + * the DPB size calculated by the driver in the following situation. + * - Change 10bit mem_type at INIT_BUF. + * - Use single-fd format without extra bytes. + * In the above case, if the driver forcibly changes the DPB size, + * it fails due to buffer size error at V4L2 Qbuf. + * And when F/W really needs min DPB size in scenario like VP9 interframe= DRC, + * if the driver does not force change the DPB size, + * No.57(INSUFFICIENT_DPB_SIZE) error occurs in F/W. + */ + if (fmt->mem_planes =3D=3D 1) + check_min_dpb_size =3D 0; + + if (check_min_dpb_size) { + for (i =3D 0; i < raw->num_planes; i++) { + if (raw->plane_size[i] < ctx->min_dpb_size[i]) { + mfc_ctx_info("[FRAME] plane[%d] size %d / min size %d\n", + i, raw->plane_size[i], ctx->min_dpb_size[i]); + raw->plane_size[i] =3D ctx->min_dpb_size[i]; + } + } + } + + for (i =3D 0; i < raw->num_planes; i++) { + raw->total_plane_size +=3D raw->plane_size[i]; + mfc_ctx_debug(2, "[FRAME] Plane[%d] size =3D %d, stride =3D %d\n", + i, raw->plane_size[i], raw->stride[i]); + } + mfc_ctx_debug(2, "[FRAME] total plane size: %d\n", raw->total_plane_size); + + if (IS_H264_DEC(ctx)) { + ctx->mv_size =3D DEC_MV_SIZE_MB(ctx->img_width, ctx->img_height); + ctx->mv_size =3D ALIGN(ctx->mv_size, 32); + } else { + ctx->mv_size =3D 0; + } +} + +void mfc_calc_base_addr(struct mfc_ctx *ctx, struct vb2_buffer *vb, + struct mfc_fmt *fmt) +{ + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + int i, idx, max_idx; + + if ((fmt->type & MFC_FMT_FRAME) && ctx->multi_view_enable) + max_idx =3D MFC_MV_BUF_IDX_MAX; + else + max_idx =3D 1; + + for (i =3D 0; i < max_idx; i++) { + /* It means there is no plane in the buffer. */ + if (ctx->view_buf_info[i].num_fd =3D=3D 0) + continue; + + for (idx =3D 0; idx < ctx->view_buf_info[i].num_fd; idx++) + buf->addr[i][idx] =3D mfc_mem_get_daddr_vb + (vb, ctx->view_buf_info[i].offset + idx); + } + + for (i =3D 0; i < fmt->num_planes; i++) + mfc_ctx_debug(2, "[MEMINFO] plane[%d] addr %#llx\n", i, buf->addr[0][i]); +} + +void mfc_set_view_buf_info(struct mfc_ctx *ctx, + int mem_planes, + int num_fd_depth_map, + int num_fd_sub_view_meta) +{ + int offset =3D 0; + + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW0].offset =3D offset; + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW0].num_fd =3D mem_planes; + offset +=3D mem_planes; + + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW0_DEPTH].offset =3D offset; + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW0_DEPTH].num_fd =3D num_fd_depth_ma= p; + offset +=3D num_fd_depth_map; + + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1].offset =3D offset; + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1].num_fd =3D mem_planes; + offset +=3D mem_planes; + + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1_DEPTH].offset =3D offset; + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1_DEPTH].num_fd =3D num_fd_depth_ma= p; + offset +=3D num_fd_depth_map; + + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1_META].offset =3D offset; + ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1_META].num_fd =3D num_fd_sub_view_= meta; + offset +=3D num_fd_sub_view_meta; +} + +void mfc_core_idle_checker(struct timer_list *t) +{ + struct mfc_core *core =3D timer_container_of(core, t, mfc_idle_timer); + struct mfc_dev *dev =3D core->dev; + + mfc_core_debug(5, "[MFCIDLE] MFC HW idle checker is ticking!\n"); + + if (dev->move_ctx_cnt) { + MFC_TRACE_RM("[MFCIDLE] migration working\n"); + mfc_core_idle_checker_start_tick(core); + return; + } + + if (atomic_read(&core->qos_req_cur) =3D=3D 0) { + mfc_core_debug(6, "[MFCIDLE] MFC QoS not started yet\n"); + mfc_core_idle_checker_start_tick(core); + return; + } + + if (core->sched->is_work(core)) { + MFC_TRACE_CORE("[MFCIDLE] there is work to do\n"); + mfc_core_debug(6, "[MFCIDLE] there is work to do\n"); + core->sched->queue_work(core); + mfc_core_idle_checker_start_tick(core); + return; + } + + if (!atomic_read(&core->hw_run_bits) && !atomic_read(&core->dev->queued_b= its)) + mfc_core_change_idle_mode(core, MFC_IDLE_MODE_RUNNING); + +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ + queue_work(core->mfc_idle_wq, &core->mfc_idle_work); +#endif +} diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h index 320dc96a40ed..dedfb049e6fc 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h @@ -12,7 +12,84 @@ #ifndef __MFC_UTILS_H #define __MFC_UTILS_H __FILE__ =20 -#include "mfc_common.h" +#include "mfc_rate_calculate.h" +#include "mfc_format.h" + +/* bit operation */ +#define mfc_clear_bits(reg, mask, shift) ((reg) &=3D ~((mask) << (shift))) +#define mfc_set_bits(reg, mask, shift, value) ((reg) |=3D ((value) & (mask= )) << (shift)) +#define mfc_clear_set_bits(reg, mask, shift, value) \ + do { \ + typeof(shift) s =3D shift; \ + typeof(mask) m =3D mask; \ + (reg) &=3D ~(m << s); \ + (reg) |=3D ((value) & m) << s; \ + } while (0) + +#define mfc_get_upper(x) (((unsigned long)(x) >> 32) & U32_MAX) +#define mfc_get_lower(x) ((x) & U32_MAX) + +#define MFC_FPS(x) ((x) / 1000) + +static inline void mfc_set_bit(int num, struct mfc_bits *data) +{ + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + __set_bit(num, &data->bits); + spin_unlock_irqrestore(&data->lock, flags); +} + +static inline void mfc_clear_bit(int num, struct mfc_bits *data) +{ + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + __clear_bit(num, &data->bits); + spin_unlock_irqrestore(&data->lock, flags); +} + +static inline int mfc_is_all_bits_cleared(struct mfc_bits *data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&data->lock, flags); + ret =3D ((data->bits) =3D=3D 0) ? 1 : 0; + spin_unlock_irqrestore(&data->lock, flags); + return ret; +} + +static inline void mfc_clear_all_bits(struct mfc_bits *data) +{ + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + data->bits =3D 0; + spin_unlock_irqrestore(&data->lock, flags); +} + +static inline unsigned long mfc_get_bits(struct mfc_bits *data) +{ + unsigned long flags; + unsigned long ret; + + spin_lock_irqsave(&data->lock, flags); + ret =3D data->bits; + spin_unlock_irqrestore(&data->lock, flags); + return ret; +} + +static inline void mfc_create_bits(struct mfc_bits *data) +{ + spin_lock_init(&data->lock); + mfc_clear_all_bits(data); +} + +static inline void mfc_delete_bits(struct mfc_bits *data) +{ + mfc_clear_all_bits(data); +} =20 static inline void mfc_core_clean_dev_int_flags(struct mfc_core *core) { @@ -65,6 +142,7 @@ static inline void mfc_core_change_fw_state(struct mfc_c= ore *core, prev_stat, core->fw.status, set ? "set" : "clear", state); mfc_core_debug(2, "[F/W] normal status: %#x -> %#x (%s: %#x)\n", prev_stat, core->fw.status, set ? "set" : "clear", state); + } =20 static inline enum mfc_node_type mfc_get_node_type(struct file *file) @@ -121,6 +199,27 @@ static inline void mfc_clear_mb_flag(struct mfc_buf *m= fc_buf) mfc_buf->flag =3D 0; } =20 +static inline void mfc_set_mb_flag(struct mfc_buf *mfc_buf, enum mfc_mb_fl= ag f) +{ + mfc_buf->flag |=3D BIT(f); +} + +static inline int mfc_check_mb_flag(struct mfc_buf *mfc_buf, enum mfc_mb_f= lag f) +{ + if (mfc_buf->flag & BIT(f)) + return 1; + + return 0; +} + +int mfc_check_vb_with_fmt(struct mfc_fmt *fmt, struct vb2_buffer *vb); +int mfc_check_resolution(struct mfc_ctx *ctx); +void mfc_set_linear_stride_size(struct mfc_ctx *ctx, struct mfc_raw_info *= raw, struct mfc_fmt *fmt); +void mfc_dec_calc_dpb_size(struct mfc_ctx *ctx, struct mfc_raw_info *raw, = struct mfc_fmt *fmt); +void mfc_calc_base_addr(struct mfc_ctx *ctx, struct vb2_buffer *vb, struct= mfc_fmt *fmt); +void mfc_set_view_buf_info(struct mfc_ctx *ctx, int mem_planes, + int num_fd_depth_map, int num_fd_sub_view_meta); + static inline u32 mfc_dec_get_strm_size(struct mfc_ctx *ctx, struct mfc_bu= f *src_mb) { struct vb2_plane *vb_plane; @@ -157,6 +256,41 @@ static inline u32 mfc_dec_get_strm_size(struct mfc_ctx= *ctx, struct mfc_buf *src =20 return strm_size; } + +static inline int mfc_dec_get_strm_offset(struct mfc_ctx *ctx, struct mfc_= buf *src_mb) +{ + struct vb2_plane *vb_plane; + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned int offset; + + vb_plane =3D &src_mb->vb.vb2_buf.planes[0]; + offset =3D vb_plane->data_offset; + if (dec->consumed) + offset +=3D dec->consumed; + + mfc_ctx_debug(2, "[STREAM] offset: %d (bytesused %d, data_offset %d, cons= umed %d)\n", + offset, vb_plane->bytesused, vb_plane->data_offset, dec->consumed); + + return offset; +} + +static inline int mfc_dec_status_decoding(unsigned int dst_frame_status) +{ + if (dst_frame_status =3D=3D MFC_REG_DEC_STATUS_DECODING_DISPLAY || + dst_frame_status =3D=3D MFC_REG_DEC_STATUS_DECODING_ONLY) + return 1; + return 0; +} + +static inline int mfc_dec_status_display(unsigned int dst_frame_status) +{ + if (dst_frame_status =3D=3D MFC_REG_DEC_STATUS_DISPLAY_ONLY || + dst_frame_status =3D=3D MFC_REG_DEC_STATUS_DECODING_DISPLAY) + return 1; + + return 0; +} + /* Meerkat interval */ #define MEERKAT_TICK_INTERVAL 1000 /* After how many executions meerkat should assume lock up */ @@ -190,6 +324,20 @@ static inline void mfc_core_idle_update_hw_run(struct = mfc_core *core, spin_unlock_irqrestore(&core->dev->idle_bits_lock, flags); } =20 +static inline void mfc_idle_update_queued(struct mfc_dev *dev, + struct mfc_ctx *ctx) +{ + unsigned long flags; + int bits; + + spin_lock_irqsave(&dev->idle_bits_lock, flags); + + bits =3D atomic_read(&dev->queued_bits); + atomic_set(&dev->queued_bits, (bits | BIT(ctx->num))); + + spin_unlock_irqrestore(&dev->idle_bits_lock, flags); +} + static inline void mfc_core_change_idle_mode(struct mfc_core *core, enum mfc_idle_mode idle_mode) { @@ -206,4 +354,68 @@ static inline void mfc_ctx_change_idle_mode(struct mfc= _ctx *ctx, MFC_TRACE_CTX("**[c:%d] idle mode : %d\n", ctx->num, idle_mode); ctx->idle_mode =3D idle_mode; } + +static inline void mfc_print_ctx_info(struct mfc_ctx *ctx) +{ + struct mfc_fmt *codec =3D NULL; + struct mfc_fmt *fmt =3D NULL; + + if (ctx->type =3D=3D MFCINST_DECODER) { + codec =3D ctx->src_fmt; + fmt =3D ctx->dst_fmt; + } else { + codec =3D ctx->dst_fmt; + fmt =3D ctx->src_fmt; + } + + if (!codec) + codec =3D &mfc_formats[0]; + if (!fmt) + fmt =3D &mfc_formats[0]; + mfc_ctx_info("- %s%s, %s, %dx%d %lu fps(ts %lu fps, op %lu fps, rt %lu fp= s)", + codec->name, + ctx->multi_view_enable ? "(MV-HEVC)" : "", + fmt->name, + ctx->img_width, ctx->img_height, + MFC_FPS(ctx->framerate), + MFC_FPS(ctx->last_framerate), + MFC_FPS(ctx->operating_framerate), + MFC_FPS(mfc_rate_get_rt_framerate(ctx, ctx->rt))); + mfc_ctx_info("mb %lu(%d%%), main core %d, op_mode %d(stream %d), rt %d\n", + ctx->weighted_mb, ctx->load, + ctx->op_core_num[MFC_CORE_MAIN], + ctx->op_mode, ctx->stream_op_mode, ctx->rt); +} + +static inline void mfc_show_ctx_info(struct mfc_ctx *ctx) +{ + struct mfc_fmt *codec =3D NULL; + struct mfc_fmt *fmt =3D NULL; + + if (ctx->type =3D=3D MFCINST_DECODER) { + codec =3D ctx->src_fmt; + fmt =3D ctx->dst_fmt; + } else { + codec =3D ctx->dst_fmt; + fmt =3D ctx->src_fmt; + } + + if (!codec) + codec =3D &mfc_formats[0]; + if (!fmt) + fmt =3D &mfc_formats[0]; + + mfc_ctx_debug(3, "- %s, %s, %dx%d %lu fps(ts %lu fps, op %lu fps, rt %lu = fps)", + codec->name, + fmt->name, + ctx->img_width, ctx->img_height, + MFC_FPS(ctx->framerate), + MFC_FPS(ctx->last_framerate), + MFC_FPS(ctx->operating_framerate), + MFC_FPS(mfc_rate_get_rt_framerate(ctx, ctx->rt))); + mfc_ctx_debug(3, "mb %lu(%d%%), main core %d, op_mode %d(stream %d), rt %= d\n", + ctx->weighted_mb, ctx->load, + ctx->op_core_num[MFC_CORE_MAIN], + ctx->op_mode, ctx->stream_op_mode, ctx->rt); +} #endif /* __MFC_UTILS_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) (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 6F79F26FDA6 for ; Tue, 30 Sep 2025 03:56:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204578; cv=none; b=FuKAiHWSNoKCiHNd912qdvR9CZhzpYTMQlXXPLWr2Pp95fK60qKCXpHpSfvdMMWOoCExYpbziZyODRqXrJgIb0OccNTa2D6cFiXmm8s4byBwkThfUU60PHlRgSqf8Uh3LvI8rvxHdnoARkDiKreiGs0TQ19yqFZ7DbqkeDrMlQ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204578; c=relaxed/simple; bh=GIFTNMtlQEz+tZWuD9MEnVZneCfILaDaJSdvP7ksZ8o=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=Kms2E2QhV3ZVcU8EwXySwEO4htv0qhAuCOVP3pKXInMIWQ/xoRow8yIuUzl4tg4Ndbzj7GYhyN3tSJ7bNLDxEHZUN0rouSTaIb/ZIXcd74IWoKZ5TbsVyQehLy3dXd0vdJUM3iShpYevvwAuY+QgZ5qu/1Z2QPr/BC+0MJSbsxg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=nLz0+MWj; arc=none smtp.client-ip=203.254.224.24 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="nLz0+MWj" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250930035612epoutp01251a54d0160329b8779e068adba6857e~p8z0JpeTv3202032020epoutp01Q for ; Tue, 30 Sep 2025 03:56:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250930035612epoutp01251a54d0160329b8779e068adba6857e~p8z0JpeTv3202032020epoutp01Q DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204572; bh=k5Q0yXYyvjizI5MAYaxFlS+olIR1FX8bEG0KWGhdOAA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nLz0+MWjceufWaCbJDYDIsVvVMBlJfcXGvrvX/GyXbdKjZczrOjN4fgjNPSE4whfz UXqWLr4oiM595ru8i11pTRWsbntr81MugoM3i10ArkjVqot1SJkERgvN9OnQONQ4qH b8YG5HryqxGW67bakqoEN9h/mG08eaDLQpQ2jX2s= Received: from epsnrtp03.localdomain (unknown [182.195.42.155]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPS id 20250930035612epcas5p395f3424e6367daa128cb17808d991aca~p8zzm0-q20522705227epcas5p3W; Tue, 30 Sep 2025 03:56:12 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.87]) by epsnrtp03.localdomain (Postfix) with ESMTP id 4cbPP71m43z3hhTJ; Tue, 30 Sep 2025 03:56:11 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035610epcas5p3c64bccc78b92b0bb907a73615103ebd3~p8zxyLcX01490114901epcas5p3F; Tue, 30 Sep 2025 03:56:10 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035606epsmtip1933618e1ab88f050fecc7e60fc752e3c~p8zuGQuZe2938429384epsmtip14; Tue, 30 Sep 2025 03:56:06 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 12/29] media: mfc: Introduce QoS support and instance context handling Date: Tue, 30 Sep 2025 09:33:31 +0530 Message-Id: <20250930040348.3702923-13-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035610epcas5p3c64bccc78b92b0bb907a73615103ebd3 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035610epcas5p3c64bccc78b92b0bb907a73615103ebd3 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Implement QoS support (load estimation, dynamic level adjustment, PM=E2=80=91QoS & idle=E2=80=91mode handling) - Add mfc_qos.c/h and integrate it into the Makefile - Introduce instance=E2=80=91context allocation/release APIs (mfc_alloc_instance_context, mfc_release_instance_context) - Update driver code to use the new APIs and add debugging/trace hooks - Provide documentation/comments for the new functionality Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 1 + .../samsung/exynos-mfc/base/mfc_buf.c | 207 ++++ .../samsung/exynos-mfc/base/mfc_buf.h | 8 + .../samsung/exynos-mfc/base/mfc_qos.c | 965 ++++++++++++++++++ .../samsung/exynos-mfc/base/mfc_qos.h | 99 ++ 5 files changed, 1280 insertions(+) create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index bd5f80953bab..9def2686cd4e 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -21,5 +21,6 @@ exynos_mfc-y +=3D mfc_core_hw_reg_api.o mfc_core_reg_api.o #Common base layer exynos_mfc-y +=3D base/mfc_rate_calculate.o base/mfc_queue.o base/mfc_util= s.o exynos_mfc-y +=3D base/mfc_buf.o base/mfc_mem.o +exynos_mfc-y +=3D base/mfc_qos.o #Tracing # exynos_mfc-y +=3D trace/mfc_trace_points.o diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.c index b8b140824aab..bd1baf34e0b0 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c @@ -88,6 +88,213 @@ int mfc_alloc_common_context(struct mfc_core *core) return ret; } =20 +/* Release instance buffer */ +void mfc_release_instance_context(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + + mfc_debug_enter(); + + mfc_iova_pool_free(core->dev, &core_ctx->instance_ctx_buf); + + mfc_mem_special_buf_free(core->dev, &core_ctx->instance_ctx_buf); + + mfc_debug_leave(); +} + +/* Allocate memory for instance data buffer */ +int mfc_alloc_instance_context(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_ctx_buf_size *buf_size; + + mfc_debug_enter(); + + buf_size =3D dev->variant->buf_size->ctx_buf; + + switch (ctx->codec_mode) { + case MFC_REG_CODEC_H264_DEC: + case MFC_REG_CODEC_H264_MVC_DEC: + core_ctx->instance_ctx_buf.size =3D buf_size->h264_dec_ctx; + break; + default: + core_ctx->instance_ctx_buf.size =3D 0; + mfc_err("Codec type(%d) should be checked!\n", ctx->codec_mode); + return -ENOMEM; + } + + core_ctx->instance_ctx_buf.buftype =3D MFCBUF_NORMAL; + + snprintf(core_ctx->instance_ctx_buf.name, + MFC_NUM_SPECIAL_BUF_NAME, + "MFC%d ctx%d instance", + core_ctx->core->id, + core_ctx->num); + if (mfc_mem_special_buf_alloc(dev, &core_ctx->instance_ctx_buf)) { + mfc_err("Allocating context buffer failed\n"); + return -ENOMEM; + } + + if (mfc_iova_pool_alloc(dev, &core_ctx->instance_ctx_buf)) { + mfc_err("[POOL] failed to get iova\n"); + mfc_release_instance_context(core_ctx); + return -ENOMEM; + } + + mfc_debug_leave(); + + return 0; +} + +static void __mfc_dec_calc_codec_buffer_size(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + + /* Codecs have different memory requirements */ + switch (ctx->codec_mode) { + case MFC_REG_CODEC_H264_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D ctx->scratch_buf_size; + ctx->mv_buf.size =3D dec->mv_count * ctx->mv_size; + break; + default: + core_ctx->codec_buf.size =3D 0; + mfc_err("invalid codec type: %d\n", ctx->codec_mode); + break; + } + + mfc_debug(2, + "[MEMINFO] scratch: %zu, MV: %zu x count %d\n", + ctx->scratch_buf_size, + ctx->mv_size, + dec->mv_count); + if (dec->loop_filter_mpeg4) + mfc_debug(2, + "[MEMINFO] (loopfilter luma: %zu, chroma: %zu) x count %d\n", + ctx->loopfilter_luma_size, + ctx->loopfilter_chroma_size, + NUM_MPEG4_LF_BUF); +} + +/* Allocate codec buffers */ +int mfc_alloc_codec_buffers(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_dev *dev =3D core->dev; + struct mfc_ctx *ctx =3D core_ctx->ctx; + + mfc_debug_enter(); + + if (ctx->type =3D=3D MFCINST_DECODER) { + __mfc_dec_calc_codec_buffer_size(core_ctx); + } else { + mfc_err("invalid type: %d\n", ctx->type); + return -EINVAL; + } + + core_ctx->codec_buf.buftype =3D MFCBUF_NORMAL; + ctx->mv_buf.buftype =3D MFCBUF_NORMAL; + + if (core_ctx->codec_buf.size > 0) { + snprintf(core_ctx->codec_buf.name, + MFC_NUM_SPECIAL_BUF_NAME, + "MFC%d ctx%d codec", + core->id, + core_ctx->num); + if (mfc_mem_special_buf_alloc(dev, &core_ctx->codec_buf)) { + mfc_err("Allocating codec buffer failed\n"); + return -ENOMEM; + } + core_ctx->codec_buffer_allocated =3D 1; + } else if (ctx->codec_mode =3D=3D MFC_REG_CODEC_MPEG2_DEC) { + core_ctx->codec_buffer_allocated =3D 1; + } + + if (!ctx->mv_buffer_allocated && ctx->mv_buf.size > 0) { + snprintf(ctx->mv_buf.name, + MFC_NUM_SPECIAL_BUF_NAME, + "MFC%d ctx%d MV", + core->id, + ctx->num); + if (mfc_mem_special_buf_alloc(dev, &ctx->mv_buf)) { + mfc_err("Allocating MV buffer failed\n"); + return -ENOMEM; + } + ctx->mv_buffer_allocated =3D 1; + } + + mfc_debug_leave(); + + return 0; +} + +/* Release buffers allocated for codec */ +void mfc_release_codec_buffers(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + + if (core_ctx->codec_buffer_allocated) { + mfc_mem_special_buf_free(ctx->dev, &core_ctx->codec_buf); + core_ctx->codec_buffer_allocated =3D 0; + } + + if (ctx->mv_buffer_allocated) { + mfc_mem_special_buf_free(ctx->dev, &ctx->mv_buf); + ctx->mv_buffer_allocated =3D 0; + } + + mfc_release_scratch_buffer(core_ctx); +} + +int mfc_alloc_scratch_buffer(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_dev *dev =3D core->dev; + struct mfc_ctx *ctx =3D core_ctx->ctx; + + mfc_debug_enter(); + + if (core_ctx->scratch_buffer_allocated) { + mfc_mem_special_buf_free(dev, &core_ctx->scratch_buf); + core_ctx->scratch_buffer_allocated =3D 0; + mfc_debug(2, "[MEMINFO] Release the scratch buffer ctx[%d]\n", core_ctx-= >num); + } + + core_ctx->scratch_buf.buftype =3D MFCBUF_NORMAL; + + core_ctx->scratch_buf.size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + if (core_ctx->scratch_buf.size > 0) { + snprintf(core_ctx->scratch_buf.name, + MFC_NUM_SPECIAL_BUF_NAME, + "MFC%d ctx%d scratch", + core->id, + core_ctx->num); + if (mfc_mem_special_buf_alloc(dev, &core_ctx->scratch_buf)) { + mfc_err("Allocating scratch_buf buffer failed\n"); + return -ENOMEM; + } + core_ctx->scratch_buffer_allocated =3D 1; + } + + mfc_debug_leave(); + + return 0; +} + +void mfc_release_scratch_buffer(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + + mfc_debug_enter(); + if (core_ctx->scratch_buffer_allocated) { + mfc_mem_special_buf_free(ctx->dev, &core_ctx->scratch_buf); + core_ctx->scratch_buffer_allocated =3D 0; + } + mfc_debug_leave(); +} + /* Allocation buffer of debug infor memory for FW debugging */ int mfc_alloc_dbg_info_buffer(struct mfc_core *core) { diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.h index 8291e043b81a..6907cf6ac775 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h @@ -18,6 +18,14 @@ void mfc_release_common_context(struct mfc_core *core); int mfc_alloc_common_context(struct mfc_core *core); =20 +void mfc_release_instance_context(struct mfc_core_ctx *core_ctx); +int mfc_alloc_instance_context(struct mfc_core_ctx *core_ctx); + +int mfc_alloc_codec_buffers(struct mfc_core_ctx *core_ctx); +void mfc_release_codec_buffers(struct mfc_core_ctx *core_ctx); +int mfc_alloc_scratch_buffer(struct mfc_core_ctx *core_ctx); +void mfc_release_scratch_buffer(struct mfc_core_ctx *core_ctx); + int mfc_alloc_firmware(struct mfc_core *core); int mfc_load_firmware(struct mfc_core *core, struct mfc_special_buf *fw_buf, diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_qos.c new file mode 100644 index 000000000000..f6548543f07c --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c @@ -0,0 +1,965 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_qos.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ +#include +#endif + +#include "mfc_qos.h" +#include "mfc_utils.h" +#include "mfc_queue.h" + +static inline int __mfc_core_get_qos_steps(struct mfc_core *core, int tabl= e_type) +{ + return core->core_pdata->num_default_qos_steps; +} + +static inline struct mfc_qos *__mfc_core_get_qos_table(struct mfc_core *co= re, int table_type) +{ + return core->core_pdata->default_qos_table; +} + +static inline unsigned long __mfc_qos_add_weight(struct mfc_ctx *ctx, unsi= gned long mb) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_qos_weight *qos_weight =3D &ctx->dev->pdata->qos_weight; + u32 num_planes =3D ctx->dst_fmt->num_planes; + int weight =3D 1000; + unsigned long weighted_mb; + + switch (ctx->codec_mode) { + case MFC_REG_CODEC_H264_DEC: + weight =3D (weight * 100) / qos_weight->weight_h264_hevc; + mfc_ctx_debug(3, "[QoS] h264, hevc codec, weight: %d\n", weight / 10); + if (num_planes =3D=3D 3) { + weight =3D (weight * 100) / qos_weight->weight_3plane; + mfc_ctx_debug(3, "[QoS] 3 plane, weight: %d\n", weight / 10); + } + break; + default: + mfc_ctx_err("[QoS] wrong codec_mode (%d), no weight\n", ctx->codec_mode); + } + + if (dec) { + if (dec->num_of_tile_over_4) { + weight =3D (weight * 100) / qos_weight->weight_num_of_tile; + mfc_ctx_debug(3, "[QoS] num of tile >=3D 4, weight: %d\n", weight / 10); + } + if (dec->is_mbaff) { + weight =3D (weight * 100) / qos_weight->weight_mbaff; + mfc_ctx_debug(3, "[QoS] MBAFF, weight: %d\n", weight / 10); + } + } + + weighted_mb =3D (mb * weight) / 1000; + mfc_ctx_debug(3, "%s %d, %s %d, %s %d, 422format: %d (mb: %ld)\n", + "[QoS] weight:", weight / 10, + "codec:", ctx->codec_mode, + "num planes:", num_planes, + ctx->is_422, + weighted_mb); + + return weighted_mb; +} + +void mfc_qos_get_weighted_mb(struct mfc_ctx *ctx, enum mfc_real_time rt) +{ + unsigned long mb; + unsigned int max_mb =3D ctx->dev->core[MFC_DEC_DEFAULT_CORE]->core_pdata-= >max_mb; + + ctx->mb_width =3D WIDTH_MB(ctx->img_width); + ctx->mb_height =3D HEIGHT_MB(ctx->img_height); + mb =3D ctx->mb_width * ctx->mb_height * (mfc_rate_get_rt_framerate(ctx, r= t) / 1000); + + /* Instance individual load regardless of operating in the multi core */ + ctx->weighted_mb =3D __mfc_qos_add_weight(ctx, mb); + ctx->load =3D ctx->weighted_mb * 100 / max_mb; + + mfc_ctx_debug(3, "[QoS] weighted_mb: %ld(load: %u)\n", + ctx->weighted_mb, ctx->load); +} + +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ +enum { + MFC_QOS_ADD =3D 0, + MFC_QOS_UPDATE, + MFC_QOS_REMOVE, + MFC_QOS_BW, +}; + +enum { + MFC_PERF_BOOST_DVFS =3D BIT(0), + MFC_PERF_BOOST_MO =3D BIT(1), + MFC_PERF_BOOST_CPU =3D BIT(2), +}; + +void __mfc_qos_cpu_boost_enable(struct mfc_core *core) +{ + struct mfc_core_platdata *pdata =3D core->core_pdata; + struct mfc_qos_boost *qos_boost_table =3D pdata->qos_boost_table; + struct cpufreq_policy *policy; + int i; + + for (i =3D 0; i < qos_boost_table->num_cluster; i++) { + policy =3D cpufreq_cpu_get(qos_boost_table->num_cpu[i]); + if (policy) { + freq_qos_tracer_add_request(&policy->constraints, + &core->qos_req_cluster[i], FREQ_QOS_MIN, + qos_boost_table->freq_cluster[i]); + mfc_core_debug(2, "[QoS][BOOST] CPU cluster[%d]: %d\n", + i, qos_boost_table->freq_cluster[i]); + } + } + + core->cpu_boost_enable =3D 1; +} + +void __mfc_qos_cpu_boost_disable(struct mfc_core *core) +{ + struct mfc_core_platdata *pdata =3D core->core_pdata; + struct mfc_qos_boost *qos_boost_table =3D pdata->qos_boost_table; + int i; + + for (i =3D 0; i < qos_boost_table->num_cluster; i++) { + freq_qos_tracer_remove_request(&core->qos_req_cluster[i]); + mfc_core_debug(2, "[QoS][BOOST] CPU cluster[%d] off\n", i); + } + + core->cpu_boost_enable =3D 0; +} + +void mfc_qos_set_portion(struct mfc_core *core, struct mfc_ctx *ctx) +{ + int idx; + + /* + * When only it is single instance, + * there is an exact meaning in the qos portion. + */ + if (!ctx->mfc_qos_portion || core->num_inst > 1) + return; + + idx =3D atomic_read(&core->qos_req_cur) - 1; + if (idx =3D=3D -1) + return; + + ctx->mfc_qos_portion[idx]++; +} + +void mfc_qos_get_portion(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_qos *qos_table; + int num_qos_steps; + int i, sum =3D 0; + int table_type; + + if (!ctx->mfc_qos_portion) + return; + + table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; + + num_qos_steps =3D __mfc_core_get_qos_steps(core, table_type); + qos_table =3D __mfc_core_get_qos_table(core, table_type); + + for (i =3D 0; i < num_qos_steps; i++) { + sum +=3D ctx->mfc_qos_portion[i]; + mfc_ctx_debug(2, "[QoS][portion] lv%d: %d frame, %d%% (type: %d, mfc: %d= , int: %d, mif: %d, mo: %s)\n", + i, ctx->mfc_qos_portion[i], + ctx->mfc_qos_portion[i] * 100 / ctx->frame_cnt, + core->last_table_type, + qos_table[i].freq_mfc, qos_table[i].freq_int, + qos_table[i].freq_mif, qos_table[i].name); + } + mfc_ctx_debug(2, "[QoS][portion] total %d frame (recorded %d)\n", + ctx->frame_cnt, sum); +} + +bool mfc_qos_mb_calculate(struct mfc_core *core, struct mfc_core_ctx *core= _ctx, + unsigned int processing_cycle, unsigned int frame_type) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct list_head *head =3D &core_ctx->mb_list; + struct mfc_mb_control *temp_mb; + struct mfc_mb_control *new_mb; + unsigned int avg_fps, need_fps, total_fps =3D 0; + unsigned long frame_time, drv_time, hw_mb, need_mb, avg_mb, margin_mb, to= tal_mb =3D 0; + int table_type, num_qos_steps, cur_qos, count =3D 0, level_num; + bool update =3D false; + + if (!core->dev->pdata->dynamic_weight || + (core->dev->debugfs.feature_option & MFC_OPTION_USE_FIXED_WEIGHT)) + return update; + + if (ctx->frame_cnt < (MFC_MIN_FPS / 1000) || + ctx->framerate > MFC_MAX_FPS || + core->dev->num_inst > 1) { + core_ctx->dynamic_weight_level =3D 0; + core_ctx->dynamic_weight_started =3D 0; + return update; + } + + if (frame_type =3D=3D 7) { + mfc_debug(4, "[QoS] Empty decoding\n"); + return update; + } + + mutex_lock(&core->qos_mutex); + + if (!core_ctx->dynamic_weight_started) { + mfc_debug(4, "[QoS] Clear MB list\n"); + + while (!list_empty(head)) { + temp_mb =3D list_entry(head->next, struct mfc_mb_control, list); + list_del(&temp_mb->list); + } + + core_ctx->mb_index =3D 0; + core_ctx->mb_is_full =3D 0; + core_ctx->mb_not_coded_time =3D 0; + core_ctx->dynamic_weight_level =3D 0; + core_ctx->dynamic_weight_started =3D 1; + core_ctx->mb_update_time =3D MFC_MAX_MB_TABLE; + } + + new_mb =3D &core_ctx->mb_table[core_ctx->mb_index]; + + /* setup macroblock table list */ + if (core_ctx->mb_is_full && !core_ctx->mb_not_coded_time) { + temp_mb =3D list_entry(head->next, struct mfc_mb_control, list); + list_del(&temp_mb->list); + } + + hw_mb =3D ((ctx->crop_width + 15) / 16) * ((ctx->crop_height + 15) / 16); + + if (IS_MULTI_STREAM(ctx)) + drv_time =3D MFC_2CORE_DRV_TIME; + else if (ctx->type =3D=3D MFCINST_DECODER) + drv_time =3D MFC_DRV_TIME; + else + drv_time =3D 0; + + frame_time =3D processing_cycle / (core->last_mfc_freq / 1000) + drv_time; + if (core_ctx->mb_not_coded_time) { + mfc_debug(4, "[QoS] Add not coded time. %lu + %lu\n", + frame_time, core_ctx->mb_not_coded_time); + frame_time +=3D core_ctx->mb_not_coded_time; + core_ctx->mb_not_coded_time =3D 0; + } else { + list_add_tail(&new_mb->list, head); + } + + if (frame_type =3D=3D 0) { + mfc_debug(4, "[QoS] Not coded frame type. it accumulated to next frame\n= "); + if (frame_time) + core_ctx->mb_not_coded_time =3D frame_time; + else + core_ctx->mb_not_coded_time =3D 1; + goto qos_end; + } + + if (frame_time) { + new_mb->mb_per_sec =3D (USEC_PER_SEC * hw_mb) / frame_time; + new_mb->fps =3D 1000000 / frame_time; + } else { + new_mb->mb_per_sec =3D 0; + new_mb->fps =3D 0; + } + + mfc_debug(4, "[QoS] hw_mb: %ld, cycle: %d, t: %ld, mb: %ld, fps: %d, freq= : %d\n", + hw_mb, processing_cycle, frame_time, new_mb->mb_per_sec, + new_mb->fps, core->last_mfc_freq); + + mfc_debug(4, "[QoS] -------------- mb_table (MFC: %dKHz)\n", core->last_m= fc_freq); + list_for_each_entry(temp_mb, head, list) { + mfc_debug(4, "[QoS][%d] %lu MB/sec, %u fps\n", + count, temp_mb->mb_per_sec, temp_mb->fps); + total_mb +=3D temp_mb->mb_per_sec; + total_fps +=3D temp_mb->fps; + count++; + } + + if (count =3D=3D 0) { + mfc_err("[QoS] There is no list for MB\n"); + goto qos_end; + } + + core_ctx->mb_index++; + if (core_ctx->mb_index =3D=3D MFC_MAX_MB_TABLE) { + core_ctx->mb_is_full =3D 1; + core_ctx->mb_index =3D 0; + } + + /* Skip additional updates until the changed QoS is reflected */ + if (core_ctx->mb_update_time) + core_ctx->mb_update_time--; + + /* Calculate macroblock average */ + if (ctx->disp_ratio) + need_fps =3D ((ctx->framerate / 1000) * ctx->disp_ratio) / 100; + else + need_fps =3D ctx->framerate / 1000; + if (IS_TWO_MODE2(ctx)) + need_fps =3D need_fps / core->dev->num_core; + need_mb =3D hw_mb * need_fps; + avg_mb =3D total_mb / count; + avg_fps =3D total_fps / count; + core_ctx->avg_runtime =3D avg_fps ? (USEC_PER_SEC / avg_fps) : 0; + + mfc_debug(2, "[QoS] MB/sec op: %lu, need: %lu, cur: %lu, fps op: %lu, nee= d: %u cur: %u\n", + hw_mb * ctx->framerate / 1000, need_mb, avg_mb, + ctx->framerate / 1000, need_fps, avg_fps); + + /* Calculate dynamic macroblock weight, it can be minus value */ + if (ctx->type =3D=3D MFCINST_DECODER) + margin_mb =3D MFC_DEC_MB_PER_TABLE; + else + margin_mb =3D MFC_ENC_MB_PER_TABLE; + + if (need_mb < margin_mb) + margin_mb =3D need_mb / 2; + else if ((need_mb > core->core_pdata->max_mb) || + (IS_MULTI_STREAM(ctx) && IS_MFC_HEAVY_PERF(ctx, need_fps))) + margin_mb =3D margin_mb * core->dev->num_core; + + if (ctx->type =3D=3D MFCINST_DECODER) + table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; + + num_qos_steps =3D __mfc_core_get_qos_steps(core, table_type); + + if (atomic_read(&core->qos_req_cur)) { + cur_qos =3D atomic_read(&core->qos_req_cur); + if (ctx->type =3D=3D MFCINST_DECODER && + new_mb->mb_per_sec < need_mb && + cur_qos < num_qos_steps) { + level_num =3D ((need_mb - new_mb->mb_per_sec) + MFC_DEC_MB_PER_TABLE - = 1) / + MFC_DEC_MB_PER_TABLE; + if (cur_qos + level_num <=3D num_qos_steps) + core_ctx->dynamic_weight_level =3D + core_ctx->dynamic_weight_level + level_num; + else + core_ctx->dynamic_weight_level =3D num_qos_steps - cur_qos; + mfc_debug(2, "[QoS] dec per frame perf is insufficient (weight level %d= )\n", + core_ctx->dynamic_weight_level); + update =3D true; + } else if ((avg_mb <=3D need_mb) && + (cur_qos < num_qos_steps) && + !core_ctx->mb_update_time) { + core_ctx->dynamic_weight_level++; + mfc_debug(2, "[QoS] avg perf is insufficient (weight level %d)\n", + core_ctx->dynamic_weight_level); + update =3D true; + } else if ((avg_mb > need_mb + margin_mb) && + (cur_qos > 1) && + !core_ctx->mb_update_time) { + core_ctx->dynamic_weight_level--; + mfc_debug(2, "[QoS] perf is enough (weight level %d)\n", + core_ctx->dynamic_weight_level); + update =3D true; + } else if (!core_ctx->mb_update_time) { + mfc_debug(2, "[QoS] perf is suitable\n"); + } + } + + if (update) { + while (!list_empty(head)) { + temp_mb =3D list_entry(head->next, struct mfc_mb_control, list); + list_del(&temp_mb->list); + } + + core_ctx->mb_index =3D 0; + core_ctx->mb_is_full =3D 0; + core_ctx->mb_update_time =3D MFC_MAX_MB_TABLE; + + mfc_debug(2, "[QoS] dynamic weight level: %d\n", core_ctx->dynamic_weigh= t_level); + } + +qos_end: + mutex_unlock(&core->qos_mutex); + + return update; +} + +static void __mfc_qos_operate(struct mfc_core *core, int opr_type, int tab= le_type, int idx) +{ + struct mfc_core_platdata *pdata =3D core->core_pdata; + struct mfc_qos *qos_table; + int freq_mfc; + + qos_table =3D __mfc_core_get_qos_table(core, table_type); + /* When removing QoS, do not update because the table_type is not accurat= e. */ + if (opr_type !=3D MFC_QOS_REMOVE) + core->last_table_type =3D table_type; + + if (core->mfc_freq_by_bps > qos_table[idx].freq_mfc) + freq_mfc =3D core->mfc_freq_by_bps; + else + freq_mfc =3D qos_table[idx].freq_mfc; + + switch (opr_type) { + case MFC_QOS_ADD: + core->last_mfc_freq =3D freq_mfc; + if (pdata->mfc_freq_control) + exynos_pm_qos_add_request(&core->qos_req_mfc, pdata->pm_qos_id, + freq_mfc); + exynos_pm_qos_add_request(&core->qos_req_int, PM_QOS_DEVICE_THROUGHPUT, + qos_table[idx].freq_int); + exynos_pm_qos_add_request(&core->qos_req_mif, PM_QOS_BUS_THROUGHPUT, + qos_table[idx].freq_mif); + + atomic_set(&core->qos_req_cur, idx + 1); + mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", atomic_read(&core->qos_req_= cur) - 1); + MFC_TRACE_CORE("QoS add[%d] - mfc:%d(%s), int:%d, mif:%d\n", + idx, freq_mfc, pdata->mfc_freq_control ? "used" : "un-used", + qos_table[idx].freq_int, qos_table[idx].freq_mif); + mfc_core_debug(2, "[QoS] QoS add[%d] - mfc:%d(%s), int:%d, mif:%d\n", + idx, freq_mfc, pdata->mfc_freq_control ? "used" : "un-used", + qos_table[idx].freq_int, qos_table[idx].freq_mif); + break; + case MFC_QOS_UPDATE: + core->last_mfc_freq =3D freq_mfc; + if (pdata->mfc_freq_control) + exynos_pm_qos_update_request(&core->qos_req_mfc, freq_mfc); + exynos_pm_qos_update_request(&core->qos_req_int, qos_table[idx].freq_int= ); + exynos_pm_qos_update_request(&core->qos_req_mif, qos_table[idx].freq_mif= ); + + atomic_set(&core->qos_req_cur, idx + 1); + mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", atomic_read(&core->qos_req_= cur) - 1); + MFC_TRACE_CORE("QoS update[%d] - mfc:%d(%s), int:%d, mif:%d\n", + idx, freq_mfc, pdata->mfc_freq_control ? "used" : "un-used", + qos_table[idx].freq_int, qos_table[idx].freq_mif); + mfc_core_debug(2, "[QoS] QoS update[%d] - mfc:%d(%s), int:%d, mif:%d\n", + idx, freq_mfc, pdata->mfc_freq_control ? "used" : "un-used", + qos_table[idx].freq_int, qos_table[idx].freq_mif); + break; + case MFC_QOS_REMOVE: + core->last_mfc_freq =3D 0; + if (atomic_read(&core->qos_req_cur) =3D=3D 0) { + MFC_TRACE_CORE("QoS already removed\n"); + mfc_core_debug(2, "[QoS] QoS already removed\n"); + break; + } + + mutex_lock(&core->pm_qos_mutex); + if (pdata->mfc_freq_control) + exynos_pm_qos_remove_request(&core->qos_req_mfc); + exynos_pm_qos_remove_request(&core->qos_req_int); + exynos_pm_qos_remove_request(&core->qos_req_mif); + + atomic_set(&core->qos_req_cur, 0); + mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", atomic_read(&core->qos_req_= cur) - 1); + MFC_TRACE_CORE("QoS remove\n"); + mfc_core_debug(2, "[QoS] QoS remove\n"); + mutex_unlock(&core->pm_qos_mutex); + break; + case MFC_QOS_BW: + break; + default: + mfc_core_err("[QoS] Unknown request for opr [%d]\n", opr_type); + break; + } +} + +static void __mfc_qos_set(struct mfc_core *core, struct mfc_ctx *ctx, + int table_type, int i) +{ + struct mfc_core_platdata *pdata =3D core->core_pdata; + struct mfc_qos *qos_table; + int num_qos_steps; + int freq_mfc; + + num_qos_steps =3D __mfc_core_get_qos_steps(core, table_type); + qos_table =3D __mfc_core_get_qos_table(core, table_type); + + mfc_ctx_debug(2, "[QoS] %s table[%d] covered mb %d ~ %d (mfc: %d, int:%d,= mif:%d)\n", + table_type ? "enc" : "default", i, qos_table[i].threshold_mb, + i =3D=3D num_qos_steps - 1 ? pdata->max_mb : qos_table[i + 1].thre= shold_mb, + qos_table[i].freq_mfc, qos_table[i].freq_int, + qos_table[i].freq_mif); + + mfc_core_debug(3, "[QoS] qos_req_cur: %d\n", atomic_read(&core->qos_req_c= ur) - 1); + if (atomic_read(&core->qos_req_cur) =3D=3D 0) { + __mfc_qos_operate(core, MFC_QOS_ADD, table_type, i); + } else { + /* + * 1) QoS level is changed + * 2) MFC freq should be high regardless of QoS level + */ + if (atomic_read(&core->qos_req_cur) !=3D (i + 1)) { + __mfc_qos_operate(core, MFC_QOS_UPDATE, table_type, i); + } else { + if (core->mfc_freq_by_bps > qos_table[i].freq_mfc) + freq_mfc =3D core->mfc_freq_by_bps; + else + freq_mfc =3D qos_table[i].freq_mfc; + if (freq_mfc !=3D core->last_mfc_freq) { + mfc_ctx_debug(2, "[QoS] mfc freq changed (last: %d, by bps: %d, QoS ta= ble: %d)\n", + core->last_mfc_freq, + core->mfc_freq_by_bps, + qos_table[i].freq_mfc); + __mfc_qos_operate(core, MFC_QOS_UPDATE, table_type, i); + } + } + } +} + +static inline unsigned long __mfc_qos_get_mb_per_second(struct mfc_core *c= ore, + struct mfc_core_ctx *core_ctx, + unsigned int max_mb) +{ + struct mfc_dev *dev =3D core->dev; + struct mfc_ctx *ctx =3D core_ctx->ctx; + unsigned long mb_width, mb_height, fps, frame_mb, mb, qos_weighted_mb; + + mb_width =3D (ctx->crop_width + 15) / 16; + mb_height =3D (ctx->crop_height + 15) / 16; + frame_mb =3D mb_width * mb_height; + if (IS_MULTI_MODE(ctx)) + fps =3D ctx->framerate / 1000 / dev->num_core; + else + fps =3D ctx->framerate / 1000; + + /* If decoder resolution is larger than HD and smaller than FHD, apply FH= D for perf */ + if (ctx->type =3D=3D MFCINST_DECODER && frame_mb > MFC_HD_RES_MB && + frame_mb < MFC_FHD_RES_MB) { + mfc_debug(3, "[QoS] frame MB size is changed %lu -> %d (%dx%d)\n", + frame_mb, MFC_FHD_RES_MB, + ctx->crop_width, ctx->crop_height); + frame_mb =3D MFC_FHD_RES_MB; + } + + mb =3D frame_mb * fps; + qos_weighted_mb =3D __mfc_qos_add_weight(ctx, mb); + + mfc_debug(3, "[QoS] ctx[%d:%s] %d x %d @ %ld fps (mb: %ld), %dkbps\n", + ctx->num, ctx->type =3D=3D MFCINST_ENCODER ? "ENC" : "DEC", + ctx->crop_width, ctx->crop_height, fps, mb, ctx->kbps); + + if (ctx->update_framerate) { + core_ctx->dynamic_weight_level =3D 0; + core_ctx->dynamic_weight_started =3D 0; + mfc_debug(4, "[QoS] clear dynamic weight, update_framerate: %d\n", + ctx->update_framerate); + } + + mfc_debug(4, "[QoS] weight (hw_mb: %lu)\n", qos_weighted_mb); + return qos_weighted_mb; +} + +void __mfc_qos_calculate(struct mfc_core *core, struct mfc_ctx *ctx, int d= elete) +{ + struct mfc_core_platdata *pdata =3D core->core_pdata; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_qos *qos_table; + struct mfc_ctx *qos_ctx; + struct mfc_core_ctx *qos_core_ctx; + unsigned long hw_mb =3D 0, total_mb =3D 0, total_fps =3D 0; + int total_bps =3D 0, mfc_freq_idx; + unsigned int fw_time, sw_time; + int i, qos_level, found =3D 0, dec_found =3D 0, heif_found =3D 0; + int table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT, num_qos_steps; + + /* get the hw macroblock */ + list_for_each_entry(qos_core_ctx, &core->qos_queue, qos_list) { + if (delete && qos_core_ctx =3D=3D core->core_ctx[ctx->num]) { + found =3D 1; + continue; + } + + qos_ctx =3D qos_core_ctx->ctx; + if (qos_ctx->idle_mode =3D=3D MFC_IDLE_MODE_IDLE) { + mfc_ctx_debug(3, "[QoS][MFCIDLE] skip idle ctx [%d]\n", qos_ctx->num); + continue; + } + if (qos_ctx->is_heif_mode) + heif_found +=3D 1; + + if (qos_ctx->type =3D=3D MFCINST_DECODER) + dec_found +=3D 1; + hw_mb +=3D __mfc_qos_get_mb_per_second(core, qos_core_ctx, pdata->max_mb= ); + total_fps +=3D (qos_ctx->framerate / 1000); + total_bps +=3D qos_ctx->kbps; + } + + if (found) + list_del(&core->core_ctx[ctx->num]->qos_list); + + if (dec_found) + table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; + + num_qos_steps =3D __mfc_core_get_qos_steps(core, table_type); + qos_table =3D __mfc_core_get_qos_table(core, table_type); + + /* search the suitable qos table */ + for (i =3D num_qos_steps - 1; i >=3D 0; i--) { + fw_time =3D qos_table[i].time_fw; + sw_time =3D (MFC_DRV_TIME + fw_time); + + if ((total_fps * sw_time) >=3D 1000000) + total_mb =3D pdata->max_mb; + else + total_mb =3D ((1000000 * hw_mb) / (1000000 - (total_fps * sw_time))); + + mfc_ctx_debug(4, "%s %s %s[%d] %s %dus, %s %ld, %s %d, %s %ld, %s %ld\n", + "[QoS]", table_type ? "enc" : "default", + "table", i, + "fw_time:", fw_time, + "hw_mb:", hw_mb, + "sw_time:", sw_time, + "total_fps:", total_fps, + "total_mb:", total_mb); + + if (total_mb > qos_table[i].threshold_mb || total_mb =3D=3D 0 || i =3D= =3D 0) + break; + } + + if (total_mb > pdata->max_mb) + mfc_ctx_debug(4, "[QoS] overspec mb %ld > %d\n", total_mb, pdata->max_mb= ); + + if (dec_found) { + /* search the suitable independent mfc freq using bps */ + mfc_freq_idx =3D mfc_rate_get_bps_section_by_bps + (core->dev, total_bps, core->dev->max_kbps); + core->mfc_freq_by_bps =3D core->dev->pdata->mfc_freqs[mfc_freq_idx]; + } else { + core->mfc_freq_by_bps =3D 0; + } + + if (delete && (list_empty(&core->qos_queue) || total_mb =3D=3D 0)) { + if (core->cpu_boost_enable) + __mfc_qos_cpu_boost_disable(core); + __mfc_qos_operate(core, MFC_QOS_REMOVE, table_type, 0); + } else { + if (heif_found) { + qos_level =3D num_qos_steps - 1; + mfc_ctx_debug(2, "[QoS][BOOST] use max level for HEIF\n"); + if (!core->cpu_boost_enable) + __mfc_qos_cpu_boost_enable(core); + } else { + qos_level =3D i; + if (core_ctx->dynamic_weight_level) { + qos_level +=3D core_ctx->dynamic_weight_level; + if (qos_level >=3D num_qos_steps) + qos_level =3D num_qos_steps - 1; + else if (qos_level < 0) + qos_level =3D 0; + mfc_ctx_debug(2, "[QoS] add dynamic weight level %d. table[%d]\n", + core_ctx->dynamic_weight_level, qos_level); + } + if (core->cpu_boost_enable) + __mfc_qos_cpu_boost_disable(core); + } + __mfc_qos_set(core, ctx, table_type, qos_level); + } +} + +void mfc_qos_on(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *qos_core_ctx; + int found =3D 0; + + if (core->core_ctx[ctx->num] && core->core_ctx[ctx->num]->state =3D=3D MF= CINST_FREE) { + mfc_ctx_info("[QoS] instance not started yet\n"); + return; + } + + mutex_lock(&core->qos_mutex); + list_for_each_entry(qos_core_ctx, &core->qos_queue, qos_list) + if (qos_core_ctx =3D=3D core->core_ctx[ctx->num]) + found =3D 1; + + if (!found) + list_add_tail(&core->core_ctx[ctx->num]->qos_list, + &core->qos_queue); + + __mfc_qos_calculate(core, ctx, MFC_QOS_ADD); + + mutex_unlock(&core->qos_mutex); +} + +void mfc_qos_off(struct mfc_core *core, struct mfc_ctx *ctx) +{ + int table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; + + mutex_lock(&core->qos_mutex); + + if (list_empty(&core->qos_queue)) { + if (atomic_read(&core->qos_req_cur) !=3D 0) { + mfc_ctx_err("[QoS] MFC request count is wrong!\n"); + if (core->cpu_boost_enable) + __mfc_qos_cpu_boost_disable(core); + __mfc_qos_operate(core, MFC_QOS_REMOVE, table_type, 0); + } + goto out; + } + + if (ON_RES_CHANGE(core->core_ctx[ctx->num])) + goto out; + + __mfc_qos_calculate(core, ctx, MFC_QOS_REMOVE); + +out: + mutex_unlock(&core->qos_mutex); +} + +void mfc_qos_update(struct mfc_core *core, int on) +{ + struct mfc_platdata *pdata =3D core->dev->pdata; + struct mfc_platdata *dev_pdata =3D core->dev->pdata; + unsigned int mfc_freq; + int qos, i; + + if (core->dev->debugfs.feature_option & MFC_OPTION_DYNAMIC_QOS_DISABLE) + return; + + mfc_core_debug_enter(); + + mutex_lock(&core->qos_mutex); + + if ((atomic_read(&core->qos_req_cur) <=3D 1) || + (atomic_read(&core->qos_req_cur) > (dev_pdata->qos_ctrl_level + 1)) || + core->last_table_type !=3D MFC_QOS_TABLE_TYPE_DEFAULT) { + mutex_unlock(&core->qos_mutex); + return; + } + + if (on) { + qos =3D atomic_read(&core->qos_req_cur) - 1; + mfc_freq =3D core->last_mfc_freq; + mfc_core_debug(3, "[QoS] ON: QoS update[%d], mfc freq %d\n", + qos, mfc_freq); + } else { + qos =3D 0; + mfc_freq =3D pdata->mfc_freqs[0]; + mfc_core_debug(3, "[QoS] OFF: QoS update[%d], mfc freq %d\n", + qos, mfc_freq); + } + + i =3D core->qos_ctrl_last_idx; + core->qos_ctrl[i].idx =3D qos; + core->qos_ctrl[i].table_type =3D core->last_table_type; + core->qos_ctrl[i].mfc_freq =3D mfc_freq; + core->qos_ctrl_last_idx++; + if (core->qos_ctrl_last_idx >=3D MAX_NUM_QOS_DYNAMIC) + core->qos_ctrl_last_idx =3D 0; + + mutex_unlock(&core->qos_mutex); + + queue_work(core->qos_ctrl_wq, &core->qos_ctrl_work); + + mfc_core_debug_leave(); +} + +void mfc_qos_ctrl_worker(struct work_struct *work) +{ + struct mfc_core *core; + struct mfc_core_platdata *pdata; + struct mfc_platdata *dev_pdata; + struct mfc_qos *qos_table; + int idx, i; + + core =3D container_of(work, struct mfc_core, qos_ctrl_work); + pdata =3D core->core_pdata; + dev_pdata =3D core->dev->pdata; + + mutex_lock(&core->qos_mutex); + + if (!core->qos_ctrl_last_idx) { + mutex_unlock(&core->qos_mutex); + return; + } + + i =3D core->qos_ctrl_last_idx; + do { + core->qos_ctrl_last_idx =3D 0; + + if ((atomic_read(&core->qos_req_cur) > (dev_pdata->qos_ctrl_level + 1)) = || + atomic_read(&core->qos_req_cur) <=3D 1) { + mutex_unlock(&core->qos_mutex); + return; + } + + i--; + idx =3D core->qos_ctrl[i].idx; + qos_table =3D __mfc_core_get_qos_table(core, core->qos_ctrl[i].table_typ= e); + + mutex_unlock(&core->qos_mutex); + + /* use pm_qos_mutex to reduce pm_qos_update latency */ + mutex_lock(&core->pm_qos_mutex); + if (atomic_read(&core->qos_req_cur) =3D=3D 0) { + mutex_unlock(&core->pm_qos_mutex); + return; + } + + if (pdata->mfc_freq_control) + exynos_pm_qos_update_request + (&core->qos_req_mfc, core->qos_ctrl[i].mfc_freq); + + exynos_pm_qos_update_request(&core->qos_req_int, qos_table[idx].freq_int= ); + exynos_pm_qos_update_request(&core->qos_req_mif, qos_table[idx].freq_mif= ); + mfc_core_debug(3, "[QoS] WORKER: QoS update[%d], mfc freq %d\n", + idx, core->qos_ctrl[i].mfc_freq); + + mutex_unlock(&core->pm_qos_mutex); + + mutex_lock(&core->qos_mutex); + i =3D core->qos_ctrl_last_idx; + } while (i); + + mutex_unlock(&core->qos_mutex); +} + +void __mfc_qos_on_idle(struct mfc_core *core) +{ + struct mfc_ctx *ctx; + int i; + + mutex_lock(&core->dev->mfc_migrate_mutex); + if (!core->num_inst) { + mutex_unlock(&core->dev->mfc_migrate_mutex); + return; + } + + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (core->core_ctx[i]) { + ctx =3D core->core_ctx[i]->ctx; + mfc_qos_on(core, ctx); + break; + } + } + mutex_unlock(&core->dev->mfc_migrate_mutex); +} + +void __mfc_qos_off_all(struct mfc_core *core) +{ + struct mfc_core_ctx *qos_core_ctx, *tmp_core_ctx; + + mutex_lock(&core->qos_mutex); + if (list_empty(&core->qos_queue)) { + mfc_core_err("[QoS][MFCIDLE] MFC QoS list already empty (%d)\n", + atomic_read(&core->qos_req_cur)); + mutex_unlock(&core->qos_mutex); + return; + } + + /* Delete all of QoS list */ + list_for_each_entry_safe(qos_core_ctx, tmp_core_ctx, &core->qos_queue, qo= s_list) + list_del(&qos_core_ctx->qos_list); + + /* Select the opend ctx structure for QoS remove */ + if (core->cpu_boost_enable) + __mfc_qos_cpu_boost_disable(core); + __mfc_qos_operate(core, MFC_QOS_REMOVE, MFC_QOS_TABLE_TYPE_DEFAULT, 0); + mutex_unlock(&core->qos_mutex); +} +#else +bool mfc_qos_mb_calculate(struct mfc_core *core, struct mfc_core_ctx *core= _ctx, + unsigned int processing_cycle, unsigned int frame_type) +{ + return false; +} +#endif + +void mfc_qos_idle_worker(struct work_struct *work) +{ + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_ctx *ctx; + int is_idle =3D 0, qos_num_inst =3D 0; + + core =3D container_of(work, struct mfc_core, mfc_idle_work); + + mutex_lock(&core->dev->mfc_mutex); + + mutex_lock(&core->idle_qos_mutex); + + /* Check idle mode for all context */ + mutex_lock(&core->qos_mutex); + list_for_each_entry(core_ctx, &core->qos_queue, qos_list) { + ctx =3D core_ctx->ctx; + qos_num_inst++; + if (((atomic_read(&core->hw_run_bits) & BIT(ctx->num)) =3D=3D 0) && + ((atomic_read(&core->dev->queued_bits) & BIT(ctx->num)) =3D=3D 0)) { + mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_IDLE); + mfc_debug(3, "[MFCIDLE] ctx[%d] is idle (hw %#x Q %#x)\n", ctx->num, + atomic_read(&core->hw_run_bits), + atomic_read(&core->dev->queued_bits)); + is_idle =3D 1; + } else { + mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE); + } + } + mutex_unlock(&core->qos_mutex); + + if (core->idle_mode =3D=3D MFC_IDLE_MODE_CANCEL) { + mfc_core_change_idle_mode(core, MFC_IDLE_MODE_NONE); + mfc_core_debug(2, "[QoS][MFCIDLE] idle mode is canceled\n"); + goto ctx_idle; + } else if (core->idle_mode =3D=3D MFC_IDLE_MODE_NONE) { + mfc_core_idle_checker_start_tick(core); + goto ctx_idle; + } + +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ + __mfc_qos_off_all(core); +#endif + if (qos_num_inst =3D=3D 1) { + mfc_core_info("[QoS][MFCIDLE] go to idle mode (src %d(ready %d), dst %d,= framecnt %d)\n", + mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_buf_queu= e), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_ready_que= ue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue), + ctx->frame_cnt); + } else { + mfc_core_info("[QoS][MFCIDLE] go to idle mode\n"); + } + + mfc_core_change_idle_mode(core, MFC_IDLE_MODE_IDLE); + mutex_unlock(&core->idle_qos_mutex); + mutex_unlock(&core->dev->mfc_mutex); + return; + +ctx_idle: + if (is_idle) { + mfc_core_debug(2, "[QoS][MFCIDLE] idle mode is for ctx\n"); +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ + __mfc_qos_on_idle(core); +#endif + } + + mutex_unlock(&core->idle_qos_mutex); + mutex_unlock(&core->dev->mfc_mutex); +} + +bool mfc_qos_idle_trigger(struct mfc_core *core, struct mfc_ctx *ctx) +{ + bool update_idle =3D false; + + mutex_lock(&core->idle_qos_mutex); + if (core->idle_mode =3D=3D MFC_IDLE_MODE_IDLE) { + mfc_ctx_debug(2, "[QoS][MFCIDLE] restart QoS control\n"); + mfc_core_change_idle_mode(core, MFC_IDLE_MODE_NONE); + update_idle =3D true; + } else if (core->idle_mode =3D=3D MFC_IDLE_MODE_RUNNING) { + mfc_ctx_debug(2, "[QoS][MFCIDLE] restart QoS control, cancel idle\n"); + mfc_core_change_idle_mode(core, MFC_IDLE_MODE_CANCEL); + update_idle =3D true; + } + + if (ctx->idle_mode =3D=3D MFC_IDLE_MODE_IDLE) { + mfc_ctx_debug(2, "[QoS][MFCIDLE] restart QoS control for ctx\n"); + mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE); + update_idle =3D true; + } + mutex_unlock(&core->idle_qos_mutex); + + return update_idle; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.h b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_qos.h new file mode 100644 index 000000000000..d9c0e2828bfa --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_qos.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_QOS_H +#define __MFC_QOS_H __FILE__ + +#include "mfc_common.h" + +#define MB_COUNT_PER_UHD_FRAME 32400 +#define MAX_FPS_PER_UHD_FRAME 120 +#define MIN_BW_PER_SEC 1 + +#define MFC_2CORE_DRV_TIME 1500 +#define MFC_DRV_TIME 500 +#define MFC_DEC_MB_PER_TABLE 650000 +#define MFC_ENC_MB_PER_TABLE 400000 + +/* It is the same value with ID_DEFAULT of exynos-bts.c */ +#define BTS_DEFAULT_SCEN_IDX 0 + +enum { + MFC_QOS_TABLE_TYPE_DEFAULT =3D 0, + MFC_QOS_TABLE_TYPE_ENCODER =3D 1, +}; + +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ +void mfc_qos_on(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_qos_off(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_qos_update(struct mfc_core *core, int on); +void mfc_qos_ctrl_worker(struct work_struct *work); +void mfc_qos_set_portion(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_qos_get_portion(struct mfc_core *core, struct mfc_ctx *ctx); +#else + +#define mfc_qos_on(core, ctx) ({ \ + (void)core; /* Not used */ \ + (void)ctx; /* Not used */ \ + do {} while (0); \ +}) + +#define mfc_qos_off(core, ctx) ({ \ + (void)core; /* Not used */ \ + (void)ctx; /* Not used */ \ + do {} while (0); \ +}) + +#define mfc_qos_update(core, on) ({ \ + (void)core; /* Not used */ \ + (void)on; /* Not used */ \ + do {} while (0); \ +}) + +#define mfc_qos_ctrl_worker(work) ({ \ + (void)work; /* Not used */ \ + do {} while (0); \ +}) + +#define mfc_qos_set_portion(core, ctx) ({ \ + (void)core; /* Not used */ \ + (void)ctx; /* Not used */ \ + do {} while (0); \ +}) + +#define mfc_qos_get_portion(core, ctx) ({ \ + (void)core; /* Not used */ \ + (void)ctx; /* Not used */ \ + do {} while (0); \ +}) +#endif + +bool mfc_qos_mb_calculate(struct mfc_core *core, + struct mfc_core_ctx *core_ctx, + unsigned int processing_cycle, + unsigned int frame_type); + +void mfc_qos_idle_worker(struct work_struct *work); +bool mfc_qos_idle_trigger(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_qos_get_weighted_mb(struct mfc_ctx *ctx, enum mfc_real_time rt); + +static inline void mfc_qos_get_disp_ratio(struct mfc_ctx *ctx, int dec_cnt= , int disp_cnt) +{ + int delta; + + delta =3D dec_cnt - disp_cnt; + ctx->disp_ratio =3D ((dec_cnt + delta) * 100) / disp_cnt; + if (ctx->disp_ratio > 100) + mfc_ctx_debug(2, "[QoS] need to more performance dec %d/disp %d, disp_ra= tio: x%d.%d\n", + dec_cnt, disp_cnt, + ctx->disp_ratio / 100, ctx->disp_ratio % 100); +} +#endif /* __MFC_QOS_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) (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 9754927280B for ; Tue, 30 Sep 2025 03:56:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204584; cv=none; b=jO+OZcibjEnZpi0rtkujsKy8OjGWjPlKdRdzswoGZ9Jefb5UWsyLGBr37pJ7cWLcCFE6qpbBSmFuIanSFLI7YfASZE7mFxbPCVTLH3BmXGHRMUSVqM6BqbHM06T8k9oQ0Dfgm9ciQgDktIulvU205bAamP58Trp2tBmWu70G5K0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204584; c=relaxed/simple; bh=Y0GHrlUPyXqtts61v+JzEWRdSBRnC++d72qoJ4R1iLs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=BCsMmKb8RiPKoNiJROnn9dq2oJtaHOguKmwpoil95jon9sASekrJQP+HqfgIpjUqktMVPNwS4vHnqfFNlNhFFv5qw3Odrh/Z62uWslHAezu5BrFoGN2lzHpz4G2RJkd0521BOMsuS0g9CDrxQBYa1SL0lRckmKQ49Ahgh+Nu/pY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=THxEI3FB; arc=none smtp.client-ip=203.254.224.24 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="THxEI3FB" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250930035619epoutp01e1d8d9bdbeb4641ecdb3f17ba0227e34~p8z52hhSP2952829528epoutp01N for ; Tue, 30 Sep 2025 03:56:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250930035619epoutp01e1d8d9bdbeb4641ecdb3f17ba0227e34~p8z52hhSP2952829528epoutp01N DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204579; bh=nE3BdWnQEBzg3sZAV6SckBdnxY0P3OiY1Zx34roYU9w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=THxEI3FBQ+6FNiyL8pEEFxYHGKkwqF8cOJxLB43VoDHwx6PVI11BZCCw5XuZjThYN K3+Lhxc5949NEDGfaKjZAacI2wzUfsN4MDv0/5HsEBedHpcyBHqgQItw852LVtXKkj xBvQSVg0XtqqrzqpkCbU4DGvVrZVli+kEg1CnYBU= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035617epcas5p4447709c1f5d8128d495c242ec1a65e26~p8z41S5H30713607136epcas5p4I; Tue, 30 Sep 2025 03:56:17 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.89]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cbPPF0Hy5z6B9m7; Tue, 30 Sep 2025 03:56:17 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035616epcas5p3696fbcd8fbe66221433651290859e198~p8z3RhJqp0522705227epcas5p3l; Tue, 30 Sep 2025 03:56:16 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035611epsmtip126d96a1a75eb2ff554f6d5785b5f0de8~p8zyXb4qG2908129081epsmtip1y; Tue, 30 Sep 2025 03:56:10 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 13/29] media: mfc: Add decoder core sync functions Date: Tue, 30 Sep 2025 09:33:32 +0530 Message-Id: <20250930040348.3702923-14-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035616epcas5p3696fbcd8fbe66221433651290859e198 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035616epcas5p3696fbcd8fbe66221433651290859e198 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Add core command extensions and decoder=E2=80=91specific commands. - Update core register APIs with helpers for processing=E2=80=91cycle, DPB, scratch size, SEI, black=E2=80=91bar detection, MVC IDs, profile, display delay, two=E2=80=91core mode, and inline utilities for crop info, tag updates, migration address, and multi=E2=80=91core context flush. - Refactor core synchronization: migration=E2=80=91aware lock functions, wait=E2=80=91queues to prevent races, targeted wake=E2=80=91ups, and ready= =E2=80=91bit handling for header parsing, frame decoding, buffer setup, resolution changes, and two=E2=80=91core DRC paths. - Add macros, helper functions, comments, and debug prints to support the new decoder=E2=80=91side core coordination and upcoming multi=E2=80=91core decoding. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/mfc_core_cmd.c | 288 +++++++++++++++ .../samsung/exynos-mfc/mfc_core_cmd.h | 9 + .../samsung/exynos-mfc/mfc_core_reg_api.c | 346 ++++++++++++++++++ .../samsung/exynos-mfc/mfc_core_reg_api.h | 214 +++++++++++ .../samsung/exynos-mfc/mfc_core_sync.c | 187 +++++++++- .../samsung/exynos-mfc/mfc_core_sync.h | 9 + 6 files changed, 1049 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c index 5be9fd086a93..fe7946bb49e7 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c @@ -87,6 +87,8 @@ void mfc_core_cmd_open_inst(struct mfc_core *core, struct= mfc_ctx *ctx) =20 MFC_CORE_WRITEL(core_ctx->instance_ctx_buf.daddr, MFC_REG_CONTEXT_MEM_ADD= R); MFC_CORE_WRITEL(core_ctx->instance_ctx_buf.size, MFC_REG_CONTEXT_MEM_SIZE= ); + if (ctx->type =3D=3D MFCINST_DECODER) + MFC_CORE_WRITEL(ctx->dec_priv->crc_enable, MFC_REG_D_CRC_CTRL); =20 if (dev->debugfs.feature_option & MFC_OPTION_SET_MULTI_CORE_FORCE) { reg =3D MFC_CORE_READL(MFC_REG_DBG_INFO_ENABLE); @@ -135,6 +137,45 @@ void mfc_core_cmd_abort_inst(struct mfc_core *core, st= ruct mfc_ctx *ctx) mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_NAL_ABORT); } =20 +void mfc_core_cmd_move_inst(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_MOVE_INSTANCE); +} + +void mfc_core_cmd_dpb_flush(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + u32 reg =3D 0; + + if (ON_RES_CHANGE(core_ctx)) + mfc_err("dpb flush on res change(state:%d)\n", + core_ctx->state); + + mutex_lock(&ctx->op_mode_mutex); + + /* + * NAL_START_OPTIONS[4] should set when every NAL_START/DPB_FLUSH, + * it will be cleared by F/W. + */ + reg =3D MFC_CORE_READL(MFC_REG_D_NAL_START_OPTIONS); + reg &=3D ~BIT(MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + if (IS_MULTI_MODE(ctx) || ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2) + reg |=3D BIT(MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + else + reg |=3D (0 << MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + MFC_CORE_WRITEL(reg, MFC_REG_D_NAL_START_OPTIONS); + mfc_debug(3, "NAL_START_OPTIONS: %#x, op_mode: %d\n", reg, ctx->op_mode); + mutex_unlock(&ctx->op_mode_mutex); + + mfc_clean_core_ctx_int_flags(core_ctx); + + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_DPB_FLUSH); +} + void mfc_core_cmd_cache_flush(struct mfc_core *core) { struct mfc_core_ctx *core_ctx =3D core->core_ctx[core->curr_core_ctx]; @@ -156,3 +197,250 @@ void mfc_core_cmd_cache_flush(struct mfc_core *core) mfc_core_clean_dev_int_flags(core); mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_CACHE_FLUSH); } + +/* Initialize decoding */ +void mfc_core_cmd_dec_seq_header(struct mfc_core *core, struct mfc_ctx *ct= x) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + unsigned int reg =3D 0; + int fmo_aso_ctrl =3D 0; + + mfc_debug_enter(); + + mfc_debug(2, "inst_no: %d/%d\n", core_ctx->inst_no, MFC_REG_H2R_CMD_SEQ_H= EADER); + mfc_debug(2, "BUFs: %llx\n", MFC_CORE_DMA_READL(MFC_REG_D_CPB_BUFFER_ADDR= )); + + /* + * When user sets desplay_delay to 0, + * It works as "display_delay enable" and delay set to 0. + * If user wants display_delay disable, It should be + * set to negative value. + */ + if (dec->display_delay >=3D 0) { + reg |=3D BIT(MFC_REG_D_DEC_OPT_DISPLAY_DELAY_EN_SHIFT); + MFC_CORE_WRITEL(dec->display_delay, MFC_REG_D_DISPLAY_DELAY); + } + + /* FMO_ASO_CTRL - 0: Enable, 1: Disable */ + reg |=3D ((fmo_aso_ctrl & MFC_REG_D_DEC_OPT_FMO_ASO_CTRL_MASK) + << MFC_REG_D_DEC_OPT_FMO_ASO_CTRL_SHIFT); + + reg |=3D ((dec->idr_decoding & MFC_REG_D_DEC_OPT_IDR_DECODING_MASK) + << MFC_REG_D_DEC_OPT_IDR_DECODING_SHIFT); + + /* conceal control to specific color */ + reg |=3D (0x4 << MFC_REG_D_DEC_OPT_CONCEAL_CONTROL_SHIFT); + + /* Realloc buffer for resolution decrease case in NAL QUEUE mode */ + reg |=3D BIT(MFC_REG_D_DEC_OPT_REALLOC_CONTROL_SHIFT); + + /* Parsing all including PPS */ + reg |=3D BIT(MFC_REG_D_DEC_OPT_SPECIAL_PARSING_SHIFT); + + /* Enabe decoding order */ + if (dec->decoding_order || + (dev->debugfs.feature_option & MFC_OPTION_DECODING_ORDER)) + reg |=3D BIT(MFC_REG_D_DEC_OPT_DECODING_ORDER_ENABLE); + + MFC_CORE_WRITEL(reg, MFC_REG_D_DEC_OPTIONS); + + MFC_CORE_WRITEL(MFC_CONCEAL_COLOR, MFC_REG_D_FORCE_PIXEL_VAL); + + mfc_core_set_pixel_format(core, ctx, ctx->dst_fmt->fourcc); + + reg =3D 0; + /* Enable realloc interface if SEI is enabled */ + if (dec->sei_parse) + reg |=3D BIT(MFC_REG_D_SEI_ENABLE_NEED_INIT_BUFFER_SHIFT); + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->static_info_dec)) { + reg |=3D BIT(MFC_REG_D_SEI_ENABLE_CONTENT_LIGHT_SHIFT); + reg |=3D BIT(MFC_REG_D_SEI_ENABLE_MASTERING_DISPLAY_SHIFT); + } + reg |=3D BIT(MFC_REG_D_SEI_ENABLE_RECOVERY_PARSING_SHIFT); + + MFC_CORE_WRITEL(reg, MFC_REG_D_SEI_ENABLE); + mfc_debug(2, "SEI enable was set, 0x%x\n", MFC_CORE_READL(MFC_REG_D_SEI_E= NABLE)); + + /* Enable metadata */ + reg =3D 0; + + MFC_CORE_WRITEL(reg, MFC_REG_METADATA_ENABLE); + + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_SEQ_HEADER); + + mfc_debug_leave(); +} + +int mfc_core_cmd_dec_init_buffers(struct mfc_core *core, struct mfc_ctx *c= tx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + unsigned int reg =3D 0; + int ret; + + mfc_core_set_pixel_format(core, ctx, ctx->dst_fmt->fourcc); + + mfc_clean_core_ctx_int_flags(core_ctx); + ret =3D mfc_core_set_dec_codec_buffers(core_ctx); + if (ret) { + mfc_info("isn't enough codec buffer size, re-alloc!\n"); + + mfc_release_codec_buffers(core_ctx); + ret =3D mfc_alloc_codec_buffers(core_ctx); + if (ret) { + mfc_err("Failed to allocate decoding buffers\n"); + return ret; + } + ret =3D mfc_core_set_dec_codec_buffers(core_ctx); + if (ret) { + mfc_err("Failed to alloc frame mem\n"); + return ret; + } + } + + if (IS_MULTI_MODE(ctx)) { + reg |=3D ((ctx->subcore_inst_no & MFC_REG_RET_INSTANCE_ID_OF_MFC1_MASK) + << MFC_REG_RET_INSTANCE_ID_OF_MFC1_SHIFT); + reg |=3D (core_ctx->inst_no & MFC_REG_RET_INSTANCE_ID_MASK); + MFC_CORE_WRITEL(reg, MFC_REG_INSTANCE_ID); + } else { + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + } + + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_INIT_BUFFERS); + + return ret; +} + +static int __mfc_set_scratch_dpb_buffer(struct mfc_core *core, struct mfc_= ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret; + + ret =3D mfc_alloc_scratch_buffer(core_ctx); + if (ret) { + mfc_err("Failed to allocate scratch buffers\n"); + return ret; + } + + /* set decoder DPB size, stride, scratch buffer */ + mfc_core_set_dec_dpb_and_scratch(core_ctx, core_ctx->scratch_buf.daddr); + + return 0; +} + +/* Decode a single frame */ +int mfc_core_cmd_dec_one_frame(struct mfc_core *core, struct mfc_ctx *ctx, + int last_frame, int src_index) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_dec *dec =3D ctx->dec_priv; + u32 reg =3D 0; + int ret =3D 0; + u32 timeout_value =3D MFC_TIMEOUT_VALUE; + unsigned long dynamic_set; + + if (dec->is_dynamic_dpb) + dynamic_set =3D core_ctx->dynamic_set; + else + dynamic_set =3D dec->dynamic_set; + + mfc_debug(2, "[MFC-%d][DPB] set dpb: %#lx, used: %#lx\n", + core->id, dynamic_set, dec->dynamic_used); + + reg =3D MFC_CORE_READL(MFC_REG_D_NAL_START_OPTIONS); + /* Black bar */ + reg &=3D ~BIT(MFC_REG_D_NAL_START_OPT_BLACK_BAR_SHIFT); + reg |=3D ((dec->detect_black_bar & 0x1) << MFC_REG_D_NAL_START_OPT_BLACK_= BAR_SHIFT); + if (core->dev->debugfs.feature_option & MFC_OPTION_BLACK_BAR_ENABLE) + reg |=3D BIT(MFC_REG_D_NAL_START_OPT_BLACK_BAR_SHIFT); + /* Scratch buf & DPB changes when interframe resolution change */ + if (dec->inter_res_change) { + ret =3D __mfc_set_scratch_dpb_buffer(core, ctx); + if (ret) + return ret; + reg |=3D BIT(MFC_REG_D_NAL_START_OPT_NEW_SCRATCH_SHIFT); + reg |=3D BIT(MFC_REG_D_NAL_START_OPT_NEW_DPB_SHIFT); + dec->inter_res_change =3D 0; + } else { + reg &=3D ~BIT(MFC_REG_D_NAL_START_OPT_NEW_SCRATCH_SHIFT); + reg &=3D ~BIT(MFC_REG_D_NAL_START_OPT_NEW_DPB_SHIFT); + } + + /* Operation core mode */ + mutex_lock(&ctx->op_mode_mutex); + + if ((ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1 || + ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE2) && + ctx->op_mode !=3D MFC_OP_SWITCH_BUT_MODE2) { + ctx->cmd_counter++; + mfc_debug(2, "[2CORE] cmd_counter : %d\n", ctx->cmd_counter); + } + + reg &=3D ~BIT(MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + if (IS_MULTI_MODE(ctx) || ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2) + reg |=3D BIT(MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + else + reg |=3D (0 << MFC_REG_D_NAL_START_OPT_TWO_MFC_ENABLE_SHIFT); + if (ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2) { + mfc_debug(2, "[2CORE] operate once op_mode %d\n", ctx->op_mode); + mfc_change_op_mode(ctx, MFC_OP_SWITCH_TO_SINGLE); + } else if (ctx->op_mode =3D=3D MFC_OP_SWITCHING) { + mfc_err("[2CORE] It is a mode that can not operate\n"); + } else if (ctx->op_mode =3D=3D MFC_OP_SWITCH_TO_SINGLE) { + ctx->last_op_core =3D core->id; + mfc_debug(2, "[2CORE] last_op_core of switch_to_single : %d\n", + ctx->last_op_core); + } + + /* If it is switched to single, interrupt lock is not needed. */ + if (IS_SWITCH_SINGLE_MODE(ctx)) + mfc_clear_core_intlock(ctx); + mutex_unlock(&ctx->op_mode_mutex); + MFC_CORE_WRITEL(reg, MFC_REG_D_NAL_START_OPTIONS); + mfc_debug(3, "NAL_START_OPTIONS: %#x, op_mode: %d\n", reg, ctx->op_mode); + + if (core->last_mfc_freq) + timeout_value =3D (core->last_mfc_freq * MFC_TIMEOUT_VALUE_IN_MSEC); + mfc_debug(2, "Last MFC Freq: %d, Timeout Value: %d\n", + core->last_mfc_freq, timeout_value); + + MFC_CORE_WRITEL(mfc_get_lower(dynamic_set), MFC_REG_D_DYNAMIC_DPB_FLAG_LO= WER); + MFC_CORE_WRITEL(mfc_get_upper(dynamic_set), MFC_REG_D_DYNAMIC_DPB_FLAG_UP= PER); + MFC_CORE_WRITEL(mfc_get_lower(dynamic_set), MFC_REG_D_AVAILABLE_DPB_FLAG_= LOWER); + MFC_CORE_WRITEL(mfc_get_upper(dynamic_set), MFC_REG_D_AVAILABLE_DPB_FLAG_= UPPER); + + MFC_CORE_WRITEL(dec->slice_enable, MFC_REG_D_SLICE_IF_ENABLE); + MFC_CORE_WRITEL(timeout_value, MFC_REG_TIMEOUT_VALUE); + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + + /* source index for 2core mode2 should set just before sent command */ + if (IS_TWO_MODE2(ctx) || IS_SWITCH_SINGLE_MODE(ctx)) { + mutex_lock(&ctx->op_mode_mutex); + mfc_debug(2, "[MFC-%d][STREAM] set cpb: %d, curr index: %d\n", + core->id, src_index, ctx->curr_src_index); + ctx->curr_src_index =3D src_index; + } + + /* + * Issue different commands to instance basing on whether it + * is the last frame or not. + */ + switch (last_frame) { + case 0: + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_NAL_START); + break; + case 1: + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_LAST_FRAME); + break; + } + + if (IS_TWO_MODE2(ctx) || IS_SWITCH_SINGLE_MODE(ctx)) + mutex_unlock(&ctx->op_mode_mutex); + + mfc_debug(2, "Decoding a usual frame\n"); + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h index 86b82d63f3b5..216d07c564ae 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h @@ -22,5 +22,14 @@ void mfc_core_cmd_wakeup(struct mfc_core *core); void mfc_core_cmd_open_inst(struct mfc_core *core, struct mfc_ctx *ctx); int mfc_core_cmd_close_inst(struct mfc_core *core, struct mfc_ctx *ctx); void mfc_core_cmd_abort_inst(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_core_cmd_move_inst(struct mfc_core *core, struct mfc_ctx *ctx); + +void mfc_core_cmd_dpb_flush(struct mfc_core *core, struct mfc_ctx *ctx); void mfc_core_cmd_cache_flush(struct mfc_core *core); + +void mfc_core_cmd_dec_seq_header(struct mfc_core *core, struct mfc_ctx *ct= x); + +int mfc_core_cmd_dec_init_buffers(struct mfc_core *core, struct mfc_ctx *c= tx); +int mfc_core_cmd_dec_one_frame(struct mfc_core *core, struct mfc_ctx *ctx, + int last_frame, int src_index); #endif /* __MFC_CORE_CMD_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c index ec6699dbd451..6950b8451c3d 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c @@ -42,3 +42,349 @@ void mfc_core_dbg_set_addr(struct mfc_core *core) MFC_CORE_WRITEL(core->dbg_info_buf.daddr, MFC_REG_DBG_BUFFER_ADDR); MFC_CORE_WRITEL(buf_size->dbg_info_buf, MFC_REG_DBG_BUFFER_SIZE); } + +unsigned int mfc_get_frame_error_type(struct mfc_ctx *ctx, unsigned int er= r) +{ + struct mfc_dev *dev =3D ctx->dev; + + if (!err) { + mfc_ctx_debug(4, "[FRAME] there is no error frame\n"); + return MFC_ERR_FRAME_NO_ERR; + } + + if (mfc_get_warn(err) =3D=3D MFC_REG_ERR_BROKEN_LINK) { + mfc_ctx_debug(2, "[FRAME] Broken frame error (%d)\n", mfc_get_warn(err)); + return MFC_ERR_FRAME_BROKEN; + } else if (mfc_get_warn(err) =3D=3D MFC_REG_ERR_SYNC_POINT_NOT_RECEIVED) { + mfc_ctx_debug(2, "[FRAME] Sync point frame error (%d), type %d\n", + mfc_get_warn(err), dev->pdata->display_err_type); + return dev->pdata->display_err_type; + } + + mfc_ctx_debug(2, "[FRAME] Concealment frame error (%d)\n", mfc_get_warn(e= rr)); + return MFC_ERR_FRAME_CONCEALMENT; +} + +void mfc_core_set_dec_dpb_and_scratch(struct mfc_core_ctx *core_ctx, dma_a= ddr_t scratch_addr) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_raw_info *raw; + int i; + + raw =3D &ctx->raw_buf; + + MFC_CORE_WRITEL(dec->total_dpb_count, MFC_REG_D_NUM_DPB); + for (i =3D 0; i < raw->num_planes; i++) { + mfc_debug(2, "[FRAME] buf[%d] size: %d, stride: %d\n", + i, raw->plane_size[i], raw->stride[i]); + MFC_CORE_WRITEL(raw->plane_size[i], MFC_REG_D_FIRST_PLANE_DPB_SIZE + (i = * 4)); + MFC_CORE_WRITEL(raw->stride[i], + MFC_REG_D_FIRST_PLANE_DPB_STRIDE_SIZE + (i * 4)); + } + + /* set scratch buffer */ + MFC_CORE_DMA_WRITEL(scratch_addr, MFC_REG_D_SCRATCH_BUFFER_ADDR); + MFC_CORE_WRITEL(ctx->scratch_buf_size, MFC_REG_D_SCRATCH_BUFFER_SIZE); + mfc_debug(2, "[FRAME] scratch buf addr: 0x%#llx size %ld\n", + scratch_addr, ctx->scratch_buf_size); +} + +/* Set decoding frame buffer */ +int mfc_core_set_dec_codec_buffers(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned int i; + size_t frame_size_mv; + dma_addr_t buf_addr; + int buf_size; + int align_gap; + unsigned int reg =3D 0; + + buf_addr =3D core_ctx->codec_buf.daddr; + buf_size =3D core_ctx->codec_buf.size; + + mfc_debug(2, "[MEMINFO] codec buf 0x%llx size: %d\n", buf_addr, buf_size); + mfc_debug(2, "Total DPB COUNT: %d, display delay: %d\n", + dec->total_dpb_count, dec->display_delay); + + if (core_ctx->codec_buffer_allocated && buf_size && + buf_size > core_ctx->codec_buf.dma_buf->size) { + mfc_info("[MEMINFO] Not enough codec buf size %d alloc size %zu\n", + buf_size, core_ctx->codec_buf.dma_buf->size); + return -ENOMEM; + } + + /* set decoder DPB size, stride, scratch buffer */ + mfc_core_set_dec_dpb_and_scratch(core_ctx, buf_addr); + buf_addr +=3D ctx->scratch_buf_size; + buf_size -=3D ctx->scratch_buf_size; + + if (IS_H264_DEC(ctx)) + MFC_CORE_WRITEL(ctx->mv_size, MFC_REG_D_MV_BUFFER_SIZE); + + reg |=3D (dec->is_dynamic_dpb << MFC_REG_D_INIT_BUF_OPT_DYNAMIC_DPB_SET_S= HIFT); + + /* 16byte align, It is valid only for VP9 */ + reg &=3D ~BIT(MFC_REG_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN); + + reg &=3D ~BIT(MFC_REG_D_INIT_BUF_OPT_TWO_MODE_ENABLE_SHIFT); + if (IS_MULTI_MODE(ctx)) { + reg |=3D BIT(MFC_REG_D_INIT_BUF_OPT_TWO_MODE_ENABLE_SHIFT); + mfc_debug(2, "[2CORE] two mode(op_mode: %d) enable, reg %#x\n", + ctx->op_mode, reg); + } + + if (dev->pdata->support_mv_hevc) + reg |=3D BIT(MFC_REG_D_INIT_BUF_OPT_ENABLE_DECODE_VIEW1); + else + reg &=3D ~BIT(MFC_REG_D_INIT_BUF_OPT_ENABLE_DECODE_VIEW1); + MFC_CORE_WRITEL(reg, MFC_REG_D_INIT_BUFFER_OPTIONS); + + mfc_debug(2, "[MEMINFO] codec buf remained size: %d\n", buf_size); + if (buf_size < 0) { + mfc_debug(2, "[MEMINFO] Not enough memory has been allocated\n"); + return -ENOMEM; + } + + /* MV buffers */ + buf_addr =3D ctx->mv_buf.daddr; + buf_size =3D ctx->mv_buf.size; + mfc_debug(2, "[MEMINFO] MV buf 0x%llx size: %d\n", buf_addr, buf_size); + + frame_size_mv =3D ctx->mv_size; + MFC_CORE_WRITEL(dec->mv_count, MFC_REG_D_NUM_MV); + if (IS_H264_DEC(ctx)) { + if (ctx->mv_buffer_allocated && buf_size && + buf_size > ctx->mv_buf.dma_buf->size) { + mfc_info("[MEMINFO] Not enough MV buf size %d alloc size %zu\n", + buf_size, ctx->mv_buf.dma_buf->size); + return -ENOMEM; + } + + for (i =3D 0; i < dec->mv_count; i++) { + align_gap =3D buf_addr; + buf_addr =3D ALIGN(buf_addr, 16); + align_gap =3D buf_addr - align_gap; + buf_size -=3D align_gap; + + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_D_MV_BUFFER0 + i * 4); + buf_addr +=3D frame_size_mv; + buf_size -=3D frame_size_mv; + } + } + + mfc_debug(2, "[MEMINFO] MV buf remained size: %d\n", buf_size); + if (buf_size < 0) { + mfc_debug(2, "[MEMINFO] Not enough memory has been allocated\n"); + return -ENOMEM; + } + + return 0; +} + +/* Set registers for decoding stream buffer */ +int mfc_core_set_dec_stream_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + struct mfc_buf *mfc_buf, unsigned int start_num_byte, + unsigned int strm_size) +{ + size_t need_cpb_buf_size =3D 0, buf_size =3D 0; + dma_addr_t addr =3D 0; + struct vb2_buffer *vb =3D NULL; + + mfc_ctx_debug_enter(); + + if (mfc_buf) { + vb =3D &mfc_buf->vb.vb2_buf; + addr =3D mfc_buf->addr[0][0]; + + if (core->dev->pdata->stream_buf_limit) { + need_cpb_buf_size =3D ALIGN(strm_size + 511, STREAM_BUF_ALIGN); + buf_size =3D mfc_buf->sg_size; + } else { + need_cpb_buf_size =3D strm_size; + buf_size =3D vb->planes[0].dbuf->size; + } + + if (buf_size < need_cpb_buf_size) + mfc_ctx_info("Decrease buffer size: %zu(need) -> %zu(alloc)\n", + need_cpb_buf_size, buf_size); + + mfc_ctx_debug(2, "[BUFINFO] set src index: %d(%d), addr: 0x%08llx\n", + vb->index, mfc_buf->src_index, addr); + } + + mfc_ctx_debug(2, "[STREAM] strm_size, %#x, %d, offset, %#x, %u, need_buf_= size, %zu, buf_size, %zu\n", + strm_size, strm_size, start_num_byte, start_num_byte, + need_cpb_buf_size, buf_size); + + if (strm_size =3D=3D 0) + mfc_ctx_info("stream size is 0\n"); + + MFC_CORE_WRITEL(strm_size, MFC_REG_D_STREAM_DATA_SIZE); + MFC_CORE_DMA_WRITEL(addr, MFC_REG_D_CPB_BUFFER_ADDR); + MFC_CORE_WRITEL(buf_size, MFC_REG_D_CPB_BUFFER_SIZE); + MFC_CORE_WRITEL(start_num_byte, MFC_REG_D_CPB_BUFFER_OFFSET); + ctx->last_src_addr =3D addr; + + mfc_ctx_debug_leave(); + return 0; +} + +void mfc_core_set_dynamic_dpb(struct mfc_core *core, struct mfc_ctx *ctx, + struct mfc_buf *dst_mb) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_raw_info *raw =3D &ctx->raw_buf; + int index_view =3D ctx->select_view =3D=3D MFC_VIEW_ID_MAIN ? MFC_MV_BUF_= IDX_VIEW0 + : MFC_MV_BUF_IDX_VIEW1; + /* offset about fd */ + int offset =3D ctx->view_buf_info[index_view].offset; + int dst_index; + int i; + + if (ctx->select_view =3D=3D MFC_VIEW_ID_MAIN) + dst_index =3D dst_mb->dpb_index; + else + dst_index =3D dst_mb->dpb_index + MFC_MAX_DPBS / 2; + + /* for debugging about black bar detection */ + if ((MFC_FEATURE_SUPPORT(dev, dev->pdata->black_bar) && dec->detect_black= _bar) || + (dev->debugfs.feature_option & MFC_OPTION_BLACK_BAR_ENABLE)) { + for (i =3D 0; i < raw->num_planes; i++) { + dec->frame_vaddr[i][dec->frame_cnt] =3D + vb2_plane_vaddr(&dst_mb->vb.vb2_buf, i); + dec->frame_daddr[i][dec->frame_cnt] =3D dst_mb->addr[0][i]; + dec->frame_size[i][dec->frame_cnt] =3D raw->plane_size[i]; + dec->index[i][dec->frame_cnt] =3D dst_index; + dec->fd[i][dec->frame_cnt] =3D dst_mb->vb.vb2_buf.planes[0].m.fd; + } + dec->frame_cnt++; + if (dec->frame_cnt >=3D 30) + dec->frame_cnt =3D 0; + } + + for (i =3D 0; i < raw->num_planes; i++) { + MFC_CORE_WRITEL(raw->plane_size[i], + MFC_REG_D_FIRST_PLANE_DPB_SIZE + i * 4); + MFC_CORE_DMA_WRITEL(dst_mb->addr[index_view][i], + MFC_REG_D_FIRST_PLANE_DPB0 + (i * 0x100 + dst_index * 4)); + ctx->last_dst_addr[i] =3D dst_mb->addr[index_view][i]; + mfc_debug(2, "%s[%d][%d], addr[%d]: 0x%08llx, fd: %d, size: %d%s %d, off= set: %d\n", + "[BUFINFO][DPB] set dst index: ", dst_mb->vb.vb2_buf.index, + dst_mb->dpb_index, i, + dst_mb->addr[index_view][i], dst_mb->vb.vb2_buf.planes[offset + i].m.= fd, + raw->plane_size[i], + ", index_view:", index_view, + offset); + } + + MFC_TRACE_CORE_CTX("Set dst[%d] fd: %d, %#llx / used %#lx\n", + dst_index, dst_mb->vb.vb2_buf.planes[offset].m.fd, + dst_mb->addr[index_view][0], dec->dynamic_used); +} + +void mfc_core_get_img_size(struct mfc_core *core, struct mfc_ctx *ctx, + enum mfc_get_img_size img_size) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned int w, h; + int i; + + w =3D ctx->img_width; + h =3D ctx->img_height; + + ctx->img_width =3D mfc_core_get_img_width(); + ctx->img_height =3D mfc_core_get_img_height(); + ctx->crop_width =3D ctx->img_width; + ctx->crop_height =3D ctx->img_height; + + for (i =3D 0; i < ctx->dst_fmt->num_planes; i++) + ctx->raw_buf.stride[i] =3D mfc_core_get_stride_size(i); + + mfc_ctx_debug(2, "[FRAME][DRC] resolution changed, %dx%d =3D> %dx%d (stri= de: %d)\n", w, h, + ctx->img_width, ctx->img_height, ctx->raw_buf.stride[0]); + + if (img_size =3D=3D MFC_GET_RESOL_SIZE) { + dec->disp_drc.width[dec->disp_drc.push_idx] =3D ctx->img_width; + dec->disp_drc.height[dec->disp_drc.push_idx] =3D ctx->img_height; + dec->disp_drc.disp_res_change++; + mfc_ctx_debug(3, "[DRC] disp_res_change[%d] count %d\n", + dec->disp_drc.push_idx, dec->disp_drc.disp_res_change); + dec->disp_drc.push_idx =3D (dec->disp_drc.push_idx + 1) % MFC_MAX_DRC_FR= AME; + } else if (img_size =3D=3D MFC_GET_RESOL_DPB_SIZE) { + ctx->scratch_buf_size =3D mfc_core_get_scratch_size(); + for (i =3D 0; i < ctx->dst_fmt->num_planes; i++) + ctx->min_dpb_size[i] =3D mfc_core_get_min_dpb_size(i); + + mfc_ctx_debug(2, "[FRAME][DRC] DPB count %d, %s %d(%#x) %s %d scratch %z= u(%#zx)\n", + ctx->dpb_count, "min_dpb_size", ctx->min_dpb_size[0], + ctx->min_dpb_size[0], "min_dpb_size_2bits", + ctx->min_dpb_size_2bits[0], + ctx->scratch_buf_size, ctx->scratch_buf_size); + } +} + +void mfc_core_set_pixel_format(struct mfc_core *core, struct mfc_ctx *ctx, + unsigned int format) +{ + unsigned int reg =3D 0; + unsigned int pix_val; + unsigned int block_align =3D 0; + + switch (format) { + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV12MT_16X16: + case V4L2_PIX_FMT_NV16M: + pix_val =3D 0; + break; + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV61M: + pix_val =3D 1; + break; + case V4L2_PIX_FMT_YVU420M: + pix_val =3D 2; + break; + case V4L2_PIX_FMT_YUV420M: + pix_val =3D 3; + break; + case V4L2_PIX_FMT_ARGB32: + pix_val =3D 8; + break; + case V4L2_PIX_FMT_RGB24: + pix_val =3D 9; + break; + case V4L2_PIX_FMT_RGB565: + pix_val =3D 10; + break; + case V4L2_PIX_FMT_RGB32: + pix_val =3D 11; + break; + case V4L2_PIX_FMT_RGB32X: + pix_val =3D 12; + break; + case V4L2_PIX_FMT_BGR32: + pix_val =3D 13; + break; + default: + pix_val =3D 0; + break; + } + mfc_set_bits(reg, 0xf, 0, pix_val); + + /* for YUV format */ + if (pix_val < 4) + mfc_set_bits(reg, 0x3, 4, ctx->mem_type_10bit); + + mfc_set_bits(reg, 0x3, 10, block_align); + + MFC_CORE_WRITEL(reg, MFC_REG_PIXEL_FORMAT); + mfc_ctx_debug(2, "[FRAME] pix format: %d, mem_type_10bit: %d, (reg: %#x)\= n", + pix_val, ctx->mem_type_10bit, reg); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h index a23d25edce5d..e7c28b2f2b5d 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h @@ -13,6 +13,7 @@ #define __MFC_CORE_REG_API_H __FILE__ =20 #include "base/mfc_regs.h" + #include "base/mfc_utils.h" =20 /* version */ @@ -34,13 +35,226 @@ #define mfc_core_get_mfc_version() ((MFC_CORE_READL(MFC_REG_MFC_VERSION) \ >> MFC_REG_MFC_VER_SHFT) \ & MFC_REG_MFC_VER_MASK) +#define mfc_core_get_processing_cycle() MFC_CORE_READL(MFC_REG_PROCESSING_= CYCLE) + +/* decoding & display information */ +#define mfc_core_get_dec_status() (MFC_CORE_READL(MFC_REG_D_DECODED_STATUS= ) \ + & MFC_REG_DEC_STATUS_DECODED_STATUS_MASK) +#define mfc_core_get_disp_status() (MFC_CORE_READL(MFC_REG_D_DISPLAY_STATU= S) \ + & MFC_REG_DISP_STATUS_DISPLAY_STATUS_MASK) +#define mfc_core_get_res_change() ((MFC_CORE_READL(MFC_REG_D_DISPLAY_STATU= S) \ + >> MFC_REG_DISP_STATUS_RES_CHANGE_SHIFT) \ + & MFC_REG_DISP_STATUS_RES_CHANGE_MASK) +#define mfc_core_get_black_bar_detection() ((MFC_CORE_READL(MFC_REG_D_DISP= LAY_STATUS) \ + >> MFC_REG_DISP_STATUS_BLACK_BAR_DETECT_SHIFT) \ + & MFC_REG_DISP_STATUS_BLACK_BAR_DETECT_MASK) +#define mfc_core_get_decoded_frame_cnt() MFC_CORE_READL(MFC_REG_D_DECODED_= FRAME_COUNT) +#define mfc_core_get_display_frame_cnt() MFC_CORE_READL(MFC_REG_D_DISPLAY_= FRAME_COUNT) +#define mfc_core_get_dpb_change() ((MFC_CORE_READL(MFC_REG_D_DISPLAY_STATU= S) \ + >> MFC_REG_DISP_STATUS_NEED_DPB_CHANGE_SHIFT) \ + & MFC_REG_DISP_STATUS_NEED_DPB_CHANGE_MASK) +#define mfc_core_get_scratch_change() ((MFC_CORE_READL(MFC_REG_D_DISPLAY_S= TATUS) \ + >> MFC_REG_DISP_STATUS_NEED_SCRATCH_CHANGE_SHIFT) \ + & MFC_REG_DISP_STATUS_NEED_SCRATCH_CHANGE_MASK) +#define mfc_core_get_uncomp() ((MFC_CORE_READL(MFC_REG_D_DISPLAY_STATUS) \ + >> MFC_REG_DISP_STATUS_UNCOMP_SHIFT) \ + & MFC_REG_DISP_STATUS_UNCOMP_MASK) +#define mfc_core_get_last_display() ((MFC_CORE_READL(MFC_REG_D_DISPLAY_STA= TUS) \ + >> MFC_REG_DISP_STATUS_LAST_DISPLAY_FRAME_SHIFT) \ + & MFC_REG_DISP_STATUS_LAST_DISPLAY_FRAME_MASK) + +#define mfc_core_get_disp_frame_type() (MFC_CORE_READL(MFC_REG_D_DISPLAY_F= RAME_TYPE) \ + & MFC_REG_DISPLAY_FRAME_MASK) +#define mfc_core_get_dec_frame_type() (MFC_CORE_READL(MFC_REG_D_DECODED_FR= AME_TYPE) \ + & MFC_REG_DECODED_FRAME_MASK) +#define mfc_core_get_disp_idr_flag() \ + ((MFC_CORE_READL(MFC_REG_D_DISPLAY_FRAME_TYPE) \ + >> MFC_REG_DISPLAY_IDR_FLAG_SHIFT) \ + & MFC_REG_DISPLAY_IDR_FLAG_MASK) +#define mfc_core_get_dec_idr_flag() \ + ((MFC_CORE_READL(MFC_REG_D_DECODED_FRAME_TYPE) \ + >> MFC_REG_DECODED_IDR_FLAG_SHIFT) \ + & MFC_REG_DECODED_IDR_FLAG_MASK) +#define mfc_core_get_dec_temporal_id() \ + ((MFC_CORE_READL(MFC_REG_D_H264_INFO) \ + >> MFC_REG_D_H264_INFO_TEMPORAL_ID_SHIFT) \ + & MFC_REG_D_H264_INFO_TEMPORAL_ID_MASK) +#define mfc_core_get_interlace_type() ((MFC_CORE_READL(MFC_REG_D_DISPLAY_F= RAME_TYPE) \ + >> MFC_REG_DISPLAY_TEMP_INFO_SHIFT) \ + & MFC_REG_DISPLAY_TEMP_INFO_MASK) +#define mfc_core_is_interlace_picture() ((MFC_CORE_READL(MFC_REG_D_DISPLAY= _STATUS) \ + >> MFC_REG_DISP_STATUS_INTERLACE_SHIFT)\ + & MFC_REG_DISP_STATUS_INTERLACE_MASK) +#define mfc_core_is_mbaff_picture() ((MFC_CORE_READL(MFC_REG_D_H264_INFO) = \ + >> MFC_REG_D_H264_INFO_MBAFF_FRAME_FLAG_SHIFT)\ + & MFC_REG_D_H264_INFO_MBAFF_FRAME_FLAG_MASK) + +#define mfc_core_get_img_width() MFC_CORE_READL(MFC_REG_D_DISPLAY_FRAME_W= IDTH) +#define mfc_core_get_img_height() MFC_CORE_READL(MFC_REG_D_DISPLAY_FRAME_H= EIGHT) +#define mfc_core_get_disp_y_addr() MFC_CORE_DMA_READL(MFC_REG_D_DISPLAY_LU= MA_ADDR) +#define mfc_core_get_dec_y_addr() MFC_CORE_DMA_READL(MFC_REG_D_DECODED_LUM= A_ADDR) +#define mfc_core_get_crc_luma() MFC_CORE_READL(MFC_REG_D_DISPLAY_FIRST_PL= ANE_CRC) +#define mfc_core_get_crc_chroma() MFC_CORE_READL(MFC_REG_D_DISPLAY_SECOND_= PLANE_CRC) =20 /* kind of interrupt */ #define mfc_core_get_int_err() MFC_CORE_READL(MFC_REG_ERROR_CODE) =20 +/* additional information */ +#define mfc_core_get_consumed_stream() MFC_CORE_READL(MFC_REG_D_DECODED_N= AL_SIZE) +#define mfc_core_get_dpb_count() MFC_CORE_READL(MFC_REG_D_MIN_NUM_DPB) +#define mfc_core_get_min_dpb_size(x) \ + MFC_CORE_READL(MFC_REG_D_MIN_FIRST_PLANE_DPB_SIZE + ((x) * 4)) +#define mfc_core_get_scratch_size() MFC_CORE_READL(MFC_REG_D_MIN_SCRATCH_= BUFFER_SIZE) +#define mfc_core_get_stride_size(x) \ + MFC_CORE_READL(MFC_REG_D_FIRST_PLANE_DPB_STRIDE_SIZE + ((x) * 4)) + +#define mfc_core_get_mv_count() MFC_CORE_READL(MFC_REG_D_MIN_NUM_MV) #define mfc_core_get_inst_no() MFC_CORE_READL(MFC_REG_RET_INSTANCE_ID) +#define mfc_core_get_sei_avail() MFC_CORE_READL(MFC_REG_D_SEI_AVAIL) +#define mfc_core_get_sei_content_light() \ + MFC_CORE_READL(MFC_REG_D_CONTENT_LIGHT_LEVEL_INFO_SEI) +#define mfc_core_get_sei_mastering0() \ + MFC_CORE_READL(MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_0) +#define mfc_core_get_sei_mastering1() \ + MFC_CORE_READL(MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_1) +#define mfc_core_get_sei_mastering2() \ + MFC_CORE_READL(MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_2) +#define mfc_core_get_sei_mastering3() \ + MFC_CORE_READL(MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_3) +#define mfc_core_get_sei_mastering4() \ + MFC_CORE_READL(MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_4) +#define mfc_core_get_sei_mastering5() \ + MFC_CORE_READL(MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_5) +#define mfc_core_get_sei_avail_frame_pack() (MFC_CORE_READL(MFC_REG_D_SEI_= AVAIL) \ + & MFC_REG_D_SEI_AVAIL_FRAME_PACK_MASK) +#define mfc_core_get_sei_avail_content_light() ((MFC_CORE_READL(MFC_REG_D_= SEI_AVAIL) \ + >> MFC_REG_D_SEI_AVAIL_CONTENT_LIGHT_SHIFT) \ + & MFC_REG_D_SEI_AVAIL_CONTENT_LIGHT_MASK) +#define mfc_core_get_sei_avail_mastering_display() ((MFC_CORE_READL(MFC_RE= G_D_SEI_AVAIL) \ + >> MFC_REG_D_SEI_AVAIL_MASTERING_DISPLAY_SHIFT) \ + & MFC_REG_D_SEI_AVAIL_MASTERING_DISPLAY_MASK) + +#define mfc_core_get_video_signal_type() ((MFC_CORE_READL(MFC_REG_D_VIDEO_= SIGNAL_TYPE) \ + >> MFC_REG_D_VIDEO_SIGNAL_TYPE_FLAG_SHIFT) \ + & MFC_REG_D_VIDEO_SIGNAL_TYPE_FLAG_MASK) +#define mfc_core_get_colour_description() ((MFC_CORE_READL(MFC_REG_D_VIDEO= _SIGNAL_TYPE) \ + >> MFC_REG_D_COLOUR_DESCRIPTION_FLAG_SHIFT) \ + & MFC_REG_D_COLOUR_DESCRIPTION_FLAG_MASK) + +#define mfc_core_get_black_bar_pos_x() ((MFC_CORE_READL(MFC_REG_D_BLACK_B= AR_START_POS) \ + >> MFC_REG_D_BLACK_BAR_START_X_SHIFT) \ + & MFC_REG_D_BLACK_BAR_START_X_MASK) +#define mfc_core_get_black_bar_pos_y() ((MFC_CORE_READL(MFC_REG_D_BLACK_B= AR_START_POS) \ + >> MFC_REG_D_BLACK_BAR_START_Y_SHIFT) \ + & MFC_REG_D_BLACK_BAR_START_Y_MASK) +#define mfc_core_get_black_bar_image_w() ((MFC_CORE_READL(MFC_REG_D_BLACK_= BAR_IMAGE_SIZE) \ + >> MFC_REG_D_BLACK_BAR_IMAGE_W_SHIFT) \ + & MFC_REG_D_BLACK_BAR_IMAGE_W_MASK) +#define mfc_core_get_black_bar_image_h() ((MFC_CORE_READL(MFC_REG_D_BLACK_= BAR_IMAGE_SIZE) \ + >> MFC_REG_D_BLACK_BAR_IMAGE_H_SHIFT) \ + & MFC_REG_D_BLACK_BAR_IMAGE_H_MASK) +#define mfc_core_get_mvc_disp_view_id() (MFC_CORE_READL(MFC_REG_D_MVC_VIE= W_ID) \ + & MFC_REG_D_MVC_VIEW_ID_DISP_MASK) +#define mfc_core_get_mvc_view_id_dec_order() ((MFC_CORE_READL(MFC_REG_D_MV= C_VIEW_ID) \ + >> MFC_REG_D_MVC_VIEW_ID_DEC_SHIFT) \ + & MFC_REG_D_MVC_VIEW_ID_DEC_MASK) +#define mfc_core_get_mvc_view_id_disp_order() (MFC_CORE_READL(MFC_REG_D_MV= C_VIEW_ID) \ + & MFC_REG_D_MVC_VIEW_ID_DISP_MASK) +#define mfc_core_get_mvc_right_view_id() ((MFC_CORE_READL(MFC_REG_D_MVC_VI= EW_ID) \ + >> MFC_REG_D_MVC_RIGHT_VIEW_ID_SHIFT) \ + & MFC_REG_D_MVC_RIGHT_VIEW_ID_MASK) +#define mfc_core_get_profile() (MFC_CORE_READL(MFC_REG_D_DECODED_PICTURE_= PROFILE) \ + & MFC_REG_D_DECODED_PIC_PROFILE_MASK) + +#define mfc_core_get_display_delay() \ + ((MFC_CORE_READL(MFC_REG_D_DECODED_PICTURE_PROFILE) \ + >> MFC_REG_D_DISPLAY_DELAY_SHIFT) \ + & MFC_REG_D_DISPLAY_DELAY_MASK) +#define mfc_core_get_two_core_mode() \ + ((MFC_CORE_READL(MFC_REG_D_DECODED_PICTURE_PROFILE) \ + >> MFC_REG_D_TWO_MFC_MODE_SHIFT) \ + & MFC_REG_D_TWO_MFC_MODE_MASK) +#define mfc_core_get_dec_used_flag() (((unsigned long)(MFC_CORE_READL \ + (MFC_REG_D_USED_DPB_FLAG_UPPER)) << 32) | \ + MFC_CORE_READL(MFC_REG_D_USED_DPB_FLAG_LOWER)) + +#define mfc_core_get_num_of_tile() ((MFC_CORE_READL(MFC_REG_D_DECODED_STA= TUS) \ + >> MFC_REG_DEC_STATUS_NUM_OF_TILE_SHIFT) \ + & MFC_REG_DEC_STATUS_NUM_OF_TILE_MASK) + +static inline void mfc_core_dec_get_crop_info(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + u32 left, right, top, bottom; + + left =3D MFC_CORE_READL(MFC_REG_D_DISPLAY_CROP_INFO1); + right =3D left >> MFC_REG_D_SHARED_CROP_RIGHT_SHIFT; + left =3D left & MFC_REG_D_SHARED_CROP_LEFT_MASK; + top =3D MFC_CORE_READL(MFC_REG_D_DISPLAY_CROP_INFO2); + bottom =3D top >> MFC_REG_D_SHARED_CROP_BOTTOM_SHIFT; + top =3D top & MFC_REG_D_SHARED_CROP_TOP_MASK; + + dec->cr_left =3D left; + dec->cr_right =3D right; + dec->cr_top =3D top; + dec->cr_bot =3D bottom; +} + +static inline void mfc_core_update_tag(struct mfc_core *core, struct mfc_c= tx *ctx, + int tag) +{ + if (MFC_CORE_READL(MFC_REG_D_PICTURE_TAG) !=3D tag) { + mfc_ctx_debug(2, "reused src's tag is different so update to %d\n", + tag); + MFC_CORE_WRITEL(tag, MFC_REG_D_PICTURE_TAG); + } +} + +static inline void mfc_core_set_migration_addr(struct mfc_core *core, stru= ct mfc_ctx *ctx, + dma_addr_t fw_addr, dma_addr_t common_ctx_addr) +{ + struct mfc_special_buf *ctx_buf =3D NULL; + + ctx_buf =3D &core->common_ctx_buf; + + MFC_CORE_DMA_WRITEL(fw_addr, MFC0_REG_RISC_BASE_ADDR); + MFC_CORE_WRITEL(common_ctx_addr, MFC0_REG_COMMON_CONTEXT_MEM_ADDR); + MFC_CORE_WRITEL(ctx_buf->daddr, MFC1_REG_COMMON_CONTEXT_MEM_ADDR); + mfc_core_debug(2, "migration MFC0 FW base: %#llx, context: %#llx, MFC1 co= ntext: %#llx\n", + fw_addr, common_ctx_addr, ctx_buf->daddr); +} + +static inline void mfc_core_clear_main_core_context_flush_done(struct mfc_= core *core) +{ + /* + * When op_mode is changed from single to multi, + * by calling NAL_START of both core simultaneously, + * FW can get the invalid information of main core. + * So, when switching op_mode to multi-core mode, + * clear the status of main core about this. + */ + MFC_CORE_WRITEL(0, MFC0_REG_CONTEXT_FLUSH_DONE); + MFC_TRACE_CORE("Clear MAIN_CORE_CONTEXT_FLUSH_DONE\n"); +} =20 void mfc_core_dbg_enable(struct mfc_core *core); void mfc_core_dbg_disable(struct mfc_core *core); void mfc_core_dbg_set_addr(struct mfc_core *core); + +unsigned int mfc_get_frame_error_type(struct mfc_ctx *ctx, unsigned int er= r); + +void mfc_core_set_dec_dpb_and_scratch(struct mfc_core_ctx *core_ctx, dma_a= ddr_t scratch_addr); +int mfc_core_set_dec_codec_buffers(struct mfc_core_ctx *core_ctx); +int mfc_core_set_dec_stream_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + struct mfc_buf *mfc_buf, unsigned int start_num_byte, + unsigned int buf_size); + +void mfc_core_set_dynamic_dpb(struct mfc_core *core, struct mfc_ctx *ctx, + struct mfc_buf *dst_vb); + +void mfc_core_get_img_size(struct mfc_core *core, struct mfc_ctx *ctx, + enum mfc_get_img_size img_size); +void mfc_core_set_pixel_format(struct mfc_core *core, struct mfc_ctx *ctx, + unsigned int format); #endif /* __MFC_CORE_REG_API_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c b/dr= ivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c index de13cdb9c99a..e4b839eda2da 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c @@ -13,6 +13,9 @@ =20 #include "mfc_core_hw_reg_api.h" =20 +#include "base/mfc_qos.h" +#include "base/mfc_queue.h" + #define R2H_BIT(x) ({ \ typeof(x) _x =3D (x); \ (_x > 0) ? BIT(_x - 1) : 0; \ @@ -40,10 +43,26 @@ static inline unsigned int __mfc_r2h_bit_mask(int cmd) void mfc_get_corelock_ctx(struct mfc_ctx *ctx) { unsigned long flags; + unsigned int timeout =3D MFC_INT_TIMEOUT * MFC_INT_TIMEOUT_CNT; + int ret; =20 spin_lock_irqsave(&ctx->corelock.lock, flags); - mfc_ctx_debug(3, "[CORELOCK] get_corelock: cnt %d\n", - ctx->corelock.cnt); + mfc_ctx_debug(3, "[CORELOCK] get_corelock: cnt %d, migrate %d\n", + ctx->corelock.cnt, ctx->corelock.migrate); + + if (ctx->corelock.migrate) { + spin_unlock_irqrestore(&ctx->corelock.lock, flags); + mfc_ctx_debug(2, "[CORELOCK] now migration... start waiting corelock\n"); + ret =3D wait_event_timeout(ctx->corelock.migrate_wq, + (ctx->corelock.migrate =3D=3D 0), + msecs_to_jiffies(timeout)); + if (ret =3D=3D 0) + mfc_ctx_err("[CORELOCK] waiting corelock for migration timed out\n"); + else + mfc_ctx_debug(2, "[CORELOCK] finished waiting corelock for migration\n"= ); + + spin_lock_irqsave(&ctx->corelock.lock, flags); + } =20 ctx->corelock.cnt++; =20 @@ -62,8 +81,57 @@ void mfc_release_corelock_ctx(struct mfc_ctx *ctx) else if (ctx->corelock.cnt < 0) mfc_ctx_err("[CORELOCK] wrong corelock cnt %d\n", ctx->corelock.cnt); =20 - mfc_ctx_debug(3, "[CORELOCK] release_corelock: cnt %d\n", - ctx->corelock.cnt); + mfc_ctx_debug(3, "[CORELOCK] release_corelock: cnt %d, migrate %d\n", + ctx->corelock.cnt, ctx->corelock.migrate); + spin_unlock_irqrestore(&ctx->corelock.lock, flags); +} + +int mfc_get_corelock_migrate(struct mfc_ctx *ctx) +{ + unsigned long flags; + unsigned int timeout =3D MFC_INT_TIMEOUT * MFC_INT_TIMEOUT_CNT; + int ret; + + spin_lock_irqsave(&ctx->corelock.lock, flags); + ctx->corelock.migrate =3D 1; + mfc_ctx_debug(2, "[CORELOCK] get_corelock_migrate: cnt %d, migrate %d\n", + ctx->corelock.cnt, ctx->corelock.migrate); + + if (ctx->corelock.cnt) { + spin_unlock_irqrestore(&ctx->corelock.lock, flags); + mfc_ctx_debug(2, "[CORELOCK] start waiting corelock\n"); + ret =3D wait_event_timeout(ctx->corelock.wq, + (ctx->corelock.cnt =3D=3D 0), + msecs_to_jiffies(timeout)); + if (ret =3D=3D 0) { + spin_lock_irqsave(&ctx->corelock.lock, flags); + ctx->corelock.migrate =3D 0; + wake_up(&ctx->corelock.migrate_wq); + spin_unlock_irqrestore(&ctx->corelock.lock, flags); + mfc_ctx_err("[CORELOCK] waiting corelock timed out (%d)\n", + ctx->corelock.cnt); + return -EBUSY; + } + + mfc_ctx_debug(2, "[CORELOCK] finished waiting corelock\n"); + spin_lock_irqsave(&ctx->corelock.lock, flags); + } + + spin_unlock_irqrestore(&ctx->corelock.lock, flags); + + return 0; +} + +void mfc_release_corelock_migrate(struct mfc_ctx *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->corelock.lock, flags); + + ctx->corelock.migrate =3D 0; + mfc_ctx_debug(2, "[CORELOCK] wakeup migration wait\n"); + wake_up(&ctx->corelock.migrate_wq); + spin_unlock_irqrestore(&ctx->corelock.lock, flags); } =20 @@ -188,3 +256,114 @@ void mfc_wake_up_core(struct mfc_core *core, unsigned= int reason, core->int_err =3D err; wake_up(&core->cmd_wq); } + +/* Wake up context wait_queue */ +void mfc_wake_up_core_ctx(struct mfc_core_ctx *core_ctx, unsigned int reas= on, + unsigned int err) +{ + core_ctx->int_condition =3D 1; + core_ctx->int_reason =3D reason; + core_ctx->int_err =3D err; + wake_up(&core_ctx->cmd_wq); +} + +static int __mfc_dec_ctx_ready_set_bit(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + int is_ready =3D 0; + int src_buf_cnt, dst_buf_cnt; + + src_buf_cnt =3D mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_= buf_queue); + dst_buf_cnt =3D mfc_get_available_dpb_count(core_ctx); + + /* Context is to parse header */ + if (core_ctx->state =3D=3D MFCINST_GOT_INST && src_buf_cnt) + is_ready =3D 1; + + /* Context is to decode a frame */ + else if ((core_ctx->state =3D=3D MFCINST_RUNNING) && (ctx->wait_state =3D= =3D WAIT_NONE)) { + if (src_buf_cnt && dst_buf_cnt) + is_ready =3D 1; + } + + /* Context is to return last frame */ + else if (core_ctx->state =3D=3D MFCINST_FINISHING && dst_buf_cnt) + is_ready =3D 1; + + /* Context is to set buffers */ + else if (core_ctx->state =3D=3D MFCINST_HEAD_PARSED && + (ctx->wait_state =3D=3D WAIT_NONE && dst_buf_cnt)) + is_ready =3D 1; + + /* Resolution change + * In the case of 2core DRC, state could be SWITCH_TO_SINGLE. + * After subcore deinit, state will be changed to SINGLE. + */ + else if ((core_ctx->state =3D=3D MFCINST_RES_CHANGE_INIT || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH_FINISHED) && dst_buf_cn= t && + (IS_SINGLE_MODE(ctx) || ctx->op_mode =3D=3D MFC_OP_SWITCH_TO_SINGLE)) + is_ready =3D 1; + + else if (core_ctx->state =3D=3D MFCINST_RES_CHANGE_END && src_buf_cnt) + is_ready =3D 1; + + return is_ready; +} + +int mfc_ctx_ready_set_bit_raw(struct mfc_core_ctx *core_ctx, unsigned long= *bits, bool set) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_ctx *ctx =3D core_ctx->ctx; + int is_ready =3D 0; + + /* If shutdown is called, do not try any cmd */ + if (core->shutdown) + return 0; + + mfc_debug(1, "[MFC-%d][c:%d] src =3D %d(ready =3D %d), dst =3D %d, state = =3D %d, capstat =3D %d, waitstat =3D %d\n", + core->id, ctx->num, + mfc_get_queue_count(&ctx->buf_queue_lock, + &core_ctx->src_buf_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, + &ctx->src_buf_ready_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, + &ctx->dst_buf_queue), + core_ctx->state, ctx->capture_state, ctx->wait_state); + + is_ready =3D __mfc_dec_ctx_ready_set_bit(core_ctx); + + if (is_ready && set) { + /* if the ctx is ready and request set_bit, set the work_bit */ + __set_bit(ctx->num, bits); + } else if (!is_ready && !set) { + /* if the ctx is not ready and request clear_bit, clear the work_bit */ + __clear_bit(ctx->num, bits); + } else { + if (set) { + /* If the ctx is not ready, this is not included to S/W driver margin */ + mfc_debug(2, "ctx is not ready\n"); + } + } + + return is_ready; +} + +static int __mfc_ctx_ready_set_bit(struct mfc_core_ctx *core_ctx, + struct mfc_bits *data, bool set) +{ + unsigned long flags; + int is_ready =3D 0; + + /* The ready condition check and set work_bit should be synchronized */ + spin_lock_irqsave(&data->lock, flags); + is_ready =3D mfc_ctx_ready_set_bit_raw(core_ctx, &data->bits, set); + spin_unlock_irqrestore(&data->lock, flags); + + return is_ready; +} + +int mfc_ctx_ready_set_bit(struct mfc_core_ctx *core_ctx, struct mfc_bits *= data) +{ + return __mfc_ctx_ready_set_bit(core_ctx, data, true); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.h b/dr= ivers/media/platform/samsung/exynos-mfc/mfc_core_sync.h index 2c19819048de..94c1128b16cc 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.h @@ -16,10 +16,19 @@ =20 void mfc_get_corelock_ctx(struct mfc_ctx *ctx); void mfc_release_corelock_ctx(struct mfc_ctx *ctx); +int mfc_get_corelock_migrate(struct mfc_ctx *ctx); +void mfc_release_corelock_migrate(struct mfc_ctx *ctx); =20 int mfc_wait_for_done_core(struct mfc_core *core, int command); int mfc_wait_for_done_core_ctx(struct mfc_core_ctx *core_ctx, int command); void mfc_wake_up_core(struct mfc_core *core, unsigned int reason, unsigned int err); +void mfc_wake_up_core_ctx(struct mfc_core_ctx *core_ctx, unsigned int reas= on, + unsigned int err); + int mfc_core_get_new_ctx(struct mfc_core *core); + +int mfc_ctx_ready_set_bit_raw(struct mfc_core_ctx *core_ctx, unsigned long= *bits, bool set); +int mfc_ctx_ready_set_bit(struct mfc_core_ctx *core_ctx, struct mfc_bits *= data); + #endif /* __MFC_CORE_SYNC_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:11 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 2CC102737EB for ; Tue, 30 Sep 2025 03:56:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204587; cv=none; b=iU9u6/40Rh2w2KP+kOEqtnXheahWzUtPit1ZGF9S2zteP91uIeoZYATc9tNXHCbAaJUrahNCX8UBTGEmVrkzLmh8wX1MXGt4WERb7N5dJcYg6X/fYKCc1PtvbWf92vx7gKz6xSHEorHToROYNm6wB/4nydgc72WT7IKJ1mwg2t8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204587; c=relaxed/simple; bh=i6E64WhEgJsqp2ayv6Lc4jnBEnKJIN2hCXvbLp6fdWo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=NF3+W/4sY2AXenUiSnJH6nFgergR21rzBDJSDZcz8wA0SYyF9ZfIZAhZaaYVOmevsVKAt/2DnTjYe8fwr5PmH9p6FPVAj+vLXgrvrdZjduaNEbSNqTfDZB0K3qxJoSCWw9g6jhVw9RRPXZ+osoSCB4HEk/gEjbgHVqXSi/NuDpE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=GmPIpW5/; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="GmPIpW5/" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035621epoutp021efe3493f7a4f951d3c05a04eefc4c46~p8z8NJiK32603326033epoutp02r for ; Tue, 30 Sep 2025 03:56:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035621epoutp021efe3493f7a4f951d3c05a04eefc4c46~p8z8NJiK32603326033epoutp02r DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204581; bh=70sagfgmj1f/Q2Q8CPKwElFUb4xo/jeygmeqzYkwHvY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GmPIpW5/bdZ3BU1x2N5cDbHNFxAcPwK5SeP6fGW7AqXC5TZEC63w1KyHKbz1LevZZ Dy9vHnWxBetxySmcGj/xjLALNeLj7h2RDtSOXfFTfnW4uMJFH2GtGyLlkr6LAdJuZU OFh4iaEb4TNGs2gMo+fTXgjVY9IMwriKltu+6R8M= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035620epcas5p1354fd9a7e19e9ec61a9b26f38ee5f11d~p8z7jqe981575615756epcas5p1m; Tue, 30 Sep 2025 03:56:20 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.93]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPPH69Trz6B9m7; Tue, 30 Sep 2025 03:56:19 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035619epcas5p1a3a8cd3e71442235e571c840a78286d8~p8z59h9tt1532115321epcas5p1z; Tue, 30 Sep 2025 03:56:19 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035616epsmtip199ef4b002a7cb9438b2754d1267cffad~p8z3hPQXq2880128801epsmtip1U; Tue, 30 Sep 2025 03:56:16 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 14/29] =?UTF-8?q?media:=20mfc:=20Add=20buffer=E2=80=91cont?= =?UTF-8?q?rol=20framework?= Date: Tue, 30 Sep 2025 09:33:33 +0530 Message-Id: <20250930040348.3702923-15-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035619epcas5p1a3a8cd3e71442235e571c840a78286d8 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035619epcas5p1a3a8cd3e71442235e571c840a78286d8 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Add core buffer=E2=80=91control module (mfc_core_buf_ctrl.c) with set/get/recover/restore ops. - Add context=E2=80=91control layer (mfc_ctx_ctrl.c) defining V4L2 decoder controls and per=E2=80=91buffer handling. - Export mfc_bufs_ops and mfc_ctrls_ops for integration with existing MFC core Provide flexible V4L2=E2=80=91based management of hardware registers, including volatile/non=E2=80=91volatile settings and buffer=E2=80=91level restore/recovery. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 4 +- .../samsung/exynos-mfc/mfc_core_buf_ctrl.c | 160 +++++ .../samsung/exynos-mfc/mfc_ctx_ctrl.c | 666 ++++++++++++++++++ 3 files changed, 828 insertions(+), 2 deletions(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_= ctrl.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index 9def2686cd4e..19e38c886255 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -5,7 +5,7 @@ ccflags-y +=3D -I$(srctree)/$(src) #Dev interface layer exynos_mfc-y +=3D mfc.o #Dev control layer -exynos_mfc-y +=3D mfc_rm.o mfc_debugfs.o +exynos_mfc-y +=3D mfc_rm.o mfc_ctx_ctrl.o mfc_debugfs.o #Core interface layer exynos_mfc-y +=3D mfc_core.o mfc_core_ops.o mfc_core_isr.o #Core control layer @@ -13,7 +13,7 @@ exynos_mfc-y +=3D mfc_core_hwlock.o mfc_core_intlock.o mf= c_core_run.o exynos_mfc-y +=3D mfc_core_pm.o exynos_mfc-y +=3D mfc_core_sync.o mfc_core_sched_prio.o #Core HW access layer -exynos_mfc-y +=3D mfc_core_cmd.o +exynos_mfc-y +=3D mfc_core_buf_ctrl.o mfc_core_cmd.o exynos_mfc-y +=3D mfc_core_hw_reg_api.o mfc_core_reg_api.o #Plugin interface layer #Plugin control layer diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c = b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c new file mode 100644 index 000000000000..56dc3e734d02 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_buf_ctrl.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_reg_api.h" + +static int mfc_core_set_buf_ctrls(struct mfc_core *core, + struct mfc_ctx *ctx, struct list_head *head) +{ + struct mfc_buf_ctrl *buf_ctrl; + unsigned int value =3D 0; + + list_for_each_entry(buf_ctrl, head, list) { + if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET) || !buf_ctrl->has_new) + continue; + + if (buf_ctrl->mode =3D=3D MFC_CTRL_MODE_SFR) { + /* read old vlaue */ + value =3D MFC_CORE_READL(buf_ctrl->addr); + + /* save old vlaue for recovery */ + if (buf_ctrl->is_volatile) + buf_ctrl->old_val =3D + (value >> buf_ctrl->shft) & buf_ctrl->mask; + + /* write new value */ + value &=3D ~(buf_ctrl->mask << buf_ctrl->shft); + value |=3D + ((buf_ctrl->val & buf_ctrl->mask) + << buf_ctrl->shft); + MFC_CORE_WRITEL(value, buf_ctrl->addr); + } + + /* set change flag bit */ + if (buf_ctrl->flag_mode =3D=3D MFC_CTRL_MODE_SFR) { + value =3D MFC_CORE_READL(buf_ctrl->flag_addr); + value |=3D BIT(buf_ctrl->flag_shft); + MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); + } + + buf_ctrl->has_new =3D 0; + buf_ctrl->updated =3D 1; + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG) + ctx->stored_tag =3D buf_ctrl->val; + + mfc_ctx_debug(6, + "[CTRLS] Set buffer control id: 0x%08x, val: %d (%#x)\n", + buf_ctrl->id, buf_ctrl->val, buf_ctrl->val); + } + + return 0; +} + +static int mfc_core_get_buf_ctrls(struct mfc_core *core, + struct mfc_ctx *ctx, struct list_head *head) +{ + struct mfc_buf_ctrl *buf_ctrl; + unsigned int value =3D 0; + + list_for_each_entry(buf_ctrl, head, list) { + if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET)) + continue; + + if (buf_ctrl->mode =3D=3D MFC_CTRL_MODE_SFR) + value =3D MFC_CORE_READL(buf_ctrl->addr); + + value =3D (value >> buf_ctrl->shft) & buf_ctrl->mask; + + buf_ctrl->val =3D value; + buf_ctrl->has_new =3D 1; + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_FRAME_ERROR_TYPE) + buf_ctrl->val =3D mfc_get_frame_error_type(ctx, value); + + mfc_ctx_debug(6, + "[CTRLS] Get buffer control id: 0x%08x, val: %d (%#x)\n", + buf_ctrl->id, buf_ctrl->val, buf_ctrl->val); + } + + return 0; +} + +static int mfc_core_recover_buf_ctrls(struct mfc_core *core, + struct mfc_ctx *ctx, + struct list_head *head) +{ + struct mfc_buf_ctrl *buf_ctrl; + unsigned int value =3D 0; + + list_for_each_entry(buf_ctrl, head, list) { + if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET) || + !buf_ctrl->is_volatile || !buf_ctrl->updated) + continue; + + if (buf_ctrl->mode =3D=3D MFC_CTRL_MODE_SFR) { + value =3D MFC_CORE_READL(buf_ctrl->addr); + value &=3D ~(buf_ctrl->mask << buf_ctrl->shft); + value |=3D + ((buf_ctrl->old_val & buf_ctrl->mask) + << buf_ctrl->shft); + MFC_CORE_WRITEL(value, buf_ctrl->addr); + } + + /* clear change flag bit */ + if (buf_ctrl->flag_mode =3D=3D MFC_CTRL_MODE_SFR) { + value =3D MFC_CORE_READL(buf_ctrl->flag_addr); + value &=3D ~BIT(buf_ctrl->flag_shft); + MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); + } + + buf_ctrl->updated =3D 0; + mfc_ctx_debug(6, + "[CTRLS] Recover buffer control id: 0x%08x, old val: %d\n", + buf_ctrl->id, buf_ctrl->old_val); + } + + return 0; +} + +/* + * This function is used when you want to restore buffer ctrls. + * 1) NAL_Q stop: It is enqueued in the NAL_Q queue, + * but it must be restored because HW is not used. + * 2) DRC: The input buffer that caused the DRC was used for HW, but it mu= st be reused. + */ +static int mfc_core_restore_buf_ctrls(struct mfc_ctx *ctx, + struct list_head *head) +{ + struct mfc_buf_ctrl *buf_ctrl; + + list_for_each_entry(buf_ctrl, head, list) { + if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET) || + !(buf_ctrl->updated)) + continue; + + buf_ctrl->has_new =3D 1; + buf_ctrl->updated =3D 0; + + mfc_ctx_debug(6, + "[CTRLS] Restore buffer control id: 0x%08x, val: %d\n", + buf_ctrl->id, buf_ctrl->val); + } + + return 0; +} + +struct mfc_bufs_ops mfc_bufs_ops =3D { + .core_set_buf_ctrls =3D mfc_core_set_buf_ctrls, + .core_get_buf_ctrls =3D mfc_core_get_buf_ctrls, + .core_recover_buf_ctrls =3D mfc_core_recover_buf_ctrls, + .core_restore_buf_ctrls =3D mfc_core_restore_buf_ctrls, +}; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c new file mode 100644 index 000000000000..8846230f1e20 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_ctx_ctrl.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "base/mfc_common.h" + +static struct mfc_ctrl_cfg mfc_dec_ctrl_list[] =3D { + { + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_PICTURE_TAG, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_RET_PICTURE_TAG_TOP, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_DISPLAY_STATUS, + .mask =3D 0x7, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + /* CRC related definitions are based on non-H.264 type */ + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_DISPLAY_FIRST_PLANE_CRC, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_DISPLAY_SECOND_PLANE_CRC, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA1, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_DISPLAY_THIRD_PLANE_CRC, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_LUMA, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_DISPLAY_FIRST_PLANE_2BIT_CRC, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_2BIT_CHROMA, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_DISPLAY_SECOND_PLANE_2BIT_CRC, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_GENERATED, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_DISPLAY_STATUS, + .mask =3D 0x1, + .shft =3D 6, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_SEI_AVAIL, + .mask =3D 0x1, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRGMENT_ID, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_FRAME_PACK_ARRGMENT_ID, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FP_INFO, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_FRAME_PACK_SEI_INFO, + .mask =3D 0x3FFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FP_GRID_POS, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_FRAME_PACK_GRID_POS, + .mask =3D 0xFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_H264_MVC_VIEW_ID, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_MVC_VIEW_ID, + .mask =3D 0xFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MAX_PIC_AVERAGE_LIGHT, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_CONTENT_LIGHT_LEVEL_INFO_SEI, + .mask =3D 0xFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MAX_CONTENT_LIGHT, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_CONTENT_LIGHT_LEVEL_INFO_SEI, + .mask =3D 0xFFFF, + .shft =3D 16, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MAX_DISPLAY_LUMINANCE, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_0, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MIN_DISPLAY_LUMINANCE, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_1, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_MATRIX_COEFFICIENTS, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_VIDEO_SIGNAL_TYPE, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_FORMAT, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_VIDEO_SIGNAL_TYPE, + .mask =3D 0x7, + .shft =3D 26, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_VIDEO_SIGNAL_TYPE, + .mask =3D 0x1, + .shft =3D 25, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_VIDEO_SIGNAL_TYPE, + .mask =3D 0xFF, + .shft =3D 16, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_TRANSFER_CHARACTERISTICS, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_VIDEO_SIGNAL_TYPE, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_WHITE_POINT, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_2, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_0, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_3, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_1, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_4, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_2, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_MASTERING_DISPLAY_COLOUR_VOLUME_SEI_5, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_POC, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_D_RET_PICTURE_TIME_TOP, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_FRAME_ERROR_TYPE, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_ERROR_CODE, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* buffer additional information */ + .type =3D MFC_CTRL_TYPE_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_NONE, + .flag_mode =3D MFC_CTRL_MODE_NONE, + }, + { /* buffer additional information */ + .type =3D MFC_CTRL_TYPE_DST, + .id =3D V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_NONE, + .flag_mode =3D MFC_CTRL_MODE_NONE, + } +}; + +#define NUM_DEC_CTRL_CFGS ARRAY_SIZE(mfc_dec_ctrl_list) + +static void mfc_ctrl_cleanup_ctx(struct mfc_ctx *ctx) +{ + struct mfc_ctx_ctrl *ctx_ctrl; + + while (!list_empty(&ctx->ctrls)) { + ctx_ctrl =3D list_entry((&ctx->ctrls)->next, + struct mfc_ctx_ctrl, list); + list_del(&ctx_ctrl->list); + kfree(ctx_ctrl); + } + + INIT_LIST_HEAD(&ctx->ctrls); +} + +static int __mfc_ctrl_init_ctx(struct mfc_ctx *ctx, struct mfc_ctrl_cfg *c= trl_list, int count) +{ + unsigned long i; + struct mfc_ctx_ctrl *ctx_ctrl; + + INIT_LIST_HEAD(&ctx->ctrls); + + for (i =3D 0; i < count; i++) { + ctx_ctrl =3D kzalloc(sizeof(*ctx_ctrl), GFP_KERNEL); + if (!ctx_ctrl) { + mfc_ctrl_cleanup_ctx(ctx); + + return -ENOMEM; + } + + ctx_ctrl->type =3D ctrl_list[i].type; + ctx_ctrl->id =3D ctrl_list[i].id; + ctx_ctrl->addr =3D ctrl_list[i].addr; + ctx_ctrl->set.has_new =3D 0; + ctx_ctrl->set.val =3D 0; + ctx_ctrl->get.has_new =3D 0; + ctx_ctrl->get.val =3D 0; + + list_add_tail(&ctx_ctrl->list, &ctx->ctrls); + } + + return 0; +} + +static int mfc_ctrl_init_ctx(struct mfc_ctx *ctx) +{ + if (ctx->type =3D=3D MFCINST_DECODER) + return __mfc_ctrl_init_ctx(ctx, mfc_dec_ctrl_list, NUM_DEC_CTRL_CFGS); + + mfc_ctx_err("[CTRLS] invalid type %d\n", ctx->type); + return -EINVAL; +} + +static void mfc_ctrl_reset_buf(struct list_head *head) +{ + struct mfc_buf_ctrl *buf_ctrl; + + list_for_each_entry(buf_ctrl, head, list) { + buf_ctrl->has_new =3D 0; + buf_ctrl->val =3D 0; + buf_ctrl->old_val =3D 0; + buf_ctrl->updated =3D 0; + } +} + +static void __mfc_ctrl_cleanup_buf(struct list_head *head) +{ + struct mfc_buf_ctrl *buf_ctrl; + + while (!list_empty(head)) { + buf_ctrl =3D list_entry(head->next, + struct mfc_buf_ctrl, list); + list_del(&buf_ctrl->list); + kfree(buf_ctrl); + } + + INIT_LIST_HEAD(head); +} + +static int mfc_ctrl_cleanup_buf(struct mfc_ctx *ctx, enum mfc_ctrl_type ty= pe, unsigned int index) +{ + struct list_head *head; + + if (index >=3D MFC_MAX_BUFFERS) { + mfc_ctx_err("Per-buffer control index is out of range\n"); + return -EINVAL; + } + + if (type & MFC_CTRL_TYPE_SRC) { + if (!(test_and_clear_bit(index, ctx->src_ctrls_avail))) + return 0; + + head =3D &ctx->src_ctrls[index]; + } else if (type & MFC_CTRL_TYPE_DST) { + if (!(test_and_clear_bit(index, ctx->dst_ctrls_avail))) + return 0; + + head =3D &ctx->dst_ctrls[index]; + } else { + mfc_ctx_err("Control type mismatch. type : %d\n", type); + return -EINVAL; + } + + __mfc_ctrl_cleanup_buf(head); + + return 0; +} + +static int __mfc_ctrl_init_buf(struct mfc_ctx *ctx, struct mfc_ctrl_cfg *c= trl_list, + enum mfc_ctrl_type type, unsigned int index, int count) +{ + unsigned long i; + struct mfc_buf_ctrl *buf_ctrl; + struct list_head *head; + + if (index >=3D MFC_MAX_BUFFERS) { + mfc_ctx_err("Per-buffer control index is out of range\n"); + return -EINVAL; + } + + if (type & MFC_CTRL_TYPE_SRC) { + if (test_bit(index, ctx->src_ctrls_avail)) { + mfc_ctrl_reset_buf(&ctx->src_ctrls[index]); + + return 0; + } + + head =3D &ctx->src_ctrls[index]; + } else if (type & MFC_CTRL_TYPE_DST) { + if (test_bit(index, ctx->dst_ctrls_avail)) { + mfc_ctrl_reset_buf(&ctx->dst_ctrls[index]); + + return 0; + } + + head =3D &ctx->dst_ctrls[index]; + } else { + mfc_ctx_err("Control type mismatch. type : %d\n", type); + return -EINVAL; + } + + INIT_LIST_HEAD(head); + + for (i =3D 0; i < count; i++) { + if (!(type & ctrl_list[i].type)) + continue; + + buf_ctrl =3D kzalloc(sizeof(*buf_ctrl), GFP_KERNEL); + if (!buf_ctrl) { + __mfc_ctrl_cleanup_buf(head); + + return -ENOMEM; + } + + buf_ctrl->type =3D ctrl_list[i].type; + buf_ctrl->id =3D ctrl_list[i].id; + buf_ctrl->is_volatile =3D ctrl_list[i].is_volatile; + buf_ctrl->mode =3D ctrl_list[i].mode; + buf_ctrl->addr =3D ctrl_list[i].addr; + buf_ctrl->mask =3D ctrl_list[i].mask; + buf_ctrl->shft =3D ctrl_list[i].shft; + buf_ctrl->flag_mode =3D ctrl_list[i].flag_mode; + buf_ctrl->flag_addr =3D ctrl_list[i].flag_addr; + buf_ctrl->flag_shft =3D ctrl_list[i].flag_shft; + + list_add_tail(&buf_ctrl->list, head); + } + + mfc_ctrl_reset_buf(head); + + if (type & MFC_CTRL_TYPE_SRC) + set_bit(index, ctx->src_ctrls_avail); + else + set_bit(index, ctx->dst_ctrls_avail); + + return 0; +} + +static int mfc_ctrl_init_buf(struct mfc_ctx *ctx, enum mfc_ctrl_type type,= unsigned int index) +{ + if (ctx->type =3D=3D MFCINST_DECODER) + return __mfc_ctrl_init_buf(ctx, mfc_dec_ctrl_list, type, index, NUM_DEC_= CTRL_CFGS); + + mfc_ctx_err("[CTRLS] invalid type %d\n", ctx->type); + return -EINVAL; +} + +static void mfc_ctrl_to_buf(struct mfc_ctx *ctx, struct list_head *head) +{ + struct mfc_ctx_ctrl *ctx_ctrl; + struct mfc_buf_ctrl *buf_ctrl; + + list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) { + if (!(ctx_ctrl->type & MFC_CTRL_TYPE_SET) || + !ctx_ctrl->set.has_new) + continue; + + list_for_each_entry(buf_ctrl, head, list) { + if (!(buf_ctrl->type & MFC_CTRL_TYPE_SET)) + continue; + + if (buf_ctrl->id =3D=3D ctx_ctrl->id) { + buf_ctrl->has_new =3D 1; + buf_ctrl->val =3D ctx_ctrl->set.val; + + if (buf_ctrl->is_volatile) + buf_ctrl->updated =3D 0; + + ctx_ctrl->set.has_new =3D 0; + + break; + } + } + } +} + +static void mfc_ctrl_to_ctx(struct mfc_ctx *ctx, struct list_head *head) +{ + struct mfc_ctx_ctrl *ctx_ctrl; + struct mfc_buf_ctrl *buf_ctrl; + + list_for_each_entry(buf_ctrl, head, list) { + if (!(buf_ctrl->type & MFC_CTRL_TYPE_GET) || !buf_ctrl->has_new) + continue; + + list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) { + if (!(ctx_ctrl->type & MFC_CTRL_TYPE_GET)) + continue; + + if (ctx_ctrl->id =3D=3D buf_ctrl->id) { + if (ctx_ctrl->get.has_new) + mfc_ctx_debug(8, "Overwrite ctx_ctrl value id: %#x, val: %d\n", + ctx_ctrl->id, ctx_ctrl->get.val); + + ctx_ctrl->get.has_new =3D 1; + ctx_ctrl->get.val =3D buf_ctrl->val; + + buf_ctrl->has_new =3D 0; + } + } + } +} + +static int mfc_ctrl_get_buf_val(struct mfc_ctx *ctx, struct list_head *hea= d, unsigned int id) +{ + struct mfc_buf_ctrl *buf_ctrl; + int value =3D 0; + + list_for_each_entry(buf_ctrl, head, list) { + if (buf_ctrl->id =3D=3D id) { + value =3D buf_ctrl->val; + mfc_ctx_debug(6, "[CTRLS] Get buffer control id: 0x%08x, val: %d (%#x)\= n", + buf_ctrl->id, value, value); + break; + } + } + + return value; +} + +static void mfc_ctrl_update_buf_val(struct mfc_ctx *ctx, struct list_head = *head, + unsigned int id, int value) +{ + struct mfc_buf_ctrl *buf_ctrl; + + list_for_each_entry(buf_ctrl, head, list) { + if (buf_ctrl->id =3D=3D id) { + buf_ctrl->val =3D value; + mfc_ctx_debug(6, "[CTRLS] Update buffer control id: 0x%08x, val: %d (%#= x)\n", + buf_ctrl->id, buf_ctrl->val, buf_ctrl->val); + break; + } + } +} + +struct mfc_ctrls_ops mfc_ctrls_ops =3D { + .cleanup_ctx_ctrls =3D mfc_ctrl_cleanup_ctx, + .init_ctx_ctrls =3D mfc_ctrl_init_ctx, + .reset_buf_ctrls =3D mfc_ctrl_reset_buf, + .cleanup_buf_ctrls =3D mfc_ctrl_cleanup_buf, + .init_buf_ctrls =3D mfc_ctrl_init_buf, + .to_buf_ctrls =3D mfc_ctrl_to_buf, + .to_ctx_ctrls =3D mfc_ctrl_to_ctx, + .get_buf_ctrl_val =3D mfc_ctrl_get_buf_val, + .update_buf_val =3D mfc_ctrl_update_buf_val, +}; --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 2AB692749F1 for ; Tue, 30 Sep 2025 03:56:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204593; cv=none; b=uYI3MZQrbXUrS6BYTDW4Ifb5T9TnidL3Qc0DpWRAvU2yKJyD2ZabSUxbGczr+/NgeZCIjeI4cGx2pr1lMlkmrpNVybzS9FThgUd+TZiB+Oy9YidaOxuZnzqKm076HpTGrJ19FfL7enpyBHsp4vawCNFHGqG7MRLXmjQU3FbUnYo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204593; c=relaxed/simple; bh=YDzeiQCOSuGmSqbfTcElU6gDLCwZ4N76PhwDMSZD/vM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=Bk4vb1xk7yByhfsG7N4bw96ZoMyKJC0WYbFnqqSj+++OfaSDrX+TBgWoy60UR4mX9euOz1h32qlX2t0/lNBm2tvZJZxow3WNGX5InltYTVHSYn6gGZZU4KCMX0/rberGdKCcqAZ10hwS3hjZrnDWiLoKGcemMijfd33oQSY/nc4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=VCmCa1j3; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="VCmCa1j3" Received: from epcas5p2.samsung.com (unknown [182.195.41.40]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035626epoutp04f368dd657f5ad05b36a195d60cd7e718~p80A6AWE91923019230epoutp04A for ; Tue, 30 Sep 2025 03:56:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035626epoutp04f368dd657f5ad05b36a195d60cd7e718~p80A6AWE91923019230epoutp04A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204586; bh=XDNWNV3NScVDqQ1Dv0PFYRnUzvtTwPUSHufaY43zJ+U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VCmCa1j3TVWYiN7uHZKmKiGUBPD5ZVYSy5y7VHLS8wUZxJB3p940GU4kCfMEVfNGU t6hx88A5aFDGDlEgjt9jyBWJg2OEpmjVKF4J2bCqa5gtudVoca8XxKOImTH98n11aG cV/rrJcT3zPZfNrTEioxULGS1qIMk8lXHqo/uY1E= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035625epcas5p498132453b2292c8418c07a72fd0d9154~p80AFgsJ21003510035epcas5p40; Tue, 30 Sep 2025 03:56:25 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.90]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPPN3Hxjz2SSKg; Tue, 30 Sep 2025 03:56:24 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035623epcas5p3447703a70d34c96a719a3e92d0ff6d39~p8z_WMSSq1975219752epcas5p3B; Tue, 30 Sep 2025 03:56:23 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035619epsmtip1d07f70671fc3bf9edea6af90bf52f4f2~p8z6R0lXB2938429384epsmtip18; Tue, 30 Sep 2025 03:56:19 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 15/29] =?UTF-8?q?media:=20mfc:=20Add=20decoder=20resource?= =?UTF-8?q?=E2=80=91management=20(RM)=20support=20and=20load=E2=80=91balan?= =?UTF-8?q?cing?= Date: Tue, 30 Sep 2025 09:33:34 +0530 Message-Id: <20250930040348.3702923-16-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035623epcas5p3447703a70d34c96a719a3e92d0ff6d39 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035623epcas5p3447703a70d34c96a719a3e92d0ff6d39 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Introduce dynamic load=E2=80=91balancing and core=E2=80=91selection for M= FC decoder. - Add complete RM infrastructure: real=E2=80=91time QoS, multi=E2=80=91core= handling, core migration. - Implement per=E2=80=91core load tracking and weighted=E2=80=91MB calculat= ion. - Refactor driver paths (open/close/setup/stop, buffer movement, Butler requests) to use the RM layer and improve error handling/cleanup. - Enables dual=E2=80=91core utilization, real=E2=80=91time priority support, and prepares the driver for upstream/mainline integration. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/mfc_rm.c | 2590 ++++++++++++++++- .../platform/samsung/exynos-mfc/mfc_rm.h | 52 + 2 files changed, 2568 insertions(+), 74 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c b/drivers/m= edia/platform/samsung/exynos-mfc/mfc_rm.c index a25a05642cf2..0a805464fbc9 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c @@ -16,127 +16,2569 @@ #include "mfc_core_hw_reg_api.h" #include "mfc_core_pm.h" =20 +#include "base/mfc_qos.h" #include "base/mfc_buf.h" +#include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_mem.h" =20 +static void __mfc_rm_request_butler(struct mfc_dev *dev, struct mfc_ctx *c= tx) +{ + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int i; + + if (ctx) { + /* main core first if it is working */ + for (i =3D 0; i < MFC_CORE_TYPE_NUM; i++) { + if (ctx->op_core_num[i] =3D=3D MFC_CORE_INVALID) + break; + + core =3D dev->core[ctx->op_core_num[i]]; + if (!core) { + mfc_ctx_err("[RM] There is no core[%d]\n", + ctx->op_core_num[i]); + return; + } + + core_ctx =3D core->core_ctx[ctx->num]; + if (core->sched->enqueue_work(core, core_ctx)) + core->core_ops->request_work(core, + MFC_WORK_BUTLER, ctx); + } + } else { + /* all of alive core */ + for (i =3D 0; i < dev->num_core; i++) { + core =3D dev->core[i]; + if (!core) { + mfc_dev_debug(2, "[RM] There is no core[%d]\n", i); + continue; + } + + core->core_ops->request_work(core, MFC_WORK_BUTLER, NULL); + } + } +} + +static void __mfc_rm_update_core_load(struct mfc_ctx *ctx, int move_core, = int multi_mode) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + int i; + + /* + * @move_core + * 0: no move of core. + * 1: update the load to the moving core, because have to move the main c= ore. + */ + if (move_core) + core =3D dev->core[ctx->move_core_num[MFC_CORE_MAIN]]; + else + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no main core\n"); + return; + } + + /* + * @multi_core + * 0: no multi core mode (single core). + * 1: update the load separately, because it works in multi core mode. + */ + if (mfc_rm_query_state(ctx, EQUAL_BIGGER, MFCINST_INIT)) { + if (multi_mode) { + for (i =3D 0; i < dev->num_core; i++) + dev->core[i]->total_mb +=3D (ctx->weighted_mb / dev->num_core); + } else { + core->total_mb +=3D ctx->weighted_mb; + } + } + + mfc_ctx_debug(3, "[RMLB] load(%lu) balanced to %s core-%d (total load: [0= ] %lu, [1] %lu)\n", + ctx->weighted_mb, + multi_mode ? "multi" : "single", core->id, + dev->core[0]->total_mb, dev->core[1]->total_mb); + MFC_TRACE_RM("[c:%d] load(%lu) balanced to %s core-%d (total load: [0] %l= u, [1] %lu)\n", + ctx->num, ctx->weighted_mb, + multi_mode ? "multi" : "single", core->id, + dev->core[0]->total_mb, dev->core[1]->total_mb); +} + +static int __mfc_rm_get_core_num_by_load(struct mfc_dev *dev, struct mfc_c= tx *ctx, + int default_core) +{ + struct mfc_core *core; + int total_load[MFC_NUM_CORE]; + int core_num, surplus_core; + int curr_load; + + if (default_core =3D=3D MFC_DEC_DEFAULT_CORE) + surplus_core =3D MFC_SURPLUS_CORE; + else + surplus_core =3D MFC_DEC_DEFAULT_CORE; + mfc_ctx_debug(2, "[RMLB] default core-%d, surplus core-%d\n", + default_core, surplus_core); + + if (!dev->core[default_core]->total_mb && !dev->core[surplus_core]->total= _mb) { + mfc_ctx_debug(2, "[RMLB] there is no instance, use the default core\n"); + return default_core; + } + + core =3D dev->core[default_core]; + total_load[default_core] =3D core->total_mb * 100 / core->core_pdata->max= _mb; + core =3D dev->core[surplus_core]; + total_load[surplus_core] =3D core->total_mb * 100 / core->core_pdata->max= _mb; + + curr_load =3D ctx->weighted_mb * 100 / core->core_pdata->max_mb; + mfc_ctx_debug(2, "[RMLB] load%s fixed (curr mb: %ld, load: %d%%)\n", + ctx->src_ts.ts_is_full ? " " : " not", ctx->weighted_mb, curr_load= ); + + if (dev->debugfs.core_balance) + dev->core_balance =3D dev->debugfs.core_balance; + else + dev->core_balance =3D dev->pdata->core_balance; + + /* 1) Default core: Default core has not yet been balanced */ + if ((total_load[default_core] + curr_load) <=3D dev->core_balance) { + core_num =3D default_core; + goto fix_core; + /* 2) Surplus core: Default core has been balanced already */ + } else if ((total_load[default_core] > dev->core_balance) && + (total_load[surplus_core] < dev->core_balance)) { + core_num =3D surplus_core; + goto fix_core; + } + + /* 3) Core with lower load */ + if (total_load[default_core] > total_load[surplus_core]) + core_num =3D surplus_core; + else + core_num =3D default_core; + +fix_core: + mfc_ctx_debug(2, "[RMLB] total load: [0] %ld(%d%%), [1] %ld(%d%%), curr_l= oad: %ld(%d%%), select core: %d\n", + dev->core[0]->total_mb, total_load[0], + dev->core[1]->total_mb, total_load[1], + ctx->weighted_mb, curr_load, core_num); + MFC_TRACE_RM("[c:%d] load [0] %ld(%d) [1] %ld(%d) curr %ld(%d) select %d\= n", + ctx->num, + dev->core[0]->total_mb, total_load[0], + dev->core[1]->total_mb, total_load[1], + ctx->weighted_mb, curr_load, core_num); + + return core_num; +} + +static int __mfc_rm_get_core_num(struct mfc_ctx *ctx, int curr_core_num) +{ + struct mfc_dev *dev =3D ctx->dev; + int core_num; + + /* Default core according to standard */ + core_num =3D ctx->op_core_type; + + if (dev->debugfs.core_balance) + dev->pdata->core_balance =3D dev->debugfs.core_balance; + + if (dev->pdata->core_balance =3D=3D 100) { + mfc_ctx_debug(4, "[RMLB] do not want to load balancing\n"); + if (ctx->op_core_type =3D=3D MFC_OP_CORE_ALL) + core_num =3D curr_core_num; + return core_num; + } + + /* Change core according to load */ + if (ctx->op_core_type =3D=3D MFC_OP_CORE_ALL) + core_num =3D __mfc_rm_get_core_num_by_load(dev, ctx, MFC_DEC_DEFAULT_COR= E); + + return core_num; +} + +static int __mfc_rm_move_core_open(struct mfc_ctx *ctx, int to_core_num, i= nt from_core_num) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *to_core =3D dev->core[to_core_num]; + struct mfc_core *from_core =3D dev->core[from_core_num]; + int ret =3D 0; + + mfc_ctx_info("[RMLB] open core changed MFC%d -> MFC%d\n", + from_core_num, to_core_num); + MFC_TRACE_RM("[c:%d] open core changed MFC%d -> MFC%d\n", + ctx->num, from_core_num, to_core_num); + + ret =3D from_core->core_ops->instance_deinit(from_core, ctx); + if (ret) { + mfc_ctx_err("[RMLB] Failed to deinit\n"); + return ret; + } + + if (from_core->sched->is_work(from_core)) + from_core->sched->queue_work(from_core); + + ctx->op_core_num[MFC_CORE_MAIN] =3D to_core_num; + + ret =3D to_core->core_ops->instance_init(to_core, ctx); + if (ret) { + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_CORE_INVALID; + mfc_ctx_err("[RMLB] Failed to init\n"); + return ret; + } + + return ret; +} + +static int __mfc_rm_move_core_running(struct mfc_ctx *ctx, int to_core_num= , int from_core_num) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *to_core; + struct mfc_core *from_core; + struct mfc_core *maincore; + struct mfc_core *subcore; + struct mfc_core_ctx *core_ctx; + int is_to_core =3D 0; + int ret =3D 0; + + ret =3D mfc_get_corelock_migrate(ctx); + if (ret < 0) { + mfc_ctx_err("[RMLB] failed to get corelock\n"); + return -EAGAIN; + } + + to_core =3D dev->core[to_core_num]; + from_core =3D dev->core[from_core_num]; + maincore =3D dev->core[MFC_CORE_MAIN]; + subcore =3D dev->core[MFC_CORE_SUB]; + if (!to_core || !from_core || !maincore || !subcore) { + mfc_ctx_err("The core is invalid(to: %p, from: %p, main: %p, sub: %p)\n", + to_core, from_core, maincore, subcore); + mfc_release_corelock_migrate(ctx); + return -EINVAL; + } + + core_ctx =3D from_core->core_ctx[ctx->num]; + if (!core_ctx) { + mfc_ctx_err("there is no core_ctx\n"); + mfc_release_corelock_migrate(ctx); + return -EINVAL; + } + + /* 1. Change state on from_core */ + ret =3D mfc_core_get_hwlock_dev(maincore); + if (ret < 0) { + mfc_ctx_err("Failed to get hwlock of maincore\n"); + mfc_release_corelock_migrate(ctx); + return ret; + } + + ret =3D mfc_core_get_hwlock_dev(subcore); + if (ret < 0) { + mfc_err("Failed to get hwlock of subcore\n"); + goto err_state; + } + is_to_core =3D 1; + + if (core_ctx->state !=3D MFCINST_RUNNING) { + mfc_debug(3, "[RMLB] it is not running state: %d\n", core_ctx->state); + goto err_state; + } + + if (IS_MULTI_MODE(ctx)) { + mfc_err("[RMLB] multi core mode couldn't migration, need to switch to si= ngle\n"); + goto err_state; + } + + mfc_change_state(core_ctx, MFCINST_MOVE_INST); + + /* 2. Cache flush on to_core */ + + if (from_core->sleep || to_core->sleep) { + mfc_err("Failed to move inst. Sleep was called\n"); + ret =3D -EAGAIN; + goto err_migrate; + } + + ret =3D to_core->core_ops->instance_move_to(to_core, ctx); + if (ret) { + mfc_err("Failed to instance move init\n"); + goto err_migrate; + } + + kfree(to_core->core_ctx[ctx->num]); + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_CORE_INVALID; + + /* 3. Enable the clock of to_core because from_core accesses to_core */ + mfc_core_pm_clock_on(to_core, 0); + + /* 4. Set F/W and ctx address on MFC1 */ + mfc_core_set_migration_addr(dev->core[1], ctx, dev->core[0]->fw_buf.daddr, + dev->core[0]->common_ctx_buf.daddr); + + /* 5. Move and close instance on from_core */ + ret =3D from_core->core_ops->instance_move_from(from_core, ctx); + if (ret) { + mfc_err("Failed to instance move\n"); + MFC_TRACE_RM("[c:%d] instance_move_from fail\n", ctx->num); + goto err_move; + } + mfc_debug(3, "[RMLB] move and close instance on from_core-%d\n", from_cor= e->id); + MFC_TRACE_RM("[c:%d] move and close inst_no %d\n", ctx->num, core_ctx->in= st_no); + + /* 6. Clean up */ + ctx->op_core_num[MFC_CORE_MAIN] =3D to_core->id; + to_core->core_ctx[ctx->num] =3D core_ctx; + core_ctx->core =3D to_core; + + from_core->sched->clear_work(from_core, core_ctx); + from_core->core_ctx[core_ctx->num] =3D 0; + mfc_core_pm_clock_off(to_core, 0); + + mfc_core_release_hwlock_dev(subcore); + mfc_core_release_hwlock_dev(maincore); + + mfc_change_state(core_ctx, MFCINST_RUNNING); + mfc_qos_on(to_core, ctx); + + mfc_release_corelock_migrate(ctx); + + mfc_debug(2, "[RMLB] ctx[%d] migration finished. op_core:%d\n", ctx->num,= to_core->id); + + __mfc_rm_request_butler(dev, ctx); + + return 0; + +err_move: + mfc_core_pm_clock_off(to_core, 0); +err_migrate: + mfc_change_state(core_ctx, MFCINST_RUNNING); +err_state: + if (is_to_core) + mfc_core_release_hwlock_dev(subcore); + mfc_core_release_hwlock_dev(maincore); + + mfc_release_corelock_migrate(ctx); + + return ret; +} + +static struct mfc_core *__mfc_rm_switch_to_single_mode(struct mfc_ctx *ctx= , int need_lock, + enum mfc_op_core_type op_core_type) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *maincore; + struct mfc_core *subcore; + struct mfc_core_ctx *core_ctx; + struct mfc_buf *src_mb =3D NULL; + int last_op_core, switch_single_core; + int ret; + + maincore =3D mfc_get_main_core(ctx->dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + return NULL; + } + + core_ctx =3D maincore->core_ctx[ctx->num]; + if (!core_ctx) { + mfc_ctx_err("[RM] There is no main core_ctx\n"); + return NULL; + } + + subcore =3D mfc_get_sub_core(ctx->dev, ctx); + if (!subcore) { + mfc_info("[RM] There is no sub core for switch single, use main core\n"); + return maincore; + } + + core_ctx =3D subcore->core_ctx[ctx->num]; + if (!core_ctx) { + mfc_ctx_info("[RM] There is no sub core_ctx for switch single, use main = core\n"); + return maincore; + } + + ret =3D mfc_core_get_hwlock_dev(maincore); + if (ret < 0) { + mfc_err("Failed to get main core hwlock\n"); + return NULL; + } + + ret =3D mfc_core_get_hwlock_dev(subcore); + if (ret < 0) { + mfc_err("Failed to get sub core hwlock\n"); + mfc_core_release_hwlock_dev(maincore); + return NULL; + } + + if (need_lock) + mutex_lock(&ctx->op_mode_mutex); + + if (IS_SWITCH_SINGLE_MODE(ctx) || IS_SINGLE_MODE(ctx)) { + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + + /* + * This(Here) means that it has already been switched to single mode + * while waiting for op_mode_mutex. + * Select a new maincore because the maincore may have been different + * from the maincore before waiting. + */ + maincore =3D mfc_get_main_core(ctx->dev, ctx); + if (!maincore) + mfc_err("[RM] There is no main core\n"); + if (need_lock) + mutex_unlock(&ctx->op_mode_mutex); + return maincore; + } + + mfc_change_op_mode(ctx, MFC_OP_SWITCHING); + + /* need to cleanup src buffer */ + mfc_return_buf_to_ready_queue(ctx, &maincore->core_ctx[ctx->num]->src_buf= _queue, + &subcore->core_ctx[ctx->num]->src_buf_queue); + mfc_debug(2, "[RM] op_mode %d change to switch to single\n", ctx->stream_= op_mode); + MFC_TRACE_RM("[c:%d] op_mode %d->4: Move src all\n", ctx->num, ctx->strea= m_op_mode); + + core_ctx =3D maincore->core_ctx[ctx->num]; + if (core_ctx->state =3D=3D MFCINST_FINISHING) { + mfc_change_state(core_ctx, MFCINST_RUNNING); + } else if (core_ctx->state =3D=3D MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHE= CKED_YET) { + mfc_change_state(core_ctx, MFCINST_RUNNING); + + core_ctx =3D subcore->core_ctx[ctx->num]; + mfc_change_state(core_ctx, MFCINST_RUNNING); + } + + /* + * Select switch to single core number + * If the op_core_type of the instance that caused switch_to_single + * is only MFC_OP_CORE_FIXED_(N), select the other core. + * Otherwise, select a lower load core. + */ + if (ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_0) + switch_single_core =3D 0; + else if (ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_1) + switch_single_core =3D 1; + else if (op_core_type =3D=3D MFC_OP_CORE_FIXED_1) + switch_single_core =3D 0; + else if (op_core_type =3D=3D MFC_OP_CORE_FIXED_0) + switch_single_core =3D 1; + else if (ctx->cmd_counter =3D=3D 0) + switch_single_core =3D ctx->last_op_core; + else if ((ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE2) && + (ctx->curr_src_index !=3D -1)) + switch_single_core =3D ctx->curr_src_index % ctx->dev->num_core; + else + switch_single_core =3D __mfc_rm_get_core_num_by_load(dev, ctx, MFC_SURPL= US_CORE); + mfc_debug(2, "[RM] switch to single to core: %d\n", switch_single_core); + MFC_TRACE_RM("[c:%d] switch to single to core: %d\n", ctx->num, switch_si= ngle_core); + + mfc_rm_set_core_num(ctx, switch_single_core); + maincore =3D mfc_get_main_core(ctx->dev, ctx); + if (!maincore) { + mfc_err("[RM] There is no main core\n"); + if (need_lock) + mutex_unlock(&ctx->op_mode_mutex); + return NULL; + } + subcore =3D mfc_get_sub_core(dev, ctx); + if (!subcore) { + mfc_err("[RM] There is no sub core\n"); + if (need_lock) + mutex_unlock(&ctx->op_mode_mutex); + return NULL; + } + + /* main core should have one src buffer */ + core_ctx =3D maincore->core_ctx[ctx->num]; + if (mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_buf_queue) = =3D=3D 0) { + src_mb =3D mfc_get_move_buf(ctx, &core_ctx->src_buf_queue, + &ctx->src_buf_ready_queue, + MFC_BUF_NO_TOUCH_USED, MFC_QUEUE_ADD_BOTTOM); + if (src_mb) { + mfc_debug(2, "[RM][BUFINFO] MFC-%d uses src index: %d(%d)\n", + maincore->id, src_mb->vb.vb2_buf.index, + src_mb->src_index); + MFC_TRACE_RM("[c:%d] MFC-%d uses src index: %d(%d)\n", + ctx->num, maincore->id, src_mb->vb.vb2_buf.index, + src_mb->src_index); + } + } else { + mfc_debug(2, "[RM][BUFINFO] MFC-%d has src buffer already\n", maincore->= id); + } + + mfc_change_op_mode(ctx, MFC_OP_SWITCH_TO_SINGLE); + if (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE2 && (ctx->curr_src_index != =3D -1)) { + last_op_core =3D ctx->curr_src_index % ctx->dev->num_core; + if (last_op_core !=3D switch_single_core) { + mfc_debug(2, "[RM] last op core-%d but switch core-%d, should operate o= nce with mode2\n", + last_op_core, switch_single_core); + mfc_change_op_mode(ctx, MFC_OP_SWITCH_BUT_MODE2); + } + } + + /* for check whether command is sent during switch to single */ + ctx->cmd_counter =3D 0; + + if (need_lock) + mutex_unlock(&ctx->op_mode_mutex); + + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + mfc_qos_off(subcore, ctx); + mfc_qos_on(maincore, ctx); + + mfc_debug(2, "[RM] switch single mode run with core%d\n", maincore->id); + + return maincore; +} + +static int __mfc_rm_check_multi_core_mode(struct mfc_dev *dev, enum mfc_op= _core_type op_core_type) +{ + struct mfc_core *core =3D NULL; + struct mfc_core_ctx *core_ctx =3D NULL; + struct mfc_ctx *ctx =3D NULL; + int i; + + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (test_bit(i, &dev->multi_core_inst_bits)) { + MFC_TRACE_RM("[c:%d] multi core instance\n", i); + ctx =3D dev->ctx[i]; + if (!ctx) { + mfc_dev_err("[RM] There is no ctx\n"); + continue; + } + + if (!IS_MULTI_MODE(ctx)) { + mfc_ctx_debug(3, "[RM] already switched to single\n"); + continue; + } + + if (!mfc_rm_query_state(ctx, EQUAL, MFCINST_RUNNING) && + !mfc_rm_query_state(ctx, EQUAL, + MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_YET)) { + mfc_ctx_debug(2, "[RM] op_mode%d but setup of 2core is not yet done\n", + ctx->op_mode); + continue; + } + + /* multi mode instance should be switch to single mode */ + core =3D __mfc_rm_switch_to_single_mode(ctx, 1, op_core_type); + if (!core) + return -EINVAL; + + mfc_ctx_debug(2, "[RM][2CORE] switch single for multi instance op_mode:= %d\n", + ctx->op_mode); + MFC_TRACE_RM("[c:%d] switch to single for multi inst\n", i); + + core_ctx =3D core->core_ctx[ctx->num]; + if (core->sched->enqueue_work(core, core_ctx)) + core->core_ops->request_work(core, MFC_WORK_BUTLER, ctx); + } + } + + return 0; +} + +static void __mfc_rm_move_buf_ready_set_bit(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core =3D NULL; + struct mfc_core_ctx *core_ctx =3D NULL; + struct mfc_buf *src_mb =3D NULL; + unsigned int num; + + mutex_lock(&ctx->op_mode_mutex); + + /* search for next running core */ + src_mb =3D mfc_get_buf(ctx, &ctx->src_buf_ready_queue, + MFC_BUF_NO_TOUCH_USED); + if (!src_mb) { + mfc_ctx_debug(3, "[RM][2CORE] there is no src buffer\n"); + mutex_unlock(&ctx->op_mode_mutex); + return; + } + + /* handle last frame with switch_to_single mode */ + if (mfc_check_mb_flag(src_mb, MFC_FLAG_LAST_FRAME)) { + ctx->serial_src_index =3D 0; + mfc_ctx_debug(2, "[RM][2CORE] EOS, reset serial src index\n"); + MFC_TRACE_RM("[c:%d] EOS: Reset serial src index\n", ctx->num); + + if (ctx->curr_src_index < src_mb->src_index - 1) { + mfc_ctx_debug(2, "[RM][2CORE] waiting src index %d, curr src index %d i= s working\n", + src_mb->src_index, + ctx->curr_src_index); + goto butler; + } + + core =3D __mfc_rm_switch_to_single_mode(ctx, 0, ctx->op_core_type); + if (!core) { + mutex_unlock(&ctx->op_mode_mutex); + return; + } + mfc_ctx_debug(2, "[RM][2CORE] switch single for LAST FRAME(EOS) op_mode:= %d\n", + ctx->op_mode); + goto butler; + } + + if (ctx->curr_src_index =3D=3D src_mb->src_index - 1) { + num =3D src_mb->src_index % dev->num_core; + core =3D dev->core[num]; + mfc_ctx_debug(2, "[RM][2CORE] src index %d(%d) run in MFC-%d, curr: %d\n= ", + src_mb->vb.vb2_buf.index, + src_mb->src_index, num, + ctx->curr_src_index); + } else { + mfc_ctx_debug(2, "[RM][2CORE] waiting src index %d, curr src index %d is= working\n", + src_mb->src_index, + ctx->curr_src_index); + goto butler; + } + + /* move src buffer to src_buf_queue from src_buf_ready_queue */ + core_ctx =3D core->core_ctx[ctx->num]; + src_mb =3D mfc_get_move_buf(ctx, &core_ctx->src_buf_queue, + &ctx->src_buf_ready_queue, + MFC_BUF_NO_TOUCH_USED, MFC_QUEUE_ADD_BOTTOM); + if (src_mb) { + mfc_debug(2, "[RM][BUFINFO] MFC-%d uses src index: %d(%d)\n", + core->id, src_mb->vb.vb2_buf.index, + src_mb->src_index); + MFC_TRACE_RM("[c:%d] READY: Move src[%d] to MFC-%d\n", + ctx->num, src_mb->src_index, core->id); + } + +butler: + mutex_unlock(&ctx->op_mode_mutex); + __mfc_rm_request_butler(dev, ctx); +} + +static void __mfc_rm_migrate_all_to_one_core(struct mfc_dev *dev) +{ + int i, ret, total_load[MFC_NUM_CORE]; + struct mfc_ctx *tmp_ctx, *ctx; + unsigned long flags; + int core_num, to_core_num, from_core_num; + int op_core_fixed0 =3D 0, op_core_fixed1 =3D 0; + int op_core0 =3D 0, op_core1 =3D 0; + + if (dev->move_ctx_cnt) { + mfc_dev_debug(4, "[RMLB] instance migration working yet, move_ctx: %d\n", + dev->move_ctx_cnt); + return; + } + + spin_lock_irqsave(&dev->ctx_list_lock, flags); + + if (list_empty(&dev->ctx_list)) { + mfc_dev_debug(2, "[RMLB] there is no ctx for load balancing\n"); + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + return; + } + + list_for_each_entry(tmp_ctx, &dev->ctx_list, list) { + if (!IS_SINGLE_MODE(tmp_ctx)) { + mfc_dev_info("[RMLB] there is multi core ctx:%d, op_mode :%d\n", + tmp_ctx->num, tmp_ctx->op_mode); + MFC_TRACE_RM("there is multi core ctx:%d, op_mode :%d\n", + tmp_ctx->num, tmp_ctx->op_mode); + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + return; + } + + if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_0) + op_core_fixed0++; + else if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_1) + op_core_fixed1++; + + /* op main core */ + if (tmp_ctx->op_core_num[MFC_CORE_MAIN] =3D=3D MFC_DEC_DEFAULT_CORE) + op_core0++; + else if (tmp_ctx->op_core_num[MFC_CORE_MAIN] =3D=3D MFC_SURPLUS_CORE) + op_core1++; + mfc_dev_debug(3, "[RMLB] ctx[%d] op_core_type: %d (fixed0: %d, fixed1: %= d)\n", + tmp_ctx->num, tmp_ctx->op_core_type, + op_core_fixed0, op_core_fixed1); + } + + if (op_core_fixed0 && op_core_fixed1) { + mfc_dev_info("[RMLB] all instance should be work with fixed core\n"); + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + return; + } else if ((!op_core_fixed0 && !op_core_fixed1) && + ((op_core0 && !op_core1) || (!op_core0 && op_core1))) { + mfc_dev_debug(3, "[RMLB] all instance already worked in one core\n"); + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + return; + } else if (op_core_fixed0 && !op_core_fixed1) { + core_num =3D MFC_OP_CORE_FIXED_0; + } else { + core_num =3D MFC_OP_CORE_FIXED_1; + } + + for (i =3D 0; i < dev->num_core; i++) { + total_load[i] =3D dev->core[i]->total_mb * 100 / dev->core[i]->core_pdat= a->max_mb; + mfc_dev_debug(3, "[RMLB] core-%d total load: %d%% (mb: %lu)\n", + i, total_load[i], dev->core[i]->total_mb); + dev->core[i]->total_mb =3D 0; + } + + mfc_dev_info("[RMLB] load balance all to core-%d for multi core mode inst= ance\n", + core_num); + MFC_TRACE_RM("load balance all to core-%d\n", core_num); + dev->move_ctx_cnt =3D 0; + list_for_each_entry(tmp_ctx, &dev->ctx_list, list) { + if (tmp_ctx->op_core_num[MFC_CORE_MAIN] !=3D core_num) { + mfc_dev_debug(3, "[RMLB] ctx[%d] move to core-%d\n", + tmp_ctx->num, core_num); + MFC_TRACE_RM("[c:%d] move to core-%d (mb: %lu)\n", + tmp_ctx->num, core_num, tmp_ctx->weighted_mb); + tmp_ctx->move_core_num[MFC_CORE_MAIN] =3D core_num; + dev->move_ctx[dev->move_ctx_cnt++] =3D tmp_ctx; + __mfc_rm_update_core_load(tmp_ctx, 1, 0); + } else { + mfc_dev_debug(3, "[RMLB] ctx[%d] keep core%d (mb: %lu)\n", + tmp_ctx->num, core_num, tmp_ctx->weighted_mb); + __mfc_rm_update_core_load(tmp_ctx, 0, 0); + } + } + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + + /* + * For sequential work, migration is performed directly without queue_wor= k. + * This is because switch_to_single() should get the hwlock + * after migration_to_one_core(). + */ + for (i =3D 0; i < dev->move_ctx_cnt; i++) { + mutex_lock(&dev->mfc_migrate_mutex); + ctx =3D dev->move_ctx[i]; + dev->move_ctx[i] =3D NULL; + + from_core_num =3D ctx->op_core_num[MFC_CORE_MAIN]; + to_core_num =3D ctx->move_core_num[MFC_CORE_MAIN]; + mfc_ctx_debug(2, "[RMLB] ctx[%d] will be moved MFC%d -> MFC%d\n", + ctx->num, from_core_num, to_core_num); + MFC_TRACE_RM("[c:%d] will be moved MFC%d -> MFC%d\n", + ctx->num, from_core_num, to_core_num); + ret =3D __mfc_rm_move_core_running(ctx, to_core_num, from_core_num); + if (ret) { + mfc_ctx_info("[RMLB] migration stopped by ctx[%d]\n", + ctx->num); + MFC_TRACE_RM("migration fail by ctx[%d]\n", ctx->num); + } + mutex_unlock(&dev->mfc_migrate_mutex); + } + + mfc_dev_debug(2, "[RMLB] all instance migration finished\n"); + dev->move_ctx_cnt =3D 0; +} + +static void __mfc_rm_guarantee_init_buf(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *maincore; + struct mfc_core *subcore; + struct mfc_core_ctx *core_ctx; + int ret; + bool need_call_init_buf_main =3D true; + bool need_call_init_buf_sub =3D true; + + /* Check only ready, do not set bit to work_bits */ + if (mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)= ) { + mfc_ctx_debug(3, "[RM] ctx is not ready for INIT_BUF\n"); + return; + } + + maincore =3D mfc_get_main_core(ctx->dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + return; + } + + subcore =3D mfc_get_sub_core(ctx->dev, ctx); + if (!subcore) { + mfc_ctx_err("[RM] There is no sub core for switch single\n"); + return; + } + + /* + * No other command should be sent + * while sending INIT_BUFFER command to 2 core in mode2 + */ + ret =3D mfc_core_get_hwlock_dev(maincore); + if (ret < 0) { + mfc_ctx_err("Failed to get main core hwlock\n"); + return; + } + + ret =3D mfc_core_get_hwlock_dev(subcore); + if (ret < 0) { + mfc_ctx_err("Failed to get sub core hwlock\n"); + mfc_core_release_hwlock_dev(maincore); + return; + } + + /* main core ready set bit*/ + core_ctx =3D maincore->core_ctx[ctx->num]; + if (!maincore->sched->enqueue_work(maincore, core_ctx)) { + if (core_ctx->state =3D=3D MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_Y= ET) { + mfc_ctx_debug(3, "init_buf was done between calling this func and here\= n"); + need_call_init_buf_main =3D false; + } else { + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + return; + } + } + + /* sub core ready set bit*/ + core_ctx =3D subcore->core_ctx[ctx->num]; + if (IS_TWO_MODE2(ctx)) { + if (!subcore->sched->enqueue_work(subcore, core_ctx)) { + if (core_ctx->state =3D=3D MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_= YET) { + need_call_init_buf_sub =3D false; + } else { + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + return; + } + } + } + + /* + * In order not to perform MFC_OFF register control(HWACG) when multi cor= e mode, + * synchronize MFC_OFF register to zero. + */ + mfc_core_mfc_on(maincore); + mfc_core_mfc_on(subcore); + + /* multi_core_inst_bits is set after completing SEQ_START on both cores */ + set_bit(ctx->num, &dev->multi_core_inst_bits); + + MFC_TRACE_RM("[c:%d] op_mode %d try INIT_BUFFER\n", ctx->num, ctx->op_mod= e); + mfc_debug(3, "[RM] op_mode %d try INIT_BUFFER\n", ctx->op_mode); + if (need_call_init_buf_main) { + ret =3D maincore->core_ops->instance_init_buf(maincore, ctx); + if (ret < 0) { + mfc_err("failed main core init buffer\n"); + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + return; + } + } + + if (IS_TWO_MODE2(ctx) && need_call_init_buf_sub) { + ret =3D subcore->core_ops->instance_init_buf(subcore, ctx); + if (ret < 0) { + mfc_err("failed sub core init buffer\n"); + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + return; + } + } + /* + * When mode1, sub core run without command but + * clk_enable is needed because clk mux set to OSC by clk_disable. + * Also, turn off the idle timer because hw_run_bits are not set. + */ + if (IS_TWO_MODE1(ctx)) { + mfc_change_state(core_ctx, MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_Y= ET); + timer_delete(&subcore->mfc_idle_timer); + mfc_core_pm_clock_on(subcore, 0); + } + + if (dev->num_inst =3D=3D 1) { + core_ctx =3D maincore->core_ctx[ctx->num]; + mfc_change_state(core_ctx, MFCINST_RUNNING); + + core_ctx =3D subcore->core_ctx[ctx->num]; + mfc_change_state(core_ctx, MFCINST_RUNNING); + } + + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + + mfc_debug(2, "[RM][2CORE] multi core mode setup done, check multi inst\n"= ); + MFC_TRACE_RM("[c:%d] mode2 setup done\n", ctx->num); + if (dev->num_inst > 1) { + /* + * If the load is already distributed in two cores, + * need to move to one core for multi core mode(8K) instance. + */ + if (IS_8K_RES(ctx) && dev->num_inst > 2) + __mfc_rm_migrate_all_to_one_core(dev); + __mfc_rm_check_multi_core_mode(dev, ctx->op_core_type); + + __mfc_rm_request_butler(dev, NULL); + } +} + +static void __mfc_rm_move_buf_request_work(struct mfc_ctx *ctx, enum mfc_r= equest_work work) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int i; + + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_RUNNING)) { + /* search for next running core and running */ + __mfc_rm_move_buf_ready_set_bit(ctx); + } else if (mfc_rm_query_state(ctx, EQUAL, MFCINST_HEAD_PARSED)) { + __mfc_rm_guarantee_init_buf(ctx); + } else { + /* + * If it is not RUNNING, + * each core can work a given job individually. + */ + for (i =3D 0; i < MFC_CORE_TYPE_NUM; i++) { + if (ctx->op_core_num[i] =3D=3D MFC_CORE_INVALID) + break; + + core =3D dev->core[ctx->op_core_num[i]]; + if (!core) { + mfc_ctx_err("[RM] There is no core[%d]\n", + ctx->op_core_num[i]); + return; + } + mfc_ctx_debug(2, "[RM] request work to MFC-%d\n", core->id); + + core_ctx =3D core->core_ctx[ctx->num]; + if (!core_ctx || (core_ctx && core_ctx->state < MFCINST_GOT_INST)) + continue; + + if (core->sched->enqueue_work(core, core_ctx)) + if (core->core_ops->request_work(core, work, ctx)) + mfc_debug(3, "[RM] failed to request_work\n"); + } + } +} + +static void __mfc_rm_rearrange_cpb(struct mfc_core *maincore, struct mfc_c= ore_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_buf *src_mb; + unsigned long flags; + + /* re-arrangement cpb for mode2 */ + mfc_debug(2, "[RM][2CORE] main core-%d src count %d\n", + maincore->id, core_ctx->src_buf_queue.count); + MFC_TRACE_RM("[c:%d] main core-%d src count %d\n", ctx->num, + maincore->id, core_ctx->src_buf_queue.count); + + mfc_move_buf_all(ctx, &ctx->src_buf_ready_queue, + &core_ctx->src_buf_queue, MFC_QUEUE_ADD_TOP); + + mfc_debug(2, "[RM][2CORE] ready %d maincore %d\n", + ctx->src_buf_ready_queue.count, + maincore->core_ctx[ctx->num]->src_buf_queue.count); + MFC_TRACE_RM("[c:%d] ready %d maincore %d\n", ctx->num, + ctx->src_buf_ready_queue.count, + maincore->core_ctx[ctx->num]->src_buf_queue.count); + + ctx->serial_src_index =3D 0; + ctx->curr_src_index =3D -1; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + if (!list_empty(&ctx->src_buf_ready_queue.head)) { + list_for_each_entry(src_mb, &ctx->src_buf_ready_queue.head, list) { + if (src_mb) { + mfc_debug(2, "[RM][2CORE] src index(%d) changed to %d\n", + src_mb->src_index, ctx->serial_src_index); + MFC_TRACE_RM("[c:%d] src index(%d) changed to %d\n", + ctx->num, src_mb->src_index, + ctx->serial_src_index); + src_mb->src_index =3D ctx->serial_src_index++; + src_mb->used =3D 0; + } + } + } else { + mfc_debug(2, "[RM][2CORE] there is no src in ready(%d)\n", + ctx->src_buf_ready_queue.count); + MFC_TRACE_RM("[c:%d] there is no src in ready(%d)\n", ctx->num, + ctx->src_buf_ready_queue.count); + } + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); +} + +static int __mfc_rm_switch_to_multi_mode(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *maincore; + struct mfc_core *subcore; + struct mfc_core_ctx *core_ctx; + int ret; + struct mfc_core *changed_subcore; + + maincore =3D mfc_get_main_core(ctx->dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + return -EINVAL; + } + + core_ctx =3D maincore->core_ctx[ctx->num]; + if (!core_ctx) { + mfc_ctx_err("[RM] There is no main core_ctx\n"); + return -EINVAL; + } + + if (!maincore->sched->enqueue_work(maincore, core_ctx)) { + mfc_ctx_debug(2, "[RM] there is no work to do after switch_to_multi\n"); + MFC_TRACE_RM("no work after switch to multi\n"); + return -EINVAL; + } + + subcore =3D mfc_get_sub_core(ctx->dev, ctx); + if (!subcore) { + mfc_ctx_err("[RM] There is no sub core for switch single\n"); + return -EINVAL; + } + + ret =3D mfc_core_get_hwlock_dev(maincore); + if (ret < 0) { + mfc_ctx_err("Failed to get main core hwlock\n"); + return -EINVAL; + } + + ret =3D mfc_core_get_hwlock_dev(subcore); + if (ret < 0) { + mfc_ctx_err("Failed to get sub core hwlock\n"); + mfc_core_release_hwlock_dev(maincore); + return -EINVAL; + } + + mutex_lock(&ctx->op_mode_mutex); + + if (ctx->op_mode =3D=3D MFC_OP_SWITCH_BUT_MODE2) { + mfc_ctx_debug(2, "[RMLB] just go to mode2\n"); + } else { + if (!ctx->cmd_counter) { + mfc_ctx_err("It didn't worked on switch to single\n"); + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + mutex_unlock(&ctx->op_mode_mutex); + __mfc_rm_request_butler(ctx->dev, ctx); + return -EINVAL; + } + + mfc_change_op_mode(ctx, MFC_OP_SWITCHING); + __mfc_rm_rearrange_cpb(maincore, core_ctx); + } + + /* main core number of multi core mode should MFC-0 */ + mfc_rm_set_core_num(ctx, MFC_DEC_DEFAULT_CORE); + + /* Change done, it will be work with multi core mode */ + mfc_change_op_mode(ctx, ctx->stream_op_mode); + mfc_ctx_debug(2, "[RM][2CORE] reset multi core op_mode: %d\n", ctx->op_mo= de); + + changed_subcore =3D mfc_get_sub_core(ctx->dev, ctx); + if (!changed_subcore) { + mfc_ctx_err("[RM] There is no sub core for flushing context\n"); + return -EINVAL; + } + mfc_core_clear_main_core_context_flush_done(changed_subcore); + + /* for check whether command is sent during switch to multi */ + ctx->cmd_counter =3D 0; + + mutex_unlock(&ctx->op_mode_mutex); + + mfc_core_release_hwlock_dev(maincore); + mfc_core_release_hwlock_dev(subcore); + mfc_qos_on(maincore, ctx); + mfc_qos_on(subcore, ctx); + + __mfc_rm_move_buf_ready_set_bit(ctx); + + return 0; +} + +static void __mfc_rm_check_instance(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + int ret; + + if (!IS_MULTI_CORE_DEVICE(dev)) + return; + + if (dev->num_inst =3D=3D 1 && IS_SWITCH_SINGLE_MODE(ctx)) { + /* + * If there is only one instance and it is still switch to single mode, + * switch to multi core mode again. + */ + ret =3D __mfc_rm_switch_to_multi_mode(ctx); + if (ret) + mfc_ctx_info("[RM] Keep switch to single mode\n"); + } else if (dev->num_inst > 1) { + /* + * If there are more than one instance and it is still multi core mode, + * switch to single mode. + */ + ret =3D __mfc_rm_check_multi_core_mode(dev, ctx->op_core_type); + if (ret < 0) + mfc_ctx_err("[RM] failed multi core instance switching\n"); + } +} + +void mfc_rm_migration_worker(struct work_struct *work) +{ + struct mfc_dev *dev; + struct mfc_ctx *ctx; + int to_core_num, from_core_num; + int i, ret =3D 0; + + dev =3D container_of(work, struct mfc_dev, migration_work); + + for (i =3D 0; i < dev->move_ctx_cnt; i++) { + mutex_lock(&dev->mfc_migrate_mutex); + ctx =3D dev->move_ctx[i]; + dev->move_ctx[i] =3D NULL; + + /* + * If one instance fails migration, + * the rest of instnaces will not migrate. + */ + if (ret || !ctx) { + MFC_TRACE_RM("migration fail\n"); + mutex_unlock(&dev->mfc_migrate_mutex); + continue; + } + + ret =3D mfc_get_corelock_migrate(ctx); + if (ret < 0) { + mfc_ctx_err("[RMLB] failed to get corelock\n"); + mutex_unlock(&dev->mfc_migrate_mutex); + return; + } + if (IS_SWITCH_SINGLE_MODE(ctx)) { + mutex_unlock(&dev->mfc_migrate_mutex); + mfc_ctx_debug(2, "[RMLB][2CORE] ctx[%d] will change op_mode: %d -> %d\n= ", + ctx->num, ctx->op_mode, ctx->stream_op_mode); + MFC_TRACE_RM("[c:%d] will change op_mode: %d -> %d\n", + ctx->num, ctx->op_mode, ctx->stream_op_mode); + ret =3D __mfc_rm_switch_to_multi_mode(ctx); + if (dev->move_ctx_cnt > 1) { + mfc_ctx_err("[RMLB] there shouldn't be another instance because of mod= e2\n"); + MFC_TRACE_RM("[c:%d] no another inst for mode2\n", ctx->num); + ret =3D -EINVAL; + } + mfc_release_corelock_migrate(ctx); + continue; + } else { + mfc_release_corelock_migrate(ctx); + } + + from_core_num =3D ctx->op_core_num[MFC_CORE_MAIN]; + to_core_num =3D ctx->move_core_num[MFC_CORE_MAIN]; + mfc_ctx_debug(2, "[RMLB] ctx[%d] will be moved MFC%d -> MFC%d\n", + ctx->num, from_core_num, to_core_num); + MFC_TRACE_RM("[c:%d] will be moved MFC%d -> MFC%d\n", + ctx->num, from_core_num, to_core_num); + ret =3D __mfc_rm_move_core_running(ctx, to_core_num, from_core_num); + if (ret) { + mfc_ctx_info("[RMLB] migration stopped by ctx[%d]\n", + ctx->num); + MFC_TRACE_RM("migration fail by ctx[%d]\n", ctx->num); + } + mutex_unlock(&dev->mfc_migrate_mutex); + } + + mfc_dev_debug(2, "[RMLB] all instance migration finished\n"); + dev->move_ctx_cnt =3D 0; + + __mfc_rm_request_butler(dev, NULL); +} + +static int __mfc_rm_load_delete(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_ctx *mfc_ctx, *tmp_ctx; + + list_for_each_entry_safe(mfc_ctx, tmp_ctx, &dev->ctx_list, list) { + if (mfc_ctx =3D=3D ctx) { + list_del(&mfc_ctx->list); + mfc_ctx_debug(3, "[RMLB] ctx[%d] is deleted from list\n", ctx->num); + MFC_TRACE_RM("[c:%d] load delete\n", ctx->num); + return 0; + } + } + + return 1; +} + +static int __mfc_rm_load_add(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_ctx *tmp_ctx; + + /* list create */ + if (list_empty(&dev->ctx_list)) { + list_add(&ctx->list, &dev->ctx_list); + MFC_TRACE_RM("[c:%d] load add first\n", ctx->num); + return 1; + } + + /* If ctx has already added, delete for reordering */ + __mfc_rm_load_delete(ctx); + + /* dev->ctx_list is aligned in descending order of load */ + list_for_each_entry_reverse(tmp_ctx, &dev->ctx_list, list) { + if (tmp_ctx->weighted_mb > ctx->weighted_mb) { + list_add(&ctx->list, &tmp_ctx->list); + mfc_ctx_debug(3, "[RMLB] ctx[%d] is added to list\n", ctx->num); + MFC_TRACE_RM("[c:%d] load add\n", ctx->num); + return 0; + } + } + + /* add to the front of dev->list */ + list_add(&ctx->list, &dev->ctx_list); + mfc_ctx_debug(3, "[RMLB] ctx[%d] is added to list\n", ctx->num); + MFC_TRACE_RM("[c:%d] load add\n", ctx->num); + + return 0; +} + +static int __mfc_rm_check_load_balancing_condition(struct mfc_ctx *ctx, in= t load_add) +{ + struct mfc_dev *dev =3D ctx->dev; + int i; + + if (!IS_MULTI_CORE_DEVICE(dev)) { + mfc_ctx_debug(4, "[RMLB] it is not multi core device\n"); + return 1; + } + + if (!ctx->src_ts.ts_is_full && load_add =3D=3D MFC_RM_LOAD_ADD) { + mfc_ctx_debug(2, "[RMLB] instance load is not yet fixed\n"); + return 1; + } + + if (dev->move_ctx_cnt || load_add =3D=3D MFC_RM_LOAD_DELETE || + (dev->num_inst =3D=3D 1 && load_add =3D=3D MFC_RM_LOAD_ADD)) { + mfc_ctx_debug(4, "[RMLB] instance migration isn't need (num_inst: %d, mo= ve_ctx: %d)\n", + dev->num_inst, dev->move_ctx_cnt); + return 1; + } + + if (list_empty(&dev->ctx_list)) { + for (i =3D 0; i < dev->num_core; i++) + dev->core[i]->total_mb =3D 0; + mfc_ctx_debug(2, "[RMLB] there is no ctx for load balancing\n"); + return 1; + } + + return 0; +} + +void mfc_rm_load_balancing(struct mfc_ctx *ctx, int load_add) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_ctx *tmp_ctx; + unsigned long flags; + int i, core_num, ret =3D 0; + + if (dev->pdata->core_balance =3D=3D 100) { + mfc_ctx_debug(4, "[RMLB] do not want to load balancing\n"); + return; + } + + spin_lock_irqsave(&dev->ctx_list_lock, flags); + if (load_add =3D=3D MFC_RM_LOAD_ADD) + ret =3D __mfc_rm_load_add(ctx); + else + ret =3D __mfc_rm_load_delete(ctx); + if (ret) { + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + return; + } + + /* check the MFC IOVA and control lazy unmap */ + mfc_check_iova(dev); + + /* check if load balancing is not required */ + if (__mfc_rm_check_load_balancing_condition(ctx, load_add)) { + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + return; + } + + /* Clear total mb each core for load re-calculation */ + for (i =3D 0; i < dev->num_core; i++) + dev->core[i]->total_mb =3D 0; + + /* 1) Load balancing of instance with fixed core */ + list_for_each_entry(tmp_ctx, &dev->ctx_list, list) { + if (tmp_ctx->idle_mode =3D=3D MFC_IDLE_MODE_IDLE) { + mfc_ctx_debug(3, "[RMLB][MFCIDLE] idle ctx[%d] excluded from load balan= cing\n", + tmp_ctx->num); + continue; + } + if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_0 || + tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_1) + __mfc_rm_update_core_load(tmp_ctx, 0, 0); + } + + /* 2) Load balancing of instance with not-fixed core */ + list_for_each_entry(tmp_ctx, &dev->ctx_list, list) { + if (tmp_ctx->idle_mode =3D=3D MFC_IDLE_MODE_IDLE) { + mfc_ctx_debug(3, "[RMLB][MFCIDLE] idle ctx[%d] excluded from load balan= cing\n", + tmp_ctx->num); + continue; + } + if (tmp_ctx->op_core_type !=3D MFC_OP_CORE_ALL) { + mfc_ctx_debug(3, "[RMLB] fixed core ctx[%d] can't be moved\n", + tmp_ctx->num); + continue; + } + + if (IS_MULTI_MODE(tmp_ctx)) { + __mfc_rm_update_core_load(tmp_ctx, 0, 1); + continue; + } else if (IS_SWITCH_SINGLE_MODE(tmp_ctx) && (dev->num_inst =3D=3D 1)) { + mfc_ctx_debug(2, "[RMLB] ctx[%d] can be changed to mode%d\n", + tmp_ctx->num, tmp_ctx->stream_op_mode); + MFC_TRACE_RM("[c:%d] can be changed to mode%d\n", + tmp_ctx->num, tmp_ctx->stream_op_mode); + dev->move_ctx[dev->move_ctx_cnt++] =3D tmp_ctx; + __mfc_rm_update_core_load(tmp_ctx, 0, 1); + continue; + } + core_num =3D __mfc_rm_get_core_num_by_load(dev, tmp_ctx, MFC_DEC_DEFAULT= _CORE); + if (IS_SWITCH_SINGLE_MODE(tmp_ctx) || + core_num =3D=3D tmp_ctx->op_core_num[MFC_CORE_MAIN]) { + mfc_ctx_debug(3, "[RMLB] ctx[%d] keep core%d\n", tmp_ctx->num, + tmp_ctx->op_core_num[MFC_CORE_MAIN]); + __mfc_rm_update_core_load(tmp_ctx, 0, 0); + } else { + /* Instance should move */ + mfc_ctx_debug(3, "[RMLB] ctx[%d] move to core-%d\n", + tmp_ctx->num, core_num); + MFC_TRACE_RM("[c:%d] move to core-%d\n", tmp_ctx->num, core_num); + tmp_ctx->move_core_num[MFC_CORE_MAIN] =3D core_num; + dev->move_ctx[dev->move_ctx_cnt++] =3D tmp_ctx; + __mfc_rm_update_core_load(tmp_ctx, 1, 0); + } + } + + /* For debugging */ + mfc_ctx_debug(3, "[RMLB] =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3Dctx list=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\= n"); + list_for_each_entry(tmp_ctx, &dev->ctx_list, list) + mfc_show_ctx_info(tmp_ctx); + + mfc_ctx_debug(3, "[RMLB] >>>> core balance %d%%\n", dev->core_balance); + for (i =3D 0; i < dev->num_core; i++) + mfc_ctx_debug(3, "[RMLB] >> MFC-%d total load: %lu%%\n", i, + dev->core[i]->total_mb * 100 / dev->core[i]->core_pdata->max_mb); + mfc_ctx_debug(3, "[RMLB] =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D\n"); + + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + + if (dev->move_ctx_cnt) + queue_work(dev->migration_wq, &dev->migration_work); +} + int mfc_rm_instance_init(struct mfc_dev *dev, struct mfc_ctx *ctx) { struct mfc_core *core; - int i, ret; + int num_qos_steps; + int i, ret; + + mfc_ctx_debug_enter(); + + mfc_get_corelock_ctx(ctx); + + /* + * The FW memory for all cores is allocated in advance. + * (Only once at first time) + * Because FW base address should be the lowest address + * than all DVA that FW approaches. + */ + for (i =3D 0; i < dev->num_core; i++) { + core =3D dev->core[i]; + if (!core) { + mfc_ctx_err("[RM] There is no MFC-%d\n", i); + continue; + } + + if (!(core->fw.status & MFC_FW_ALLOC)) { + ret =3D mfc_alloc_firmware(core); + if (ret) + goto err_inst_init; + } + + if (!(core->fw.status & MFC_CTX_ALLOC)) { + ret =3D mfc_alloc_common_context(core); + if (ret) + goto err_inst_init; + } + } + + mfc_change_op_mode(ctx, MFC_OP_SINGLE); + ctx->op_core_type =3D MFC_OP_CORE_NOT_FIXED; + if (ctx->type =3D=3D MFCINST_DECODER) + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_DEC_DEFAULT_CORE; + else + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_ENC_DEFAULT_CORE; + + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no main core\n"); + ret =3D -EINVAL; + goto err_inst_init; + } + + mfc_ctx_debug(2, "[RM] init instance core-%d\n", + ctx->op_core_num[MFC_CORE_MAIN]); + ret =3D core->core_ops->instance_init(core, ctx); + if (ret) { + ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_CORE_INVALID; + mfc_ctx_err("[RM] Failed to init\n"); + } + + /* + * QoS portion data should be allocated + * only once per instance after maincore is determined. + */ + num_qos_steps =3D core->core_pdata->num_default_qos_steps; + + ctx->mfc_qos_portion =3D vmalloc(sizeof(unsigned int) * num_qos_steps); + if (!ctx->mfc_qos_portion) + ret =3D -ENOMEM; + +err_inst_init: + mfc_release_corelock_ctx(ctx); + + mfc_ctx_debug_leave(); + + return ret; +} + +int mfc_rm_instance_deinit(struct mfc_dev *dev, struct mfc_ctx *ctx) +{ + struct mfc_core *core =3D NULL, *subcore; + int i, ret =3D 0; + + mfc_ctx_debug_enter(); + + mfc_get_corelock_ctx(ctx); + + /* reset original stream mode */ + mutex_lock(&ctx->op_mode_mutex); + if (IS_SWITCH_SINGLE_MODE(ctx)) { + mfc_rm_set_core_num(ctx, MFC_DEC_DEFAULT_CORE); + mfc_change_op_mode(ctx, ctx->stream_op_mode); + } + mutex_unlock(&ctx->op_mode_mutex); + + if (IS_TWO_MODE1(ctx)) { + subcore =3D mfc_get_sub_core(dev, ctx); + if (!subcore) + mfc_ctx_err("[RM] There is no sub core for clock off\n"); + else + mfc_core_pm_clock_off(subcore, 0); + } + + for (i =3D (MFC_CORE_TYPE_NUM - 1); i >=3D 0; i--) { + if (ctx->op_core_num[i] =3D=3D MFC_CORE_INVALID) + continue; + + core =3D dev->core[ctx->op_core_num[i]]; + if (!core) { + mfc_ctx_err("[RM] There is no core[%d]\n", + ctx->op_core_num[i]); + ret =3D -EINVAL; + goto err_inst_deinit; + } + + mfc_core_debug(2, "[RM] core%d will be deinit, ctx[%d]\n", + i, ctx->num); + ret =3D core->core_ops->instance_deinit(core, ctx); + if (ret) + mfc_core_err("[RM] Failed to deinit\n"); + } + + clear_bit(ctx->num, &dev->multi_core_inst_bits); + mfc_change_op_mode(ctx, MFC_OP_SINGLE); + ctx->op_core_type =3D MFC_OP_CORE_NOT_FIXED; + +err_inst_deinit: + if (core) + mfc_qos_get_portion(core, ctx); + vfree(ctx->mfc_qos_portion); + mfc_release_corelock_ctx(ctx); + + mfc_ctx_debug_leave(); + + return ret; +} + +int mfc_rm_instance_open(struct mfc_dev *dev, struct mfc_ctx *ctx) +{ + struct mfc_core *core; + int ret =3D 0, core_num; + int is_corelock =3D 0; + + mfc_ctx_debug_enter(); + + ctx->op_core_type =3D dev->pdata->mfc_resource[ctx->codec_mode].op_core_t= ype; + mfc_qos_get_weighted_mb(ctx, ctx->rt); + + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no main core\n"); + ret =3D -EINVAL; + goto err_inst_open; + } + + if (IS_MULTI_CORE_DEVICE(dev)) { + mfc_get_corelock_ctx(ctx); + is_corelock =3D 1; + + /* Core balance by both standard and load */ + core_num =3D __mfc_rm_get_core_num(ctx, core->id); + if (core_num !=3D core->id) { + ret =3D __mfc_rm_move_core_open(ctx, core_num, core->id); + if (ret) + goto err_inst_open; + + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no main core\n"); + ret =3D -EINVAL; + goto err_inst_open; + } + } + + /* + * When there is instance of multi core mode, + * other instance should be open in MFC-0 + */ + ret =3D __mfc_rm_check_multi_core_mode(dev, ctx->op_core_type); + if (ret < 0) { + mfc_ctx_err("[RM] failed multi core instance switching\n"); + goto err_inst_open; + } + } + + ret =3D core->core_ops->instance_open(core, ctx); + if (ret) { + mfc_core_err("[RM] Failed to open\n"); + goto err_inst_open; + } + +err_inst_open: + if (is_corelock) + mfc_release_corelock_ctx(ctx); + + mfc_ctx_debug_leave(); + + return ret; +} + +/* For 2core DRC. + * 1) CACHE_FLUSH(main) -> CLOSE_INST(sub) -> CACHE_FLUSH(sub) + * 2) Clear multi_core_inst_bits + * 3) Change state to MFCINST_RES_CHANGE_END + */ +static void __mfc_rm_handle_drc_sub_core_deinit(struct mfc_ctx *ctx) +{ + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_core *subcore; + int ret; + + mutex_lock(&ctx->op_mode_mutex); + + core =3D mfc_get_main_core(ctx->dev, ctx); + if (!core) + return; + + core_ctx =3D core->core_ctx[ctx->num]; + + subcore =3D mfc_get_sub_core(ctx->dev, ctx); + if (!subcore) { + mutex_unlock(&ctx->op_mode_mutex); + return; + } + + if (IS_SINGLE_MODE(ctx)) { + mutex_unlock(&ctx->op_mode_mutex); + return; + } + + ctx->subcore_inst_no =3D MFC_NO_INSTANCE_SET; + ctx->op_core_num[MFC_CORE_SUB] =3D MFC_CORE_INVALID; + ctx->op_core_type =3D MFC_OP_CORE_ALL; + + ctx->stream_op_mode =3D MFC_OP_SINGLE; + mfc_change_op_mode(ctx, MFC_OP_SINGLE); + + mfc_ctx_debug(2, "[RM][2CORE][DRC] DRC update op_mode: %d\n", ctx->op_mod= e); + + mutex_unlock(&ctx->op_mode_mutex); + + /* deinit subcore, this instance will be operate with single core */ + if (IS_TWO_MODE1(ctx)) + mfc_core_pm_clock_off(subcore, 0); + + /* If 2-core mode, cache_flush(MAIN) is needed + * after LAST_FRAME(MAIN) & before CLOSE_INST(SUB) + */ + core->core_ops->instance_cache_flush(core, ctx); + + ret =3D subcore->core_ops->instance_deinit(subcore, ctx); + + mutex_lock(&ctx->op_mode_mutex); + clear_bit(ctx->num, &ctx->dev->multi_core_inst_bits); + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_END); + mutex_unlock(&ctx->op_mode_mutex); + + if (ret) + mfc_core_err("[RM][2CORE][DRC] Failed to deinit\n"); +} + +static void __mfc_rm_inst_dec_dst_stop(struct mfc_dev *dev, struct mfc_ctx= *ctx) +{ + struct mfc_core *maincore; + struct mfc_core *subcore; + struct mfc_core_ctx *core_ctx; + int ret; + + mfc_ctx_debug(3, "op_mode: %d\n", ctx->op_mode); + + mfc_get_corelock_ctx(ctx); + + if (IS_TWO_MODE2(ctx) || IS_SWITCH_SINGLE_MODE(ctx)) { + /* After sub core operation, dpb flush from main core */ + subcore =3D mfc_get_sub_core(dev, ctx); + if (!subcore) { + mfc_ctx_err("[RM] There is no sub core for switch single\n"); + goto err_dst_stop; + } + ret =3D mfc_core_get_hwlock_dev(subcore); + if (ret < 0) { + mfc_ctx_err("Failed to get sub core hwlock\n"); + goto err_dst_stop; + } + mfc_core_release_hwlock_dev(subcore); + + maincore =3D mfc_get_main_core(dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + goto err_dst_stop; + } + maincore->core_ops->instance_dpb_flush(maincore, ctx); + /* Check one more time, because sub core could be denit-ed by DRC */ + if (IS_TWO_MODE2(ctx) || IS_SWITCH_SINGLE_MODE(ctx)) { + /* If drc is running yet, we need to deinit first. */ + core_ctx =3D subcore->core_ctx[ctx->num]; + if (ON_RES_CHANGE(core_ctx)) + __mfc_rm_handle_drc_sub_core_deinit(ctx); + else + subcore->core_ops->instance_dpb_flush(subcore, ctx); + } + } else { + maincore =3D mfc_get_main_core(dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + goto err_dst_stop; + } + + maincore->core_ops->instance_dpb_flush(maincore, ctx); + } + + mfc_clear_core_intlock(ctx); + +err_dst_stop: + mfc_release_corelock_ctx(ctx); +} + +static void __mfc_core_cleanup_subcore_for_drc(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core *maincore, *subcore =3D 0; + + core =3D __mfc_rm_switch_to_single_mode(ctx, 1, MFC_OP_CORE_FIXED_1); + if (!core) { + mfc_ctx_err("[RM][2CORE] failed to switch to single for src_stop\n"); + return; + } + mfc_ctx_debug(2, "[RM][2CORE] switch single for src_stop, op_mode: %d\n", + ctx->op_mode); + + /* reset original stream mode */ + mutex_lock(&ctx->op_mode_mutex); + mfc_rm_set_core_num(ctx, MFC_DEC_DEFAULT_CORE); + mfc_change_op_mode(ctx, ctx->stream_op_mode); + maincore =3D mfc_get_main_core(dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + goto release_op_mode_mutex; + } + + subcore =3D mfc_get_sub_core(dev, ctx); + if (!subcore) { + mfc_ctx_err("[RM] There is no sub core\n"); + goto release_op_mode_mutex; + } + + mutex_unlock(&ctx->op_mode_mutex); + + mfc_qos_on(maincore, ctx); + mfc_qos_on(subcore, ctx); + +release_op_mode_mutex: + mutex_unlock(&ctx->op_mode_mutex); +} + +static void __mfc_core_cancel_drc(struct mfc_core *core, struct mfc_core_c= tx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + + mfc_info("[DRC] DRC is running yet (state: %d) cancel DRC\n", core_ctx->s= tate); + + mutex_lock(&ctx->drc_wait_mutex); + + if (!ctx->handle_drc_multi_mode) + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_END); + + ctx->wait_state &=3D ~(WAIT_STOP); + mfc_debug(2, "clear WAIT_STOP %d\n", ctx->wait_state); + MFC_TRACE_CORE_CTX("** DEC clear WAIT_STOP(wait_state %d)\n", + ctx->wait_state); + + if (ctx->wait_state !=3D WAIT_G_FMT) { + ctx->wait_state =3D WAIT_G_FMT; + mfc_debug(2, "set WAIT_G_FMT only for inform to user that needs g_fmt\n"= ); + } + mutex_unlock(&ctx->drc_wait_mutex); +} + +static void __mfc_core_src_cleanup(struct mfc_core *maincore, struct mfc_c= ore *subcore, + struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core_ctx *maincore_ctx =3D maincore->core_ctx[ctx->num]; + struct mfc_core_ctx *subcore_ctx =3D 0; + int index =3D 0; + + /* Header parsed buffer is in src_buf_ready_queue */ + mfc_move_buf_all(ctx, &maincore_ctx->src_buf_queue, + &ctx->src_buf_ready_queue, MFC_QUEUE_ADD_BOTTOM); + MFC_TRACE_CTX("Move all src to queue\n"); + dec->consumed =3D 0; + dec->has_multiframe =3D 0; + maincore_ctx->check_dump =3D 0; + ctx->curr_src_index =3D -1; + ctx->serial_src_index =3D 0; + + mfc_cleanup_queue(&ctx->buf_queue_lock, &maincore_ctx->src_buf_queue); + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue); + + mfc_init_queue(&maincore_ctx->src_buf_queue); + mfc_init_queue(&ctx->src_buf_ready_queue); + + maincore->sched->clear_work(maincore, maincore_ctx); + + if (ON_RES_CHANGE(maincore_ctx)) + __mfc_core_cancel_drc(maincore, maincore_ctx); + + if (subcore) { + subcore_ctx =3D subcore->core_ctx[ctx->num]; + mfc_move_buf_all(ctx, &subcore_ctx->src_buf_queue, + &ctx->src_buf_ready_queue, MFC_QUEUE_ADD_BOTTOM); + MFC_TRACE_CTX("Move all src(sub) to queue\n"); + + subcore_ctx->check_dump =3D 0; + + mfc_cleanup_queue(&ctx->buf_queue_lock, &subcore_ctx->src_buf_queue); + mfc_init_queue(&subcore_ctx->src_buf_queue); + + subcore->sched->clear_work(subcore, subcore_ctx); + + if (ON_RES_CHANGE(subcore_ctx)) + __mfc_core_cancel_drc(subcore, subcore_ctx); + } + + while (index < MFC_MAX_BUFFERS) { + index =3D find_next_bit(ctx->src_ctrls_avail, MFC_MAX_BUFFERS, index); + if (index < MFC_MAX_BUFFERS) + call_cop(ctx, reset_buf_ctrls, &ctx->src_ctrls[index]); + index++; + } + + if (maincore_ctx->state =3D=3D MFCINST_FINISHING) + mfc_change_state(maincore_ctx, MFCINST_RUNNING); + + if (subcore_ctx && subcore_ctx->state =3D=3D MFCINST_FINISHING) + mfc_change_state(subcore_ctx, MFCINST_RUNNING); + + mfc_ctx_debug(2, "decoder source stop sequence done\n"); +} + +static void __mfc_rm_inst_dec_src_stop(struct mfc_dev *dev, struct mfc_ctx= *ctx) +{ + struct mfc_core *maincore; + struct mfc_core *subcore =3D 0; + struct mfc_core_ctx *maincore_ctx =3D 0, *subcore_ctx =3D 0; + int ret; + + mfc_ctx_debug(2, "op_mode: %d\n", ctx->op_mode); + + mfc_get_corelock_ctx(ctx); + + maincore =3D mfc_get_main_core(dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + goto release_corelock; + } + maincore_ctx =3D maincore->core_ctx[ctx->num]; + if (!maincore_ctx) { + mfc_ctx_err("[RM] There is no main core_ctx\n"); + goto release_corelock; + } + ret =3D mfc_core_get_hwlock_dev(maincore); + if (ret < 0) { + mfc_ctx_err("Failed to get hwlock of maincore\n"); + goto release_corelock; + } + + if (IS_TWO_MODE2(ctx)) { + subcore =3D mfc_get_sub_core(dev, ctx); + if (!subcore) { + mfc_ctx_err("[RM] There is no sub core\n"); + goto release_hwlock_main; + } + subcore_ctx =3D subcore->core_ctx[ctx->num]; + if (!subcore_ctx) { + mfc_ctx_err("[RM] There is no sub core_ctx\n"); + goto release_hwlock_main; + } + ret =3D mfc_core_get_hwlock_dev(subcore); + if (ret < 0) { + mfc_ctx_err("Failed to get hwlock of subcore\n"); + goto release_hwlock_main; + } + } + + mutex_lock(&ctx->op_mode_mutex); + + __mfc_core_src_cleanup(maincore, subcore, ctx); + + if (maincore->state =3D=3D MFCCORE_ERROR || maincore_ctx->state =3D=3D MF= CINST_ERROR || + (subcore_ctx && subcore_ctx->state =3D=3D MFCINST_ERROR)) + goto release_op_mode_mutex; + + mutex_unlock(&ctx->op_mode_mutex); + + if (subcore) + mfc_core_release_hwlock_dev(subcore); + mfc_core_release_hwlock_dev(maincore); + + if (IS_TWO_MODE2(ctx) && !ON_RUNNING(maincore_ctx)) + __mfc_core_cleanup_subcore_for_drc(ctx); + + mfc_release_corelock_ctx(ctx); + + maincore->sched->enqueue_work(maincore, maincore_ctx); + if (maincore->sched->is_work(maincore)) + maincore->sched->queue_work(maincore); + return; + +release_op_mode_mutex: + mutex_unlock(&ctx->op_mode_mutex); + if (subcore) + mfc_core_release_hwlock_dev(subcore); +release_hwlock_main: + mfc_core_release_hwlock_dev(maincore); +release_corelock: + mfc_release_corelock_ctx(ctx); + + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue); + if (maincore_ctx) + mfc_cleanup_queue(&ctx->buf_queue_lock, &maincore_ctx->src_buf_queue); + if (subcore_ctx) + mfc_cleanup_queue(&ctx->buf_queue_lock, &subcore_ctx->src_buf_queue); +} + +void mfc_rm_instance_dec_stop(struct mfc_dev *dev, struct mfc_ctx *ctx, + unsigned int type) +{ + mfc_ctx_debug_enter(); + + if (type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + __mfc_rm_inst_dec_dst_stop(dev, ctx); + else if (type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + __mfc_rm_inst_dec_src_stop(dev, ctx); + + mfc_ctx_debug_leave(); +} + +int mfc_rm_subcore_seq_start(struct mfc_dev *dev, struct mfc_ctx *ctx) +{ + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; =20 - mfc_ctx_debug_enter(); + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no sub core\n"); + return -EINVAL; + } + + core_ctx =3D core->core_ctx[ctx->num]; + + if (core->sched->enqueue_work(core, core_ctx)) + core->core_ops->request_work(core, MFC_WORK_BUTLER, ctx); + + mfc_debug(2, "[RM] waiting for header parsing of sub core\n"); + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_SEQ_DONE_RET)) { + mfc_err("[RM] sub core header parsing failed\n"); + return -EAGAIN; + } + + mfc_change_op_mode(ctx, ctx->stream_op_mode); + mfc_qos_on(core, ctx); + + return 0; +} + +int mfc_rm_instance_setup(struct mfc_dev *dev, struct mfc_ctx *ctx) +{ + struct mfc_core *maincore, *core; + struct mfc_core_ctx *core_ctx, *maincore_ctx; + struct mfc_buf_queue *from_queue; + struct mfc_buf *src_mb =3D NULL; + int ret =3D 0; + + if (ctx->op_core_num[MFC_CORE_SUB] !=3D MFC_CORE_INVALID) { + mfc_ctx_info("[RM] sub core already setup\n"); + return 0; + } + + mfc_rm_set_core_num(ctx, ctx->op_core_num[MFC_CORE_MAIN]); + + maincore =3D mfc_get_main_core(dev, ctx); + if (!maincore) { + mfc_ctx_err("[RM] There is no main core\n"); + return -EINVAL; + } + maincore_ctx =3D maincore->core_ctx[ctx->num]; + + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no sub core\n"); + return -EINVAL; + } + + ret =3D core->core_ops->instance_init(core, ctx); + if (ret) { + mfc_ctx_err("[RM] sub core init failed\n"); + goto fail_init; + } + core_ctx =3D core->core_ctx[ctx->num]; + + ret =3D core->core_ops->instance_open(core, ctx); + if (ret) { + mfc_err("[RM] sub core open failed\n"); + goto fail_open; + } + + if (ctx->type =3D=3D MFCINST_DECODER) { + from_queue =3D &maincore_ctx->src_buf_queue; + + src_mb =3D mfc_get_buf(ctx, from_queue, MFC_BUF_NO_TOUCH_USED); + if (!src_mb) { + mfc_err("[RM] there is no header buffers\n"); + ret =3D -EAGAIN; + goto fail_open; + } + + mutex_lock(&ctx->op_mode_mutex); + /* When DRC case, it needs to rearrange src buffer for mode1,2 */ + if (ctx->wait_state) { + if (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE2) { + __mfc_rm_rearrange_cpb(maincore, maincore_ctx); + from_queue =3D &ctx->src_buf_ready_queue; + } + if (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1) { + mfc_move_buf_all(ctx, &ctx->src_buf_ready_queue, + &maincore_ctx->src_buf_queue, MFC_QUEUE_ADD_TOP); + from_queue =3D &ctx->src_buf_ready_queue; + } + } + mutex_unlock(&ctx->op_mode_mutex); + + /* Move the header buffer to sub core */ + src_mb =3D mfc_get_move_buf(ctx, &core_ctx->src_buf_queue, from_queue, + MFC_BUF_NO_TOUCH_USED, MFC_QUEUE_ADD_TOP); + if (!src_mb) { + mfc_err("[RM] there is no header buffers\n"); + ret =3D -EAGAIN; + goto fail_open; + } else { + MFC_TRACE_RM("[c:%d] SETUP: Move src[%d] to queue\n", + ctx->num, src_mb->src_index); + } + + if (ctx->dec_priv->consumed) { + mfc_debug(2, "[STREAM][2CORE] src should be without consumed\n"); + ctx->dec_priv->consumed =3D 0; + } + } + + if (core->sched->enqueue_work(core, core_ctx)) + core->core_ops->request_work(core, MFC_WORK_BUTLER, ctx); + + mfc_debug(2, "[RM] waiting for header parsing of sub core\n"); + if (mfc_wait_for_done_core_ctx(core_ctx, + MFC_REG_R2H_CMD_SEQ_DONE_RET)) { + mfc_err("[RM] sub core header parsing failed\n"); + ret =3D -EAGAIN; + goto fail_open; + } + + if (ctx->type =3D=3D MFCINST_DECODER) { + /* Move back the header buffer to ready_queue */ + mfc_get_move_buf(ctx, &ctx->src_buf_ready_queue, + &core_ctx->src_buf_queue, + MFC_BUF_RESET_USED, MFC_QUEUE_ADD_TOP); + } + + /* main core number of multi core mode should MFC-0 */ + if (ctx->op_core_num[MFC_CORE_SUB] =3D=3D MFC_DEC_DEFAULT_CORE) { + /* return src buffers of main&sub core to ready_queue */ + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_err("[RM] There is no sub core\n"); + return -EINVAL; + } + mfc_return_buf_to_ready_queue(ctx, &maincore->core_ctx[ctx->num]->src_bu= f_queue, + &core->core_ctx[ctx->num]->src_buf_queue); + + mfc_rm_set_core_num(ctx, MFC_DEC_DEFAULT_CORE); + mfc_debug(2, "[RM] main core changed to MFC0\n"); + + maincore =3D mfc_get_main_core(ctx->dev, ctx); + if (!maincore) { + mfc_err("[RM] There is no main core\n"); + return -EINVAL; + } =20 + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_err("[RM] There is no sub core\n"); + return -EINVAL; + } + + /* main core should have one src buffer */ + core_ctx =3D maincore->core_ctx[ctx->num]; + if (mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_buf_queue) = =3D=3D 0) { + src_mb =3D mfc_get_move_buf(ctx, &core_ctx->src_buf_queue, + &ctx->src_buf_ready_queue, + MFC_BUF_NO_TOUCH_USED, MFC_QUEUE_ADD_BOTTOM); + if (src_mb) { + mfc_debug(2, "[RM][BUFINFO] MFC-%d uses src index: %d(%d)\n", + maincore->id, src_mb->vb.vb2_buf.index, + src_mb->src_index); + MFC_TRACE_RM("[c:%d] MFC-%d uses src index: %d(%d)\n", + ctx->num, maincore->id, src_mb->vb.vb2_buf.index, + src_mb->src_index); + } + } else { + mfc_debug(2, "[RM][BUFINFO] MFC-%d has src buffer already\n", maincore-= >id); + } + + /* sub core inst_no is needed at INIT_BUF */ + ctx->subcore_inst_no =3D core->core_ctx[ctx->num]->inst_no; + mfc_debug(2, "[RM] sub core setup inst_no: %d\n", ctx->subcore_inst_no); + } else { + /* sub core inst_no is needed at INIT_BUF */ + ctx->subcore_inst_no =3D core_ctx->inst_no; + mfc_debug(2, "[RM] sub core setup inst_no: %d\n", ctx->subcore_inst_no); + } + + mfc_change_op_mode(ctx, ctx->stream_op_mode); + mfc_qos_on(core, ctx); + + return ret; + +fail_open: + if (core->core_ops->instance_deinit(core, ctx)) + mfc_ctx_err("[RMLB] Failed to deinit\n"); +fail_init: + ctx->op_core_num[MFC_CORE_SUB] =3D MFC_CORE_INVALID; + + return ret; +} + +void mfc_rm_request_work(struct mfc_dev *dev, enum mfc_request_work work, + struct mfc_ctx *ctx) +{ + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int is_corelock =3D 0; + + /* This is a request that may not be struct mfc_ctx */ + if (work =3D=3D MFC_WORK_BUTLER) { + __mfc_rm_request_butler(dev, ctx); + return; + } + + if (!ctx) { + mfc_dev_err("[RM] ctx is needed (request work: %#x)\n", work); + return; + } + + if (mfc_rm_query_state(ctx, EQUAL_OR, MFCINST_RES_CHANGE_INIT) && !IS_SIN= GLE_MODE(ctx)) { + /* When 2-core DRC, switch_to_single is needed */ + __mfc_rm_switch_to_single_mode(ctx, 1, ctx->op_core_type); + } + + /* If 2core DRC, CACHE_FLUSH of main core and deinit of sub core are need= ed. + * If not 2core DRC, just change the state to MFCINST_RES_CHANGE_END. + */ + if (ctx->handle_drc_multi_mode && + mfc_rm_query_state(ctx, EQUAL_OR, MFCINST_RES_CHANGE_FLUSH_FINISHED)) + __mfc_rm_handle_drc_sub_core_deinit(ctx); + + if (IS_TWO_MODE2(ctx)) { + __mfc_rm_move_buf_request_work(ctx, work); + return; + } else if (IS_MODE_SWITCHING(ctx)) { + mfc_ctx_debug(3, "[RM] mode switching op_mode: %d\n", ctx->op_mode); + MFC_TRACE_RM("[c:%d] mode switching op_mode: %d\n", ctx->num, ctx->op_mo= de); + return; + } + + if (IS_TWO_MODE1(ctx)) { + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_HEAD_PARSED) || + mfc_rm_query_state(ctx, EQUAL_OR, + MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_YET)) { + __mfc_rm_guarantee_init_buf(ctx); + return; + } + } mfc_get_corelock_ctx(ctx); + is_corelock =3D 1; + + mutex_lock(&ctx->op_mode_mutex); + + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mutex_unlock(&ctx->op_mode_mutex); + goto err_req_work; + } + + if (IS_TWO_MODE2(ctx) || IS_MODE_SWITCHING(ctx)) { + /* do not move the src buffer */ + mfc_ctx_debug(2, "[RM] mode was changed op_mode: %d\n", ctx->op_mode); + MFC_TRACE_RM("[c:%d] mode was changed op_mode: %d\n", ctx->num, ctx->op_= mode); + mutex_unlock(&ctx->op_mode_mutex); + goto err_req_work; + } + + /* move src buffer to src_buf_queue from src_buf_ready_queue */ + core_ctx =3D core->core_ctx[ctx->num]; + if (!core_ctx) { + mfc_ctx_err("[RM] core_ctx is NULL\n"); + mutex_unlock(&ctx->op_mode_mutex); + goto err_req_work; + } + mfc_move_buf_all(ctx, &core_ctx->src_buf_queue, + &ctx->src_buf_ready_queue, MFC_QUEUE_ADD_BOTTOM); =20 /* - * The FW memory for all cores is allocated in advance. - * (Only once at first time) - * Because FW base address should be the lowest address - * than all DVA that FW approaches. + * When op_mode is changed at that time, + * if the two cores are not RUNNING state, they are not ready. */ - for (i =3D 0; i < dev->num_core; i++) { - core =3D dev->core[i]; - if (!core) { - mfc_ctx_err("[RM] There is no MFC-%d\n", i); - continue; + if (IS_TWO_MODE1(ctx) && !mfc_rm_query_state(ctx, EQUAL_BIGGER, MFCINST_H= EAD_PARSED)) { + mfc_ctx_debug(2, "[RM] request_work to run SEQ_START again for mode-1\n"= ); + } else if (IS_MULTI_MODE(ctx) && !mfc_rm_query_state(ctx, EQUAL_BIGGER, M= FCINST_RUNNING)) { + mfc_ctx_debug(2, "[RM] op_mode%d set but not ready\n", ctx->op_mode); + MFC_TRACE_RM("[c:%d] op_mode%d set but not ready\n", ctx->num, ctx->op_m= ode); + mutex_unlock(&ctx->op_mode_mutex); + goto err_req_work; + } + + mutex_unlock(&ctx->op_mode_mutex); + + mutex_lock(&ctx->drc_wait_mutex); + if (ctx->wait_state =3D=3D WAIT_G_FMT && + (mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue) > 0)) + mfc_dec_drc_find_del_buf(core_ctx); + mutex_unlock(&ctx->drc_wait_mutex); + + /* set core context work bit if it is ready */ + if (core->sched->enqueue_work(core, core_ctx)) + if (core->core_ops->request_work(core, work, ctx)) + mfc_ctx_debug(3, "[RM] failed to request_work\n"); + +err_req_work: + if (is_corelock) + mfc_release_corelock_ctx(ctx); +} + +void mfc_rm_qos_control(struct mfc_ctx *ctx, enum mfc_qos_control qos_cont= rol) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + bool update_idle =3D 0; + + mfc_get_corelock_ctx(ctx); + + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_debug(2, "[RM] There is no main core\n"); + goto release_corelock; + } + + switch (qos_control) { + case MFC_QOS_ON: + mfc_qos_on(core, ctx); + if (IS_MULTI_MODE(ctx)) { + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no sub core\n"); + goto release_corelock; + } + + mfc_qos_on(core, ctx); } =20 - if (!(core->fw.status & MFC_FW_ALLOC)) { - ret =3D mfc_alloc_firmware(core); - if (ret) - goto err_inst_init; + mfc_rm_load_balancing(ctx, MFC_RM_LOAD_ADD); + __mfc_rm_check_instance(ctx); + break; + case MFC_QOS_OFF: + mfc_qos_off(core, ctx); + if (IS_MULTI_MODE(ctx)) { + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no sub core\n"); + goto release_corelock; + } + + mfc_qos_off(core, ctx); } + break; + case MFC_QOS_TRIGGER: + update_idle =3D mfc_qos_idle_trigger(core, ctx); + if (ctx->update_framerate) + mfc_qos_get_weighted_mb(ctx, ctx->rt); + if (update_idle || ctx->update_bitrate || ctx->update_framerate) + mfc_qos_on(core, ctx); + if (IS_MULTI_MODE(ctx)) { + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no sub core\n"); + goto release_corelock; + } =20 - if (!(core->fw.status & MFC_CTX_ALLOC)) { - ret =3D mfc_alloc_common_context(core); - if (ret) - goto err_inst_init; + update_idle =3D mfc_qos_idle_trigger(core, ctx); + if (update_idle || ctx->update_bitrate || ctx->update_framerate) + mfc_qos_on(core, ctx); } + + if (ctx->update_framerate) + mfc_rm_load_balancing(ctx, MFC_RM_LOAD_ADD); + + ctx->update_bitrate =3D false; + break; + default: + mfc_ctx_err("[RM] not supported QoS control type: %#x\n", + qos_control); } =20 - mfc_change_op_mode(ctx, MFC_OP_SINGLE); - ctx->op_core_type =3D MFC_OP_CORE_NOT_FIXED; - if (ctx->type =3D=3D MFCINST_DECODER) - ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_DEC_DEFAULT_CORE; - else - ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_ENC_DEFAULT_CORE; +release_corelock: + mfc_release_corelock_ctx(ctx); +} + +int mfc_rm_query_state(struct mfc_ctx *ctx, enum mfc_inst_state_query quer= y, + enum mfc_inst_state state) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + enum mfc_inst_state maincore_state =3D MFCINST_FREE; + enum mfc_inst_state subcore_state =3D MFCINST_FREE; + int maincore_condition =3D 0, subcore_condition =3D 0; + int ret =3D 0; + + mfc_get_corelock_ctx(ctx); =20 core =3D mfc_get_main_core(dev, ctx); if (!core) { - mfc_ctx_err("[RM] There is no main core\n"); - ret =3D -EINVAL; - goto err_inst_init; + mfc_ctx_debug(3, "[RM] There is no main core\n"); + goto err_query_state; } =20 - mfc_ctx_debug(2, "[RM] init instance core-%d\n", - ctx->op_core_num[MFC_CORE_MAIN]); - ret =3D core->core_ops->instance_init(core, ctx); - if (ret) { - ctx->op_core_num[MFC_CORE_MAIN] =3D MFC_CORE_INVALID; - mfc_ctx_err("[RM] Failed to init\n"); + core_ctx =3D core->core_ctx[ctx->num]; + maincore_state =3D core_ctx->state; + + if (IS_MULTI_MODE(ctx)) { + core =3D mfc_get_sub_core(dev, ctx); + if (!core) { + mfc_ctx_debug(4, "[RM] There is no sub core\n"); + goto err_query_state; + } + + core_ctx =3D core->core_ctx[ctx->num]; + if (!core_ctx) { + mfc_ctx_debug(4, "[RM] There is no sub core_ctx\n"); + goto err_query_state; + } + subcore_state =3D core_ctx->state; } =20 -err_inst_init: - mfc_release_corelock_ctx(ctx); + switch (query) { + case EQUAL: + if (maincore_state =3D=3D state) + maincore_condition =3D 1; + if (subcore_state =3D=3D state) + subcore_condition =3D 1; + break; + case BIGGER: + if (maincore_state > state) + maincore_condition =3D 1; + if (subcore_state > state) + subcore_condition =3D 1; + break; + case SMALLER: + if (maincore_state < state) + maincore_condition =3D 1; + if (subcore_state < state) + subcore_condition =3D 1; + break; + case EQUAL_BIGGER: + if (maincore_state >=3D state) + maincore_condition =3D 1; + if (subcore_state >=3D state) + subcore_condition =3D 1; + break; + case EQUAL_SMALLER: + if (maincore_state <=3D state) + maincore_condition =3D 1; + if (subcore_state <=3D state) + subcore_condition =3D 1; + break; + case EQUAL_OR: + if (maincore_state =3D=3D state || subcore_state =3D=3D state) { + maincore_condition =3D 1; + subcore_condition =3D 1; + } + break; + default: + mfc_err("[RM] not supported state query type: %d\n", query); + goto err_query_state; + } =20 - mfc_ctx_debug_leave(); + if (IS_MULTI_MODE(ctx)) { + if (maincore_condition && subcore_condition) + ret =3D 1; + else + mfc_debug(2, "[RM] multi core main core state: %d, sub core state: %d\n= ", + maincore_state, subcore_state); + } else { + if (maincore_condition) + ret =3D 1; + else + mfc_debug(2, "[RM] single core main core state: %d\n", + maincore_state); + } + +err_query_state: + mfc_release_corelock_ctx(ctx); =20 return ret; } =20 -int mfc_rm_instance_deinit(struct mfc_dev *dev, struct mfc_ctx *ctx) +static int __mfc_rm_check_real_time_resource(struct mfc_ctx *ctx) { - struct mfc_core *core =3D NULL, *subcore; - int i, ret =3D 0; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_ctx *tmp_ctx; + struct mfc_ctx *rt_ctx[MFC_NUM_CONTEXTS]; + unsigned long flags; + unsigned long fix_rt_mb[MFC_NUM_CORE], all_rt_mb =3D 0; + unsigned long fix_rt_mb_sum =3D 0, max_avail_mb =3D 0; + int i, cnt =3D 0; =20 - mfc_ctx_debug_enter(); + for (i =3D 0; i < dev->num_core; i++) { + max_avail_mb +=3D dev->core[i]->core_pdata->max_mb; + fix_rt_mb[i] =3D 0; + } =20 - mfc_get_corelock_ctx(ctx); + /* 1. Calculation of RT instance load in operation */ + spin_lock_irqsave(&dev->ctx_list_lock, flags); + list_for_each_entry(tmp_ctx, &dev->ctx_list, list) { + if (tmp_ctx =3D=3D ctx) { + mfc_ctx_debug(4, "[RT] already reserved resource\n"); + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); + return 0; + } + if (tmp_ctx->rt !=3D MFC_RT) { + mfc_ctx_debug(4, "[RM][c:%d] This is not RT (%d)\n", + tmp_ctx->num, tmp_ctx->rt); + continue; + } + if (tmp_ctx->idle_mode =3D=3D MFC_IDLE_MODE_IDLE) { + mfc_ctx_debug(3, "[RT][MFCIDLE] idle ctx[%d] excluded\n", tmp_ctx->num); + continue; + } + /* + * - fix_rt_mb[]: RT MB that must operate in a fixed core + * - all_rt_mb: RT MB that operable in all cores + */ + if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_0 || + tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_1) + fix_rt_mb[tmp_ctx->op_core_type] +=3D tmp_ctx->weighted_mb; + else + all_rt_mb +=3D tmp_ctx->weighted_mb; + rt_ctx[cnt++] =3D tmp_ctx; + mfc_show_ctx_info(tmp_ctx); + } + spin_unlock_irqrestore(&dev->ctx_list_lock, flags); =20 - /* reset original stream mode */ - mutex_lock(&ctx->op_mode_mutex); - if (IS_SWITCH_SINGLE_MODE(ctx)) { - mfc_rm_set_core_num(ctx, MFC_DEC_DEFAULT_CORE); - mfc_change_op_mode(ctx, ctx->stream_op_mode); + mfc_ctx_debug(3, "[RM] =3D=3D=3D=3D=3D RT MB in operation\n"); + for (i =3D 0; i < dev->num_core; i++) { + fix_rt_mb_sum +=3D fix_rt_mb[i]; + mfc_ctx_debug(3, "[RM] core%d RT MB %lu / %u\n", + i, fix_rt_mb[i], dev->core[i]->core_pdata->max_mb); } - mutex_unlock(&ctx->op_mode_mutex); + mfc_ctx_debug(3, "[RM] other RT MB %lu / %lu\n", all_rt_mb, max_avail_mb = - fix_rt_mb_sum); =20 - if (IS_TWO_MODE1(ctx)) { - subcore =3D mfc_get_sub_core(dev, ctx); - if (!subcore) - mfc_ctx_err("[RM] There is no sub core for clock off\n"); - else - mfc_core_pm_clock_off(subcore, 0); + /* 2. Adds current instance load */ + if (ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_0 || + ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_1) { + fix_rt_mb[ctx->op_core_type] +=3D ctx->weighted_mb; + } else { + /* Only one instance can be operated in multi core mode */ + if (dev->num_inst =3D=3D 1 && + (ctx->type =3D=3D MFCINST_DECODER && IS_MULTI_MODE_RES(ctx))) { + for (i =3D 0; i < dev->num_core; i++) + fix_rt_mb[i] +=3D (ctx->weighted_mb / 2); + } else { + all_rt_mb +=3D ctx->weighted_mb; + } } =20 - for (i =3D (MFC_CORE_TYPE_NUM - 1); i >=3D 0; i--) { - if (ctx->op_core_num[i] =3D=3D MFC_CORE_INVALID) + mfc_ctx_debug(2, "[RM] =3D=3D=3D=3D=3D RT MB after adding curr load %lu\n= ", ctx->weighted_mb); + fix_rt_mb_sum =3D 0; + for (i =3D 0; i < dev->num_core; i++) { + fix_rt_mb_sum +=3D fix_rt_mb[i]; + mfc_ctx_debug(2, "[RM] core%d RT MB %lu / %u\n", + i, fix_rt_mb[i], dev->core[i]->core_pdata->max_mb); + } + mfc_ctx_debug(3, "[RM] other RT MB %lu / %lu\n", all_rt_mb, max_avail_mb = - fix_rt_mb_sum); + + /* 3. Check whether the current RT ctx resource reservation */ + for (i =3D 0; i < dev->num_core; i++) { + if (fix_rt_mb[i] > dev->core[i]->core_pdata->max_mb) { + mfc_ctx_info("[RM] core%d RT resource is full for %lu (mb %lu / %u)\n", + i, ctx->weighted_mb, fix_rt_mb[i], + dev->core[i]->core_pdata->max_mb); + goto fail_reserve; + } + } + + if ((all_rt_mb + fix_rt_mb_sum) > max_avail_mb) { + mfc_ctx_info("[RM] RT resource is full for %lu, fixed %lu + others %lu (= mb %lu / %lu)\n", + ctx->weighted_mb, fix_rt_mb_sum, all_rt_mb, + fix_rt_mb_sum + all_rt_mb, max_avail_mb); + goto fail_reserve; + } + + /* 4. Add reserved resource to load list */ + mfc_rm_load_balancing(ctx, MFC_RM_LOAD_ADD); + mfc_ctx_debug(2, "[RM] RT resource is reserved for %lu, fixed %lu + other= s %lu (mb %lu / %lu)\n", + ctx->weighted_mb, fix_rt_mb_sum, all_rt_mb, + fix_rt_mb_sum + all_rt_mb, max_avail_mb); + + return 0; + +fail_reserve: + for (i =3D 0; i < cnt; i++) { + if (!rt_ctx[i]) continue; + mfc_print_ctx_info(rt_ctx[i]); + } + mfc_ctx_info(":: Current instance\n"); + mfc_print_ctx_info(ctx); + return -EBUSY; +} =20 - core =3D dev->core[ctx->op_core_num[i]]; - if (!core) { - mfc_ctx_err("[RM] There is no core[%d]\n", - ctx->op_core_num[i]); - ret =3D -EINVAL; - goto err_inst_deinit; +void mfc_rm_update_real_time(struct mfc_ctx *ctx) +{ + struct mfc_core *core; + int cur_rt, cur_prio, new_rt, new_prio, ret; + + mfc_ctx_debug_enter(); + + new_prio =3D ctx->user_prio; + + if (ctx->operating_framerate > 0) { + if (new_prio =3D=3D 0) { + new_rt =3D MFC_RT; + } else if (new_prio >=3D 1) { + new_rt =3D MFC_RT_CON; + } else { + /* Undefined is handled like RT_LOW (priority 1) */ + new_prio =3D 1; + new_rt =3D MFC_RT_LOW; } + } else { + if (new_prio =3D=3D 0) { + /* In case of google photo app, user sets priority 0 */ + new_prio =3D 1; + new_rt =3D MFC_RT_LOW; + } else if (new_prio >=3D 1) { + new_rt =3D MFC_NON_RT; + } else { + /* Undefined is handled like RT_LOW (priority 1) */ + new_prio =3D 1; + new_rt =3D MFC_RT_UNDEFINED; + } + } =20 - mfc_core_debug(2, "[RM] core%d will be deinit, ctx[%d]\n", - i, ctx->num); - ret =3D core->core_ops->instance_deinit(core, ctx); - if (ret) - mfc_core_err("[RM] Failed to deinit\n"); + mfc_ctx_debug(2, "[PRIO] update rt: %d -> %d, operating fps: %lu, prio: %= d->%d\n", + ctx->rt, new_rt, (ctx->operating_framerate / 1000), + ctx->prio, new_prio); + + mfc_qos_get_weighted_mb(ctx, new_rt); + + if (new_rt =3D=3D MFC_RT) { + ret =3D __mfc_rm_check_real_time_resource(ctx); + if (ret) { + new_prio =3D 1; + mfc_ctx_debug(2, "[PRIO] RT resource is full so update prio: %d->%d\n", + ctx->prio, new_prio); + } } =20 - clear_bit(ctx->num, &dev->multi_core_inst_bits); - mfc_change_op_mode(ctx, MFC_OP_SINGLE); - ctx->op_core_type =3D MFC_OP_CORE_NOT_FIXED; + if (new_rt !=3D ctx->rt || new_prio !=3D ctx->prio) { + cur_rt =3D ctx->rt; + cur_prio =3D ctx->prio; + core =3D mfc_get_main_core_lock(ctx->dev, ctx); + if (core) { + ret =3D core->sched->change_prio_work(core, ctx, cur_rt, + cur_prio, new_rt, new_prio); + } else { + mfc_ctx_err("[PRIO] There is no main core\n"); + ret =3D -EINVAL; + } + if (ret) { + ctx->prio =3D new_prio; + ctx->rt =3D new_rt; + } =20 -err_inst_deinit: - mfc_release_corelock_ctx(ctx); + if (!IS_SINGLE_MODE(ctx)) { + core =3D mfc_get_sub_core_lock(ctx->dev, ctx); + if (core) + core->sched->change_prio_work(core, ctx, cur_rt, + cur_prio, new_rt, new_prio); + else + mfc_ctx_err("[PRIO] There is no sub core\n"); + } + } =20 mfc_ctx_debug_leave(); - - return ret; } diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h b/drivers/m= edia/platform/samsung/exynos-mfc/mfc_rm.h index 8f9e7494057e..b73ef905718a 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h @@ -18,6 +18,40 @@ #define MFC_RM_LOAD_ADD 1 #define MFC_RM_LOAD_DELETE_UPDATE 2 =20 +static inline struct mfc_core *mfc_get_main_core_lock(struct mfc_dev *dev, + struct mfc_ctx *ctx) +{ + struct mfc_core *core; + + mfc_get_corelock_ctx(ctx); + + if (ctx->op_core_num[MFC_CORE_MAIN] =3D=3D MFC_CORE_INVALID) + core =3D NULL; + else + core =3D dev->core[ctx->op_core_num[MFC_CORE_MAIN]]; + + mfc_release_corelock_ctx(ctx); + + return core; +} + +static inline struct mfc_core *mfc_get_sub_core_lock(struct mfc_dev *dev, + struct mfc_ctx *ctx) +{ + struct mfc_core *core; + + mfc_get_corelock_ctx(ctx); + + if (ctx->op_core_num[MFC_CORE_SUB] =3D=3D MFC_CORE_INVALID) + core =3D NULL; + else + core =3D dev->core[ctx->op_core_num[MFC_CORE_SUB]]; + + mfc_release_corelock_ctx(ctx); + + return core; +} + static inline struct mfc_core *mfc_get_main_core(struct mfc_dev *dev, struct mfc_ctx *ctx) { @@ -50,7 +84,25 @@ static inline void mfc_rm_set_core_num(struct mfc_ctx *c= tx, int main_core_num) ctx->op_core_num[MFC_CORE_SUB]); } =20 +/* load balancing */ +void mfc_rm_migration_worker(struct work_struct *work); +void mfc_rm_load_balancing(struct mfc_ctx *ctx, int load_add); + /* core ops */ int mfc_rm_instance_init(struct mfc_dev *dev, struct mfc_ctx *ctx); int mfc_rm_instance_deinit(struct mfc_dev *dev, struct mfc_ctx *ctx); +int mfc_rm_instance_open(struct mfc_dev *dev, struct mfc_ctx *ctx); +void mfc_rm_instance_dec_stop(struct mfc_dev *dev, struct mfc_ctx *ctx, + unsigned int type); +int mfc_rm_subcore_seq_start(struct mfc_dev *dev, struct mfc_ctx *ctx); +int mfc_rm_instance_setup(struct mfc_dev *dev, struct mfc_ctx *ctx); +void mfc_rm_request_work(struct mfc_dev *dev, enum mfc_request_work work, + struct mfc_ctx *ctx); + +/* utils */ +void mfc_rm_qos_control(struct mfc_ctx *ctx, enum mfc_qos_control qos_cont= rol); +int mfc_rm_query_state(struct mfc_ctx *ctx, enum mfc_inst_state_query quer= y, + enum mfc_inst_state state); + +void mfc_rm_update_real_time(struct mfc_ctx *ctx); #endif /* __MFC_RM_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 56D21275AF5 for ; Tue, 30 Sep 2025 03:56:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204599; cv=none; b=Fc2beeNBelYHPkHxXZhBZxP3o+OdSblxolK5or7SPT5stxq1l6sZ87SOvgelvc7Z2rK4PaAls/AuDDx2Fc9J0Fl3MPB3QLP8MV6lBOqxMO29sswvH+eHFx18+YR5G2tWPnU4EYdb9WMKyH3pbDdKNIadPwRG3tbDIDtX9ZlyIhk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204599; c=relaxed/simple; bh=qC/Unt8PjPQiV43hLSvBWLIglCKclNGNEbqWiV/+QBk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=OUNMwVb4QsdUnfdxaWizcr9o88LNI5l5zWeCpbDWd9zuv4WTnHzI1IMFZdiw1duEXPxnc2vVNglL/Z7tQjiBr3ni5i/xHO/qI5povGsZVZ50cf3t86gq/TfN5q1aMFRHcgVjyC+p2jdyaQoHkRc5wrTbvXJ8nYiL7jQ9JyBzxc8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=uIhd4Y1o; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="uIhd4Y1o" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035631epoutp04bac2c166e46d9c8b6e06a6688219e083~p80FkGALj2083320833epoutp04B for ; Tue, 30 Sep 2025 03:56:31 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035631epoutp04bac2c166e46d9c8b6e06a6688219e083~p80FkGALj2083320833epoutp04B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204591; bh=dzCqB0n5Pyt67l3/FHPMhMXuVfK/h58LiWvHMQHMPxs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uIhd4Y1ouatmJdv9z/5Wq2DJcx391Ndsi8pkEkKLjZrfezwTcsQaQgzjsIpDL4WOr 4Lt3yvb7ZiD7fT1QWXd97eP386j7IZUHpws2kVt6aJC/FEYUCWrIERPF3PebtbEl3X 0kRdtGCuV2iC1upbyqW8SKqqNfiXbYdk47rSE6jg= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPS id 20250930035630epcas5p333bf64141bc0c1cdd792ede3949e1f24~p80EUp_UW1975619756epcas5p3H; Tue, 30 Sep 2025 03:56:30 +0000 (GMT) Received: from epcas5p2.samsung.com (unknown [182.195.38.87]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cbPPT1x3Dz6B9m5; Tue, 30 Sep 2025 03:56:29 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035628epcas5p1c0ebecf84b6b9c17aa291b097e411df0~p80C30F7r1549615496epcas5p19; Tue, 30 Sep 2025 03:56:28 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035624epsmtip123f0067485e425f10327d74fd0fafa55~p8z_wGbqR2885328853epsmtip1a; Tue, 30 Sep 2025 03:56:24 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 16/29] =?UTF-8?q?media:=20mfc:=20Enhance=20HW=E2=80=91lock?= =?UTF-8?q?=20handling,=20scheduling=20and=20error=20recovery?= Date: Tue, 30 Sep 2025 09:33:35 +0530 Message-Id: <20250930040348.3702923-17-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035628epcas5p1c0ebecf84b6b9c17aa291b097e411df0 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035628epcas5p1c0ebecf84b6b9c17aa291b097e411df0 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Enable HW=E2=80=91lock migration across cores and centralize lock acquisition for better load=E2=80=91balancing and dynamic resolution. - Refactor decoder run=E2=80=91path helpers to streamline init, per=E2=80=91frame, and final=E2=80=91frame processing with dynamic DPB ma= nagement. - Add IRQ=E2=80=91level lock handler that deals with pre=E2=80=91empted con= texts, waiting queues, QoS updates and work scheduling, reducing race conditions. - Expand QoS tracking and black=E2=80=91bar detection with detailed debug o= utput. - Consolidate error handling into a dedicated ISR=E2=80=91invoked path, improving recovery reliability and logging. - Update headers/comments and add missing declarations for new helpers. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/mfc_core_hwlock.c | 364 +++++ .../samsung/exynos-mfc/mfc_core_hwlock.h | 8 + .../samsung/exynos-mfc/mfc_core_isr.c | 1353 ++++++++++++++++- .../samsung/exynos-mfc/mfc_core_isr.h | 3 + .../samsung/exynos-mfc/mfc_core_run.c | 145 ++ .../samsung/exynos-mfc/mfc_core_run.h | 4 + .../platform/samsung/exynos-mfc/mfc_rm.c | 1 + 7 files changed, 1877 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c b/= drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c index 4de836543e82..0b594429fd59 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c @@ -19,8 +19,10 @@ #include "mfc_core_cmd.h" #include "mfc_core_hw_reg_api.h" =20 +#include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_sched.h" +#include "base/mfc_qos.h" =20 static inline void __mfc_print_hwlock(struct mfc_core *core) { @@ -291,6 +293,221 @@ void mfc_core_release_hwlock_ctx(struct mfc_core_ctx = *core_ctx) spin_unlock_irqrestore(&core->hwlock.lock, flags); } =20 +void mfc_core_move_hwlock_ctx(struct mfc_core *to_core, struct mfc_core *f= rom_core, + struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_listable_wq *listable_wq; + bool is_move =3D false; + + if (from_core->hwlock.bits & (1UL << core_ctx->num)) + is_move =3D true; + + list_for_each_entry(listable_wq, &from_core->hwlock.waiting_list, list) { + if (!listable_wq->core_ctx) + continue; + + if (listable_wq->core_ctx->num =3D=3D core_ctx->num) + is_move =3D true; + } + + if (is_move) { + mfc_debug(2, "There is a waiting module to moving core%d\n", + from_core->id); + MFC_TRACE_RM("[c:%d] hwlock wait in moving core%d\n", + ctx->num, from_core->id); + /* remove waiting list from from_core->hwlock */ + core_ctx->core =3D from_core; + __mfc_remove_listable_wq_ctx(core_ctx); + + /* add waiting list to to_core->hwlock */ + core_ctx->core =3D to_core; + list_add_tail(&core_ctx->hwlock_wq.list, &to_core->hwlock.waiting_list); + to_core->hwlock.wl_count++; + to_core->hwlock.owned_by_irq =3D 1; + mfc_info("hwlock waiting module moved MFC%d -> MFC%d\n", + from_core->id, to_core->id); + MFC_TRACE_RM("[c:%d] hwlock module move %d -> %d\n", + ctx->num, from_core->id, to_core->id); + } +} + +static inline void __mfc_yield_hwlock(struct mfc_core *core, + struct mfc_core_ctx *core_ctx) +{ + mfc_core_release_hwlock_ctx(core_ctx); + + /* Trigger again if other instance's work is waiting */ + if (core->sched->is_work(core)) + core->sched->queue_work(core); +} + +/* + * Should be called with hwlock.lock + */ +static inline void __mfc_transfer_hwlock_ctx_protected(struct mfc_core *co= re, + int ctx_index) +{ + core->hwlock.dev =3D 0; + core->hwlock.bits =3D 0; + set_bit(ctx_index, &core->hwlock.bits); +} + +/* + * Should be called with hwlock.lock + * + * Return value description + * >=3D0: succeeded to get hwlock_bit for the context, index of new cont= ext + * -1, -EINVAL: failed to get hwlock_bit for a context + */ +static int __mfc_try_to_get_new_ctx_protected(struct mfc_core *core) +{ + struct mfc_dev *dev =3D core->dev; + int ret =3D 0; + int index; + struct mfc_ctx *new_ctx; + + if (core->shutdown) { + mfc_core_info("Couldn't lock HW. Shutdown was called\n"); + return -EINVAL; + } + + if (core->sleep) { + mfc_core_info("Couldn't lock HW. Sleep was called\n"); + return -EINVAL; + } + + /* Check whether hardware is not running */ + if (core->hwlock.bits !=3D 0 || core->hwlock.dev !=3D 0) { + /* This is perfectly ok, the scheduled ctx should wait */ + mfc_core_debug(2, "Couldn't lock HW\n"); + return -1; + } + + /* Choose the context to run */ + index =3D core->sched->pick_next_work(core); + if (index < 0) { + /* This is perfectly ok, the scheduled ctx should wait + * No contexts to run + */ + mfc_core_debug(2, "No ctx is scheduled to be run\n"); + ret =3D -1; + return ret; + } + + new_ctx =3D dev->ctx[index]; + if (!new_ctx) { + mfc_core_err("no mfc context to run\n"); + ret =3D -1; + return ret; + } + + set_bit(new_ctx->num, &core->hwlock.bits); + ret =3D index; + + return ret; +} + +/* + * Should be called without hwlock holding + * + * Try to run an operation on hardware + */ +void mfc_core_try_run(struct mfc_core *core) +{ + int new_ctx_index; + int ret; + unsigned long flags; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_info("[MSR] Couldn't run HW. It's Error state\n"); + return; + } + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + new_ctx_index =3D __mfc_try_to_get_new_ctx_protected(core); + if (new_ctx_index < 0) { + mfc_core_debug(2, "Failed to get new context to run\n"); + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + return; + } + + core->hwlock.owned_by_irq =3D 1; + + __mfc_print_hwlock(core); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + ret =3D mfc_core_just_run(core, new_ctx_index); + if (ret) + __mfc_yield_hwlock(core, core->core_ctx[new_ctx_index]); +} + +static int __mfc_just_run_dec(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret =3D 0; + + switch (core_ctx->state) { + case MFCINST_FINISHING: + ret =3D mfc_core_run_dec_last_frames(core, ctx); + break; + case MFCINST_RUNNING: + ret =3D mfc_core_run_dec_frame(core, ctx); + break; + case MFCINST_INIT: + mfc_core_cmd_open_inst(core, ctx); + break; + case MFCINST_RETURN_INST: + ret =3D mfc_core_cmd_close_inst(core, ctx); + break; + case MFCINST_GOT_INST: + ret =3D mfc_core_run_dec_init(core, ctx); + break; + case MFCINST_HEAD_PARSED: + if (core_ctx->codec_buffer_allocated =3D=3D 0) { + ctx->clear_work_bit =3D 1; + mfc_err("codec buffer is not allocated\n"); + ret =3D -EAGAIN; + break; + } + if (ctx->wait_state !=3D WAIT_NONE) { + mfc_err("wait_state(%d) is not ready\n", ctx->wait_state); + ret =3D -EAGAIN; + break; + } + ret =3D mfc_core_cmd_dec_init_buffers(core, ctx); + break; + case MFCINST_RES_CHANGE_INIT: + ret =3D mfc_core_run_dec_last_frames(core, ctx); + break; + case MFCINST_RES_CHANGE_FLUSH: + ret =3D mfc_core_run_dec_last_frames(core, ctx); + break; + case MFCINST_RES_CHANGE_END: + mfc_debug(2, "[DRC] Finished remaining frames after resolution change\n"= ); + ctx->capture_state =3D QUEUE_FREE; + mfc_debug(2, "[DRC] Will re-init the codec\n"); + ret =3D mfc_core_run_dec_init(core, ctx); + break; + case MFCINST_DPB_FLUSHING: + mfc_core_cmd_dpb_flush(core, ctx); + break; + case MFCINST_MOVE_INST: + mfc_core_cmd_move_inst(core, ctx); + break; + default: + mfc_info("can't try command(decoder just_run), state : %d\n", + core_ctx->state); + ret =3D -EAGAIN; + } + + return ret; +} + /* Run an operation on hardware */ int mfc_core_just_run(struct mfc_core *core, int new_ctx_index) { @@ -321,6 +538,12 @@ int mfc_core_just_run(struct mfc_core *core, int new_c= tx_index) core->next_ctx_idx =3D -1; } =20 + /* Got context to run in ctx */ + mfc_debug(2, "src: %d(ready: %d), dst: %d, state: %d, dpb_count =3D %d\n", + mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_buf_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue), + core_ctx->state, ctx->dpb_count); mfc_debug(2, "core_ctx->state =3D %d\n", core_ctx->state); /* Last frame has already been sent to MFC * Now obtaining frames from MFC buffer @@ -332,5 +555,146 @@ int mfc_core_just_run(struct mfc_core *core, int new_= ctx_index) else core->continue_clock_on =3D false; =20 + if (ctx->type =3D=3D MFCINST_DECODER) { + ret =3D __mfc_just_run_dec(core, ctx); + } else { + mfc_err("invalid context type: %d\n", ctx->type); + ret =3D -EAGAIN; + } + + if (ret) { + /* + * Clear any reserved F/W cache flush for next ctx, + * as this will be newly decided in Prediction code. + */ + core->cache_flush_flag =3D 0; + core->last_cmd_has_cache_flush =3D 0; + + /* + * Check again the ctx condition and clear work bits + * if ctx is not available. + */ + if (core->sched->dequeue_work(core, core_ctx) =3D=3D 0) + ctx->clear_work_bit =3D 0; + if (ctx->clear_work_bit) { + core->sched->clear_work(core, core_ctx); + ctx->clear_work_bit =3D 0; + } + + mfc_core_pm_clock_off(core, 1); + } + return ret; } + +void mfc_core_hwlock_handler_irq(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int new_ctx_index; + unsigned long flags; + int ret, need_butler =3D 0; + + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_info("[MSR] Couldn't lock HW. It's Error state\n"); + return; + } + + spin_lock_irqsave(&core->hwlock.lock, flags); + __mfc_print_hwlock(core); + + /* For handling DRC, when state is RES_CHANGE_INIT or RES_CHANGE_FLUSH, + * we need to make need_butler =3D 1. Then, rm_request_work will be calle= d. + */ + if ((IS_MULTI_MODE(ctx) && + (core_ctx->state =3D=3D MFCINST_RUNNING || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_INIT)) || + (ctx->handle_drc_multi_mode && + (core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH_FINISHED))) + need_butler =3D 1; + + if (core->hwlock.owned_by_irq) { + if (core->preempt_core_ctx > MFC_NO_INSTANCE_SET) { + mfc_debug(2, "There is a preempt_core_ctx\n"); + core->continue_clock_on =3D true; + mfc_wake_up_core_ctx(core_ctx, reason, err); + new_ctx_index =3D core->preempt_core_ctx; + mfc_debug(2, "preempt_core_ctx is : %d\n", new_ctx_index); + __mfc_transfer_hwlock_ctx_protected(core, new_ctx_index); + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + ret =3D mfc_core_just_run(core, new_ctx_index); + if (ret) { + core->continue_clock_on =3D false; + __mfc_yield_hwlock(core, core->core_ctx[new_ctx_index]); + } + } else if (!list_empty(&core->hwlock.waiting_list)) { + mfc_debug(2, "There is a waiting module for hwlock\n"); + core->continue_clock_on =3D false; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_core_pm_clock_off(core, 1); + mfc_wake_up_core_ctx(core_ctx, reason, err); + mfc_core_release_hwlock_ctx(core_ctx); + core->sched->queue_work(core); + } else { + mfc_debug(2, "No preempt_ctx and no waiting module\n"); + new_ctx_index =3D core->sched->pick_next_work(core); + if (new_ctx_index < 0) { + mfc_debug(2, "No ctx to run\n"); + /* No contexts to run */ + core->continue_clock_on =3D false; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_core_pm_clock_off(core, 1); + mfc_wake_up_core_ctx(core_ctx, reason, err); + mfc_core_release_hwlock_ctx(core_ctx); + core->sched->queue_work(core); + } else { + mfc_debug(2, "There is a ctx to run\n"); + core->continue_clock_on =3D true; + mfc_wake_up_core_ctx(core_ctx, reason, err); + + /* If cache flush command is needed or there is OTF handle, + * handler should stop + */ + if (!need_butler) { + mfc_debug(2, "Work to do successively (next ctx: %d)\n", + new_ctx_index); + __mfc_transfer_hwlock_ctx_protected(core, new_ctx_index); + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + ret =3D mfc_core_just_run(core, new_ctx_index); + if (ret) { + core->continue_clock_on =3D false; + __mfc_yield_hwlock(core, + core->core_ctx[new_ctx_index]); + } + } else { + spin_unlock_irqrestore(&core->hwlock.lock, flags); + mfc_core_release_hwlock_ctx(core_ctx); + } + } + } + } else { + mfc_debug(2, "hwlock is NOT owned by irq\n"); + core->continue_clock_on =3D false; + + spin_unlock_irqrestore(&core->hwlock.lock, flags); + + mfc_core_pm_clock_off(core, 1); + mfc_wake_up_core_ctx(core_ctx, reason, err); + core->sched->queue_work(core); + } + + if (need_butler) + queue_work(core->dev->butler_wq, &core->dev->butler_work); + + __mfc_print_hwlock(core); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h b/= drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h index 35f34f306d7d..156809eafede 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.h @@ -68,5 +68,13 @@ int mfc_core_get_hwlock_ctx(struct mfc_core_ctx *core_ct= x); =20 void mfc_core_release_hwlock_dev(struct mfc_core *core); void mfc_core_release_hwlock_ctx(struct mfc_core_ctx *core_ctx); + +void mfc_core_move_hwlock_ctx(struct mfc_core *to_core, struct mfc_core *f= rom_core, + struct mfc_core_ctx *core_ctx); + +void mfc_core_try_run(struct mfc_core *core); int mfc_core_just_run(struct mfc_core *core, int new_ctx_index); +void mfc_core_hwlock_handler_irq(struct mfc_core *core, struct mfc_ctx *ct= x, + unsigned int reason, unsigned int err); + #endif /* __MFC_CORE_HWLOCK_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_isr.c index 4c6f531ffc02..94cc3c4dfdc5 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c @@ -18,7 +18,38 @@ #include "mfc_core_hw_reg_api.h" #include "mfc_core_reg_api.h" =20 +#include "base/mfc_rate_calculate.h" +#include "base/mfc_qos.h" + +#include "base/mfc_queue.h" #include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" + +static inline enum vb2_buffer_state __mfc_get_buf_state(unsigned int err) +{ + switch (err) { + case MFC_REG_ERR_NO_KEY_FRAME: + case MFC_REG_ERR_NO_VALID_SEQ_HDR: + case MFC_REG_ERR_NO_VALID_PIC_HDR: + case MFC_REG_ERR_NO_VALID_REF_FOR_SKIP: + case MFC_REG_ERR_UNSUPPORTED_FEATURE: + case MFC_REG_ERR_UNSUPPORTED_RESOLUTION: + case MFC_REG_ERR_HEADER_NOT_FOUND: + case MFC_REG_ERR_INVALID_NAL_TYPE: + case MFC_REG_ERR_SEQUENCE_HEADER_ERROR: + case MFC_REG_ERR_PICTURE_HEADER_ERROR: + case MFC_REG_ERR_SLICE_HEADER_ERROR: + case MFC_REG_ERR_MISSING_FIRST_FIELD: + case MFC_REG_ERR_SLICE_COUNT_IS_OVER_ASO: + case MFC_REG_ERR_TILE_HEADER_ERROR: + case MFC_REG_ERR_MAX_VIEW_NUM_OVER: + case MFC_REG_ERR_MFC_TIMEOUT: + return VB2_BUF_STATE_ERROR; + default: + return VB2_BUF_STATE_DONE; + } +} =20 static inline int __mfc_core_is_err_condition(unsigned int err) { @@ -35,10 +66,1216 @@ static inline int __mfc_core_is_err_condition(unsigne= d int err) } } =20 +static inline void mfc_handle_force_change_status(struct mfc_core_ctx *cor= e_ctx) +{ + if (core_ctx->state !=3D MFCINST_ABORT && + core_ctx->state !=3D MFCINST_HEAD_PARSED && + core_ctx->state !=3D MFCINST_RES_CHANGE_FLUSH && + core_ctx->state !=3D MFCINST_RES_CHANGE_FLUSH_FINISHED) + mfc_change_state(core_ctx, MFCINST_RUNNING); +} + +static void __mfc_handle_black_bar_info(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct v4l2_rect new_black_bar; + int black_bar_info; + struct mfc_dec *dec =3D ctx->dec_priv; + + black_bar_info =3D mfc_core_get_black_bar_detection(); + mfc_ctx_debug(3, "[BLACKBAR] type: %#x\n", black_bar_info); + + if (black_bar_info =3D=3D MFC_REG_DISP_STATUS_BLACK_BAR) { + new_black_bar.left =3D mfc_core_get_black_bar_pos_x(); + new_black_bar.top =3D mfc_core_get_black_bar_pos_y(); + new_black_bar.width =3D mfc_core_get_black_bar_image_w(); + new_black_bar.height =3D mfc_core_get_black_bar_image_h(); + } else if (black_bar_info =3D=3D MFC_REG_DISP_STATUS_BLACK_SCREEN) { + new_black_bar.left =3D -1; + new_black_bar.top =3D -1; + new_black_bar.width =3D ctx->img_width; + new_black_bar.height =3D ctx->img_height; + } else if (black_bar_info =3D=3D MFC_REG_DISP_STATUS_NOT_DETECTED) { + new_black_bar.left =3D 0; + new_black_bar.top =3D 0; + new_black_bar.width =3D ctx->img_width; + new_black_bar.height =3D ctx->img_height; + } else { + mfc_ctx_err("[BLACKBAR] Not supported type: %#x\n", black_bar_info); + dec->black_bar_updated =3D 0; + return; + } + + if (new_black_bar.left =3D=3D dec->black_bar.left && + new_black_bar.top =3D=3D dec->black_bar.top && + new_black_bar.width =3D=3D dec->black_bar.width && + new_black_bar.height =3D=3D dec->black_bar.height) { + mfc_ctx_debug(4, "[BLACKBAR] information was not changed\n"); + dec->black_bar_updated =3D 0; + return; + } + + dec->black_bar =3D new_black_bar; + dec->black_bar_updated =3D 1; +} + +static unsigned int __mfc_handle_frame_field(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned int interlace_type =3D 0, is_interlace =3D 0; + unsigned int field; + + if (IS_H264_DEC(ctx)) { + dec->is_mbaff =3D mfc_core_is_mbaff_picture(); + is_interlace =3D mfc_core_is_interlace_picture(); + } + + if (is_interlace) { + interlace_type =3D mfc_core_get_interlace_type(); + if (interlace_type) + field =3D V4L2_FIELD_INTERLACED_TB; + else + field =3D V4L2_FIELD_INTERLACED_BT; + } else if (dec->is_mbaff) { + field =3D V4L2_FIELD_INTERLACED_TB; + } else { + field =3D V4L2_FIELD_NONE; + } + + if (is_interlace || dec->is_mbaff) + mfc_ctx_debug(2, "[INTERLACE] is_interlace: %d (type : %d), is_mbaff: %d= , field: %#x\n", + is_interlace, interlace_type, dec->is_mbaff, field); + + return field; +} + +static struct mfc_buf *__mfc_handle_last_frame(struct mfc_core *core, stru= ct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *dst_mb; + int index, i; + + mfc_ctx_debug(2, "DQ empty DPB with stored tag\n"); + + dst_mb =3D mfc_get_del_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USE= D); + if (!dst_mb) { + mfc_ctx_err("there is no dst buffer for EOS tag\n"); + return NULL; + } + + mfc_ctx_debug(2, "Cleaning up buffer: [%d][%d]\n", + dst_mb->vb.vb2_buf.index, dst_mb->dpb_index); + + index =3D dst_mb->vb.vb2_buf.index; + + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, i, 0); + + dst_mb->vb.sequence =3D (++ctx->sequence); + dst_mb->vb.field =3D __mfc_handle_frame_field(core, ctx); + mfc_clear_mb_flag(dst_mb); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index]) = < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, ctx->stored_tag); + + mutex_lock(&dec->dpb_mutex); + + index =3D dst_mb->dpb_index; + dec->dpb[index].queued =3D 0; + clear_bit(index, &dec->queued_dpb); + dec->display_index =3D dst_mb->dpb_index; + + mutex_unlock(&dec->dpb_mutex); + mfc_ctx_debug(2, "[DPB] Cleand up index [%d][%d], used_flag =3D %#lx, que= ued =3D %#lx\n", + dst_mb->vb.vb2_buf.index, dst_mb->dpb_index, + dec->dynamic_used, dec->queued_dpb); + + return dst_mb; +} + +static void __mfc_handle_frame_unused_output(struct mfc_core *core, struct= mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *mfc_buf =3D NULL; + unsigned int index; + + while (1) { + mfc_buf =3D mfc_get_del_buf(ctx, &ctx->err_buf_queue, MFC_BUF_NO_TOUCH_U= SED); + if (!mfc_buf) + break; + + index =3D mfc_buf->vb.vb2_buf.index; + + mfc_clear_mb_flag(mfc_buf); + mfc_buf->vb.flags &=3D ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_ERROR); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index])= < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, + MFC_REG_DEC_STATUS_DECODING_ONLY); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, UNUSED_TAG); + + dec->ref_buf[dec->refcnt].fd[0] =3D mfc_buf->vb.vb2_buf.planes[0].m.fd; + dec->refcnt++; + + vb2_buffer_done(&mfc_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + mfc_ctx_debug(2, "[DPB] dst index [%d][%d] fd: %d is buffer done (not us= ed)\n", + mfc_buf->vb.vb2_buf.index, mfc_buf->dpb_index, + mfc_buf->vb.vb2_buf.planes[0].m.fd); + } +} + +static void __mfc_handle_ref_info(struct mfc_ctx *ctx, struct mfc_buf *mfc= _buf, unsigned int err) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct dec_dpb_ref_info *ref_info =3D NULL; + int i; + + if (!mfc_buf) { + for (i =3D 0; i < dec->refcnt; i++) + mfc_ctx_debug(2, "[REFINFO] Released FD =3D %d will update with display= buffer\n", + dec->ref_buf[i].fd[0]); + return; + } + + ref_info =3D &dec->ref_info[mfc_buf->vb.vb2_buf.index]; + for (i =3D 0; i < dec->refcnt; i++) + ref_info->dpb[i].fd[0] =3D dec->ref_buf[i].fd[0]; + if (dec->refcnt !=3D MFC_MAX_BUFFERS) + ref_info->dpb[i].fd[0] =3D MFC_INFO_INIT_FD; + dec->refcnt =3D 0; + + mfc_ctx_debug(2, "[DPB] dst index [%d][%d] fd: %d is buffer done\n", + mfc_buf->vb.vb2_buf.index, mfc_buf->dpb_index, + mfc_buf->vb.vb2_buf.planes[0].m.fd); + vb2_buffer_done(&mfc_buf->vb.vb2_buf, mfc_get_warn(err) ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); +} + +static void __mfc_handle_frame_all_extracted(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_buf *dst_mb; + int index, i, is_first =3D 1; + + mfc_debug(2, "Decided to finish\n"); + ctx->sequence++; + + if (core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH) + is_first =3D 0; + + while (1) { + dst_mb =3D mfc_get_del_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_US= ED); + if (!dst_mb) + break; + + mfc_debug(2, "Cleaning up buffer: [%d][%d]\n", + dst_mb->vb.vb2_buf.index, dst_mb->dpb_index); + + index =3D dst_mb->vb.vb2_buf.index; + + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, i, 0); + + dst_mb->vb.sequence =3D (ctx->sequence++); + dst_mb->vb.field =3D __mfc_handle_frame_field(core, ctx); + mfc_clear_mb_flag(dst_mb); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index])= < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + if (is_first) { + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, ctx->stored_tag); + is_first =3D 0; + } else { + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, DEFAULT_TAG); + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL, 0); + } + + mutex_lock(&dec->dpb_mutex); + + index =3D dst_mb->dpb_index; + dec->dpb[index].queued =3D 0; + clear_bit(index, &dec->queued_dpb); + + mutex_unlock(&dec->dpb_mutex); + + vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + mfc_debug(2, "[DPB] Cleand up index =3D %d, used_flag =3D %#lx, queued = =3D %#lx\n", + index, dec->dynamic_used, dec->queued_dpb); + } + + /* dequeue unused DPB */ + __mfc_handle_frame_unused_output(core, ctx); + + mfc_handle_force_change_status(core_ctx); + mfc_debug(2, "After cleanup\n"); + + mfc_cleanup_iovmm_except_used(ctx); + mfc_print_dpb_table(ctx); +} + +static void __mfc_handle_frame_copy_timestamp(struct mfc_core_ctx *core_ct= x, + dma_addr_t dec_y_addr) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_buf *dst_mb, *src_mb; + + /* Get the source buffer */ + src_mb =3D mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_NO_TOUCH_US= ED); + if (!src_mb) { + mfc_err("[TS] no src buffers\n"); + return; + } + + dst_mb =3D mfc_find_buf(ctx, &ctx->dst_buf_queue, dec_y_addr); + if (dst_mb) + dst_mb->vb.vb2_buf.timestamp =3D src_mb->vb.vb2_buf.timestamp; +} + +static struct mfc_buf *__mfc_handle_frame_output_del(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int err) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_raw_info *raw =3D &ctx->raw_buf; + struct mfc_buf *dst_mb =3D NULL; + dma_addr_t dspl_y_addr; + unsigned int frame_type; + unsigned int dst_frame_status; + unsigned int is_video_signal_type =3D 0, is_colour_description =3D 0; + unsigned int is_content_light =3D 0, is_display_colour =3D 0; + unsigned int i, idr_flag, is_last_display; + int index; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->color_aspect_dec)) { + is_video_signal_type =3D mfc_core_get_video_signal_type(); + is_colour_description =3D mfc_core_get_colour_description(); + } + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->static_info_dec)) { + is_content_light =3D mfc_core_get_sei_avail_content_light(); + is_display_colour =3D mfc_core_get_sei_avail_mastering_display(); + } + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->black_bar) && + (dec->detect_black_bar || + core->dev->debugfs.feature_option & MFC_OPTION_BLACK_BAR_ENABLE)) + __mfc_handle_black_bar_info(core, ctx); + else + dec->black_bar_updated =3D 0; + + if (dec->immediate_display =3D=3D 1) { + dspl_y_addr =3D mfc_core_get_dec_y_addr(); + frame_type =3D mfc_core_get_dec_frame_type(); + idr_flag =3D mfc_core_get_dec_idr_flag(); + } else { + dspl_y_addr =3D mfc_core_get_disp_y_addr(); + frame_type =3D mfc_core_get_disp_frame_type(); + idr_flag =3D mfc_core_get_disp_idr_flag(); + } + + is_last_display =3D mfc_core_get_last_display(); + + /* If multi_view_enable(MV-HEVC), buffer must not be delete at the irq of= main_view. + * Because, buffer will be reused for sub_view. + */ + if (!ctx->multi_view_enable || ctx->select_view_irq =3D=3D MFC_VIEW_ID_SU= B) { + dst_mb =3D mfc_find_del_buf(ctx, &ctx->dst_buf_queue, dspl_y_addr); + } else { + dst_mb =3D mfc_find_buf(ctx, &ctx->dst_buf_queue, dspl_y_addr); + mfc_ctx_debug(2, "not delete dst_mb to reuse for VIEW_1\n"); + } + + if (dst_mb) { + index =3D dst_mb->vb.vb2_buf.index; + /* Check if this is the buffer we're looking for */ + mfc_ctx_debug(2, "[BUFINFO][DPB] ctx[%d] get dst index: [%d][%d], addr[0= ]: 0x%08llx\n", + ctx->num, index, dst_mb->dpb_index, + dst_mb->addr[0][0]); + + if (dec->crc_enable && dec->crc && + (ctx->dev->debugfs.sfr_dump & MFC_DUMP_DEC_CRC)) { + if (dec->crc_idx < SZ_1K) { + dec->crc[dec->crc_idx++] =3D mfc_core_get_crc_luma(); + dec->crc[dec->crc_idx++] =3D mfc_core_get_crc_chroma(); + } else { + mfc_ctx_debug(2, "[CRC] couldn't store CRC dump (idx: %d)\n", + dec->crc_idx); + } + } + + dst_mb->vb.sequence =3D ctx->sequence; + dst_mb->vb.field =3D __mfc_handle_frame_field(core, ctx); + + /* Set flag bits in order to inform SEI information */ + if (ctx->select_view_irq =3D=3D MFC_VIEW_ID_MAIN) { + mfc_clear_mb_flag(dst_mb); + + dst_mb->vb.flags &=3D ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_ERROR); + + switch (frame_type) { + case MFC_REG_DISPLAY_FRAME_I: + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_KEYFRAME; + if (!(CODEC_HAS_IDR(ctx) && !idr_flag)) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_SYNC_FRAME); + mfc_ctx_debug(2, "[FRAME] syncframe IDR\n"); + } + break; + case MFC_REG_DISPLAY_FRAME_P: + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_PFRAME; + break; + case MFC_REG_DISPLAY_FRAME_B: + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_BFRAME; + break; + default: + break; + } + } + + if ((ctx->multi_view_enable || ctx->ready_to_be_multi_view_enable) && + mfc_core_get_mvc_right_view_id() =3D=3D MFC_VIEW_ID_MAIN) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_RIGHT_IS_MAIN_VIEW); + mfc_ctx_debug(2, "[MV-HEVC] Right veiw is the main view.\n"); + } + + if (is_content_light) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_HDR_CONTENT_LIGHT); + mfc_ctx_debug(2, "[HDR] content light level parsed\n"); + } + + if (is_display_colour) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_HDR_DISPLAY_COLOUR); + mfc_ctx_debug(2, "[HDR] mastering display colour parsed\n"); + } + + if (is_video_signal_type) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_HDR_VIDEO_SIGNAL_TYPE); + mfc_ctx_debug(2, "[HDR] video signal type parsed\n"); + if (is_colour_description) { + mfc_set_mb_flag(dst_mb, + MFC_FLAG_HDR_MAXTIX_COEFF); + mfc_ctx_debug(2, "[HDR] matrix coefficients parsed\n"); + mfc_set_mb_flag(dst_mb, + MFC_FLAG_HDR_COLOUR_DESC); + mfc_ctx_debug(2, "[HDR] colour description parsed\n"); + } + } + + if (dec->black_bar_updated) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_BLACKBAR_DETECT); + mfc_ctx_debug(3, "[BLACKBAR] black bar detected\n"); + } + + if (ctx->update_framerate) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_FRAMERATE_CH); + ctx->update_framerate =3D false; + mfc_ctx_debug(2, "[QoS] framerate changed\n"); + } + + if (is_last_display) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_LAST_FRAME); + mfc_ctx_debug(2, "[FRAME] Last display frame\n"); + } + + if (ctx->dst_fmt->mem_planes =3D=3D 1) { + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, + raw->total_plane_size); + mfc_ctx_debug(5, "single plane payload: %d\n", + raw->total_plane_size); + } else { + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) { + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, + i, + raw->plane_size[i]); + } + } + + if (mfc_get_warn(err)) { + mfc_ctx_info("Warning for displayed frame: %d\n", mfc_get_warn(err)); + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_ERROR; + } + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index])= < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + if (dec->immediate_display =3D=3D 1) { + dst_frame_status =3D mfc_core_get_dec_status(); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, + dst_frame_status); + + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, ctx->stored_tag); + + dec->immediate_display =3D 0; + } + + mfc_rate_update_last_framerate(ctx, dst_mb->vb.vb2_buf.timestamp); + + mutex_lock(&dec->dpb_mutex); + + dec->dpb[dst_mb->dpb_index].queued =3D 0; + clear_bit(dst_mb->dpb_index, &dec->queued_dpb); + dec->display_index =3D dst_mb->dpb_index; + + mutex_unlock(&dec->dpb_mutex); + } else { + mfc_print_dpb_queue_with_lock(core->core_ctx[ctx->num], dec); + } + + /* + * When main-view of MV-HEVC, should return NULL. + * Because, if address of dst_mb returned by this function is not NULL, + * the address will be used by other functions. + * It could cause kernel panic. + */ + if (ctx->multi_view_enable && ctx->select_view_irq =3D=3D MFC_VIEW_ID_MAI= N) + dst_mb =3D NULL; + + return dst_mb; +} + +static void __mfc_handle_released_buf(struct mfc_core *core, struct mfc_ct= x *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned long prev_flag, new_released_flag, released_flag =3D 0; + unsigned long flag; + unsigned int released_flag_sub, released_flag_main; + int i, main_i, sub_i; + + mutex_lock(&dec->dpb_mutex); + + prev_flag =3D dec->dynamic_used; + dec->dynamic_used =3D mfc_core_get_dec_used_flag(); + released_flag =3D prev_flag & (~dec->dynamic_used); + mfc_ctx_debug(2, "[DPB] Used flag: old =3D %#lx, new =3D %#lx, released = =3D %#lx, queued =3D %#lx\n", + prev_flag, dec->dynamic_used, released_flag, dec->queued_dpb); + + /* In the case of multi_view_enable(MV-HEVC), both main_view and sub_view= must be released + * together. This is because the driver assigns dpb_index with only the l= ower half bit + * and maps both main_view and sub_view together. + * + * 1) The driver splits the original released_flag into released_flag_sub, + * released_flag_main. + * 2) Using the 'and' operation, it calculates the bits that can be relea= sed together + * and creates new_released_flag with the result + * 3) If there are bits that can be released alone (either main or sub), + * it uses the 'xor' operation to set dynamic_used back to 1. + */ + if (ctx->multi_view_enable) { + released_flag_sub =3D released_flag >> (MFC_MAX_DPBS / 2); + released_flag_main =3D (unsigned int)released_flag; + released_flag_sub &=3D released_flag_main; + new_released_flag =3D ((unsigned long)released_flag_sub << + (MFC_MAX_DPBS / 2)) | released_flag_sub; + dec->dynamic_used |=3D (new_released_flag ^ released_flag); + released_flag =3D new_released_flag; + } + + flag =3D dec->dynamic_used | released_flag; + for (i =3D __ffs(flag); i < MFC_MAX_DPBS;) { + if (dec->dynamic_used & (1UL << i)) { + dec->dpb[i].ref =3D 1; + if (dec->dpb[i].mapcnt =3D=3D 0) + mfc_ctx_err("[DPB] %d index is no dpb table\n", i); + } + if (released_flag & (1UL << i)) { + dec->dpb[i].ref =3D 0; + + if (ctx->multi_view_enable && i >=3D MFC_MAX_DPBS / 2) + main_i =3D i - MFC_MAX_DPBS / 2; + else + main_i =3D i; + + if (dec->dpb[main_i].queued && (dec->dpb[i].new_fd !=3D -1)) { + dec->ref_buf[dec->refcnt].fd[0] =3D dec->dpb[i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] Queued DPB[%d] released fd: %d\n", + i, dec->dpb[i].fd[0]); + dec->dpb[i].fd[0] =3D dec->dpb[i].new_fd; + dec->dpb[i].new_fd =3D -1; + mfc_ctx_debug(3, "[REFINFO] Queued DPB[%d] reuse fd: %d\n", + i, dec->dpb[i].fd[0]); + } else if (!dec->dpb[main_i].queued) { + dec->ref_buf[dec->refcnt].fd[0] =3D dec->dpb[i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] Dqueued DPB[%d] released fd: %d\n", + i, dec->dpb[i].fd[0]); + + if (dec->dpb[i].new_fd !=3D -1) { + dec->ref_buf[dec->refcnt].fd[0] =3D dec->dpb[i].new_fd; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] Dqueued DPB[%d] released fd: %d\n", + i, dec->dpb[i].new_fd); + dec->dpb[i].new_fd =3D -1; + } + + /* + * Except queued buffer, + * the released DPB is deleted from dpb_table + */ + dec->dpb_table_used &=3D ~(1UL << i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, i); + } else { + mfc_ctx_debug(3, "[REFINFO] Queued DPB[%d] reuse fd: %d\n", + i, dec->dpb[i].fd[0]); + } + } + flag &=3D ~(1UL << i); + if (flag =3D=3D 0) + break; + i =3D __ffs(flag); + } + + /* The displayed and not referenced buffer must be freed from dpb_table */ + if (dec->display_index >=3D 0) { + i =3D dec->display_index; + + if (!ctx->multi_view_enable) { + if (!(dec->dynamic_used & (1UL << i)) && + dec->dpb[i].mapcnt && + !dec->dpb[i].queued) { + dec->ref_buf[dec->refcnt].fd[0] =3D dec->dpb[i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] display DPB[%d] released fd: %d\n", + i, dec->dpb[i].fd[0]); + dec->dpb_table_used &=3D ~(1UL << i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, i); + } + } else { + /* display_index is dpb_index of main view (view0) */ + main_i =3D i; + sub_i =3D i + MFC_MAX_DPBS / 2; + + /* Both main_view and sub_view must always be released together. */ + if (!(dec->dynamic_used & (1UL << main_i)) && dec->dpb[main_i].mapcnt && + !dec->dpb[main_i].queued && !(dec->dynamic_used & (1UL << sub_i)) && + dec->dpb[sub_i].mapcnt && !dec->dpb[sub_i].queued) { + dec->ref_buf[dec->refcnt].fd[0] =3D dec->dpb[main_i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] display DPB[%d] released fd: %d\n", + main_i, dec->dpb[main_i].fd[0]); + dec->dpb_table_used &=3D ~(1UL << main_i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, main_i); + + dec->ref_buf[dec->refcnt].fd[0] =3D dec->dpb[sub_i].fd[0]; + dec->refcnt++; + mfc_ctx_debug(3, "[REFINFO] display DPB[%d] released fd: %d\n", + sub_i, dec->dpb[sub_i].fd[0]); + dec->dpb_table_used &=3D ~(1UL << sub_i); + mfc_put_iovmm(ctx, dec->dpb, ctx->dst_fmt->mem_planes, sub_i); + } else if (dec->dpb[main_i].mapcnt) { + if (ctx->select_view_irq =3D=3D MFC_VIEW_ID_MAIN) + dec->dynamic_used |=3D (1UL << main_i); + else + dec->dynamic_used |=3D (1UL << sub_i); + } + } + + dec->display_index =3D -1; + } + + mfc_print_dpb_table(ctx); + + mutex_unlock(&dec->dpb_mutex); +} + +static struct mfc_buf *__mfc_handle_frame_output(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int err) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned int frame_type; + + frame_type =3D mfc_core_get_disp_frame_type(); + + if (!ctx->multi_view_enable || ctx->select_view_irq =3D=3D MFC_VIEW_ID_SU= B) + ctx->sequence++; + + if (dec->immediate_display =3D=3D 1) + frame_type =3D mfc_core_get_dec_frame_type(); + + mfc_ctx_debug(2, "[FRAME] frame type: %d\n", frame_type); + + /* If frame is same as previous then skip and do not dequeue */ + if (frame_type =3D=3D MFC_REG_DISPLAY_FRAME_NOT_CODED) + return NULL; + + /* Dequeued display buffer for user */ + return __mfc_handle_frame_output_del(core, ctx, err); +} + +static void __mfc_handle_error_state(struct mfc_ctx *ctx, struct mfc_core_= ctx *core_ctx) +{ + mfc_err("[MSR] It's Error state: cleanup queue\n"); + MFC_TRACE_CORE_CTX("*** ERROR state\n"); + + mfc_change_state(core_ctx, MFCINST_ERROR); + + /* Mark all dst buffers as having an error */ + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue); + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->err_buf_queue); + /* Mark all src buffers as having an error */ + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue); + mfc_cleanup_queue(&ctx->buf_queue_lock, &core_ctx->src_buf_queue); +} + +void mfc_core_handle_error(struct mfc_core *core) +{ + struct mfc_dev *dev =3D core->dev; + struct mfc_core_ctx *core_ctx; + int i; + + mfc_core_err("[MSR] >>>>>>>> MFC CORE is Error state <<<<<<<<\n"); + mfc_core_change_state(core, MFCCORE_ERROR); + + mutex_lock(&dev->mfc_mutex); + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (!core->core_ctx[i]) + continue; + /* TODO: need to check two core mode */ + core_ctx =3D core->core_ctx[i]; + __mfc_handle_error_state(core_ctx->ctx, core_ctx); + } + mutex_unlock(&dev->mfc_mutex); +} + +/* Error handling for interrupt */ +static inline void __mfc_handle_error(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int error_code) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_buf *src_mb; + unsigned int err, warn; + + err =3D mfc_get_err(error_code); + warn =3D mfc_get_err(error_code); + + if ((err >=3D MFC_REG_ERR_FRAME_CONCEAL && err <=3D MFC_REG_ERR_WARNINGS_= END) || + (warn >=3D MFC_REG_ERR_FRAME_CONCEAL && warn <=3D MFC_REG_ERR_WARNING= S_END)) + mfc_info("Interrupt Warn: display: %d, decoded: %d\n", warn, err); + else + mfc_err("Interrupt Error: display: %d, decoded: %d\n", warn, err); + + /* Error recovery is dependent on the state of context */ + switch (core_ctx->state) { + case MFCINST_RES_CHANGE_END: + case MFCINST_GOT_INST: + /* This error had to happen while parsing the header */ + src_mb =3D mfc_get_del_buf(ctx, &core_ctx->src_buf_queue, + MFC_BUF_NO_TOUCH_USED); + if (src_mb) { + unsigned char *stream_vir =3D NULL; + unsigned int strm_size =3D 0; + + stream_vir =3D src_mb->vir_addr[0]; + strm_size =3D src_mb->vb.vb2_buf.planes[0].bytesused; + if (strm_size > 640) + strm_size =3D 640; + + if (stream_vir && strm_size) + print_hex_dump(KERN_ERR, "No header: ", + DUMP_PREFIX_OFFSET, 32, 4, + stream_vir, strm_size, false); + + mfc_clear_mb_flag(src_mb); + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + break; + case MFCINST_INIT: + /* This error had to happen while acquireing instance */ + case MFCINST_RETURN_INST: + /* This error had to happen while releasing instance */ + case MFCINST_DPB_FLUSHING: + /* This error had to happen while flushing DPB */ + break; + case MFCINST_HEAD_PARSED: + /* This error had to happen while setting dst buffers */ + case MFCINST_RES_CHANGE_INIT: + case MFCINST_RES_CHANGE_FLUSH: + /* This error has to happen while resolution change */ + case MFCINST_ABORT_INST: + /* This error has to happen while buffer full handling */ + case MFCINST_FINISHING: + /* It is higly probable that an error occurred + * while decoding a frame + */ + __mfc_handle_error_state(ctx, core_ctx); + break; + default: + mfc_err("Encountered an error interrupt which had not been handled\n"); + mfc_err("core_ctx->state =3D %d, core_ctx->inst_no =3D %d\n", + core_ctx->state, core_ctx->inst_no); + break; + } + + mfc_wake_up_core(core, reason, error_code); +} + +static void __mfc_handle_frame_error(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_dec *dec; + struct mfc_buf *src_mb =3D NULL; + unsigned int index; + enum vb2_buffer_state vb2_state; + + dec =3D ctx->dec_priv; + if (!dec) { + mfc_err("no mfc decoder to run\n"); + return; + } + + mfc_err("Interrupt Error: %d\n", err); + + if (mfc_get_err(err) =3D=3D MFC_REG_ERR_MISSING_NON_BASE_VIEW_DETECTED) + mfc_err("[MV-HEVC] %s, Reuse current main-view, skipping previous one.\n= ", + "MFC_REG_ERR_MISSING_NON_BASE_VIEW_DETECTED"); + else + /* Get the source buffer */ + src_mb =3D mfc_get_del_buf(ctx, &core_ctx->src_buf_queue, + MFC_BUF_NO_TOUCH_USED); + + if (!src_mb) { + mfc_err("no src buffers\n"); + } else { + index =3D src_mb->vb.vb2_buf.index; + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, &ctx->src_ctrls[ind= ex]) < 0) + mfc_err("failed in core_recover_buf_ctrls\n"); + + mfc_ctx_debug(2, "MFC needs next buffer\n"); + dec->consumed =3D 0; + mfc_clear_mb_flag(src_mb); + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->src_ctrls[index])= < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + vb2_state =3D __mfc_get_buf_state(mfc_get_err(err)); + mfc_debug(2, "[STREAM] consumed only by error (state: %d)\n", vb2_state); + vb2_buffer_done(&src_mb->vb.vb2_buf, vb2_state); + } + + if (mfc_get_err(err) =3D=3D MFC_REG_ERR_UNDEFINED_EXCEPTION) + mfc_core_handle_error(core); + + mfc_debug(2, "Assesing whether this context should be run again\n"); +} + +static void __mfc_handle_frame_input(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *src_mb; + unsigned int index; + int deleted =3D 0; + unsigned int consumed; + + if (mfc_get_err(err) =3D=3D MFC_REG_ERR_NON_PAIRED_FIELD) { + /* + * For non-paired field, the same buffer need to be + * resubmitted and the consumed stream will be 0 + */ + mfc_debug(2, "Not paired field. Running again the same buffer\n"); + return; + } + + /* Get the source buffer */ + consumed =3D mfc_core_get_consumed_stream(); + src_mb =3D mfc_get_del_if_consumed(ctx, + &core_ctx->src_buf_queue, + consumed, + STUFF_BYTE, + err, + &deleted); + if (!src_mb) { + mfc_err("no src buffers\n"); + return; + } + + index =3D src_mb->vb.vb2_buf.index; + mfc_debug(2, "[BUFINFO] ctx[%d] get src index: %d(%d), addr: 0x%08llx\n", + ctx->num, index, src_mb->src_index, src_mb->addr[0][0]); + + if (!deleted) { + /* Run MFC again on the same buffer */ + mfc_debug(2, "[MULTIFRAME] Running again the same buffer\n"); + + dec->consumed +=3D consumed; + dec->has_multiframe =3D 1; + + MFC_TRACE_CORE_CTX("** consumed:%d, remained:%d\n", + dec->consumed, mfc_dec_get_strm_size(ctx, src_mb)); + /* Do not move src buffer to done_list */ + return; + } + + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, &ctx->src_ctrls[inde= x]) < 0) + mfc_err("failed in core_recover_buf_ctrls\n"); + + mfc_clear_mb_flag(src_mb); + + if (mfc_core_get_disp_status() =3D=3D MFC_REG_DEC_STATUS_DECODING_ONLY && + mfc_core_get_dec_y_addr() =3D=3D 0) { + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + mfc_debug(2, "[STREAM] decoding only but there is no address\n"); + } + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) = < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + dec->consumed =3D 0; + + if (ctx->multi_view_enable && ctx->select_view =3D=3D 0) + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + + vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + mfc_debug(2, "[STREAM] src index: %d(%d), addr: 0x%08llx is buffer done\n= ", + index, src_mb->src_index, src_mb->addr[0][0]); +} + +/* Handle frame decoding interrupt */ +static void __mfc_handle_frame(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *subcore; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + unsigned int dst_frame_status, sei_avail_frame_pack; + unsigned int res_change, need_dpb_change, need_scratch_change; + struct mfc_buf *mfc_buf =3D NULL; + bool qos_update =3D false; + int index; + + dst_frame_status =3D mfc_core_get_disp_status(); + res_change =3D mfc_core_get_res_change(); + need_dpb_change =3D mfc_core_get_dpb_change(); + need_scratch_change =3D mfc_core_get_scratch_change(); + sei_avail_frame_pack =3D mfc_core_get_sei_avail_frame_pack(); + + if (dec->immediate_display =3D=3D 1) + dst_frame_status =3D mfc_core_get_dec_status(); + + mfc_debug(2, "[FRAME] frame status: %d\n", dst_frame_status); + mfc_debug(2, "[FRAME] display status: %d, type: %d, yaddr: %#llx\n", + mfc_core_get_disp_status(), + mfc_core_get_disp_frame_type(), + mfc_core_get_disp_y_addr()); + mfc_debug(2, "[FRAME] decoded status: %d, type: %d, yaddr: %#llx\n", + mfc_core_get_dec_status(), + mfc_core_get_dec_frame_type(), + mfc_core_get_dec_y_addr()); + + mfc_debug(4, "[HDR] SEI available status: 0x%08x\n", + mfc_core_get_sei_avail()); + mfc_debug(4, "[HDR] SEI content light: 0x%08x\n", + mfc_core_get_sei_content_light()); + mfc_debug(4, "[HDR] SEI luminance: 0x%08x, 0x%08x white point: 0x%08x\n", + mfc_core_get_sei_mastering0(), + mfc_core_get_sei_mastering1(), + mfc_core_get_sei_mastering2()); + mfc_debug(4, "[HDR] SEI display primaries: 0x%08x, 0x%08x, 0x%08x\n", + mfc_core_get_sei_mastering3(), + mfc_core_get_sei_mastering4(), + mfc_core_get_sei_mastering5()); + + mfc_qos_get_disp_ratio(ctx, + mfc_core_get_decoded_frame_cnt(), + mfc_core_get_display_frame_cnt()); + qos_update =3D mfc_qos_mb_calculate(core, + core_ctx, mfc_core_get_processing_cycle(), + mfc_core_get_dec_frame_type()); + + if (core_ctx->state =3D=3D MFCINST_RES_CHANGE_INIT) + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_FLUSH); + + if (res_change) { + mfc_info("[DRC] Resolution change set to %d\n", res_change); + mutex_lock(&ctx->drc_wait_mutex); + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_INIT); + if (!IS_SINGLE_MODE(ctx)) { + ctx->handle_drc_multi_mode =3D 1; + if (!ctx->wait_state) { + /* The core that detects DRC must be switched to single */ + ctx->op_core_type =3D (core->id =3D=3D MFC_OP_CORE_FIXED_0) ? + MFC_OP_CORE_FIXED_0 : MFC_OP_CORE_FIXED_1; + ctx->wait_state =3D WAIT_G_FMT | WAIT_STOP; + mfc_debug(2, "[2CORE][DRC] MFC-%d op_core_type: %d\n", + core->id, ctx->op_core_type); + } + mfc_debug(2, "[2CORE][DRC] wait_state: %d\n", ctx->wait_state); + } else { + ctx->handle_drc_multi_mode =3D 0; + ctx->wait_state =3D WAIT_G_FMT | WAIT_STOP; + } + mfc_debug(2, "[DRC] Decoding waiting! : %d\n", ctx->wait_state); + mutex_unlock(&ctx->drc_wait_mutex); + mfc_buf =3D mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_NO_TOUCH_= USED); + if (mfc_buf) { + index =3D mfc_buf->vb.vb2_buf.index; + call_bop(ctx, core_restore_buf_ctrls, ctx, &ctx->src_ctrls[index]); + } + return; + } + + if (need_dpb_change || need_scratch_change) { + mfc_info("[DRC] Interframe resolution changed\n"); + mutex_lock(&ctx->drc_wait_mutex); + ctx->wait_state =3D WAIT_G_FMT | WAIT_STOP; + mfc_core_get_img_size(core, ctx, MFC_GET_RESOL_DPB_SIZE); + dec->inter_res_change =3D 1; + mutex_unlock(&ctx->drc_wait_mutex); + __mfc_handle_frame_all_extracted(core, ctx); + return; + } + + if (mfc_is_queue_count_same(&ctx->buf_queue_lock, &core_ctx->src_buf_queu= e, 0) && + mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)= ) { + mfc_err("Queue count is zero for src and dst\n"); + goto leave_handle_frame; + } + + if (IS_H264_DEC(ctx) && sei_avail_frame_pack && + dst_frame_status =3D=3D MFC_REG_DEC_STATUS_DECODING_ONLY) { + mfc_debug(2, "Frame packing SEI exists for a frame\n"); + mfc_debug(2, "Reallocate DPBs and issue init_buffer\n"); + mutex_lock(&ctx->drc_wait_mutex); + ctx->is_dpb_realloc =3D 1; + mfc_change_state(core_ctx, MFCINST_HEAD_PARSED); + ctx->capture_state =3D QUEUE_FREE; + ctx->wait_state =3D WAIT_STOP; + mutex_unlock(&ctx->drc_wait_mutex); + __mfc_handle_frame_all_extracted(core, ctx); + goto leave_handle_frame; + } + + /* All frames remaining in the buffer have been extracted */ + if (dst_frame_status =3D=3D MFC_REG_DEC_STATUS_DECODING_EMPTY) { + if (core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH) { + mfc_debug(2, "[DRC] Last frame received after resolution change\n"); + __mfc_handle_frame_all_extracted(core, ctx); + + /* In the case of 2core DRC, state must be changed at subcore deinit. */ + if (ctx->handle_drc_multi_mode) + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_FLUSH_FINISHED); + else + mfc_change_state(core_ctx, MFCINST_RES_CHANGE_END); + + if (IS_MULTI_CORE_DEVICE(dev)) + mfc_rm_load_balancing(ctx, MFC_RM_LOAD_DELETE); + + mfc_rate_reset_ts_list(&ctx->src_ts); + mfc_rate_reset_last_framerate(ctx); + mfc_rate_reset_bufq_framerate(ctx); + mfc_rate_set_framerate(ctx, DEC_DEFAULT_FPS); + mfc_qos_on(core, ctx); + + goto leave_handle_frame; + } else { + mfc_buf =3D __mfc_handle_last_frame(core, ctx); + } + } + + /* Detection for QoS weight */ + if (!dec->num_of_tile_over_4 && + mfc_core_get_num_of_tile() >=3D 4) { + dec->num_of_tile_over_4 =3D 1; + qos_update =3D true; + } + + if (qos_update) { + mfc_qos_on(core, ctx); + if (IS_TWO_MODE1(ctx)) { + subcore =3D mfc_get_sub_core(dev, ctx); + if (subcore) { + subcore->core_ctx[ctx->num]->dynamic_weight_level =3D + core_ctx->dynamic_weight_level; + mfc_qos_on(subcore, ctx); + } + } + } + + /* copy decoded timestamp */ + if (mfc_dec_status_decoding(dst_frame_status)) + __mfc_handle_frame_copy_timestamp(core_ctx, mfc_core_get_dec_y_addr()); + + /* Mark source buffer as complete */ + if (dst_frame_status !=3D MFC_REG_DEC_STATUS_DISPLAY_ONLY) + __mfc_handle_frame_input(core, ctx, err); + + /* A frame has been decoded and is in the buffer */ + if (mfc_dec_status_display(dst_frame_status)) { + ctx->select_view_irq =3D mfc_core_get_mvc_view_id_disp_order() % MFC_NUM= _MULTI_VIEW; + mfc_buf =3D __mfc_handle_frame_output(core, ctx, err); + } else { + ctx->select_view_irq =3D mfc_core_get_mvc_view_id_dec_order() % MFC_NUM_= MULTI_VIEW; + } + + /* arrangement of assigned dpb table */ + __mfc_handle_released_buf(core, ctx); + + /* dequeue unused DPB */ + __mfc_handle_frame_unused_output(core, ctx); + + /* There is display buffer for user, update reference information */ + __mfc_handle_ref_info(ctx, mfc_buf, err); + + mfc_rate_update_bufq_framerate(ctx, MFC_TS_DST_DQ); + + if (dst_frame_status =3D=3D MFC_REG_DEC_STATUS_DECODING_EMPTY) { + mfc_handle_force_change_status(core->core_ctx[ctx->num]); + mfc_debug(2, "It can be continue decoding again\n"); + } + + /* The first frame is only for decoding, + * so the select_view should only be increased at display interrupt. + */ + if (ctx->multi_view_enable && mfc_core_get_dec_y_addr()) + ctx->select_view =3D (ctx->select_view + 1) % MFC_NUM_MULTI_VIEW; + +leave_handle_frame: + mfc_debug(2, "Assesing whether this context should be run again\n"); +} + +static inline void __mfc_handle_done_frame(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + __mfc_handle_frame(core, ctx, reason, err); +} + +/* Handle header decoder interrupt */ +static int __mfc_handle_seq_dec(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *src_mb; + int i, is_interlace; + unsigned int strm_size, consumed; + + if (ctx->src_fmt->fourcc !=3D V4L2_PIX_FMT_FIMV1) { + ctx->img_width =3D mfc_core_get_img_width(); + ctx->img_height =3D mfc_core_get_img_height(); + ctx->crop_width =3D ctx->img_width; + ctx->crop_height =3D ctx->img_height; + mfc_info("[STREAM] resolution w: %d, h: %d\n", ctx->img_width, ctx->img_= height); + } + + ctx->dpb_count =3D mfc_core_get_dpb_count(); + mfc_ctx_debug(2, "dpb_count: %d\n", ctx->dpb_count); + + ctx->scratch_buf_size =3D mfc_core_get_scratch_size(); + + mfc_core_dec_get_crop_info(core, ctx); + dec->mv_count =3D mfc_core_get_mv_count(); + + if (ctx->img_width =3D=3D 0 || ctx->img_height =3D=3D 0) { + mfc_err("[STREAM] wrong resolution w: %d, h: %d\n", + ctx->img_width, ctx->img_height); + } else { + is_interlace =3D mfc_core_is_interlace_picture(); + dec->is_mbaff =3D mfc_core_is_mbaff_picture(); + if (is_interlace || dec->is_mbaff) + dec->is_interlaced =3D 1; + mfc_debug(2, "[INTERLACE] interlace: %d, mbaff: %d\n", + is_interlace, dec->is_mbaff); + } + + for (i =3D 0; i < ctx->dst_fmt->num_planes; i++) { + ctx->min_dpb_size[i] =3D mfc_core_get_min_dpb_size(i); + mfc_debug(2, "[FRAME] min_dpb_size[%d]: %d, min_dpb_size_2bits[%d]: %d\n= ", + i, ctx->min_dpb_size[i], i, ctx->min_dpb_size_2bits[i]); + } + + if (IS_MULTI_CORE_DEVICE(dev) && mfc_core_get_two_core_mode()) { + if (dev->debugfs.feature_option & MFC_OPTION_MULTI_CORE_DISABLE) { + mfc_info("[2CORE] op_mode: %d stream, but multi core disable\n", + mfc_core_get_two_core_mode()); + } else { + if (dev->num_inst > 1) + mfc_debug(2, "[2CORE] multi core bits: %#lx, num inst: %d\n", + dev->multi_core_inst_bits, dev->num_inst); + ctx->stream_op_mode =3D mfc_core_get_two_core_mode(); + mfc_info("[2CORE] This stream need to multi core stream_op_mode(%d)\n", + ctx->stream_op_mode); + } + } + + src_mb =3D mfc_get_buf + (ctx, &core_ctx->src_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (src_mb) { + consumed =3D mfc_core_get_consumed_stream(); + strm_size =3D mfc_dec_get_strm_size(ctx, src_mb); + mfc_debug(2, "[STREAM] header size, %d, %#x, consumed, %d, %#x\n", + strm_size, strm_size, consumed, consumed); + if ((IS_H264_DEC(ctx)) && + (consumed > 0 && strm_size > consumed)) { + dec->consumed +=3D consumed; + mfc_debug(2, "[STREAM] there is remained bytes(%d) after header parsing= \n", + (strm_size - consumed)); + } else { + dec->consumed =3D 0; + } + } + + dec->frame_display_delay =3D mfc_core_get_display_delay(); + mfc_debug(2, "[FRAME] display delay for first frame %d\n", + dec->frame_display_delay); + + mfc_change_state(core_ctx, MFCINST_HEAD_PARSED); + + return 0; +} + +static inline void __mfc_handle_nal_abort(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + + mfc_change_state(core_ctx, MFCINST_ABORT); +} + irqreturn_t mfc_core_top_half_irq(int irq, void *priv) { struct mfc_core *core =3D priv; struct mfc_core_ctx *core_ctx; + struct mfc_ctx *ctx; unsigned int err; unsigned int reason; =20 @@ -48,12 +1285,21 @@ irqreturn_t mfc_core_top_half_irq(int irq, void *priv) return IRQ_WAKE_THREAD; } =20 + ctx =3D core_ctx->ctx; reason =3D mfc_core_get_int_reason(); err =3D mfc_core_get_int_err(); =20 core->last_int =3D reason; core->last_int_time =3D ktime_to_timespec64(ktime_get()); =20 + if (reason =3D=3D MFC_REG_R2H_CMD_SEQ_DONE_RET || + reason =3D=3D MFC_REG_R2H_CMD_INIT_BUFFERS_RET || + reason =3D=3D MFC_REG_R2H_CMD_FRAME_DONE_RET) { + ctx->frame_cnt++; + /* counting QoS table portion */ + mfc_qos_set_portion(core, ctx); + } + mfc_debug(2, "[c:%d] Int reason: %d (err: %d, warn: %d)\n", core->curr_core_ctx, reason, mfc_get_err(err), mfc_get_warn(err)); MFC_TRACE_CORE_CTX("<< INT(top): %d\n", reason); @@ -78,6 +1324,83 @@ static int __mfc_irq_dev(struct mfc_core *core, unsign= ed int reason, unsigned in return 1; } =20 +static int __mfc_irq_ctx(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + + if ((core->dev->debugfs.feature_option & MFC_OPTION_MSR_ENABLE) && + ctx->frame_cnt =3D=3D 20) { + reason =3D 0; + mfc_debug(2, "[MSR] Forced entry into MSR mode, it will be recovery\n"); + core->dev->debugfs.feature_option &=3D ~MFC_OPTION_MSR_ENABLE; + } + + switch (reason) { + case MFC_REG_R2H_CMD_ERR_RET: + /* An error has occurred */ + if (core_ctx->state =3D=3D MFCINST_RUNNING || core_ctx->state =3D=3D MFC= INST_ABORT) { + if (mfc_get_err(err) >=3D MFC_REG_ERR_FRAME_CONCEAL && + mfc_get_err(err) <=3D MFC_REG_ERR_WARNINGS_END) + __mfc_handle_frame(core, ctx, reason, err); + else + __mfc_handle_frame_error(core, ctx, reason, err); + } else { + __mfc_handle_error(core, ctx, reason, err); + } + break; + case MFC_REG_R2H_CMD_SLICE_DONE_RET: + case MFC_REG_R2H_CMD_FIELD_DONE_RET: + case MFC_REG_R2H_CMD_FRAME_DONE_RET: + case MFC_REG_R2H_CMD_COMPLETE_SEQ_RET: + __mfc_handle_done_frame(core, ctx, reason, err); + break; + case MFC_REG_R2H_CMD_SEQ_DONE_RET: + if (ctx->type =3D=3D MFCINST_DECODER) + __mfc_handle_seq_dec(core, ctx); + break; + case MFC_REG_R2H_CMD_OPEN_INSTANCE_RET: + core_ctx->inst_no =3D mfc_core_get_inst_no(); + mfc_change_state(core_ctx, MFCINST_GOT_INST); + break; + case MFC_REG_R2H_CMD_CLOSE_INSTANCE_RET: + mfc_change_state(core_ctx, MFCINST_FREE); + break; + case MFC_REG_R2H_CMD_NAL_ABORT_RET: + __mfc_handle_nal_abort(core, ctx, reason); + break; + case MFC_REG_R2H_CMD_DPB_FLUSH_RET: + ctx->select_view =3D MFC_VIEW_ID_MAIN; + mfc_change_state(core_ctx, MFCINST_ABORT); + break; + case MFC_REG_R2H_CMD_INIT_BUFFERS_RET: + if (err !=3D 0) { + mfc_err("INIT_BUFFERS_RET error: %d\n", err); + break; + } + + if (IS_MULTI_MODE(ctx)) + mfc_change_state(core_ctx, MFCINST_BUF_INIT_BUT_MULTI_MODE_NOT_CHECKED_= YET); + else + mfc_change_state(core_ctx, MFCINST_RUNNING); + + if (ctx->type =3D=3D MFCINST_DECODER) { + if (ctx->is_dpb_realloc) + ctx->is_dpb_realloc =3D 0; + } + break; + case MFC_REG_R2H_CMD_MOVE_INSTANCE_RET: + break; + default: + mfc_err("Unknown int reason: %d(%#x)\n", reason, reason); + mfc_core_handle_error(core); + } + + return 1; +} + /* Interrupt processing */ irqreturn_t mfc_core_irq(int irq, void *priv) { @@ -116,7 +1439,35 @@ irqreturn_t mfc_core_irq(int irq, void *priv) ret =3D __mfc_irq_dev(core, reason, err); if (!ret) goto irq_end; - mfc_ctx_info("not implemented context irq ctx"); + + core_ctx =3D core->core_ctx[core->curr_core_ctx]; + if (!core_ctx) { + mfc_core_err("no mfc context to run\n"); + mfc_core_clear_int(); + mfc_core_pm_clock_off(core, 1); + goto irq_end; + } + ctx =3D core_ctx->ctx; + + ret =3D mfc_get_core_intlock(core_ctx); + if (ret) { + mfc_core_clear_int_only(); + goto irq_end; + } + + ret =3D __mfc_irq_ctx(core, ctx, reason, err); + if (!ret) + goto irq_end; + + /* clean-up interrupt */ + mfc_core_clear_int(); + + mfc_release_core_intlock(core_ctx); + + if (!(core_ctx->state =3D=3D MFCINST_RES_CHANGE_INIT && IS_SINGLE_MODE(ct= x))) + core->sched->dequeue_work(core, core_ctx); + + mfc_core_hwlock_handler_irq(core, ctx, reason, err); =20 irq_end: mfc_core_debug_leave(); diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_isr.h index 046b20e6d4c2..16902b1537c6 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.h @@ -19,4 +19,7 @@ =20 irqreturn_t mfc_core_top_half_irq(int irq, void *priv); irqreturn_t mfc_core_irq(int irq, void *priv); + +void mfc_core_handle_error(struct mfc_core *core); + #endif /* __MFC_CORE_ISR_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_run.c index fd7ebe95e715..118108f910e2 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c @@ -17,8 +17,10 @@ #include "mfc_core_cmd.h" #include "mfc_core_hw_reg_api.h" =20 +#include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_mem.h" +#include "base/mfc_qos.h" =20 void mfc_core_run_cache_flush(struct mfc_core *core, enum mfc_do_cache_flush do_cache_flush, @@ -263,3 +265,146 @@ int mfc_core_run_wakeup(struct mfc_core *core) =20 return ret; } + +int mfc_core_run_dec_init(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *src_mb; + unsigned int strm_size; + + /* Initializing decoding - parsing header */ + + /* Get the next source buffer */ + src_mb =3D mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_SET_USED); + if (!src_mb) { + mfc_err("no src buffers\n"); + return -EAGAIN; + } + + strm_size =3D mfc_dec_get_strm_size(ctx, src_mb); + mfc_debug(2, "Preparing to init decoding\n"); + mfc_debug(2, "[STREAM] Header size: %d, (offset: %u, consumed: %u)\n", + strm_size, + src_mb->vb.vb2_buf.planes[0].data_offset, + dec->consumed); + + mfc_core_set_dec_stream_buffer(core, ctx, src_mb, + mfc_dec_get_strm_offset(ctx, src_mb), strm_size); + + mfc_debug(2, "[BUFINFO] Header addr: 0x%08llx\n", src_mb->addr[0][0]); + mfc_clean_core_ctx_int_flags(core->core_ctx[ctx->num]); + mfc_core_cmd_dec_seq_header(core, ctx); + + return 0; +} + +static int __mfc_check_last_frame(struct mfc_core_ctx *core_ctx, + struct mfc_buf *mfc_buf) +{ + if (mfc_check_mb_flag(mfc_buf, MFC_FLAG_LAST_FRAME)) { + mfc_debug(2, "Setting core_ctx->state to FINISHING\n"); + mfc_change_state(core_ctx, MFCINST_FINISHING); + return 1; + } + + return 0; +} + +int mfc_core_run_dec_frame(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_buf *src_mb, *dst_mb; + int last_frame =3D 0; + unsigned int index, src_index; + int ret; + + /* Get the next source buffer */ + if (IS_TWO_MODE2(ctx)) { + src_mb =3D mfc_get_buf_no_used(ctx, &core_ctx->src_buf_queue, + MFC_BUF_SET_USED); + if (!src_mb) { + mfc_debug(2, "no src buffers\n"); + return -EAGAIN; + } + } else { + src_mb =3D mfc_get_buf(ctx, &core_ctx->src_buf_queue, + MFC_BUF_SET_USED); + if (!src_mb) { + mfc_debug(2, "no src buffers\n"); + return -EAGAIN; + } + } + + /* Try to use the non-referenced DPB on dst-queue */ + if (dec->is_dynamic_dpb) { + dst_mb =3D mfc_search_for_dpb(core_ctx); + if (!dst_mb) { + src_mb->used =3D MFC_BUF_RESET_USED; + mfc_debug(2, "[DPB] couldn't find dst buffers\n"); + return -EAGAIN; + } + } + + index =3D src_mb->vb.vb2_buf.index; + src_index =3D src_mb->src_index; + + if (mfc_check_mb_flag(src_mb, MFC_FLAG_EMPTY_DATA)) + src_mb->vb.vb2_buf.planes[0].bytesused =3D 0; + + mfc_core_set_dec_stream_buffer(core, ctx, src_mb, + mfc_dec_get_strm_offset(ctx, src_mb), + mfc_dec_get_strm_size(ctx, src_mb)); + + if (call_bop(ctx, core_set_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) = < 0) + mfc_err("failed in core_set_buf_ctrls\n"); + mfc_core_update_tag(core, ctx, ctx->stored_tag); + + if (dec->is_dynamic_dpb) + mfc_core_set_dynamic_dpb(core, ctx, dst_mb); + + mfc_clean_core_ctx_int_flags(core_ctx); + + last_frame =3D __mfc_check_last_frame(core_ctx, src_mb); + ret =3D mfc_core_cmd_dec_one_frame(core, ctx, last_frame, src_index); + + return ret; +} + +int mfc_core_run_dec_last_frames(struct mfc_core *core, struct mfc_ctx *ct= x) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *dst_mb; + unsigned int src_index; + + if (mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0)= ) { + mfc_debug(2, "no dst buffer\n"); + return -EAGAIN; + } + + /* Try to use the non-referenced DPB on dst-queue */ + if (dec->is_dynamic_dpb) { + dst_mb =3D mfc_search_for_dpb(core_ctx); + if (!dst_mb) { + mfc_debug(2, "[DPB] couldn't find dst buffers\n"); + return -EAGAIN; + } + } + + /* Get the next source buffer */ + mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_SET_USED); + + /* Frames are being decoded */ + mfc_core_set_dec_stream_buffer(core, ctx, 0, 0, 0); + src_index =3D ctx->curr_src_index + 1; + + if (dec->is_dynamic_dpb) + mfc_core_set_dynamic_dpb(core, ctx, dst_mb); + + mfc_clean_core_ctx_int_flags(core_ctx); + mfc_core_cmd_dec_one_frame(core, ctx, 1, src_index); + + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_run.h index 3d243dc18e15..6b9c9ef91e47 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h @@ -23,4 +23,8 @@ void mfc_core_run_deinit_hw(struct mfc_core *core); =20 int mfc_core_run_sleep(struct mfc_core *core); int mfc_core_run_wakeup(struct mfc_core *core); + +int mfc_core_run_dec_init(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_run_dec_frame(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_run_dec_last_frames(struct mfc_core *core, struct mfc_ctx *ct= x); #endif /* __MFC_CORE_RUN_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c b/drivers/m= edia/platform/samsung/exynos-mfc/mfc_rm.c index 0a805464fbc9..a7db47e58589 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c @@ -332,6 +332,7 @@ static int __mfc_rm_move_core_running(struct mfc_ctx *c= tx, int to_core_num, int from_core->core_ctx[core_ctx->num] =3D 0; mfc_core_pm_clock_off(to_core, 0); =20 + mfc_core_move_hwlock_ctx(to_core, from_core, core_ctx); mfc_core_release_hwlock_dev(subcore); mfc_core_release_hwlock_dev(maincore); =20 --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 A7D582737EB for ; Tue, 30 Sep 2025 03:56:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204600; cv=none; b=G2BbwL+6G+51Ycpugt/3MDn3RUnEiMpL70a9/rKdCzNdICV0Ln+e9TaIAP1EAJFZaLqgIxbtSF+QOm+5guBbb6UzXD40Ca9usRojwo+c/7KRuYH5rQ1iXb2fe5oMsD2zwSKApzRCwKEwnNMdOXDltXshQ1s1XmYbRTUPzP7SUbM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204600; c=relaxed/simple; bh=p7uLeOwrukAalktnGbdSQv8u6ZRWLg+m+7zNszoTllE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=s94vI5ogmn+R48sKA2GU7fDtSfBDGxkISGyBlsJ5TqgTbU6fQw2Qbg9iUuXB/FdOo7e6/0KFoNmRUSe/UFXVmZ+L5FK6OcZJer9qxJeGah8ADZRhMFK7s2hTNUnx/FQwsiP/fiosewFlXrg9knJxaYwnkVqmPoxnx5+K9vTH87Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=iBx2U+vu; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="iBx2U+vu" Received: from epcas5p1.samsung.com (unknown [182.195.41.39]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035636epoutp02c290291a0dc4e552ff7fd14aeafc39fd~p80JwqiUr2603026030epoutp026 for ; Tue, 30 Sep 2025 03:56:36 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035636epoutp02c290291a0dc4e552ff7fd14aeafc39fd~p80JwqiUr2603026030epoutp026 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204596; bh=IaY5jS/adaVvZbKjXboBNX9JuZBrD/yz8Cvz3X2BKqY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iBx2U+vupSJpY2oq1jgnT4aro5qGD1WCjmJAkcc431Qrg7CkjevKgzufjSaAvl66R TROrwZJFwLDzqY2f9l5yfQ0v+QfaO/Hst+Bk9tWsuci+IUHuOxkYEwB2V2epqq8YMK tFcM0w3FCvLLMFcuMea6yC5TZLQUzXzvQ9yfiqhQ= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035635epcas5p140112f6b04a785a9b28608ff3dd328ce~p80JKQs5S1549615496epcas5p1J; Tue, 30 Sep 2025 03:56:35 +0000 (GMT) Received: from epcas5p4.samsung.com (unknown [182.195.38.91]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPPZ2ygnz2SSKk; Tue, 30 Sep 2025 03:56:34 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPA id 20250930035633epcas5p3d859613b1d1d3319ded26da626c959bc~p80HacXXH1975219752epcas5p3U; Tue, 30 Sep 2025 03:56:33 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035629epsmtip195521ecc4b0c104190aad7cb519dbfbe~p80D8wCWG2938529385epsmtip1a; Tue, 30 Sep 2025 03:56:29 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 17/29] media: mfc: Add VB2 decoder support Date: Tue, 30 Sep 2025 09:33:36 +0530 Message-Id: <20250930040348.3702923-18-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035633epcas5p3d859613b1d1d3319ded26da626c959bc X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035633epcas5p3d859613b1d1d3319ded26da626c959bc References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Implement VB2 queue operations for decoding (setup, buffer handling, streaming control, multi=E2=80=91view and DMA direction support). - Added header exposing decoder VB2 operations to other MFC components. This introduces a full VB2 backend for video decoding, aligning the driver with standard V4L2 buffer management. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 2 +- .../platform/samsung/exynos-mfc/mfc_dec_vb2.c | 393 ++++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_dec_vb2.h | 19 + 3 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index 19e38c886255..9127f2dc4df6 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_VIDEO_EXYNOS_MFC) :=3D exynos_mfc.o ccflags-y +=3D -I$(srctree)/$(src) =20 #Dev interface layer -exynos_mfc-y +=3D mfc.o +exynos_mfc-y +=3D mfc.o mfc_dec_vb2.o #Dev control layer exynos_mfc-y +=3D mfc_rm.o mfc_ctx_ctrl.o mfc_debugfs.o #Core interface layer diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c new file mode 100644 index 000000000000..3097a6c0bf14 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_dec_vb2.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_dec_vb2.h" + +#include "mfc_rm.h" + +#include "base/mfc_queue.h" +#include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" + +static int mfc_dec_queue_setup(struct vb2_queue *vq, + unsigned int *buf_count, unsigned int *plane_count, + unsigned int psize[], struct device *alloc_devs[]) +{ + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_raw_info *raw; + int i, offset; + + mfc_ctx_debug_enter(); + + raw =3D &ctx->raw_buf; + + /* + * During queue_setup, + * context information is need to for only main core + */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + /* Video output for decoding (source) + * this can be set after getting an instance + */ + if (core_ctx->state =3D=3D MFCINST_GOT_INST && + vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_debug(4, "dec src\n"); + /* A single plane is required for input */ + *plane_count =3D 1; + if (*buf_count < 1) + *buf_count =3D 1; + if (*buf_count > MFC_MAX_BUFFERS) + *buf_count =3D MFC_MAX_BUFFERS; + + /* need to use minimum size to prevent qbuf fail */ + psize[0] =3D 1; + alloc_devs[0] =3D dev->device; + /* Video capture for decoding (destination) + * this can be set after the header was parsed + */ + } else if (core_ctx->state >=3D MFCINST_HEAD_PARSED && + vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_debug(4, "dec dst\n"); + /* Output plane count is different by the pixel format */ + *plane_count =3D ctx->num_fd_frame; + /* Setup buffer count */ + if (*buf_count < ctx->dpb_count) + *buf_count =3D ctx->dpb_count; + if (*buf_count > MFC_MAX_BUFFERS) + *buf_count =3D MFC_MAX_BUFFERS; + + if (ctx->dst_fmt->mem_planes =3D=3D 1) { + psize[0] =3D raw->total_plane_size; + alloc_devs[0] =3D dev->device; + } else { + for (i =3D 0; i < ctx->dst_fmt->num_planes; i++) { + psize[i] =3D ctx->min_dpb_size[i]; + alloc_devs[i] =3D dev->device; + } + } + if (ctx->multi_view_enable) { + offset =3D ctx->view_buf_info[MFC_MV_BUF_IDX_VIEW1].offset; + if (ctx->dst_fmt->mem_planes =3D=3D 1) { + psize[offset] =3D raw->total_plane_size; + alloc_devs[offset] =3D dev->device; + } else { + for (i =3D 0; i < ctx->dst_fmt->num_planes; i++) { + psize[offset + i] =3D ctx->min_dpb_size[i]; + alloc_devs[offset + i] =3D dev->device; + } + } + } + + /* Decoder DPB should be read for reference */ + vq->dma_dir =3D DMA_BIDIRECTIONAL; + } else { + mfc_err("State seems invalid. State =3D %d, vq->type =3D %d\n", + core_ctx->state, vq->type); + return -EINVAL; + } + + mfc_debug(2, "buf_count: %d, plane_count: %d, type: %#x\n", + *buf_count, *plane_count, vq->type); + for (i =3D 0; i < *plane_count; i++) + mfc_debug(2, "plane[%d] size: %d\n", i, psize[i]); + + mfc_ctx_debug_leave(); + + return 0; +} + +static void mfc_dec_unlock(struct vb2_queue *q) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + + mutex_unlock(&dev->mfc_mutex); +} + +static void mfc_dec_lock(struct vb2_queue *q) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + + mutex_lock(&dev->mfc_mutex); +} + +static int mfc_dec_buf_init(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + int ret; + + mfc_ctx_debug_enter(); + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret =3D mfc_check_vb_with_fmt(ctx->dst_fmt, vb); + if (ret < 0) + return ret; + mfc_calc_base_addr(ctx, vb, ctx->dst_fmt); + + buf->paddr =3D mfc_mem_get_paddr_vb(vb); + mfc_ctx_debug(2, "[DPB] vb index [%d] vb paddr %#llx daddr %#llx\n", + vb->index, buf->paddr, buf->addr[0][0]); + + if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_DST, + vb->index) < 0) + mfc_ctx_err("failed in init_buf_ctrls\n"); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret =3D mfc_check_vb_with_fmt(ctx->src_fmt, vb); + if (ret < 0) + return ret; + + buf->addr[0][0] =3D mfc_mem_get_daddr_vb(vb, 0); + + if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_SRC, + vb->index) < 0) + mfc_ctx_err("failed in init_buf_ctrls\n"); + } else { + mfc_ctx_err("unknown queue type\n"); + return -EINVAL; + } + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_dec_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + struct mfc_raw_info *raw; + unsigned int index =3D vb->index; + size_t buf_size; + int i; + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + raw =3D &ctx->raw_buf; + /* check the size per plane */ + if (ctx->dst_fmt->mem_planes =3D=3D 1) { + buf_size =3D vb2_plane_size(vb, 0); + mfc_ctx_debug(2, "[FRAME] single plane vb size: %lu, calc size: %d\n", + buf_size, raw->total_plane_size); + if (buf_size < raw->total_plane_size) { + mfc_ctx_err("[FRAME] single plane size(%lu) is smaller than (%d)\n", + buf_size, raw->total_plane_size); + return -EINVAL; + } + } else { + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) { + buf_size =3D vb2_plane_size(vb, i); + mfc_ctx_debug(2, "[FRAME] plane[%d] vb size: %lu, calc size: %d\n", + i, buf_size, raw->plane_size[i]); + if (buf_size < raw->plane_size[i]) { + mfc_ctx_err("[FRAME] plane[%d] size(%lu) is smaller than (%d)\n", + i, buf_size, raw->plane_size[i]); + return -EINVAL; + } + } + } + /* Copy dst buffer flag to buf_ctrl */ + buf->flag =3D call_cop(ctx, get_buf_ctrl_val, ctx, + &ctx->dst_ctrls[index], + V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG); + + mfc_mem_buf_prepare(vb, 0); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + buf_size =3D vb2_plane_size(vb, 0); + buf->sg_size =3D mfc_mem_get_sg_length(ctx->dev, vb2_dma_sg_plane_desc(v= b, 0)); + vb->planes[0].bytesused =3D buf->vb.planes[0].bytesused; + vb->planes[0].data_offset =3D buf->vb.planes[0].data_offset; + mfc_ctx_debug(2, "[STREAM] vb size, %ld, %s, %ld, sg_size, %zu, %s, %u, = %s, %u\n", + buf_size, + "dbuf size", vb->planes[0].dbuf->size, + buf->sg_size, + "bytesused", vb->planes[0].bytesused, + "data_offset", vb->planes[0].data_offset); + + call_cop(ctx, to_buf_ctrls, ctx, &ctx->src_ctrls[index]); + + /* Copy src buffer flag to buf_ctrl */ + buf->flag =3D call_cop(ctx, get_buf_ctrl_val, ctx, + &ctx->src_ctrls[index], + V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG); + + mfc_mem_buf_prepare(vb, 1); + } + + return 0; +} + +static void mfc_dec_buf_finish(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + unsigned int index =3D vb->index; + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + /* Copy to dst buffer flag */ + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG, buf->flag); + mfc_ctx_debug(4, "[FLAG] dst update buf[%d] flag =3D %#x\n", + index, buf->flag); + + call_cop(ctx, to_ctx_ctrls, ctx, &ctx->dst_ctrls[index]); + + mfc_mem_buf_finish(vb, 0); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* Copy to src buffer flag */ + call_cop(ctx, update_buf_val, ctx, &ctx->src_ctrls[index], + V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG, buf->flag); + mfc_ctx_debug(4, "[FLAG] src update buf[%d] flag =3D %#x\n", + index, buf->flag); + + call_cop(ctx, to_ctx_ctrls, ctx, &ctx->src_ctrls[index]); + } +} + +static void mfc_dec_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + unsigned int index =3D vb->index; + + mfc_ctx_debug_enter(); + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (call_cop(ctx, cleanup_buf_ctrls, ctx, + MFC_CTRL_TYPE_DST, index) < 0) + mfc_ctx_err("failed in cleanup_buf_ctrls\n"); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (call_cop(ctx, cleanup_buf_ctrls, ctx, + MFC_CTRL_TYPE_SRC, index) < 0) + mfc_ctx_err("failed in cleanup_buf_ctrls\n"); + } else { + mfc_ctx_err("unknown queue type\n"); + } + + mfc_ctx_debug_leave(); +} + +static int mfc_dec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + + mfc_rm_update_real_time(ctx); + + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + + return 0; +} + +static void mfc_dec_stop_streaming(struct vb2_queue *q) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + + mfc_ctx_info("dec stop_streaming is called, type : %d\n", q->type); + MFC_TRACE_CTX("** DEC streamoff(type:%d)\n", q->type); + + if (q->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + mfc_rm_request_work(ctx->dev, MFC_WORK_BUTLER, ctx); + + mfc_rm_instance_dec_stop(dev, ctx, q->type); +} + +static void mfc_dec_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + int i; + unsigned char *stream_vir =3D NULL; + + mfc_ctx_debug_enter(); + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mutex_lock(&ctx->op_mode_mutex); + buf->src_index =3D ctx->serial_src_index++; + mfc_ctx_debug(2, "[BUFINFO] ctx[%d] add src index: %d(%d), addr: 0x%08ll= x\n", + ctx->num, vb->index, buf->src_index, + buf->addr[0][0]); + mutex_unlock(&ctx->op_mode_mutex); + + if (vb->memory =3D=3D V4L2_MEMORY_DMABUF && + mfc_rm_query_state(ctx, SMALLER, MFCINST_HEAD_PARSED)) + stream_vir =3D vb2_plane_vaddr(vb, 0); + + buf->vir_addr[0] =3D stream_vir; + + mfc_add_tail_buf(ctx, &ctx->src_buf_ready_queue, buf); + + if (dev->debugfs.debug_ts =3D=3D 1) + mfc_ctx_info("[TS] framerate: %ld, timestamp: %lld\n", + ctx->framerate, buf->vb.vb2_buf.timestamp); + + MFC_TRACE_CTX("Q src[%d](%d) fd: %d, %#llx\n", + vb->index, buf->src_index, vb->planes[0].m.fd, buf->addr[0][0]); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + for (i =3D 0; i < ctx->num_fd_frame; i++) { + // ToDo: if multi_view, vir_addr array_size could be over 3 + buf->vir_addr[i] =3D vb2_plane_vaddr(vb, i); + mfc_ctx_debug(2, "[BUFINFO] ctx[%d] add dst index: %d, addr[0][%d]: 0x%= 08llx\n", + ctx->num, vb->index, i, buf->addr[0][i]); + if (ctx->multi_view_enable) { + mfc_ctx_debug(2, "[BUFINFO-VIEW1] ctx[%d] add dst index: %d, addr[2][%= d]: 0x%08llx\n", + ctx->num, vb->index, i, buf->addr[2][i]); + } + } + mfc_store_dpb(ctx, vb); + + if ((vb->memory =3D=3D V4L2_MEMORY_USERPTR || vb->memory =3D=3D V4L2_MEM= ORY_DMABUF) && + mfc_is_queue_count_same(&ctx->buf_queue_lock, + &ctx->dst_buf_queue, dec->total_dpb_count)) + ctx->capture_state =3D QUEUE_BUFS_MMAPED; + + MFC_TRACE_CTX("Q dst[%d][%d] fd: %d, %#llx / used %#lx\n", + vb->index, buf->dpb_index, vb->planes[0].m.fd, + buf->addr[0][0], dec->dynamic_used); + } else { + mfc_ctx_err("Unsupported buffer type (%d)\n", vq->type); + } + + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + + mfc_ctx_debug_leave(); +} + +static const struct vb2_ops mfc_dec_qops =3D { + .queue_setup =3D mfc_dec_queue_setup, + .wait_prepare =3D mfc_dec_unlock, + .wait_finish =3D mfc_dec_lock, + .buf_init =3D mfc_dec_buf_init, + .buf_prepare =3D mfc_dec_buf_prepare, + .buf_finish =3D mfc_dec_buf_finish, + .buf_cleanup =3D mfc_dec_buf_cleanup, + .start_streaming =3D mfc_dec_start_streaming, + .stop_streaming =3D mfc_dec_stop_streaming, + .buf_queue =3D mfc_dec_buf_queue, +}; + +const struct vb2_ops *mfc_get_dec_vb2_ops(void) +{ + return &mfc_dec_qops; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h b/driv= ers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h new file mode 100644 index 000000000000..3d0cb7b14ce5 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_dec_vb2.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_DEC_VB2_H +#define __MFC_DEC_VB2_H __FILE__ + +#include "base/mfc_common.h" + +const struct vb2_ops *mfc_get_dec_vb2_ops(void); + +#endif /* __MFC_DEC_VB2_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) (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 B19B62773F4 for ; Tue, 30 Sep 2025 03:56:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.33 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204606; cv=none; b=jyZq8lv6kQDt8yn4/LfVFJkxpMIPXAysPrV9KiNHwB5dbh7Qf7QxvqHgM+PHlYLcuhUOWi8M2ZMLHxtH1EVXY6i1Trq8a/S86HpCHT956aj0dg/fzTO1l8tQO8pEb/695v0V3ukc7N+01WvglcJ7tiTiBgAOzIbyEWzgOAx/06g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204606; c=relaxed/simple; bh=1hK1xI7Dth2UsfpIjbrY6+XpBHv7/X5AFD2XbxT2Pyw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=H0yBKgetc9fiN0uJLbAJy9iXpWy/nH2AkA5bPuE8EUcEsr7iKjsKU8gps+N0ZbNu3M1CR1YWJ+M1NHUpUrOWfnNwcGjuZg/q07bBOGUsZyXRE6nqzPnwmFAB4WYPSzFUltw2H1i4hYLrFsOZqZ3ZWQAZEgBP2pQGreKiNdZnUiY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=RYgGLehD; arc=none smtp.client-ip=203.254.224.33 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="RYgGLehD" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20250930035640epoutp0366155ce486096d360d0d6a338ff38875~p80NaT6ty3111931119epoutp037 for ; Tue, 30 Sep 2025 03:56:40 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20250930035640epoutp0366155ce486096d360d0d6a338ff38875~p80NaT6ty3111931119epoutp037 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204600; bh=iZRhKuwFGLe8CYtxJ0WD69+dHVtPRw6ZjzKWW6B4V8o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RYgGLehDs0Im3C23Ayc26xaL/b/FyIUxfibQMgCHZkr1eIYXNXaRwtGGM75mPZvRl db7dv/wg7xkOMfuwSQzza23TR6y6DwgeIg70kHr/FiWcu/fc2K4xumXoktLQOea1LN SFjwHQgL21EvHXakC3v/mGLpKnrJ2UntrheOaFI8= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035639epcas5p1f669fafdaa861161b71abc062c3b2570~p80MxvNhf1549615496epcas5p1Q; Tue, 30 Sep 2025 03:56:39 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.92]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPPf1yGQz6B9mB; Tue, 30 Sep 2025 03:56:38 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPA id 20250930035637epcas5p4d52ed2f59e17862cd7d7a650fa01bf44~p80LLtbhp0713607136epcas5p4E; Tue, 30 Sep 2025 03:56:37 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035633epsmtip153303c9146034a504efb495bc71277c3~p80HpAp4K2885028850epsmtip1k; Tue, 30 Sep 2025 03:56:33 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 18/29] media: mfc: Add V4L2 decoder driver Date: Tue, 30 Sep 2025 09:33:37 +0530 Message-Id: <20250930040348.3702923-19-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035637epcas5p4d52ed2f59e17862cd7d7a650fa01bf44 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035637epcas5p4d52ed2f59e17862cd7d7a650fa01bf44 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Add a V4L2=E2=80=91based decoder for Exynos MFC. - Implement full decoder V4L2 ioctl, control, buffer, and stream handling. - Provide ioctl getter and default format helper. Move the decoder to the standard V4L2 framework, to enable proper media=E2=80=91device registration and user=E2=80=91space i= nteraction. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 2 +- .../samsung/exynos-mfc/mfc_dec_v4l2.c | 1741 +++++++++++++++++ .../samsung/exynos-mfc/mfc_dec_v4l2.h | 22 + 3 files changed, 1764 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index 9127f2dc4df6..b6b312ae7f22 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_VIDEO_EXYNOS_MFC) :=3D exynos_mfc.o ccflags-y +=3D -I$(srctree)/$(src) =20 #Dev interface layer -exynos_mfc-y +=3D mfc.o mfc_dec_vb2.o +exynos_mfc-y +=3D mfc.o mfc_dec_v4l2.o mfc_dec_vb2.o #Dev control layer exynos_mfc-y +=3D mfc_rm.o mfc_ctx_ctrl.o mfc_debugfs.o #Core interface layer diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c new file mode 100644 index 000000000000..dd59dc352e34 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c @@ -0,0 +1,1741 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mfc_dec_v4l2.c + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "mfc_dec_v4l2.h" +#include "mfc_dec_vb2.h" +#include "mfc_rm.h" + +#include "mfc_core_hwlock.h" + +#include "base/mfc_format.h" +#include "base/mfc_queue.h" +#include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" + +#define MAX_FRAME_SIZE (2 * SZ_1K * SZ_1K) +static struct v4l2_queryctrl dec_controls[] =3D { + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H.264 Display Delay", + .minimum =3D -1, + .maximum =3D 32, + .step =3D 1, + .default_value =3D -1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Mpeg4 Loop Filter Enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Slice Interface Enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Packed PB Enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frame Tag", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "CRC enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_LUMA, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "CRC data", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CRC_DATA_CHROMA, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "CRC data", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Display status", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frame type", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Frame pack sei parse flag", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "I frame decoding mode", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frames per second in 1000x scale", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 60000, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DECODER_IMMEDIATE_DISPLAY, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Immediate Display Enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DECODER_DECODING_TIMESTAMP_MODE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Decoding Timestamp Mode Enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DECODER_WAIT_DECODING_START, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Wait until buffer setting done", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_GET_VERSION_INFO, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Get MFC version information", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_SET_DUAL_DPB_MODE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Set Dual DPB mode", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_QOS_RATIO, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "QoS ratio value", + .minimum =3D 20, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 100, + }, + { + .id =3D V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Set dynamic DPB", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Set dynamic DPB", + .minimum =3D 0, + .maximum =3D U16_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_GET_EXT_INFO, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Get extra information", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_SET_BUF_PROCESS_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Set buffer process type", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_GET_10BIT_INFO, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "10 bit contents information", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_BLACK_BAR_DETECT, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Set black bar detection option", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_HDR_USER_SHARED_HANDLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Dynamic HDR10+ SEI metadata", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_AV1_FILM_GRAIN_USER_SHARED_HANDLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "AV1 Film Grain SEI metadata", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_AV1_FILM_GRAIN_PRESENT, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "AV1 Film Grain presented", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DECODING_ORDER, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "decoding order enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_UNCOMP_FMT, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Uncompressed format", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_GET_DISPLAY_DELAY, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "display delay for first frame", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_POC, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frame POC", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Buffer flag", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Buffer flag", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SKIP_LAZY_UNMAP, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "skip lazy unmap", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_PRIORITY, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "priority", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_HEIF_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "heif mode", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_MULTI_VIEW_ENABLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Multi-View Enable", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 0, + }, +}; + +#define DEC_NUM_CTRLS ARRAY_SIZE(dec_controls) +/* Find selected format description */ +VISIBLE_IF_KUNIT struct mfc_fmt *__mfc_dec_find_format(struct mfc_ctx *ctx, + unsigned int pixelformat) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_fmt *fmt =3D NULL; + unsigned long i; + + for (i =3D 0; i < MFC_NUM_FORMATS; i++) { + if ((mfc_formats[i].type & MFC_FMT_STREAM) && + !(mfc_formats[i].type & MFC_FMT_DEC)) { + continue; + } + if (mfc_formats[i].fourcc =3D=3D pixelformat) { + fmt =3D (struct mfc_fmt *)&mfc_formats[i]; + break; + } + } + + if (fmt && !dev->pdata->support_10bit && (fmt->type & MFC_FMT_10BIT)) { + mfc_ctx_err("[FRAME] 10bit is not supported\n"); + fmt =3D NULL; + } + if (fmt && !dev->pdata->support_422 && (fmt->type & MFC_FMT_422)) { + mfc_ctx_err("[FRAME] 422 is not supported\n"); + fmt =3D NULL; + } + if (fmt && (fmt->type & MFC_FMT_RGB)) { + mfc_ctx_err("[FRAME] RGB is not supported by decoder\n"); + fmt =3D NULL; + } + if (fmt && (fmt->type & MFC_FMT_STREAM) && + dev->pdata->mfc_resource[fmt->codec_mode].op_core_type =3D=3D MFC_OP_= CORE_NOT_FIXED) { + mfc_ctx_err("[STREAM] %s is not supported\n", fmt->name); + fmt =3D NULL; + } + + return fmt; +} +EXPORT_SYMBOL_IF_KUNIT(__mfc_dec_find_format); + +static struct v4l2_queryctrl *__mfc_dec_get_ctrl(int id) +{ + unsigned long i; + + for (i =3D 0; i < DEC_NUM_CTRLS; ++i) + if (id =3D=3D dec_controls[i].id) + return &dec_controls[i]; + + return NULL; +} + +/* Check whether a ctrl value if correct */ +static int __mfc_dec_check_ctrl_val(struct mfc_ctx *ctx, struct v4l2_contr= ol *ctrl) +{ + struct v4l2_queryctrl *c; + + c =3D __mfc_dec_get_ctrl(ctrl->id); + if (!c) { + mfc_ctx_err("[CTRLS] not supported control id (%#x)\n", ctrl->id); + return -EINVAL; + } + + if (ctrl->value < c->minimum || ctrl->value > c->maximum || + (c->step !=3D 0 && ctrl->value % c->step !=3D 0)) { + mfc_ctx_err("[CTRLS][%s] id: %#x, invalid value (%d)\n", + c->name, ctrl->id, ctrl->value); + return -ERANGE; + } + + mfc_ctx_debug(5, "[CTRLS][%s] id: %#x, value: %d (%#x)\n", + c->name, ctrl->id, ctrl->value, ctrl->value); + + return 0; +} + +/* Query capabilities of the device */ +static int mfc_dec_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "MFC", sizeof(cap->driver)); + strscpy(cap->card, "decoder", sizeof(cap->card)); + + return 0; +} + +static int __mfc_dec_enum_fmt(struct mfc_dev *dev, struct v4l2_fmtdesc *f, + unsigned int type) +{ + struct mfc_fmt *fmt; + unsigned long i, j =3D 0; + + for (i =3D 0; i < MFC_NUM_FORMATS; ++i) { + if (!(mfc_formats[i].type & type)) + continue; + if (!dev->pdata->support_10bit && (mfc_formats[i].type & MFC_FMT_10BIT)) + continue; + if (!dev->pdata->support_422 && (mfc_formats[i].type & MFC_FMT_422)) + continue; + + if (j =3D=3D f->index) { + fmt =3D &mfc_formats[i]; + strscpy(f->description, fmt->name, + sizeof(f->description)); + f->pixelformat =3D fmt->fourcc; + + return 0; + } + + ++j; + } + + return -EINVAL; +} + +static int mfc_dec_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) +{ + struct mfc_dev *dev =3D video_drvdata(file); + + return __mfc_dec_enum_fmt(dev, f, MFC_FMT_FRAME); +} + +static int mfc_dec_enum_fmt_vid_out_mplane(struct file *file, void *prov, + struct v4l2_fmtdesc *f) +{ + struct mfc_dev *dev =3D video_drvdata(file); + + return __mfc_dec_enum_fmt(dev, f, MFC_FMT_STREAM); +} + +static void __mfc_dec_change_format_8bit(struct mfc_ctx *ctx) +{ + u32 org_fmt =3D ctx->dst_fmt->fourcc; + + switch (org_fmt) { + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + /* It is right format */ + break; + case V4L2_PIX_FMT_NV61M: + /* change to CrCb order format */ + ctx->dst_fmt =3D __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV21M); + break; + default: + ctx->dst_fmt =3D __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV12M); + break; + } +} + +static void __mfc_dec_change_format_8bit_422(struct mfc_ctx *ctx) +{ + u32 org_fmt =3D ctx->dst_fmt->fourcc; + + switch (org_fmt) { + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + /* It is right format */ + break; + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_YVU420M: + /* change to CrCb order format */ + ctx->dst_fmt =3D __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV61M); + break; + default: + ctx->dst_fmt =3D __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV16M); + break; + } +} + +static void __mfc_dec_change_format(struct mfc_ctx *ctx) +{ + u32 org_fmt =3D ctx->dst_fmt->fourcc; + + if (ctx->is_422) + __mfc_dec_change_format_8bit_422(ctx); + else + __mfc_dec_change_format_8bit(ctx); + + ctx->raw_buf.num_planes =3D ctx->dst_fmt->num_planes; + if (org_fmt !=3D ctx->dst_fmt->fourcc) + mfc_ctx_info("[FRAME] format is changed to %s\n", ctx->dst_fmt->name); +} + +static void __mfc_dec_set_num_fd_frame(struct mfc_ctx *ctx, + struct v4l2_pix_format_mplane *pix_fmt_mp) +{ + int calc_num_planes; + int num_fd_depth_map =3D 0; + int num_view =3D 1; + int num_fd_sub_view_meta =3D 0; + + if (ctx->multi_view_enable) { + pix_fmt_mp->flags |=3D MFC_FMT_FLAG_MULTI_VIEW; + num_view =3D MFC_NUM_MULTI_VIEW; + num_fd_sub_view_meta =3D MFC_NUM_FD_SUB_VIEW_META; + + // ToDo: Depth is not supported yet. + pix_fmt_mp->flags &=3D ~MFC_FMT_FLAG_DEPTH_MAP; + num_fd_depth_map =3D 0; + + calc_num_planes =3D + (ctx->dst_fmt->mem_planes + num_fd_depth_map) * num_view + + num_fd_sub_view_meta; + } else { + pix_fmt_mp->flags &=3D ~MFC_FMT_FLAG_MULTI_VIEW; + calc_num_planes =3D ctx->dst_fmt->mem_planes; + } + + mfc_set_view_buf_info(ctx, ctx->dst_fmt->mem_planes, + num_fd_depth_map, num_fd_sub_view_meta); + + ctx->num_fd_frame =3D calc_num_planes; + pix_fmt_mp->num_planes =3D calc_num_planes; +} + +static void __mfc_dec_update_pix_format(struct mfc_ctx *ctx, struct v4l2_f= ormat *f) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + struct mfc_raw_info *raw; + int i; + + raw =3D &ctx->raw_buf; + + pix_fmt_mp->width =3D ctx->img_width; + pix_fmt_mp->height =3D ctx->img_height; + __mfc_dec_set_num_fd_frame(ctx, pix_fmt_mp); + + if (dec->is_interlaced) + pix_fmt_mp->field =3D V4L2_FIELD_INTERLACED; + else + pix_fmt_mp->field =3D V4L2_FIELD_NONE; + + /* Set pixelformat to the format in which MFC outputs the decoded frame */ + pix_fmt_mp->pixelformat =3D ctx->dst_fmt->fourcc; + for (i =3D 0; i < ctx->dst_fmt->mem_planes; i++) { + pix_fmt_mp->plane_fmt[i].bytesperline =3D raw->stride[i]; + if (ctx->dst_fmt->mem_planes =3D=3D 1) + pix_fmt_mp->plane_fmt[i].sizeimage =3D raw->total_plane_size; + else + pix_fmt_mp->plane_fmt[i].sizeimage =3D raw->plane_size[i]; + } +} + +/* Get format */ +static int mfc_dec_g_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + int ret; + + mfc_ctx_debug_enter(); + + /* During g_fmt, context information is need to for only main core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + mfc_debug(2, "dec dst g_fmt, state: %d wait_state: %d\n", + core_ctx->state, ctx->wait_state); + MFC_TRACE_CTX("** DEC g_fmt(state:%d wait_state:%d)\n", + core_ctx->state, ctx->wait_state); + + mutex_lock(&ctx->drc_wait_mutex); + if (dec->disp_drc.disp_res_change) { + __mfc_dec_update_pix_format(ctx, f); + mutex_unlock(&ctx->drc_wait_mutex); + return 0; + } + mutex_unlock(&ctx->drc_wait_mutex); + + if (core_ctx->state =3D=3D MFCINST_GOT_INST || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_INIT || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH_FINISHED || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_END) { + /* If there is no source buffer to parsing, we can't SEQ_START */ + mutex_lock(&ctx->drc_wait_mutex); + if (((ctx->wait_state & WAIT_G_FMT) !=3D 0) && + mfc_is_queue_count_same(&ctx->buf_queue_lock, + &ctx->src_buf_ready_queue, 0) && + mfc_is_queue_count_same(&ctx->buf_queue_lock, + &core_ctx->src_buf_queue, 0)) { + mfc_err("There is no source buffer to parsing, keep previous resolution= \n"); + mutex_unlock(&ctx->drc_wait_mutex); + return -EAGAIN; + } + mutex_unlock(&ctx->drc_wait_mutex); + + /* + * If the MFC is parsing the header, + * so wait until it is finished. + */ + ret =3D mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_SEQ_DONE_RE= T); + if (ret) { + if (core_ctx->int_err =3D=3D MFC_REG_ERR_UNSUPPORTED_FEATURE) { + mfc_err("header parsing failed by unsupported feature\n"); + return -EINVAL; + } + mfc_err("header parsing failed\n"); + return -EAGAIN; + } + } + + if (core_ctx->state >=3D MFCINST_HEAD_PARSED && + core_ctx->state < MFCINST_ABORT) { + if (mfc_check_resolution(ctx)) { + mfc_ctx_err("Unsupported product resolution\n"); + return -EAGAIN; + } + + /* This is run on CAPTURE (decode output) */ + if (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1 || + ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE2) { + mfc_info("[2CORE] start the sub core\n"); + if (mfc_rm_instance_setup(dev, ctx)) { + mfc_err("[2CORE] failed to setup sub core\n"); + return -EAGAIN; + } + } + + /* + * The format should be changed according to various conditions. + * 1. bit depth (8bit or 10bit) + * 2. chroma order (CbCr or CrCb) + * 3. component in memory (multi or single) + */ + + __mfc_dec_change_format(ctx); + + /* Width and height are set to the dimensions + * of the movie, the buffer is bigger and + * further processing stages should crop to this + * rectangle. + */ + mfc_dec_calc_dpb_size(ctx, &ctx->raw_buf, ctx->dst_fmt); + + if (IS_LOW_MEM) { + unsigned int dpb_size; + /* + * If total memory requirement is too big for this device, + * then it returns error. + * DPB size : Total plane size * the number of DPBs + * 5: the number of extra DPBs + * 3: the number of DPBs for Android framework + * 600MB: being used to return an error, + * when 8K resolution video clip is being tried to be decoded + */ + dpb_size =3D (ctx->raw_buf.total_plane_size * + (ctx->dpb_count + MFC_EXTRA_DPB + 3)); + if (dpb_size > SZ_6M) { + mfc_info("required memory size is too big (%dx%d, dpb: %d)\n", + ctx->img_width, ctx->img_height, ctx->dpb_count); + return -EINVAL; + } + } + + __mfc_dec_update_pix_format(ctx, f); + } + + mutex_lock(&ctx->drc_wait_mutex); + if ((ctx->wait_state & WAIT_G_FMT) !=3D 0) { + ctx->wait_state &=3D ~(WAIT_G_FMT); + mfc_debug(2, "clear WAIT_G_FMT %d\n", ctx->wait_state); + MFC_TRACE_CTX("** DEC clear WAIT_G_FMT(wait_state %d)\n", ctx->wait_stat= e); + } + mutex_unlock(&ctx->drc_wait_mutex); + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_dec_g_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dec *dec =3D ctx->dec_priv; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + + mfc_ctx_debug_enter(); + + mfc_ctx_debug(4, "dec src g_fmt\n"); + + /* This is run on OUTPUT + * The buffer contains compressed image + * so width and height have no meaning + */ + pix_fmt_mp->width =3D 0; + pix_fmt_mp->height =3D 0; + pix_fmt_mp->field =3D V4L2_FIELD_NONE; + pix_fmt_mp->plane_fmt[0].bytesperline =3D dec->src_buf_size; + pix_fmt_mp->plane_fmt[0].sizeimage =3D dec->src_buf_size; + pix_fmt_mp->pixelformat =3D ctx->src_fmt->fourcc; + pix_fmt_mp->num_planes =3D ctx->src_fmt->mem_planes; + + mfc_ctx_debug_leave(); + + return 0; +} + +/* Try format */ +static int mfc_dec_try_fmt(struct file *file, void *priv, struct v4l2_form= at *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_fmt *fmt; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + + fmt =3D __mfc_dec_find_format(ctx, pix_fmt_mp->pixelformat); + if (!fmt) { + mfc_ctx_err("Unsupported format for %s\n", + V4L2_TYPE_IS_OUTPUT(f->type) ? "source" : "destination"); + return -EINVAL; + } + if (f->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + fmt->codec_mode =3D=3D MFC_FORMATS_NO_CODEC) { + mfc_ctx_err("MFC_FORMATS_NO_CODEC is invalid to src(fmt is %s)\n", + fmt->name); + return -EINVAL; + } + + /* For resource reservation */ + ctx->img_width =3D pix_fmt_mp->width; + ctx->img_height =3D pix_fmt_mp->height; + + if (f->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ctx->src_fmt =3D fmt; + ctx->codec_mode =3D ctx->src_fmt->codec_mode; + ctx->op_core_type =3D ctx->dev->pdata->mfc_resource[ctx->codec_mode].op_= core_type; + } + + mfc_ctx_debug(2, "[%s] resolution %dx%d, %s : %s\n", + V4L2_TYPE_IS_OUTPUT(f->type) ? "STREAM" : "FRAME", + ctx->img_width, ctx->img_height, + V4L2_TYPE_IS_OUTPUT(f->type) ? "codectype" : "pixelformat", fmt->n= ame); + + return 0; +} + +/* Set format */ +static int mfc_dec_s_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + struct mfc_fmt *fmt =3D NULL; + + mfc_ctx_debug_enter(); + + if (ctx->vq_dst.streaming) { + mfc_ctx_err("queue busy\n"); + return -EBUSY; + } + + fmt =3D __mfc_dec_find_format(ctx, pix_fmt_mp->pixelformat); + if (!fmt) { + mfc_ctx_err("Unsupported format for destination\n"); + return -EINVAL; + } + ctx->dst_fmt =3D fmt; + + if ((!ctx->img_width && !ctx->img_height) && + pix_fmt_mp->width > 0 && pix_fmt_mp->height > 0) { + ctx->img_width =3D pix_fmt_mp->width; + ctx->img_height =3D pix_fmt_mp->height; + } + + ctx->raw_buf.num_planes =3D ctx->dst_fmt->num_planes; + mfc_ctx_info("[FRAME] dec dst pixelformat : %s (%d x %d)\n", + ctx->dst_fmt->name, ctx->img_width, ctx->img_height); + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_dec_s_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mfc_dev *dev =3D video_drvdata(file); + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dec *dec =3D ctx->dec_priv; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + struct mfc_fmt *fmt =3D NULL; + int ret =3D 0; + + mfc_ctx_debug_enter(); + + if (ctx->vq_src.streaming) { + mfc_ctx_err("queue busy\n"); + return -EBUSY; + } + + fmt =3D __mfc_dec_find_format(ctx, pix_fmt_mp->pixelformat); + if (!fmt) { + mfc_ctx_err("Unsupported format for source\n"); + return -EINVAL; + } + ctx->src_fmt =3D fmt; + + ctx->codec_mode =3D ctx->src_fmt->codec_mode; + mfc_ctx_info("[STREAM] codectype: %s(%d)\n", + ctx->src_fmt->name, ctx->codec_mode); + + ctx->pix_format =3D pix_fmt_mp->pixelformat; + if (pix_fmt_mp->width > 0 && pix_fmt_mp->height > 0) { + ctx->img_height =3D pix_fmt_mp->height; + ctx->img_width =3D pix_fmt_mp->width; + } + + /* As this buffer will contain compressed data, the size is set + * to the maximum size. + */ + if (pix_fmt_mp->plane_fmt[0].sizeimage) + dec->src_buf_size =3D pix_fmt_mp->plane_fmt[0].sizeimage; + else + dec->src_buf_size =3D MAX_FRAME_SIZE; + mfc_ctx_debug(2, "[STREAM] sizeimage: %d\n", pix_fmt_mp->plane_fmt[0].siz= eimage); + pix_fmt_mp->plane_fmt[0].bytesperline =3D 0; + + ret =3D mfc_rm_instance_open(dev, ctx); + if (ret) + mfc_ctx_err("Failed to instance open\n"); + + mfc_ctx_debug_leave(); + + return ret; +} + +/* Request buffers */ +static int mfc_dec_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct mfc_dev *dev =3D video_drvdata(file); + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int i, ret =3D 0; + + mfc_ctx_debug_enter(); + + if (reqbufs->memory =3D=3D V4L2_MEMORY_MMAP) { + mfc_ctx_err("Not supported memory type (%d)\n", reqbufs->memory); + return -EINVAL; + } + + if (reqbufs->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "dec src reqbuf(%d)\n", reqbufs->count); + /* Can only request buffers after + * an instance has been opened. + */ + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_GOT_INST)) { + if (reqbufs->count =3D=3D 0) { + ret =3D vb2_reqbufs(&ctx->vq_src, reqbufs); + ctx->output_state =3D QUEUE_FREE; + return ret; + } + + /* Decoding */ + if (ctx->output_state !=3D QUEUE_FREE) { + mfc_ctx_err("Bufs have already been requested\n"); + return -EINVAL; + } + + ret =3D vb2_reqbufs(&ctx->vq_src, reqbufs); + if (ret) { + mfc_ctx_err("vb2_reqbufs on src failed\n"); + return ret; + } + + ctx->output_state =3D QUEUE_BUFS_REQUESTED; + } + } else if (reqbufs->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(4, "dec dst reqbuf(%d)\n", reqbufs->count); + if (reqbufs->count =3D=3D 0) { + ret =3D vb2_reqbufs(&ctx->vq_dst, reqbufs); + + if (!dec->inter_res_change) { + for (i =3D 0; i < MFC_CORE_TYPE_NUM; i++) { + if (ctx->op_core_num[i] =3D=3D MFC_CORE_INVALID) + break; + core =3D dev->core[ctx->op_core_num[i]]; + + core_ctx =3D core->core_ctx[ctx->num]; + mfc_release_codec_buffers(core_ctx); + } + } + ctx->capture_state =3D QUEUE_FREE; + return ret; + } + + if (ctx->capture_state !=3D QUEUE_FREE) { + mfc_ctx_err("Bufs have already been requested\n"); + return -EINVAL; + } + + ret =3D vb2_reqbufs(&ctx->vq_dst, reqbufs); + if (ret) { + mfc_ctx_err("vb2_reqbufs on capture failed\n"); + return ret; + } + + if (reqbufs->count < ctx->dpb_count) { + mfc_ctx_err("Not enough buffers allocated\n"); + reqbufs->count =3D 0; + vb2_reqbufs(&ctx->vq_dst, reqbufs); + return -ENOMEM; + } + + dec->total_dpb_count =3D reqbufs->count; + + if (!dec->inter_res_change) { + for (i =3D 0; i < MFC_CORE_TYPE_NUM; i++) { + if (ctx->op_core_num[i] =3D=3D MFC_CORE_INVALID) + break; + + core =3D dev->core[ctx->op_core_num[i]]; + core_ctx =3D core->core_ctx[ctx->num]; + ret =3D mfc_alloc_codec_buffers(core_ctx); + if (ret) { + mfc_err("Failed to allocate decoding buffers\n"); + reqbufs->count =3D 0; + vb2_reqbufs(&ctx->vq_dst, reqbufs); + return -ENOMEM; + } + } + } + + ctx->capture_state =3D QUEUE_BUFS_REQUESTED; + + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + } + + mfc_ctx_debug_leave(); + + return ret; +} + +/* Query buffer */ +static int mfc_dec_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + int ret; + + mfc_ctx_debug_enter(); + + if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(4, "dec dst querybuf\n"); + ret =3D vb2_querybuf(&ctx->vq_dst, buf); + if (ret !=3D 0) { + mfc_ctx_err("dec dst: error in vb2_querybuf()\n"); + return ret; + } + } else if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "dec src querybuf\n"); + ret =3D vb2_querybuf(&ctx->vq_src, buf); + if (ret !=3D 0) { + mfc_ctx_err("dec src: error in vb2_querybuf()\n"); + return ret; + } + } else { + mfc_ctx_err("invalid buf type (%d)\n", buf->type); + return -EINVAL; + } + + mfc_ctx_debug_leave(); + + return ret; +} + +/* Queue a buffer */ +static int mfc_dec_qbuf(struct file *file, void *priv, struct v4l2_buffer = *buf) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + int ret =3D -EINVAL; + + mfc_ctx_debug_enter(); + + if (mfc_rm_query_state(ctx, EQUAL_OR, MFCINST_ERROR)) { + mfc_ctx_err("Call on QBUF after unrecoverable error\n"); + return -EIO; + } + + if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { + mfc_ctx_err("Invalid V4L2 Buffer for driver: type(%d)\n", buf->type); + return -EINVAL; + } + + if (!buf->length) { + mfc_ctx_err("multiplanar but length is zero\n"); + return -EIO; + } + + if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "dec src buf[%d] Q\n", buf->index); + if (buf->m.planes[0].bytesused > buf->m.planes[0].length) { + mfc_ctx_err("data size (%d) %s(%d)\n", + buf->m.planes[0].bytesused, + "must be less than buffer size", + buf->m.planes[0].length); + return -EIO; + } + + mfc_idle_update_queued(dev, ctx); + mfc_rate_update_bitrate(ctx, buf->m.planes[0].bytesused); + mfc_rate_update_bufq_framerate(ctx, MFC_TS_SRC_Q); + mfc_rate_update_framerate(ctx); + mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER); + + if (!buf->m.planes[0].bytesused) { + buf->m.planes[0].bytesused =3D buf->m.planes[0].length; + mfc_ctx_debug(2, "Src size zero, changed to buf size %d\n", + buf->m.planes[0].bytesused); + } else { + mfc_ctx_debug(2, "Src size, %d, buf length, %d\n", + buf->m.planes[0].bytesused, + buf->m.planes[0].length); + } + + ret =3D vb2_qbuf(&ctx->vq_src, NULL, buf); + } else { + mfc_ctx_debug(4, "dec dst buf[%d] Q\n", buf->index); + mfc_idle_update_queued(dev, ctx); + mfc_rate_update_bufq_framerate(ctx, MFC_TS_DST_Q); + mfc_rate_update_framerate(ctx); + mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER); + ret =3D vb2_qbuf(&ctx->vq_dst, NULL, buf); + } + + mfc_ctx_debug_leave(); + return ret; +} + +/* Dequeue a buffer */ +static int mfc_dec_dqbuf(struct file *file, void *priv, struct v4l2_buffer= *buf) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dec *dec =3D ctx->dec_priv; + struct dec_dpb_ref_info *dst_buf, *src_buf; + int ret; + int ncount =3D 0; + + mfc_ctx_debug_enter(); + + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_ERROR)) { + mfc_ctx_err("Call on DQBUF after unrecoverable error\n"); + return -EIO; + } + + if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { + mfc_ctx_err("Invalid V4L2 Buffer for driver: type(%d)\n", buf->type); + return -EINVAL; + } + + if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret =3D vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); + mfc_ctx_debug(4, "dec src buf[%d] DQ\n", buf->index); + } else { + ret =3D vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); + mfc_ctx_debug(4, "dec dst buf[%d] DQ\n", buf->index); + + if (buf->index >=3D MFC_MAX_BUFFERS) { + mfc_ctx_err("buffer index[%d] range over\n", buf->index); + return -EINVAL; + } + + /* Memcpy from dec->ref_info to shared memory */ + if (dec->ref_info) { + src_buf =3D &dec->ref_info[buf->index]; + for (ncount =3D 0; ncount < MFC_MAX_BUFFERS; ncount++) { + if (src_buf->dpb[ncount].fd[0] =3D=3D MFC_INFO_INIT_FD) + break; + mfc_ctx_debug(2, "[REFINFO] DQ index[%d] Released FD =3D %d\n", + buf->index, src_buf->dpb[ncount].fd[0]); + } + + if (dec->sh_handle_dpb.vaddr) { + dst_buf =3D (struct dec_dpb_ref_info *) + dec->sh_handle_dpb.vaddr + buf->index; + memcpy(dst_buf, src_buf, sizeof(struct dec_dpb_ref_info)); + dst_buf->index =3D buf->index; + } + } + } + mfc_ctx_debug_leave(); + return ret; +} + +/* Stream on */ +static int mfc_dec_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + int ret =3D -EINVAL; + + mfc_ctx_debug_enter(); + + if (type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "dec src streamon\n"); + ret =3D vb2_streamon(&ctx->vq_src, type); + } else if (type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(4, "dec dst streamon\n"); + ret =3D vb2_streamon(&ctx->vq_dst, type); + if (!ret) + mfc_rm_qos_control(ctx, MFC_QOS_ON); + } else { + mfc_ctx_err("unknown v4l2 buffer type\n"); + } + + mfc_ctx_debug(2, "src: %d, dst: %d, dpb_count =3D %d\n", + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_ready_queu= e), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue), + ctx->dpb_count); + + mfc_ctx_debug_leave(); + + return ret; +} + +/* Stream off, which equals to a pause */ +static int mfc_dec_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + int ret =3D -EINVAL; + + mfc_ctx_debug_enter(); + + if (type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "dec src streamoff\n"); + ret =3D vb2_streamoff(&ctx->vq_src, type); + } else if (type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(4, "dec dst streamoff\n"); + mfc_rate_reset_bufq_framerate(ctx); + ret =3D vb2_streamoff(&ctx->vq_dst, type); + if (!ret) + mfc_rm_qos_control(ctx, MFC_QOS_OFF); + } else { + mfc_ctx_err("unknown v4l2 buffer type\n"); + } + + mfc_ctx_debug_leave(); + + return ret; +} + +static int __mfc_dec_ext_info(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + int val =3D 0; + + val |=3D DEC_SET_DYNAMIC_DPB; + val |=3D DEC_SET_C2_INTERFACE; + val |=3D DEC_SET_BUF_FLAG_CTRL; + val |=3D DEC_SET_FRAME_ERR_TYPE; + val |=3D DEC_SET_OPERATING_FPS; + val |=3D DEC_SET_PRIORITY; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->skype)) + val |=3D DEC_SET_SKYPE_FLAG; + + mfc_ctx_debug(5, "[CTRLS] ext info val: %#x\n", val); + + return val; +} + +/* Get ctrl */ +static int __mfc_dec_get_ctrl_val(struct mfc_ctx *ctx, struct v4l2_control= *ctrl) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_ctx_ctrl *ctx_ctrl; + int found =3D 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: + ctrl->value =3D dec->loop_filter_mpeg4; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY: + ctrl->value =3D dec->display_delay; + break; + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: + /* These context information is need to for only main core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + if (core_ctx->state >=3D MFCINST_HEAD_PARSED && + core_ctx->state < MFCINST_ABORT) { + ctrl->value =3D ctx->dpb_count; + break; + } else if (core_ctx->state !=3D MFCINST_INIT) { + mfc_err("Decoding not initialised\n"); + return -EINVAL; + } + + /* Should wait for the header to be parsed */ + if (mfc_wait_for_done_core_ctx(core_ctx, + MFC_REG_R2H_CMD_SEQ_DONE_RET)) { + core->sched->yield_work(core, core_ctx); + return -EIO; + } + + if (core_ctx->state >=3D MFCINST_HEAD_PARSED && + core_ctx->state < MFCINST_ABORT) { + ctrl->value =3D ctx->dpb_count; + } else { + mfc_err("Decoding not initialised\n"); + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: + ctrl->value =3D dec->slice_enable; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB: + /* Not used */ + break; + case V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE: + ctrl->value =3D dec->crc_enable; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE: + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + if (ctx->is_dpb_realloc && + mfc_rm_query_state(ctx, EQUAL, MFCINST_HEAD_PARSED)) + ctrl->value =3D MFCSTATE_DEC_S3D_REALLOC; + else if (core_ctx->state =3D=3D MFCINST_RES_CHANGE_FLUSH || + core_ctx->state =3D=3D MFCINST_RES_CHANGE_END || + core_ctx->state =3D=3D MFCINST_HEAD_PARSED || + dec->inter_res_change) + ctrl->value =3D MFCSTATE_DEC_RES_DETECT; + else if (mfc_rm_query_state(ctx, EQUAL, MFCINST_FINISHING)) + ctrl->value =3D MFCSTATE_DEC_TERMINATING; + else + ctrl->value =3D MFCSTATE_PROCESSING; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING: + ctrl->value =3D dec->sei_parse; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING: + ctrl->value =3D dec->idr_decoding; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE: + ctrl->value =3D mfc_rate_get_framerate(ctx); + break; + case V4L2_CID_MPEG_MFC_GET_VERSION_INFO: + ctrl->value =3D dev->pdata->ip_ver; + break; + case V4L2_CID_MPEG_VIDEO_QOS_RATIO: + ctrl->value =3D ctx->qos_ratio; + break; + case V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE: + ctrl->value =3D dec->is_dynamic_dpb; + break; + case V4L2_CID_MPEG_MFC_GET_EXT_INFO: + ctrl->value =3D __mfc_dec_ext_info(ctx); + break; + case V4L2_CID_MPEG_MFC_GET_DRIVER_INFO: + ctrl->value =3D MFC_DRIVER_INFO; + break; + case V4L2_CID_MPEG_VIDEO_UNCOMP_FMT: + if (dec->uncomp_fmt) + ctrl->value =3D dec->uncomp_fmt->fourcc; + else + ctrl->value =3D 0; + break; + case V4L2_CID_MPEG_VIDEO_GET_DISPLAY_DELAY: + /* These context information is need to for only main core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + if (core_ctx->state >=3D MFCINST_HEAD_PARSED) { + ctrl->value =3D dec->frame_display_delay; + } else { + mfc_err("display delay information not parsed yet\n"); + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_PRIORITY: + ctrl->value =3D ctx->prio; + mfc_ctx_debug(2, "[PRIO] user get priority: %d (%d)\n", + ctrl->value, ctx->user_prio); + break; + case V4L2_CID_MPEG_MFC_MULTI_VIEW_ENABLE: + ctrl->value =3D (ctx->multi_view_enable || ctx->ready_to_be_multi_view_e= nable); + break; + default: + list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) { + if (!(ctx_ctrl->type & MFC_CTRL_TYPE_GET)) + continue; + + if (ctx_ctrl->id =3D=3D ctrl->id) { + if (ctx_ctrl->get.has_new) { + ctx_ctrl->get.has_new =3D 0; + ctrl->value =3D ctx_ctrl->get.val; + } else { + mfc_ctx_debug(5, "[CTRLS] %s: 0x%08x\n", + "Control value is not up to date", + ctrl->id); + return -EINVAL; + } + + found =3D 1; + break; + } + } + + if (!found) { + mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id); + return -EINVAL; + } + break; + } + + mfc_ctx_debug(5, "[CTRLS] get id: %#x, value: %d\n", ctrl->id, ctrl->valu= e); + + return 0; +} + +/* Set a ctrl */ +static int mfc_dec_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_ctx_ctrl *ctx_ctrl; + int ret =3D 0; + int found =3D 0; + + mfc_ctx_debug_enter(); + + ret =3D __mfc_dec_check_ctrl_val(ctx, ctrl); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: + dec->loop_filter_mpeg4 =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY: + dec->display_delay =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: + dec->slice_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB: + /* Not used */ + break; + case V4L2_CID_MPEG_MFC51_VIDEO_CRC_ENABLE: + dec->crc_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING: + dec->sei_parse =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING: + dec->idr_decoding =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_DECODER_IMMEDIATE_DISPLAY: + dec->immediate_display =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_DECODER_DECODING_TIMESTAMP_MODE: + dec->is_dts_mode =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_DECODER_WAIT_DECODING_START: + mutex_lock(&ctx->drc_wait_mutex); + ctx->wait_state =3D ctrl->value; + mutex_unlock(&ctx->drc_wait_mutex); + break; + case V4L2_CID_MPEG_MFC_SET_DUAL_DPB_MODE: + mfc_ctx_err("[DPB] not supported CID: 0x%x\n", ctrl->id); + break; + case V4L2_CID_MPEG_VIDEO_QOS_RATIO: + ctx->qos_ratio =3D ctrl->value; + mfc_ctx_info("[QoS] set %d qos_ratio\n", ctrl->value); + break; + case V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE: + /* is_dynamic_dpb is controlled by driver */ + if (dec->is_dynamic_dpb =3D=3D 0) + mfc_ctx_debug(2, "[PLUGIN] is_dynamic_dpb is disabled by driver\n"); + if (!ctrl->value) + mfc_ctx_err("[DPB] user has to enable is_dynamic_dpb by default\n"); + break; + case V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE: + if (dec->sh_handle_dpb.fd =3D=3D -1) { + dec->sh_handle_dpb.fd =3D ctrl->value; + if (mfc_mem_get_user_shared_handle(ctx, &dec->sh_handle_dpb, "DPB")) + return -EINVAL; + } + break; + case V4L2_CID_MPEG_MFC_SET_BUF_PROCESS_TYPE: + ctx->buf_process_type =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BLACK_BAR_DETECT: + dec->detect_black_bar =3D ctrl->value; + if (IS_BLACKBAR_OFF(ctx)) { + mfc_ctx_info("[BLACKBAR] black bar detection doesn't work\n"); + dec->detect_black_bar =3D 0; + } + break; + case V4L2_CID_MPEG_VIDEO_DECODING_ORDER: + dec->decoding_order =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SKIP_LAZY_UNMAP: + ctx->skip_lazy_unmap =3D ctrl->value; + mfc_ctx_debug(2, "[LAZY_UNMAP] lazy unmap %s\n", + ctx->skip_lazy_unmap ? "disable" : "enable"); + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE: + mfc_ctx_debug(2, "[QoS] user set the operating frame rate: %d\n", ctrl->= value); + ctx->operating_framerate =3D ctrl->value; + mfc_rm_update_real_time(ctx); + break; + case V4L2_CID_MPEG_VIDEO_PRIORITY: + mfc_ctx_debug(2, "[PRIO] user set priority: %d\n", ctrl->value); + ctx->user_prio =3D ctrl->value; + mfc_rm_update_real_time(ctx); + break; + case V4L2_CID_MPEG_MFC_HEIF_MODE: + mfc_ctx_debug(2, "[HEIF] heif mode: %d\n", ctrl->value); + ctx->is_heif_mode =3D ctrl->value; + break; + default: + list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) { + if (!(ctx_ctrl->type & MFC_CTRL_TYPE_SET)) + continue; + + if (ctx_ctrl->id =3D=3D ctrl->id) { + ctx_ctrl->set.has_new =3D 1; + ctx_ctrl->set.val =3D ctrl->value; + + found =3D 1; + break; + } + } + + if (!found) { + mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id); + return -EINVAL; + } + break; + } + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_dec_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct v4l2_ext_control *ext_ctrl; + struct v4l2_control ctrl; + int i; + int ret =3D 0; + + if (f->which !=3D V4L2_CTRL_CLASS_CODEC && f->which !=3D V4L2_CTRL_CLASS_= USER) + return -EINVAL; + + for (i =3D 0; i < f->count; i++) { + ext_ctrl =3D (f->controls + i); + + ctrl.id =3D ext_ctrl->id; + ctrl.value =3D ext_ctrl->value; + + ret =3D mfc_dec_s_ctrl(file, priv, &ctrl); + if (ret !=3D 0) { + f->error_idx =3D i; + break; + } + mfc_ctx_debug(5, "[CTRLS] set id: %#x, value: %d\n", ctrl.id, ctrl.value= ); + } + + return 0; +} + +static void __mfc_dec_update_disp_res(struct mfc_ctx *ctx, struct v4l2_sel= ection *s) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + + s->r.left =3D 0; + s->r.top =3D 0; + s->r.width =3D dec->disp_drc.width[dec->disp_drc.pop_idx]; + s->r.height =3D dec->disp_drc.height[dec->disp_drc.pop_idx]; + mfc_ctx_debug(2, "[FRAME] Composing info: w=3D%d h=3D%d\n", s->r.width, s= ->r.height); + + dec->disp_drc.disp_res_change--; + mfc_ctx_debug(3, "[DRC] disp_res_change[%d] count %d\n", + dec->disp_drc.pop_idx, dec->disp_drc.disp_res_change); + dec->disp_drc.pop_idx =3D (dec->disp_drc.pop_idx + 1) % MFC_MAX_DRC_FRAME; + + if (!dec->disp_drc.disp_res_change) { + dec->disp_drc.push_idx =3D 0; + dec->disp_drc.pop_idx =3D 0; + } + + /* + * Do not clear WAIT_G_FMT except RUNNING state + * because the resolution change (DRC) case uses WAIT_G_FMT + */ + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_RUNNING) && + (ctx->wait_state & WAIT_G_FMT) !=3D 0) { + ctx->wait_state &=3D ~(WAIT_G_FMT); + mfc_ctx_debug(2, "clear WAIT_G_FMT %d\n", ctx->wait_state); + MFC_TRACE_CTX("** DEC clear WAIT_G_FMT(wait_state %d)\n", ctx->wait_stat= e); + } +} + +/* Get cropping information */ +static int mfc_dec_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_dec *dec =3D ctx->dec_priv; + + mfc_ctx_debug_enter(); + + if (s->type !=3D V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + mutex_lock(&ctx->drc_wait_mutex); + if (dec->disp_drc.disp_res_change) { + __mfc_dec_update_disp_res(ctx, s); + mutex_unlock(&ctx->drc_wait_mutex); + return 0; + } + mutex_unlock(&ctx->drc_wait_mutex); + + if (!NEED_TO_GET_CROP(core_ctx)) { + mfc_err("ready to get compose failed\n"); + return -EINVAL; + } + + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_RUNNING) && + dec->detect_black_bar && dec->black_bar_updated) { + s->r.left =3D dec->black_bar.left; + s->r.top =3D dec->black_bar.top; + s->r.width =3D dec->black_bar.width; + s->r.height =3D dec->black_bar.height; + mfc_debug(2, "[FRAME][BLACKBAR] Cropping info: l=3D%d t=3D%d w=3D%d h=3D= %d\n", + dec->black_bar.left, + dec->black_bar.top, + dec->black_bar.width, + dec->black_bar.height); + } else { + if (ctx->src_fmt->fourcc =3D=3D V4L2_PIX_FMT_H264 || + ctx->src_fmt->fourcc =3D=3D V4L2_PIX_FMT_HEVC) { + s->r.left =3D dec->cr_left; + s->r.top =3D dec->cr_top; + s->r.width =3D ctx->img_width - dec->cr_left - dec->cr_right; + s->r.height =3D ctx->img_height - dec->cr_top - dec->cr_bot; + mfc_debug(2, "[FRAME] %s: l=3D%d t=3D%d w=3D%d h=3D%d (r=3D%d b=3D%d fw= =3D%d fh=3D%d)\n", + "Composing info", s->r.left, s->r.top, s->r.width, s->r.height, + dec->cr_right, dec->cr_bot, ctx->img_width, ctx->img_height); + } else { + s->r.left =3D 0; + s->r.top =3D 0; + s->r.width =3D ctx->img_width; + s->r.height =3D ctx->img_height; + mfc_debug(2, "[FRAME] Composing info: w=3D%d h=3D%d fw=3D%d fh=3D%d\n", + s->r.width, s->r.height, + ctx->img_width, ctx->img_height); + } + } + + mfc_ctx_debug_leave(); + return 0; +} + +static int mfc_dec_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct v4l2_ext_control *ext_ctrl; + struct v4l2_control ctrl; + int i; + int ret =3D 0; + + if (f->which !=3D V4L2_CTRL_CLASS_CODEC && f->which !=3D V4L2_CTRL_CLASS_= USER) + return -EINVAL; + + for (i =3D 0; i < f->count; i++) { + ext_ctrl =3D (f->controls + i); + + ctrl.id =3D ext_ctrl->id; + + ret =3D __mfc_dec_get_ctrl_val(ctx, &ctrl); + if (ret =3D=3D 0) { + ext_ctrl->value =3D ctrl.value; + } else { + f->error_idx =3D i; + break; + } + + mfc_ctx_debug(5, "[CTRLS][%d] id: %#x, value: %d\n", + i, ext_ctrl->id, ext_ctrl->value); + } + + return ret; +} + +/* Initialize for default format */ +void mfc_dec_set_default_format(struct mfc_ctx *ctx) +{ + struct mfc_fmt *fmt =3D NULL; + + /* Set default format for source */ + fmt =3D __mfc_dec_find_format(ctx, V4L2_PIX_FMT_H264); + if (!fmt) { + /* NEVER come here */ + mfc_ctx_err("Wrong memory access. Set fmt by mfc_formats[0]\n"); + fmt =3D &mfc_formats[0]; + } + ctx->src_fmt =3D fmt; + + /* Set default format for destination */ + fmt =3D __mfc_dec_find_format(ctx, V4L2_PIX_FMT_NV12M); + if (!fmt) { + /* NEVER come here */ + mfc_ctx_err("Wrong memory access. Set fmt by mfc_formats[0]\n"); + fmt =3D &mfc_formats[0]; + } + ctx->dst_fmt =3D fmt; +} + +/* v4l2_ioctl_ops */ +static const struct v4l2_ioctl_ops mfc_dec_ioctl_ops =3D { + .vidioc_querycap =3D mfc_dec_querycap, + .vidioc_enum_fmt_vid_cap =3D mfc_dec_enum_fmt_vid_cap_mplane, + .vidioc_enum_fmt_vid_out =3D mfc_dec_enum_fmt_vid_out_mplane, + .vidioc_g_fmt_vid_cap_mplane =3D mfc_dec_g_fmt_vid_cap_mplane, + .vidioc_g_fmt_vid_out_mplane =3D mfc_dec_g_fmt_vid_out_mplane, + .vidioc_try_fmt_vid_cap_mplane =3D mfc_dec_try_fmt, + .vidioc_try_fmt_vid_out_mplane =3D mfc_dec_try_fmt, + .vidioc_s_fmt_vid_cap_mplane =3D mfc_dec_s_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_out_mplane =3D mfc_dec_s_fmt_vid_out_mplane, + .vidioc_reqbufs =3D mfc_dec_reqbufs, + .vidioc_querybuf =3D mfc_dec_querybuf, + .vidioc_qbuf =3D mfc_dec_qbuf, + .vidioc_dqbuf =3D mfc_dec_dqbuf, + .vidioc_streamon =3D mfc_dec_streamon, + .vidioc_streamoff =3D mfc_dec_streamoff, + .vidioc_s_ext_ctrls =3D mfc_dec_s_ext_ctrls, + .vidioc_g_selection =3D mfc_dec_g_selection, + .vidioc_g_ext_ctrls =3D mfc_dec_g_ext_ctrls, +}; + +const struct v4l2_ioctl_ops *mfc_get_dec_v4l2_ioctl_ops(void) +{ + return &mfc_dec_ioctl_ops; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h new file mode 100644 index 000000000000..04652a71cd23 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * mfc_dec_v4l2.h + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __MFC_DEC_V4L2_H +#define __MFC_DEC_V4L2_H __FILE__ + +#include "base/mfc_common.h" + +const struct v4l2_ioctl_ops *mfc_get_dec_v4l2_ioctl_ops(void); +void mfc_dec_set_default_format(struct mfc_ctx *ctx); + +#endif /* __MFC_DEC_V4L2_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) (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 5BE3025A323 for ; Tue, 30 Sep 2025 03:56:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.33 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204608; cv=none; b=TZWn46PURNwza7Bfj0B3nyrkpRYBH05ewPEXW2/aey7INN6d5MckF4Rb+CooRANLH/ZKYf8QSe0VnggFs7vY20h3QoIs4Xa0E8pSWxm08UsdFTTCWPkT6A6EHoB7gyaR8toH2ZdrKELzCRi7ACPjr8w1svoNEcleKpAhLnZEvKY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204608; c=relaxed/simple; bh=+DGXIqVnaAJHnlBCDRoDfevs7BT/z/yi5VwGUO9/GtY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=XRI9U8oxCfTVO1AksA70816KxMIwj6y4/ke5+jNcYtDi0ny+CbfrSjDcJwzB2g8X/SPGktwJiNDy0LYhpXKK4rB2nrytkL3lpXU+m48q4TURUWyXSXDaqS+QQFZytvC58WYGJeDUao/0HjWpe6wZwcz8yk7rBqQGU6Bprm0UOGY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=IWEODGUW; arc=none smtp.client-ip=203.254.224.33 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="IWEODGUW" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20250930035643epoutp037bc5e06f9d123b29115316d85630491c~p80Qx9kU63243732437epoutp03K for ; Tue, 30 Sep 2025 03:56:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20250930035643epoutp037bc5e06f9d123b29115316d85630491c~p80Qx9kU63243732437epoutp03K DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204603; bh=2LJHR8Pp/KaBAKbAXiHh5qJSpNlaDlWuy6Su0DiB9iA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IWEODGUWahxQ4BmyB16jkxjsurPDVOoDvg/c/4mO8K2slu6d0XbRQhUFjcSbbv+dT uaodxIhCcr4Irb6qjbIOJhCR+ir0uhkuyOJz3jgNJig7hK/Q8ytTdkAL/ztWcdgjSq usT8CEfchQ0akaJlwS1YBOZorTHC+VQFd+n0RnEU= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035642epcas5p42488fcfda56807f0716f5e4f09e59adf~p80QD-eu-2408224082epcas5p4I; Tue, 30 Sep 2025 03:56:42 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.92]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cbPPj5Kksz6B9m9; Tue, 30 Sep 2025 03:56:41 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPA id 20250930035641epcas5p4a5011f149b293c1b5aee60df0447666c~p80OTF8Hr1003510035epcas5p4k; Tue, 30 Sep 2025 03:56:41 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035638epsmtip1faa83375b843a9886c76548dab2615ae~p80LtZ3-t2908129081epsmtip1C; Tue, 30 Sep 2025 03:56:37 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 19/29] =?UTF-8?q?media:=20mfc:=20Add=20QoS,=20Butler=20wor?= =?UTF-8?q?kqueue,=20and=20priority=E2=80=91based=20scheduling?= Date: Tue, 30 Sep 2025 09:33:38 +0530 Message-Id: <20250930040348.3702923-20-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035641epcas5p4a5011f149b293c1b5aee60df0447666c X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035641epcas5p4a5011f149b293c1b5aee60df0447666c References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Integrate QoS handling (core, ops, PM). - Add Butler workqueue for core tasks (allocation, init, cleanup). - Implement async QoS control workqueue using atomic counters and proper sync. - Update probe to initialize workqueues, QoS structures, and improve logging. - Modify PM to trigger QoS updates on clock changes and issue idle=E2=80=91clock QoS when bus devfreq is active. - Refactor scheduler for priority=E2=80=91aware work handling via bit=E2=80=91mask tracking. - Extend ops table with extra instance and buffer management. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/mfc_core.c | 145 +++++++ .../samsung/exynos-mfc/mfc_core_ops.c | 370 ++++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_core_pm.c | 25 ++ .../samsung/exynos-mfc/mfc_core_sched_prio.c | 331 ++++++++++++++++ .../samsung/exynos-mfc/mfc_dec_v4l2.c | 12 +- .../samsung/exynos-mfc/mfc_dec_v4l2.h | 12 +- 6 files changed, 881 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core.c b/drivers= /media/platform/samsung/exynos-mfc/mfc_core.c index 4f5cf459c36f..af6fd088fad3 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core.c @@ -28,6 +28,7 @@ =20 #include "mfc_core_ops.h" #include "mfc_core_isr.h" +#include "mfc_dec_v4l2.h" #include "mfc_debugfs.h" =20 #include "mfc_core_hwlock.h" @@ -37,7 +38,9 @@ =20 #include "mfc_core_hw_reg_api.h" =20 +#include "base/mfc_qos.h" #include "base/mfc_sched.h" +#include "base/mfc_queue.h" #include "base/mfc_buf.h" #include "base/mfc_mem.h" =20 @@ -45,6 +48,45 @@ =20 struct _mfc_trace_logging g_mfc_core_trace_logging[MFC_TRACE_LOG_COUNT_MAX= ]; =20 +static void mfc_core_butler_worker(struct work_struct *work) +{ + struct mfc_core *core; + + core =3D container_of(work, struct mfc_core, butler_work); + + mfc_core_try_run(core); +} + +static int __mfc_core_parse_mfc_qos_platdata(struct device_node *np, + char *node_name, + struct mfc_qos *qosdata, + struct mfc_core *core) +{ + struct device_node *np_qos; + + np_qos =3D of_find_node_by_name(NULL, node_name); + if (!np_qos) { + dev_err(core->device, + "%s: could not find mfc_qos_platdata node\n", + node_name); + return -EINVAL; + } + + of_property_read_u32(np_qos, "thrd_mb", &qosdata->threshold_mb); + of_property_read_u32(np_qos, "freq_mfc", &qosdata->freq_mfc); + of_property_read_u32(np_qos, "freq_int", &qosdata->freq_int); + of_property_read_u32(np_qos, "freq_mif", &qosdata->freq_mif); + of_property_read_u32(np_qos, "mo_value", &qosdata->mo_value); + of_property_read_u32(np_qos, "time_fw", &qosdata->time_fw); + + of_property_read_string(np_qos, "bts_scen", &qosdata->name); + if (!qosdata->name) { + mfc_pr_err("[QoS] bts_scen is missing in '%s' node", node_name); + return -EINVAL; + } + return 0; +} + #if (IS_ENABLED(CONFIG_SAMSUNG_IOMMU)) static int mfc_core_sysmmu_fault_handler(struct iommu_domain *domain, struct device *dev, unsigned long iova, @@ -140,6 +182,9 @@ static int mfc_core_sysmmu_fault_handler(struct iommu_d= omain *domain, static int __mfc_core_parse_dt(struct device_node *np, struct mfc_core *co= re) { struct mfc_core_platdata *pdata =3D core->core_pdata; + struct device_node *np_qos; + char node_name[50]; + int i; =20 if (!np) { mfc_pr_err("there is no device node\n"); @@ -164,6 +209,61 @@ static int __mfc_core_parse_dt(struct device_node *np,= struct mfc_core *core) of_property_read_u32(np, "masterid_mask", &pdata->masterid_mask); of_property_read_u32(np, "tsmux_masterid", &pdata->tsmux_masterid); =20 + /* QoS */ + of_property_read_u32(np, "num_default_qos_steps", + &pdata->num_default_qos_steps); + of_property_read_u32(np, "max_mb", &pdata->max_mb); + of_property_read_u32(np, "max_hw_mb", &pdata->max_hw_mb); + of_property_read_u32(np, "mfc_freq_control", &pdata->mfc_freq_control); + of_property_read_u32(np, "mo_control", &pdata->mo_control); + of_property_read_u32(np, "bw_control", &pdata->bw_control); + of_property_read_u32(np, "pm_qos_id", &pdata->pm_qos_id); + + pdata->default_qos_table =3D devm_kzalloc(core->device, + sizeof(struct mfc_qos) * + pdata->num_default_qos_steps, + GFP_KERNEL); + for (i =3D 0; i < pdata->num_default_qos_steps; i++) { + snprintf(node_name, sizeof(node_name), "mfc_d_qos_variant_%d", + i); + __mfc_core_parse_mfc_qos_platdata(np, node_name, + &pdata->default_qos_table[i], + core); + } + + /* performance boost mode */ + pdata->qos_boost_table =3D devm_kzalloc(core->device, + sizeof(struct mfc_qos_boost), + GFP_KERNEL); + np_qos =3D of_find_node_by_name(np, "mfc_perf_boost_table"); + if (!np_qos) { + dev_err(core->device, + "[QoS][BOOST] could not find mfc_perf_boost_table node\n"); + return -EINVAL; + } + of_property_read_u32(np_qos, "num_cluster", + &pdata->qos_boost_table->num_cluster); + of_property_read_u32_array(np_qos, "num_cpu", + &pdata->qos_boost_table->num_cpu[0], + pdata->qos_boost_table->num_cluster); + of_property_read_u32(np_qos, "freq_mfc", + &pdata->qos_boost_table->freq_mfc); + of_property_read_u32(np_qos, "freq_int", + &pdata->qos_boost_table->freq_int); + of_property_read_u32(np_qos, "freq_mif", + &pdata->qos_boost_table->freq_mif); + of_property_read_u32_array(np_qos, "freq_cluster", + &pdata->qos_boost_table->freq_cluster[0], + pdata->qos_boost_table->num_cluster); + + of_property_read_string(np_qos, "bts_scen", + &pdata->qos_boost_table->name); + if (!pdata->qos_boost_table->name) { + mfc_pr_err + ("[QoS][BOOST] bts_scen is missing in qos_boost node"); + return -EINVAL; + } + return 0; } =20 @@ -274,6 +374,7 @@ static int mfc_core_probe(struct platform_device *pdev) struct mfc_core *core; struct mfc_dev *dev; int ret =3D -ENOENT; + int i; =20 dev_info(&pdev->dev, "%s is called\n", __func__); =20 @@ -325,6 +426,44 @@ static int mfc_core_probe(struct platform_device *pdev) =20 spin_lock_init(&core->batch_lock); =20 + /* core butler worker */ + core->butler_wq =3D alloc_workqueue("mfc_core/butler", WQ_UNBOUND + | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!core->butler_wq) { + dev_err(&pdev->dev, "failed to create workqueue for butler\n"); + goto err_butler_wq; + } + INIT_WORK(&core->butler_work, mfc_core_butler_worker); + + /* core QoS control worker */ + core->qos_ctrl_wq =3D alloc_workqueue("mfc_core/qos_ctrl", WQ_UNBOUND + | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!core->qos_ctrl_wq) { + dev_err(&pdev->dev, + "failed to create workqueue for QoS control\n"); + goto err_qos_ctrl_wq; + } +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ + INIT_WORK(&core->qos_ctrl_work, mfc_qos_ctrl_worker); +#endif + + atomic_set(&core->qos_req_cur, 0); + mutex_init(&core->qos_mutex); + mutex_init(&core->pm_qos_mutex); + + mfc_core_info("[QoS] control: mfc_freq(%d), mo(%d), bw(%d)\n", + core->core_pdata->mfc_freq_control, + core->core_pdata->mo_control, + core->core_pdata->bw_control); + mfc_core_info("[QoS]-------------------Default table\n"); + for (i =3D 0; i < core->core_pdata->num_default_qos_steps; i++) + mfc_core_info + ("[QoS] table[%d] mfc: %d, int: %d, mif: %d, bts_scen: %s(%d)\n", + i, core->core_pdata->default_qos_table[i].freq_mfc, + core->core_pdata->default_qos_table[i].freq_int, + core->core_pdata->default_qos_table[i].freq_mif, + core->core_pdata->default_qos_table[i].name, + core->core_pdata->default_qos_table[i].bts_scen_idx); #if IS_ENABLED(CONFIG_SAMSUNG_IOMMU) ret =3D samsung_iommu_register_fault_handler(core->device, mfc_core_sysmmu_fault_handler, @@ -365,6 +504,10 @@ static int mfc_core_probe(struct platform_device *pdev) samsung_iommu_unregister_fault_handler(&pdev->dev); err_sysmmu_fault_handler: #endif + destroy_workqueue(core->qos_ctrl_wq); + err_qos_ctrl_wq: + destroy_workqueue(core->butler_wq); + err_butler_wq: if (core->has_2sysmmu) iounmap(core->sysmmu1_base); iounmap(core->sysmmu0_base); @@ -388,6 +531,8 @@ static void mfc_core_remove(struct platform_device *pde= v) #if IS_ENABLED(CONFIG_SAMSUNG_IOMMU) samsung_iommu_unregister_fault_handler(&pdev->dev); #endif + flush_workqueue(core->butler_wq); + destroy_workqueue(core->butler_wq); mfc_core_destroy_listable_wq_core(core); =20 mfc_release_common_context(core); diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_ops.c index d9200bba1bb5..f8a27548b218 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c @@ -19,8 +19,10 @@ #include "mfc_core_hw_reg_api.h" =20 #include "base/mfc_sched.h" +#include "base/mfc_qos.h" #include "base/mfc_buf.h" #include "base/mfc_utils.h" +#include "base/mfc_queue.h" #include "base/mfc_mem.h" =20 static void __mfc_core_init(struct mfc_core *core, struct mfc_ctx *ctx) @@ -109,6 +111,8 @@ static int __mfc_core_deinit(struct mfc_core *core, str= uct mfc_ctx *ctx) if (core->num_inst =3D=3D 0) { mfc_core_run_deinit_hw(core); =20 + flush_workqueue(core->butler_wq); + mfc_debug(2, "power off\n"); mfc_core_pm_power_off(core); =20 @@ -121,6 +125,40 @@ static int __mfc_core_deinit(struct mfc_core *core, st= ruct mfc_ctx *ctx) } } =20 + mfc_qos_off(core, ctx); + + return 0; +} + +static int __mfc_force_close_inst(struct mfc_core *core, struct mfc_ctx *c= tx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + enum mfc_inst_state prev_state; + + if (core_ctx->state =3D=3D MFCINST_FREE) + return 0; + + prev_state =3D core_ctx->state; + mfc_change_state(core_ctx, MFCINST_RETURN_INST); + mfc_change_op_mode(ctx, MFC_OP_SINGLE); + core->sched->set_work(core, core_ctx); + mfc_clean_core_ctx_int_flags(core_ctx); + if (mfc_core_just_run(core, ctx->num)) { + mfc_err("Failed to run MFC\n"); + mfc_change_state(core_ctx, prev_state); + return -EIO; + } + + /* Wait until instance is returned or timeout occurred */ + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_CLOSE_INSTANCE_R= ET)) { + mfc_err("Waiting for CLOSE_INSTANCE timed out\n"); + mfc_change_state(core_ctx, prev_state); + return -EIO; + } + + /* Free resources */ + mfc_release_instance_context(core_ctx); + return 0; } =20 @@ -153,7 +191,12 @@ static int __mfc_core_instance_init(struct mfc_core *c= ore, struct mfc_ctx *ctx) =20 init_waitqueue_head(&core_ctx->cmd_wq); mfc_core_init_listable_wq_ctx(core_ctx); + spin_lock_init(&core_ctx->buf_queue_lock); core->sched->clear_work(core, core_ctx); + INIT_LIST_HEAD(&core_ctx->qos_list); + INIT_LIST_HEAD(&core_ctx->mb_list); + + mfc_create_queue(&core_ctx->src_buf_queue); =20 if (core->num_inst =3D=3D 1) { mfc_debug(2, "it is first instance in to core-%d\n", core->id); @@ -252,6 +295,14 @@ static int mfc_core_instance_deinit(struct mfc_core *c= ore, struct mfc_ctx *ctx) =20 core->sched->clear_work(core, core_ctx); =20 + /* If a H/W operation is in progress, wait for it complete */ + if (NEED_TO_WAIT_NAL_ABORT(core_ctx)) { + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_NAL_ABORT_RET))= { + mfc_err("Failed to wait nal abort\n"); + core->sched->yield_work(core, core_ctx); + } + } + ret =3D mfc_core_get_hwlock_ctx(core_ctx); if (ret < 0) { mfc_err("Failed to get hwlock\n"); @@ -265,9 +316,14 @@ static int mfc_core_instance_deinit(struct mfc_core *c= ore, struct mfc_ctx *ctx) if (ret) goto err_release_try; =20 + mfc_release_codec_buffers(core_ctx); + mfc_release_instance_context(core_ctx); + mfc_core_release_hwlock_ctx(core_ctx); mfc_core_destroy_listable_wq_ctx(core_ctx); =20 + mfc_delete_queue(&core_ctx->src_buf_queue); + core->core_ctx[core_ctx->num] =3D 0; kfree(core_ctx); =20 @@ -275,12 +331,326 @@ static int mfc_core_instance_deinit(struct mfc_core = *core, struct mfc_ctx *ctx) =20 err_release_try: mfc_core_release_hwlock_ctx(core_ctx); + core->sched->yield_work(core, core_ctx); + return ret; +} + +static int __mfc_core_instance_open_dec(struct mfc_ctx *ctx, + struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + int ret =3D 0; + + /* In case of calling s_fmt twice or more */ + ret =3D __mfc_force_close_inst(core, ctx); + if (ret) { + mfc_err("Failed to close already opening instance\n"); + mfc_core_release_hwlock_ctx(core_ctx); + core->sched->yield_work(core, core_ctx); + return -EIO; + } + + ret =3D mfc_alloc_instance_context(core_ctx); + if (ret) { + mfc_err("Failed to allocate dec instance[%d] buffers\n", + ctx->num); + mfc_core_release_hwlock_ctx(core_ctx); + return -ENOMEM; + } + + return 0; +} + +static int mfc_core_instance_open(struct mfc_core *core, struct mfc_ctx *c= tx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret =3D 0; + + if (!core_ctx) { + mfc_core_err("There is no instance\n"); + return -EINVAL; + } + + ret =3D mfc_core_get_hwlock_ctx(core_ctx); + if (ret < 0) { + mfc_err("Failed to get hwlock\n"); + return ret; + } + + if (ctx->type =3D=3D MFCINST_DECODER) { + if (__mfc_core_instance_open_dec(ctx, core_ctx)) + return -EAGAIN; + } else { + mfc_err("invalid codec type: %d\n", ctx->type); + return -EINVAL; + } + + mfc_change_state(core_ctx, MFCINST_INIT); + core->sched->set_work(core, core_ctx); + ret =3D mfc_core_just_run(core, ctx->num); + if (ret) { + mfc_err("Failed to run MFC\n"); + goto err_open; + } + + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_OPEN_INSTANCE_RE= T)) { + mfc_err("failed to wait OPEN_INSTANCE\n"); + mfc_change_state(core_ctx, MFCINST_FREE); + ret =3D -EIO; + goto err_open; + } + + mfc_core_release_hwlock_ctx(core_ctx); + + mfc_debug(2, "Got instance number inst_no: %d\n", core_ctx->inst_no); + + core->sched->enqueue_work(core, core_ctx); + if (core->sched->is_work(core)) + core->sched->queue_work(core); + + return ret; + +err_open: + mfc_core_release_hwlock_ctx(core_ctx); + core->sched->yield_work(core, core_ctx); + mfc_release_instance_context(core_ctx); + + return ret; +} + +static void mfc_core_instance_cache_flush(struct mfc_core *core, struct mf= c_ctx *ctx) +{ + if (core->state =3D=3D MFCCORE_ERROR) { + mfc_core_info("[MSR] Couldn't cache flush. It's Error state\n"); + return; + } + + core->curr_core_ctx =3D ctx->num; + + mfc_core_pm_clock_on(core, 0); + mfc_core_run_cache_flush + (core, + core->last_cmd_has_cache_flush ? MFC_NO_CACHEFLUSH : MFC_CACHEFLUSH, + 0); + mfc_core_pm_clock_off(core, 0); +} + +static int mfc_core_instance_move_to(struct mfc_core *core, struct mfc_ctx= *ctx) +{ + int ret =3D 0; + + ret =3D __mfc_core_instance_init(core, ctx); + if (ret) { + mfc_core_err("Failed to core instance init\n"); + return ret; + } + + if (core->num_inst > 1) { + mfc_ctx_debug(2, "to core-%d already working, send cache_flush only\n", = core->id); + mfc_core_instance_cache_flush(core, ctx); + } + + mfc_ctx_info("to core-%d is ready to move\n", core->id); + + return 0; +} + +static int mfc_core_instance_move_from(struct mfc_core *core, struct mfc_c= tx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret =3D 0; + int inst_no; + + mfc_clean_core_ctx_int_flags(core_ctx); + core->sched->set_work(core, core_ctx); + + ret =3D mfc_core_just_run(core, ctx->num); + if (ret) { + mfc_err("Failed to run MFC\n"); + return ret; + } + + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_MOVE_INSTANCE_RE= T)) { + mfc_err("time out during move instance\n"); + core->logging_data->cause |=3D BIT(MFC_CAUSE_FAIL_MOVE_INST); + return -EFAULT; + } + inst_no =3D mfc_core_get_inst_no(); + + ret =3D __mfc_core_deinit(core, ctx); + if (ret) { + mfc_err("Failed to close instance\n"); + return ret; + } + + mfc_info("inst_no.%d will be changed to no.%d\n", core_ctx->inst_no, inst= _no); + core_ctx->inst_no =3D inst_no; + return ret; } =20 +static void mfc_core_instance_dpb_flush(struct mfc_core *core, struct mfc_= ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int index =3D 0, i, ret; + int prev_state; + + if (core->state =3D=3D MFCCORE_ERROR || core_ctx->state =3D=3D MFCINST_ER= ROR) + goto cleanup; + + ret =3D mfc_core_get_hwlock_ctx(core_ctx); + if (ret < 0) { + mfc_err("Failed to get hwlock\n"); + MFC_TRACE_CTX_LT("[ERR][Release] failed to get hwlock (shutdown: %d)\n", + core->shutdown); + if (core->shutdown) + goto cleanup; + return; + } + + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue); + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->err_buf_queue); + + mutex_lock(&dec->dpb_mutex); + for (i =3D 0; i < MFC_MAX_DPBS; i++) + dec->dpb[i].queued =3D 0; + mutex_unlock(&dec->dpb_mutex); + + dec->queued_dpb =3D 0; + ctx->is_dpb_realloc =3D 0; + dec->last_dpb_max_index =3D 0; + + if (!dec->inter_res_change) { + dec->dpb_table_used =3D 0; + dec->dynamic_used =3D 0; + if (dec->is_dynamic_dpb) { + mfc_cleanup_iovmm(ctx); + dec->dynamic_set =3D 0; + core_ctx->dynamic_set =3D 0; + } else { + dec->dynamic_set =3D MFC_ALL_AVAILABLE_DPB; + } + } else { + mfc_cleanup_iovmm_except_used(ctx); + mfc_print_dpb_table(ctx); + } + + while (index < MFC_MAX_BUFFERS) { + index =3D find_next_bit(ctx->dst_ctrls_avail, MFC_MAX_BUFFERS, index); + if (index < MFC_MAX_BUFFERS) + call_cop(ctx, reset_buf_ctrls, &ctx->dst_ctrls[index]); + index++; + } + + mutex_lock(&ctx->drc_wait_mutex); + if (ctx->wait_state & WAIT_STOP) { + ctx->wait_state &=3D ~(WAIT_STOP); + mfc_debug(2, "clear WAIT_STOP %d\n", ctx->wait_state); + MFC_TRACE_CORE_CTX("** DEC clear WAIT_STOP(wait_state %d)\n", + ctx->wait_state); + + if (ctx->ready_to_be_multi_view_enable) { + ctx->ready_to_be_multi_view_enable =3D 0; + ctx->multi_view_enable =3D 1; + mfc_debug(3, "[MV-HEVC] enabled\n"); + } + } + mutex_unlock(&ctx->drc_wait_mutex); + + if (core_ctx->state =3D=3D MFCINST_FINISHING) + mfc_change_state(core_ctx, MFCINST_RUNNING); + + if (NEED_TO_DPB_FLUSH(core_ctx) && !ctx->dec_priv->inter_res_change) { + prev_state =3D core_ctx->state; + mfc_change_state(core_ctx, MFCINST_DPB_FLUSHING); + core->sched->set_work(core, core_ctx); + mfc_clean_core_ctx_int_flags(core_ctx); + mfc_info("try to DPB flush\n"); + ret =3D mfc_core_just_run(core, ctx->num); + if (ret) { + mfc_err("Failed to run MFC\n"); + mfc_core_release_hwlock_ctx(core_ctx); + core->sched->yield_work(core, core_ctx); + return; + } + + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_DPB_FLUSH_RET))= { + mfc_err("time out during DPB flush\n"); + core->logging_data->cause |=3D BIT(MFC_CAUSE_FAIL_DPB_FLUSH); + } + + mfc_change_state(core_ctx, prev_state); + } + + mfc_debug(2, "decoder destination stop sequence done\n"); + + core->sched->clear_work(core, core_ctx); + mfc_core_release_hwlock_ctx(core_ctx); + + core->sched->enqueue_work(core, core_ctx); + if (core->sched->is_work(core)) + core->sched->queue_work(core); + + return; + +cleanup: + mfc_core_info("[MSR] Cleanup dst buffers. It's Error state\n"); + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue); + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->err_buf_queue); +} + +static int mfc_core_instance_init_buf(struct mfc_core *core, struct mfc_ct= x *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + + core->sched->set_work(core, core_ctx); + mfc_clean_core_ctx_int_flags(core_ctx); + if (mfc_core_just_run(core, ctx->num)) { + mfc_err("Failed to run MFC\n"); + return -EIO; + } + + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_INIT_BUFFERS_RET= )) { + mfc_err("[RM] init buffer timeout\n"); + return -EIO; + } + + return 0; +} + +static int mfc_core_request_work(struct mfc_core *core, + enum mfc_request_work work, + struct mfc_ctx *ctx) +{ + switch (work) { + case MFC_WORK_BUTLER: + mfc_core_debug(3, "request_work: butler\n"); + if (core->sched->is_work(core)) + core->sched->queue_work(core); + break; + case MFC_WORK_TRY: + mfc_core_debug(3, "request_work: try_run\n"); + mfc_core_try_run(core); + break; + default: + mfc_core_err("not supported request work type: %#x\n", work); + return -EINVAL; + } + + return 0; +} + static const struct mfc_core_ops mfc_core_ops =3D { .instance_init =3D mfc_core_instance_init, .instance_deinit =3D mfc_core_instance_deinit, + .instance_open =3D mfc_core_instance_open, + .instance_cache_flush =3D mfc_core_instance_cache_flush, + .instance_move_to =3D mfc_core_instance_move_to, + .instance_move_from =3D mfc_core_instance_move_from, + .instance_dpb_flush =3D mfc_core_instance_dpb_flush, + .instance_init_buf =3D mfc_core_instance_init_buf, + .request_work =3D mfc_core_request_work, }; =20 const struct mfc_core_ops *mfc_get_core_ops(void) diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_core_pm.c index def7ac2a2007..a70b2b472c06 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_pm.c @@ -16,6 +16,8 @@ =20 #include "mfc_core_hw_reg_api.h" =20 +#include "base/mfc_qos.h" + void mfc_core_pm_init(struct mfc_core *core) { spin_lock_init(&core->pm.clklock); @@ -78,6 +80,9 @@ int mfc_core_pm_clock_on(struct mfc_core *core, bool qos_= update) mfc_core_debug(2, "clk_ref =3D %d\n", state); MFC_TRACE_LOG_CORE("clk_ref =3D %d", state); =20 + if (qos_update && state =3D=3D 1) + mfc_qos_update(core, 1); + return 0; } =20 @@ -113,6 +118,9 @@ void mfc_core_pm_clock_off(struct mfc_core *core, bool = qos_update) =20 mfc_core_debug(2, "clk_ref =3D %d\n", state); MFC_TRACE_LOG_CORE("clk_ref =3D %d", state); + + if (qos_update && state =3D=3D 0) + mfc_qos_update(core, 0); } =20 int mfc_core_pm_power_on(struct mfc_core *core) @@ -126,6 +134,15 @@ int mfc_core_pm_power_on(struct mfc_core *core) goto err_power_on; } =20 +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ + if (core->dev->pdata->idle_clk_ctrl) { + mfc_core_debug(2, "request mfc idle clk OFF\n"); + exynos_pm_qos_add_request(&core->qos_req_mfc_noidle, + core->core_pdata->pm_qos_id, + core->dev->pdata->mfc_freqs[0]); + } +#endif + #if (IS_ENABLED(CONFIG_COMMON_CLK_SAMSUNG)) core->pm.clock =3D clk_get(core->device, "aclk_mfc"); if (IS_ERR(core->pm.clock)) { @@ -167,6 +184,14 @@ int mfc_core_pm_power_off(struct mfc_core *core) clk_unprepare(core->pm.clock); clk_put(core->pm.clock); } + +#ifdef CONFIG_MFC_USE_BUS_DEVFREQ + if (core->dev->pdata->idle_clk_ctrl) { + exynos_pm_qos_remove_request(&core->qos_req_mfc_noidle); + mfc_core_debug(2, "request mfc idle clk ON\n"); + } +#endif + mfc_core_mfc_always_off(core); =20 ret =3D pm_runtime_put_sync(core->pm.device); diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sched_prio.= c b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sched_prio.c index a2f69a064d3d..34731229338d 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sched_prio.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sched_prio.c @@ -13,6 +13,7 @@ #include "mfc_core_sync.h" =20 #include "base/mfc_sched.h" +#include "base/mfc_qos.h" #include "base/mfc_utils.h" =20 static inline void __mfc_print_workbits(struct mfc_core *core, int prio, i= nt num) @@ -36,6 +37,42 @@ static inline void __mfc_clear_all_prio_bits(struct mfc_= core *core) spin_unlock_irqrestore(&core->prio_work_lock, flags); } =20 +static int __mfc_ctx_ready_set_bit_prio(struct mfc_core_ctx *core_ctx, boo= l set) + +{ + struct mfc_core *core; + struct mfc_ctx *ctx; + unsigned long flags; + int p, is_ready =3D 0; + + if (!core_ctx) { + mfc_pr_err("no mfc core_ctx device to run\n"); + return is_ready; + } + + core =3D core_ctx->core; + if (!core) { + mfc_err("no mfc core device to run\n"); + return is_ready; + } + + ctx =3D core_ctx->ctx; + if (!ctx) { + mfc_err("no mfc ctx device to run\n"); + return is_ready; + } + + spin_lock_irqsave(&core->prio_work_lock, flags); + + p =3D mfc_get_prio(core, ctx->rt, ctx->prio); + is_ready =3D mfc_ctx_ready_set_bit_raw(core_ctx, &core->prio_work_bits[p]= , set); + __mfc_print_workbits(core, p, core_ctx->num); + + spin_unlock_irqrestore(&core->prio_work_lock, flags); + + return is_ready; +} + static void mfc_create_work_prio(struct mfc_core *core) { int num_prio =3D core->dev->pdata->pbs_num_prio; @@ -61,6 +98,27 @@ static void mfc_clear_all_work_prio(struct mfc_core *cor= e) __mfc_clear_all_prio_bits(core); } =20 +static int mfc_is_work_prio(struct mfc_core *core) +{ + unsigned long flags; + int i, ret =3D 0; + + spin_lock_irqsave(&core->prio_work_lock, flags); + for (i =3D 0; i < core->total_num_prio; i++) { + if (core->prio_work_bits[i]) { + ret =3D 1; + break; + } + } + spin_unlock_irqrestore(&core->prio_work_lock, flags); + return ret; +} + +static void mfc_queue_work_prio(struct mfc_core *core) +{ + queue_work(core->butler_wq, &core->butler_work); +} + static void mfc_set_work_prio(struct mfc_core *core, struct mfc_core_ctx *= core_ctx) { struct mfc_ctx *ctx =3D core_ctx->ctx; @@ -91,10 +149,283 @@ static void mfc_clear_work_prio(struct mfc_core *core= , struct mfc_core_ctx *core spin_unlock_irqrestore(&core->prio_work_lock, flags); } =20 +static int mfc_enqueue_work_prio(struct mfc_core *core, struct mfc_core_ct= x *core_ctx) +{ + return __mfc_ctx_ready_set_bit_prio(core_ctx, true); +} + +static int mfc_dequeue_work_prio(struct mfc_core *core, struct mfc_core_ct= x *core_ctx) +{ + return __mfc_ctx_ready_set_bit_prio(core_ctx, false); +} + +static void mfc_yield_work_prio(struct mfc_core *core, struct mfc_core_ctx= *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + unsigned long flags; + int p; + + spin_lock_irqsave(&core->prio_work_lock, flags); + + p =3D mfc_get_prio(core, ctx->rt, ctx->prio); + __clear_bit(core_ctx->num, &core->prio_work_bits[p]); + __mfc_print_workbits(core, p, core_ctx->num); + + spin_unlock_irqrestore(&core->prio_work_lock, flags); + + mfc_core_try_run(core); +} + +static unsigned long __mfc_check_lower_prio_inst(struct mfc_core *core, in= t prio) +{ + unsigned long sum =3D 0; + int i; + + for (i =3D prio + 1; i < core->total_num_prio; i++) + sum |=3D core->prio_work_bits[i]; + + return sum; +} + +static void __mfc_update_max_runtime(struct mfc_core *core, unsigned long = bits) +{ + struct mfc_core_ctx *core_ctx; + int max_runtime =3D 0, runtime; + int i, num; + + if (core->dev->debugfs.sched_perf_disable) + return; + + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (test_bit(i, &bits)) { + core_ctx =3D core->core_ctx[i]; + if (!core_ctx) + continue; + /* if runtime is 0, use default 30fps (33msec) */ + runtime =3D core_ctx->avg_runtime ? core_ctx->avg_runtime : + MFC_DEFAULT_RUNTIME; + if (runtime > max_runtime) { + max_runtime =3D runtime; + num =3D i; + } + } + } + + if (max_runtime) + mfc_core_debug(2, "[PRIO][c:%d] max runtime is %d usec\n", num, max_runt= ime); + + core->max_runtime =3D max_runtime; +} + +static int __mfc_core_get_new_ctx_prio(struct mfc_core *core, int prio, in= t *highest, + bool predict) +{ + struct mfc_core_ctx *core_ctx; + unsigned long bits =3D 0; + int new_ctx_index; + int i, perf =3D 0, skip_curr; + + new_ctx_index =3D (core->last_core_ctx[prio] + 1) % MFC_NUM_CONTEXTS; + + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (test_bit(new_ctx_index, &core->prio_work_bits[prio])) { + if (*highest < 0) + *highest =3D new_ctx_index; + + /* If predict, do not get curr ctx */ + skip_curr =3D 0; + if (predict && new_ctx_index =3D=3D core->curr_core_ctx) { + mfc_core_debug(2, "[PRIO][PREDICT](%d) skip curr ctx %d\n", + prio, new_ctx_index); + skip_curr =3D 1; + } + + /* If the performance is insufficient, run this */ + core_ctx =3D core->core_ctx[new_ctx_index]; + perf =3D mfc_rate_check_perf_ctx(core_ctx->ctx, core->max_runtime); + if (!perf && !skip_curr) { + mfc_core_debug(2, "[PRIO]%s(%d) perf insufficient: %d\n", + (predict ? "[PREDICT]" : ""), + prio, new_ctx_index); + return new_ctx_index; + } + + /* If the perf of all instances is satisfied, run the highest */ + bits |=3D BIT(new_ctx_index); + if (((core->prio_work_bits[prio] & ~(bits)) =3D=3D 0) && + !__mfc_check_lower_prio_inst(core, prio)) { + mfc_core_debug(2, "[PRIO]%s(%d) the highest priority %d\n", + (predict ? "[PREDICT]" : ""), + prio, *highest); + return *highest; + } + } + /* If there is no ready context in prio, return */ + if ((core->prio_work_bits[prio] & ~(bits)) =3D=3D 0) + return -1; + + new_ctx_index =3D (new_ctx_index + 1) % MFC_NUM_CONTEXTS; + } + + return -1; +} + +/* This should be called with prio_work_lock */ +static int __mfc_core_get_new_ctx(struct mfc_core *core, bool predict) +{ + int new_ctx_index =3D 0, highest =3D -1; + int i; + + for (i =3D 0; i < core->total_num_prio; i++) { + if (!core->prio_work_bits[i]) + continue; + + mfc_core_debug(2, "[PRIO]%s(%d) Previous context: %d (bits %08lx)\n", + (predict ? "[PREDICT]" : ""), + i, core->last_core_ctx[i], core->prio_work_bits[i]); + + new_ctx_index =3D __mfc_core_get_new_ctx_prio(core, i, &highest, predict= ); + if (new_ctx_index >=3D 0) { + mfc_core_debug(2, "[PRIO]%s(%d) get new_ctx %d\n", + (predict ? "[PREDICT]" : ""), i, new_ctx_index); + return new_ctx_index; + } + } + + return -1; +} + +static int mfc_pick_next_work_prio(struct mfc_core *core) +{ + unsigned long flags, work_bits, hweight; + int new_ctx_index =3D -1; + + if (!mfc_is_work_prio(core)) { + mfc_core_debug(2, "[PRIO] No ctx to run\n"); + return -EAGAIN; + } + + if (core->preempt_core_ctx > MFC_NO_INSTANCE_SET) { + new_ctx_index =3D core->preempt_core_ctx; + mfc_core_debug(2, "[PRIO] preempt_core_ctx %d\n", new_ctx_index); + return new_ctx_index; + } + + spin_lock_irqsave(&core->prio_work_lock, flags); + + work_bits =3D __mfc_check_lower_prio_inst(core, -1); + mfc_core_debug(2, "[PRIO] all work_bits %#lx\n", work_bits); + + /* if single instance is ready, run it */ + hweight =3D hweight64(work_bits); + if (hweight =3D=3D 1) { + new_ctx_index =3D __ffs(work_bits); + mfc_core_debug(2, "[PRIO] new_ctx_idx %d (single)\n", new_ctx_index); + spin_unlock_irqrestore(&core->prio_work_lock, flags); + return new_ctx_index; + } + + __mfc_update_max_runtime(core, work_bits); + + /* if there is predicted next ctx, run it */ + if (core->next_ctx_idx >=3D 0 && test_bit(core->next_ctx_idx, &work_bits)= ) { + new_ctx_index =3D core->next_ctx_idx; + mfc_core_debug(2, "[PRIO] new_ctx_idx %d (predict)\n", new_ctx_index); + spin_unlock_irqrestore(&core->prio_work_lock, flags); + return new_ctx_index; + } + + new_ctx_index =3D __mfc_core_get_new_ctx(core, 0); + mfc_core_debug(2, "[PRIO] new_ctx_idx %d\n", new_ctx_index); + + spin_unlock_irqrestore(&core->prio_work_lock, flags); + + return new_ctx_index; +} + +static int mfc_get_next_work_prio(struct mfc_core *core) +{ + unsigned long flags, work_bits, hweight; + int new_ctx_index; + + if (core->dev->num_inst) + return -1; + + spin_lock_irqsave(&core->prio_work_lock, flags); + + work_bits =3D __mfc_check_lower_prio_inst(core, -1); + hweight =3D hweight64(work_bits); + /* Nothing to predict because only one instance is ready */ + if (hweight <=3D 1) { + spin_unlock_irqrestore(&core->prio_work_lock, flags); + return -1; + } + + new_ctx_index =3D __mfc_core_get_new_ctx(core, 1); + core->next_ctx_idx =3D new_ctx_index; + mfc_core_debug(2, "[PRIO][PREDICT] new_ctx_idx %d\n", new_ctx_index); + + spin_unlock_irqrestore(&core->prio_work_lock, flags); + + return new_ctx_index; +} + +static int mfc_change_prio_work_prio(struct mfc_core *core, struct mfc_ctx= *ctx, + int cur_rt, int cur_prio, int new_rt, int new_prio) +{ + unsigned long flags; + int cur_p, new_p; + + spin_lock_irqsave(&core->prio_work_lock, flags); + + if (new_prio > core->num_prio) + new_prio =3D core->num_prio; + + /* Check current priority's work_bit */ + cur_p =3D mfc_get_prio(core, cur_rt, cur_prio); + new_p =3D mfc_get_prio(core, new_rt, new_prio); + if (cur_p =3D=3D new_p) { + ctx->prio =3D new_prio; + ctx->rt =3D new_rt; + spin_unlock_irqrestore(&core->prio_work_lock, flags); + return 0; + } + + mfc_core_debug(2, "[PRIO] cur_p %d -> new_p %d is changed (%d %d -> %d %d= )\n", + cur_p, new_p, cur_rt, cur_prio, new_rt, new_prio); + + if (test_bit(ctx->num, &core->prio_work_bits[cur_p])) { + __clear_bit(ctx->num, &core->prio_work_bits[cur_p]); + mfc_core_debug(2, "[PRIO][c:%d] prio change from %d (bits %08lx)\n", + ctx->num, cur_p, core->prio_work_bits[cur_p]); + + /* Update current priority's work_bit */ + __set_bit(ctx->num, &core->prio_work_bits[new_p]); + mfc_core_debug(2, "[PRIO][c:%d] prio change to %d (bits %08lx)\n", + ctx->num, new_p, core->prio_work_bits[new_p]); + } + + /* These must be updated within the spin_lock for synchronization. */ + ctx->prio =3D new_prio; + ctx->rt =3D new_rt; + + spin_unlock_irqrestore(&core->prio_work_lock, flags); + + return 0; +} + struct mfc_sched_class mfc_sched_prio =3D { .create_work =3D mfc_create_work_prio, .init_work =3D mfc_init_work_prio, .clear_all_work =3D mfc_clear_all_work_prio, + .queue_work =3D mfc_queue_work_prio, + .is_work =3D mfc_is_work_prio, + .pick_next_work =3D mfc_pick_next_work_prio, + .get_next_work =3D mfc_get_next_work_prio, .set_work =3D mfc_set_work_prio, .clear_work =3D mfc_clear_work_prio, + .enqueue_work =3D mfc_enqueue_work_prio, + .dequeue_work =3D mfc_dequeue_work_prio, + .yield_work =3D mfc_yield_work_prio, + .change_prio_work =3D mfc_change_prio_work_prio, }; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c index dd59dc352e34..8eb68294c0b1 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.c @@ -1,14 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * mfc_dec_v4l2.c - * - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2025 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * mfc_dec_v4l2.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, */ =20 #include diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h index 04652a71cd23..08fe4ed5cf3f 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_v4l2.h @@ -1,14 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * - * mfc_dec_v4l2.h - * - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2025 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * mfc_dec_v4l2.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, */ =20 #ifndef __MFC_DEC_V4L2_H --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 35E13276054 for ; Tue, 30 Sep 2025 03:56:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204617; cv=none; b=tTJtbpnfWOxLeCyEv2xZ6YRpzhB9D3Ca2AWGZJVqVVGeV8xv6k8Bi+SRTW0Qrk93yx1mRbiBpCuVX624S9wsH89nDE6qElMCtRZQ/4kAiMTw5SS0yjkP/+rUVOM5nrnlECqowpxU1Opa2RrkG7Gis89bXrF+40ksrJiRG8yoaCA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204617; c=relaxed/simple; bh=ssi58wFizYLrf+Clx/DKjZfMCuY+3FCUjkgp7Q/Bc/I=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=D/ewa8HVz8SOj7sQa0RqLrqV2ilAfa1Kyg2P2r0/45ChtiU7lt9MXajtVNzIcLtkQ4+knVhWESRp52JEEU+lmDIEFy7eEZ/769jGthky6QWzSe70+QlQdxcoL2EATDJKF4J18L+LkKCJo+DvLKo1vKIhnRevhfus3iz1oBxrjas= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=UOOalDgj; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="UOOalDgj" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035650epoutp04ba768084a58f729c21e10f63b6d58c4d~p80XT0SkO2083120831epoutp04N for ; Tue, 30 Sep 2025 03:56:50 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035650epoutp04ba768084a58f729c21e10f63b6d58c4d~p80XT0SkO2083120831epoutp04N DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204610; bh=PWHRpdL9AtPN9y3tXCaVxM38u1RndAKBuoWEkNFK/oc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UOOalDgjA4NzX8ypkhdzLUx1d7m1zfTZzn8DWf269A+uubY4b1ql5Hubym0BrgnTP /aktyXW+26/wyOdBRLIiDiq4+kbB5u81NK9aYxutsg9raXxuYY9UuSf0Yyqa//URz3 wbTyHNfBuM+KOjU+jhtUNImUnyXXMLw0XeO+srMk= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035650epcas5p125de74b023d444c22072134632caf967~p80W1xB3p2306123061epcas5p1v; Tue, 30 Sep 2025 03:56:50 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.90]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPPn6TGSz2SSKb; Tue, 30 Sep 2025 03:56:45 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035644epcas5p178155478c949c231c14a131b00f23bc1~p80RF_Nf51532615326epcas5p1a; Tue, 30 Sep 2025 03:56:44 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035641epsmtip1d3f95c15de1c35791c17d5fb7cab0928~p80OinTSo2880128801epsmtip1l; Tue, 30 Sep 2025 03:56:41 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 20/29] media: mfc: Add H264 decoder support Date: Tue, 30 Sep 2025 09:33:39 +0530 Message-Id: <20250930040348.3702923-21-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035644epcas5p178155478c949c231c14a131b00f23bc1 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035644epcas5p178155478c949c231c14a131b00f23bc1 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Extend device=E2=80=91tree with properties for secure firmware, DRM, codec capabilities, format/stride, resolution checks, and security controls. - Introduce per=E2=80=91codec and device=E2=80=91wide bitrate limits. - Implement runtime management: scheduler workers, dedicated workqueues, and required mutex/spinlock synchronization. - Refine decoder=E2=80=91context handling (full init/cleanup, CRC dump, DPB handling, vb2 queue setup, QoS parsing). - Add polling with error=E2=80=91state handling. - Expand DebugFS to expose core=E2=80=91balance, feature flags, format supp= ort, QoS data, and queue metrics. - Update macros, include missing headers, improve logging, adjust scheduler/PBS configuration, and revise buffer=E2=80=91size structures. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../dts/exynos/exynosautov920-evt2-mfc.dtsi | 443 ++++++++++++++++++ .../media/platform/samsung/exynos-mfc/mfc.c | 420 ++++++++++++++++- .../platform/samsung/exynos-mfc/mfc_debugfs.c | 60 ++- 3 files changed, 915 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi b/arch= /arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi index 49c61958467e..40cac2b20804 100644 --- a/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi +++ b/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi @@ -67,25 +67,142 @@ mfc: mfc@19cd0000 { /* MFC version */ ip_ver =3D <0x1600010C>; =20 + /* Secure Firmware Loading */ + secure_fw_loading =3D <1>; + /* MFC firmware name */ fw_name =3D "mfc_fw_v16.0.bin"; =20 /* Debug mode */ debug_mode =3D <1>; =20 + max_num_drm_inst =3D <3>; + + /* NAL-Q size */ + nal_q_entry_size =3D <768>; + nal_q_dump_size =3D <376>; + /* Features */ + skype =3D <1 0x0>; + black_bar =3D <1 0x0>; + color_aspect_dec =3D <1 0x0>; + static_info_dec =3D <1 0x0>; + color_aspect_enc =3D <1 0x0>; + static_info_enc =3D <1 0x180314>; + vp9_stride_align =3D <1 0x0>; mem_clear =3D <1 0x0>; /* Support from v11.0 (except 11.2) */ wait_fw_status =3D <1 0x190122>; + /* DRM switch predict for cache flush */ + drm_switch_predict =3D <1 0x0>; + /* Use metadata interface */ + metadata_interface =3D <1 0x200910>; + /* Average QP of encoder per frame */ + average_qp =3D <1 0x201030>; + /* MV search mode */ + mv_search_mode =3D <1 0x201118>; + /* Encoder IDR flag */ + enc_idr_flag =3D <1 0x210611>; + /* Encoder min quality mode */ + min_quality_mode =3D <1 0x210705>; + /* Encoder time stamp delta */ + enc_ts_delta =3D <1 0x211027>; + /* Encoder I limit RC mode for WFD */ + wfd_rc_mode =3D <1 0x211229>; =20 /* Scheduler 0: round-robin, 1: PBS */ scheduler =3D <1>; /* The number of priority in PBS */ pbs_num_prio =3D <1>; =20 + /* Support AV1 Film Grain Feature */ + av1_film_grain =3D <1 0x200717>; + + /* Decoder stride align (default: 16, AMD GPU: 256) */ + stride_align =3D <256>; + /* Decoder stride calculation type (new: 1, old: 0) */ + stride_type =3D <1>; + + /* Formats */ + support_10bit =3D <1>; + support_422 =3D <0>; + support_rgb =3D <1>; + + /* Resolution check (0: do not check, 1: FHD, 2: 4K, 3: 8K) */ + support_check_res =3D <3>; + + /* error type for sync_point display */ + /* (1: concealment display, 2: error display, 3: error no display) */ + display_err_type =3D <2>; + + /* FW base security ctrl */ + security_ctrl =3D <1>; + /* output buffer Q framerate */ + display_framerate =3D <0>; + + /* Encoder default parameter: max number is 100 */ + enc_param_num =3D <25>; + enc_param_addr =3D <0xF7B4 0xF7B8 0xF7B0 0xF798 0xFA2C + 0xF790 0xFA34 0xFA38 0xFA3C 0xF7C0 + 0xF7C8 0xF7CC 0xFA60 0xFDD4 0xFDDC + 0xFB54 0xFB58 0xFBA8 0xFD90 0xFD94 + 0xFD40 0xFD48 0xFD4C 0xFD50 0xFD80>; + enc_param_val =3D <0x80 0x80 0x0 0x4000 0x3FD00 + 0x0 0x0 0x2710 0x3E8 0x0 + 0x0 0x0 0x0 0x8050D211 0x0 + 0x3011 0x0 0x0 0x2D 0xA00 + 0x1D 0xF4240 0x33003300 0x2 0x1>; + + /* BW : KB/UHD frame */ + bw_enc_h264 =3D <45456 56112 11170>; + bw_enc_hevc =3D <46756 52766 9763>; + bw_enc_hevc_10bit =3D <53865 64753 12556>; + bw_enc_vp8 =3D <64000 67318 22518>; + bw_enc_vp9 =3D <72326 59726 16530>; + bw_enc_vp9_10bit =3D <149085 114928 31419>; + bw_enc_mpeg4 =3D <44647 55324 9531>; + bw_dec_h264 =3D <32605 34381 21263>; + bw_dec_hevc =3D <29973 28851 17538>; + bw_dec_hevc_10bit =3D <52859 46245 31351>; + bw_dec_vp8 =3D <28672 30468 22324>; + bw_dec_vp9 =3D <18351 18947 16877>; + bw_dec_vp9_10bit =3D <42384 34452 31766>; + bw_dec_av1 =3D <23787 19570 15856>; + bw_dec_av1_10bit =3D <41407 35490 29699>; + bw_dec_mpeg4 =3D <31540 25368 15770>; + + /* QoS bitrate */ + num_mfc_freq =3D <7>; + mfc_freqs =3D <160000 267000 332000 400000 533000 664000 800000>; + + /* QoS weight (%) */ + dynamic_weight =3D <1>; + qos_weight_h264_hevc =3D <100>; + qos_weight_vp8_vp9 =3D <100>; + qos_weight_av1 =3D <70>; + qos_weight_other_codec =3D <25>; + qos_weight_3plane =3D <80>; + qos_weight_10bit =3D <75>; + qos_weight_422 =3D <70>; + qos_weight_bframe =3D <50>; + qos_weight_num_of_ref =3D <60>; + qos_weight_gpb =3D <50>; + qos_weight_num_of_tile =3D <75>; + qos_weight_super64_bframe =3D <60>; + qos_weight_mbaff =3D <60>; + + /* core balance(%) for resource managing */ + core_balance =3D <54>; + /* MFC IOVA threshold (MB) */ iova_threshold =3D <1700>; =20 + /* need control for mfc idle clock */ + idle_clk_ctrl =3D <1>; + + /* QoS level for pm_qos dynamic control */ + qos_ctrl_level =3D <1>; + /* Sub nodes for MFC core */ #address-cells =3D <2>; #size-cells =3D <2>; @@ -135,6 +252,15 @@ mfc_core0: MFC-0@19cd0000 { axid_mask =3D <0xFFFF>; tsmux_axid =3D <0x1>; =20 + /* QoS */ + num_default_qos_steps =3D <10>; + num_encoder_qos_steps =3D <7>; + max_mb =3D <5093269>; + max_hw_mb =3D <3888000>; + mfc_freq_control =3D <1>; + mo_control =3D <1>; + bw_control =3D <1>; + /* mem-log buffer size */ memlog_size =3D <0x80000>; memlog_sfr_size =3D <0x1000>; @@ -148,6 +274,160 @@ mfc_core0: MFC-0@19cd0000 { iommu@19c70000 { reg =3D <0x0 0x19C70000 0x0 0x9000>; }; + + /* Default QoS table */ + mfc_default_qos_table { + mfc_d_qos_variant_0 { + thrd_mb =3D <0>; + freq_mfc =3D <133000>; + freq_int =3D <133000>; + freq_mif =3D <421000>; + bts_scen =3D "default"; + time_fw =3D <1238>; + }; + mfc_d_qos_variant_1 { + thrd_mb =3D <546599>; + freq_mfc =3D <267000>; + freq_int =3D <133000>; + freq_mif =3D <546000>; + bts_scen =3D "default"; + time_fw =3D <842>; + }; + mfc_d_qos_variant_2 { + thrd_mb =3D <1167159>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <676000>; + bts_scen =3D "default"; + time_fw =3D <667>; + }; + mfc_d_qos_variant_3 { + thrd_mb =3D <1859381>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <845000>; + bts_scen =3D "default"; + time_fw =3D <578>; + }; + mfc_d_qos_variant_4 { + thrd_mb =3D <2078433>; + freq_mfc =3D <468000>; + freq_int =3D <200000>; + freq_mif =3D <1014000>; + bts_scen =3D "default"; + time_fw =3D <483>; + }; + mfc_d_qos_variant_5 { + thrd_mb =3D <2563082>; + freq_mfc =3D <533000>; + freq_int =3D <332000>; + freq_mif =3D <1352000>; + bts_scen =3D "default"; + time_fw =3D <362>; + }; + mfc_d_qos_variant_6 { + thrd_mb =3D <3173773>; + freq_mfc =3D <663000>; + freq_int =3D <400000>; + freq_mif =3D <1352000>; + bts_scen =3D "default"; + time_fw =3D <306>; + }; + mfc_d_qos_variant_7 { + thrd_mb =3D <3716529>; + freq_mfc =3D <745000>; + freq_int =3D <533000>; + freq_mif =3D <1539000>; + bts_scen =3D "default"; + time_fw =3D <267>; + }; + mfc_d_qos_variant_8 { + thrd_mb =3D <4282126>; + freq_mfc =3D <745000>; + freq_int =3D <663000>; + freq_mif =3D <2028000>; + bts_scen =3D "default"; + time_fw =3D <235>; + }; + mfc_d_qos_variant_9 { + thrd_mb =3D <4855818>; + freq_mfc =3D <800000>; + freq_int =3D <800000>; + freq_mif =3D <3172000>; + bts_scen =3D "mfc_uhd_10bit"; + time_fw =3D <175>; + }; + }; + + /* Encoder only QoS table */ + mfc_encoder_qos_table { + mfc_e_qos_variant_0 { + thrd_mb =3D <0>; + freq_mfc =3D <133000>; + freq_int =3D <133000>; + freq_mif =3D <421000>; + bts_scen =3D "default"; + time_fw =3D <1238>; + }; + mfc_e_qos_variant_1 { + thrd_mb =3D <546599>; + freq_mfc =3D <267000>; + freq_int =3D <133000>; + freq_mif =3D <546000>; + bts_scen =3D "default"; + time_fw =3D <842>; + }; + mfc_e_qos_variant_2 { + thrd_mb =3D <1167159>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <676000>; + bts_scen =3D "default"; + time_fw =3D <667>; + }; + mfc_e_qos_variant_3 { + thrd_mb =3D <1859381>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <845000>; + bts_scen =3D "default"; + time_fw =3D <578>; + }; + mfc_e_qos_variant_4 { + thrd_mb =3D <2078433>; + freq_mfc =3D <533000>; + freq_int =3D <400000>; + freq_mif =3D <845000>; + bts_scen =3D "default"; + time_fw =3D <471>; + }; + mfc_e_qos_variant_5 { + thrd_mb =3D <2553457>; + freq_mfc =3D <468000>; + freq_int =3D <332000>; + freq_mif =3D <1539000>; + bts_scen =3D "mfc_uhd"; + time_fw =3D <359>; + }; + mfc_e_qos_variant_6 { + thrd_mb =3D <3175870>; + freq_mfc =3D <663000>; + freq_int =3D <663000>; + freq_mif =3D <1716000>; + bts_scen =3D "default"; + time_fw =3D <287>; + }; + }; + + /* QoS table for performance boost mode */ + mfc_perf_boost_table { + num_cluster =3D <3>; + freq_cluster =3D <1742000 1898000 1456000>; + freq_mfc =3D <666000>; + freq_int =3D <534000>; + freq_mif =3D <2730000>; + bts_scen =3D "mfc_uhd_10bit"; + }; }; =20 mfc_core1: MFC-1@19ed0000 { @@ -173,6 +453,15 @@ mfc_core1: MFC-1@19ed0000 { share_sysmmu =3D <0>; axid_mask =3D <0xFFFF>; =20 + /* QoS */ + num_default_qos_steps =3D <10>; + num_encoder_qos_steps =3D <7>; + max_mb =3D <5315425>; + max_hw_mb =3D <3888000>; + mfc_freq_control =3D <1>; + mo_control =3D <1>; + bw_control =3D <1>; + /* Device virtual address */ #dma-address-cells =3D <1>; #dma-size-cells =3D <1>; @@ -182,6 +471,160 @@ mfc_core1: MFC-1@19ed0000 { iommu@19e70000 { reg =3D <0x0 0x19E70000 0x0 0x9000>; }; + + /* Default QoS table */ + mfc_default_qos_table { + mfc_d_qos_variant_0 { + thrd_mb =3D <0>; + freq_mfc =3D <133000>; + freq_int =3D <133000>; + freq_mif =3D <421000>; + bts_scen =3D "default"; + time_fw =3D <1238>; + }; + mfc_d_qos_variant_1 { + thrd_mb =3D <546599>; + freq_mfc =3D <234000>; + freq_int =3D <133000>; + freq_mif =3D <546000>; + bts_scen =3D "default"; + time_fw =3D <842>; + }; + mfc_d_qos_variant_2 { + thrd_mb =3D <1167159>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <676000>; + bts_scen =3D "default"; + time_fw =3D <667>; + }; + mfc_d_qos_variant_3 { + thrd_mb =3D <1859381>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <845000>; + bts_scen =3D "default"; + time_fw =3D <578>; + }; + mfc_d_qos_variant_4 { + thrd_mb =3D <2078433>; + freq_mfc =3D <468000>; + freq_int =3D <200000>; + freq_mif =3D <1014000>; + bts_scen =3D "default"; + time_fw =3D <483>; + }; + mfc_d_qos_variant_5 { + thrd_mb =3D <2563082>; + freq_mfc =3D <533000>; + freq_int =3D <332000>; + freq_mif =3D <1352000>; + bts_scen =3D "default"; + time_fw =3D <362>; + }; + mfc_d_qos_variant_6 { + thrd_mb =3D <3173773>; + freq_mfc =3D <663000>; + freq_int =3D <400000>; + freq_mif =3D <1352000>; + bts_scen =3D "default"; + time_fw =3D <306>; + }; + mfc_d_qos_variant_7 { + thrd_mb =3D <3716529>; + freq_mfc =3D <745000>; + freq_int =3D <533000>; + freq_mif =3D <1539000>; + bts_scen =3D "default"; + time_fw =3D <267>; + }; + mfc_d_qos_variant_8 { + thrd_mb =3D <4282126>; + freq_mfc =3D <745000>; + freq_int =3D <663000>; + freq_mif =3D <2028000>; + bts_scen =3D "default"; + time_fw =3D <235>; + }; + mfc_d_qos_variant_9 { + thrd_mb =3D <4855818>; + freq_mfc =3D <800000>; + freq_int =3D <800000>; + freq_mif =3D <3172000>; + bts_scen =3D "mfc_8k_dec30"; + time_fw =3D <175>; + }; + }; + + /* Encoder only QoS table */ + mfc_encoder_qos_table { + mfc_e_qos_variant_0 { + thrd_mb =3D <0>; + freq_mfc =3D <133000>; + freq_int =3D <133000>; + freq_mif =3D <421000>; + bts_scen =3D "default"; + time_fw =3D <1238>; + }; + mfc_e_qos_variant_1 { + thrd_mb =3D <546599>; + freq_mfc =3D <267000>; + freq_int =3D <133000>; + freq_mif =3D <546000>; + bts_scen =3D "default"; + time_fw =3D <842>; + }; + mfc_e_qos_variant_2 { + thrd_mb =3D <1167159>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <676000>; + bts_scen =3D "default"; + time_fw =3D <667>; + }; + mfc_e_qos_variant_3 { + thrd_mb =3D <1859381>; + freq_mfc =3D <332000>; + freq_int =3D <200000>; + freq_mif =3D <845000>; + bts_scen =3D "default"; + time_fw =3D <578>; + }; + mfc_e_qos_variant_4 { + thrd_mb =3D <2078433>; + freq_mfc =3D <533000>; + freq_int =3D <400000>; + freq_mif =3D <845000>; + bts_scen =3D "default"; + time_fw =3D <471>; + }; + mfc_e_qos_variant_5 { + thrd_mb =3D <2553457>; + freq_mfc =3D <468000>; + freq_int =3D <332000>; + freq_mif =3D <1539000>; + bts_scen =3D "mfc_uhd"; + time_fw =3D <359>; + }; + mfc_e_qos_variant_6 { + thrd_mb =3D <3175870>; + freq_mfc =3D <663000>; + freq_int =3D <663000>; + freq_mif =3D <1716000>; + bts_scen =3D "default"; + time_fw =3D <287>; + }; + }; + + /* QoS table for performance boost mode */ + mfc_perf_boost_table { + num_cluster =3D <3>; + freq_cluster =3D <1742000 1898000 1456000>; + freq_mfc =3D <666000>; + freq_int =3D <534000>; + freq_mif =3D <2730000>; + bts_scen =3D "mfc_uhd_10bit"; + }; }; }; }; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc.c b/drivers/medi= a/platform/samsung/exynos-mfc/mfc.c index b5b66083cc8b..fb9a7317e812 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc.c @@ -21,6 +21,8 @@ #include #include =20 +#include "mfc_dec_v4l2.h" +#include "mfc_dec_vb2.h" #include "mfc_rm.h" #include "mfc_debugfs.h" =20 @@ -29,6 +31,9 @@ =20 #include "mfc_core_hw_reg_api.h" =20 +#include "base/mfc_qos.h" + +#include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_buf.h" #include "base/mfc_mem.h" @@ -41,6 +46,188 @@ struct _mfc_trace g_mfc_trace[MFC_TRACE_COUNT_MAX]; struct _mfc_trace g_mfc_trace_rm[MFC_TRACE_COUNT_MAX]; struct _mfc_trace g_mfc_trace_longterm[MFC_TRACE_COUNT_MAX]; =20 +static void mfc_butler_worker(struct work_struct *work) +{ + struct mfc_dev *dev; + struct mfc_ctx *ctx; + int i; + + dev =3D container_of(work, struct mfc_dev, butler_work); + + /* If there is multi core instance, it has high priority */ + if (dev->multi_core_inst_bits) { + bool is_switch_to_single; + bool is_multi_mode; + + i =3D __ffs(dev->multi_core_inst_bits); + ctx =3D dev->ctx[i]; + if (!ctx) { + mfc_dev_err("[RM] There is no ctx\n"); + return; + } + + /* [DRC] In the case of MFC_OP_SWITCH_TO_SINGLE, + * also need to request with MFC_WORK_TRY. + * Because op_mode is maintained as MFC_OP_SWITCH_TO_SINGLE before subco= re_deinit. + * And, subcore_deinit can be started by MFC_WORK_TRY. + */ + is_multi_mode =3D IS_MULTI_MODE(ctx); + is_switch_to_single =3D + (ctx->op_mode =3D=3D MFC_OP_SWITCH_TO_SINGLE && ctx->handle_drc_multi_m= ode); + if (!is_multi_mode && !is_switch_to_single) + return; + + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + } else { + mfc_rm_request_work(dev, MFC_WORK_BUTLER, NULL); + } +} + +static void __mfc_deinit_dec_ctx(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec =3D ctx->dec_priv; + unsigned int size; + + if (dec->crc && (ctx->dev->debugfs.sfr_dump & MFC_DUMP_DEC_CRC)) { + if (dec->crc_idx * 4 > SZ_1K) + size =3D SZ_1K; + else + size =3D dec->crc_idx * 4; + print_hex_dump + (KERN_ERR, "CRC: ", DUMP_PREFIX_OFFSET, 32, 1, + dec->crc, size, false); + vfree(dec->crc); + } + + mfc_cleanup_iovmm(ctx); + + mfc_delete_queue(&ctx->src_buf_ready_queue); + mfc_delete_queue(&ctx->dst_buf_queue); + mfc_delete_queue(&ctx->err_buf_queue); + mfc_delete_queue(&ctx->meminfo_inbuf_q); + + mfc_mem_cleanup_user_shared_handle(ctx, &dec->sh_handle_dpb); + if (dec->ref_info) + vfree(dec->ref_info); + + kfree(dec); +} + +static int __mfc_init_dec_ctx(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec; + int ret =3D 0; + int i; + + dec =3D kzalloc(sizeof(*dec), GFP_KERNEL); + if (!dec) + return -ENOMEM; + + ctx->dec_priv =3D dec; + + ctx->subcore_inst_no =3D MFC_NO_INSTANCE_SET; + ctx->curr_src_index =3D -1; + ctx->user_prio =3D -1; + + mfc_create_queue(&ctx->src_buf_ready_queue); + mfc_create_queue(&ctx->dst_buf_queue); + mfc_create_queue(&ctx->err_buf_queue); + mfc_create_queue(&ctx->meminfo_inbuf_q); + + for (i =3D 0; i < MFC_MAX_BUFFERS; i++) { + INIT_LIST_HEAD(&ctx->src_ctrls[i]); + INIT_LIST_HEAD(&ctx->dst_ctrls[i]); + } + bitmap_zero(ctx->src_ctrls_avail, MFC_MAX_BUFFERS); + bitmap_zero(ctx->dst_ctrls_avail, MFC_MAX_BUFFERS); + + ctx->capture_state =3D QUEUE_FREE; + ctx->output_state =3D QUEUE_FREE; + + ctx->type =3D MFCINST_DECODER; + ctx->c_ops =3D &mfc_ctrls_ops; + ctx->b_ops =3D &mfc_bufs_ops; + + mfc_dec_set_default_format(ctx); + mfc_rate_reset_framerate(ctx); + + ctx->qos_ratio =3D 100; + INIT_LIST_HEAD(&ctx->bitrate_list); + INIT_LIST_HEAD(&ctx->src_ts.ts_list); + + dec->display_delay =3D -1; + dec->is_interlaced =3D 0; + dec->immediate_display =3D 0; + dec->is_dts_mode =3D 0; + dec->inter_res_change =3D 0; + dec->disp_drc.disp_res_change =3D 0; + dec->disp_drc.push_idx =3D 0; + dec->disp_drc.pop_idx =3D 0; + + dec->is_dynamic_dpb =3D 1; + dec->dynamic_used =3D 0; + dec->is_dpb_full =3D 0; + dec->queued_dpb =3D 0; + dec->display_index =3D -1; + dec->dpb_table_used =3D 0; + dec->sh_handle_dpb.fd =3D -1; + mutex_init(&dec->dpb_mutex); + + mfc_init_dpb_table(ctx); + + dec->sh_handle_dpb.data_size =3D sizeof(struct dec_dpb_ref_info) * MFC_MA= X_BUFFERS; + dec->ref_info =3D vmalloc(dec->sh_handle_dpb.data_size); + if (!dec->ref_info) { + mfc_ctx_err("failed to allocate decoder information data\n"); + ret =3D -ENOMEM; + goto fail_dec_init; + } + for (i =3D 0; i < MFC_MAX_BUFFERS; i++) + dec->ref_info[i].dpb[0].fd[0] =3D MFC_INFO_INIT_FD; + + if (ctx->dev->debugfs.sfr_dump & MFC_DUMP_DEC_CRC) { + dec->crc =3D vmalloc(SZ_1K); + if (!dec->crc) { + ret =3D -ENOMEM; + goto fail_dec_init; + } + dec->crc_idx =3D 0; + } + + /* Init videobuf2 queue for OUTPUT */ + ctx->vq_src.type =3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + ctx->vq_src.drv_priv =3D ctx; + ctx->vq_src.buf_struct_size =3D (unsigned int)sizeof(struct mfc_buf); + ctx->vq_src.io_modes =3D VB2_USERPTR | VB2_DMABUF; + ctx->vq_src.ops =3D mfc_get_dec_vb2_ops(); + ctx->vq_src.mem_ops =3D mfc_mem_ops(); + ctx->vq_src.timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret =3D vb2_queue_init(&ctx->vq_src); + if (ret) { + mfc_ctx_err("Failed to initialize videobuf2 queue(output)\n"); + goto fail_dec_init; + } + /* Init videobuf2 queue for CAPTURE */ + ctx->vq_dst.type =3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + ctx->vq_dst.drv_priv =3D ctx; + ctx->vq_dst.buf_struct_size =3D (unsigned int)sizeof(struct mfc_buf); + ctx->vq_dst.io_modes =3D VB2_USERPTR | VB2_DMABUF; + ctx->vq_dst.ops =3D mfc_get_dec_vb2_ops(); + ctx->vq_dst.mem_ops =3D mfc_mem_ops(); + ctx->vq_dst.timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret =3D vb2_queue_init(&ctx->vq_dst); + if (ret) { + mfc_ctx_err("Failed to initialize videobuf2 queue(capture)\n"); + goto fail_dec_init; + } + + return ret; + +fail_dec_init: + __mfc_deinit_dec_ctx(ctx); + return ret; +} + /* Open an MFC node */ static int mfc_open(struct file *file) { @@ -49,6 +236,7 @@ static int mfc_open(struct file *file) int i, ret =3D 0; enum mfc_node_type node; struct video_device *vdev =3D NULL; + unsigned long total_mb =3D 0, max_hw_mb =3D 0; =20 if (!dev) { mfc_pr_err("no mfc device to run\n"); @@ -60,6 +248,26 @@ static int mfc_open(struct file *file) if (mutex_lock_interruptible(&dev->mfc_mutex)) return -ERESTARTSYS; =20 + /* Check mfc_open() of spec over */ + for (i =3D 0; i < dev->num_core; i++) + max_hw_mb +=3D dev->core[i]->core_pdata->max_hw_mb; + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (!dev->ctx[i]) + continue; + total_mb +=3D dev->ctx[i]->weighted_mb; + mfc_show_ctx_info(dev->ctx[i]); + } + if (total_mb >=3D max_hw_mb) { + mfc_dev_info + ("[RM] now MFC work with full spec(mb: %lu / %lu)\n", + total_mb, max_hw_mb); + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + if (!dev->ctx[i]) + continue; + mfc_print_ctx_info(dev->ctx[i]); + } + } + node =3D mfc_get_node_type(file); if (node =3D=3D MFCNODE_INVALID) { mfc_dev_err("cannot specify node type\n"); @@ -109,20 +317,32 @@ static int mfc_open(struct file *file) } } =20 + spin_lock_init(&ctx->buf_queue_lock); + spin_lock_init(&ctx->meminfo_queue_lock); spin_lock_init(&ctx->corelock.lock); + spin_lock_init(&ctx->src_ts.ts_lock); + spin_lock_init(&ctx->dst_q_ts.ts_lock); + spin_lock_init(&ctx->dst_dq_ts.ts_lock); + spin_lock_init(&ctx->src_q_ts.ts_lock); mutex_init(&ctx->intlock.core_mutex); mutex_init(&ctx->op_mode_mutex); + mutex_init(&ctx->drc_wait_mutex); init_waitqueue_head(&ctx->corelock.wq); + init_waitqueue_head(&ctx->corelock.migrate_wq); + INIT_LIST_HEAD(&ctx->dst_q_ts.ts_list); + INIT_LIST_HEAD(&ctx->dst_dq_ts.ts_list); + INIT_LIST_HEAD(&ctx->src_q_ts.ts_list); =20 mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE); =20 if (mfc_is_decoder_node(node)) { - ctx->type =3D MFCINST_DECODER; + ret =3D __mfc_init_dec_ctx(ctx); dev->num_dec_inst++; } else { - ctx->type =3D MFCINST_ENCODER; dev->num_enc_inst++; } + if (ret) + goto err_ctx_init; =20 if (dev->num_inst =3D=3D 1) { /* all of the ctx list */ @@ -131,6 +351,12 @@ static int mfc_open(struct file *file) /* idle mode */ spin_lock_init(&dev->idle_bits_lock); } + if (mfc_is_decoder_node(node)) + ret =3D call_cop(ctx, init_ctx_ctrls, ctx); + if (ret) { + mfc_ctx_err("failed in init_ctx_ctrls\n"); + goto err_ctx_init; + } =20 mfc_ctx_info ("NORMAL %s instance is opened [%d]\n", @@ -155,11 +381,16 @@ static int mfc_open(struct file *file) ("[INFO] %s opened (ctx:%d, total:%d)\n", mfc_is_decoder_node(node) ? "DEC" : "ENC", ctx->num, dev->num_inst); =20 + queue_work(dev->butler_wq, &dev->butler_work); + mutex_unlock(&dev->mfc_mutex); return ret; =20 /* Deinit when failure occurred */ err_inst_init: + call_cop(ctx, cleanup_ctx_ctrls, ctx); + +err_ctx_init: if (mfc_is_decoder_node(node)) dev->num_dec_inst--; else @@ -192,10 +423,13 @@ static int mfc_release(struct file *file) { struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); struct mfc_dev *dev =3D ctx->dev; + struct mfc_ctx *move_ctx; int ret =3D 0; + int i; unsigned long flags; =20 mutex_lock(&dev->mfc_mutex); + mutex_lock(&dev->mfc_migrate_mutex); =20 mfc_ctx_info ("%s instance release is called [%d]\n", @@ -215,6 +449,12 @@ static int mfc_release(struct file *file) * So, we need to performed stop_streaming * before instance de-init(CLOSE_INSTANCE). */ + if (ctx->type =3D=3D MFCINST_DECODER) { + vb2_queue_release(&ctx->vq_src); + vb2_queue_release(&ctx->vq_dst); + + call_cop(ctx, cleanup_ctx_ctrls, ctx); + } =20 ret =3D mfc_rm_instance_deinit(dev, ctx); if (ret) { @@ -224,13 +464,27 @@ static int mfc_release(struct file *file) =20 dev->num_inst--; =20 - if (ctx->type =3D=3D MFCINST_DECODER) + if (IS_MULTI_CORE_DEVICE(dev)) + mfc_rm_load_balancing(ctx, MFC_RM_LOAD_DELETE_UPDATE); + + if (ctx->type =3D=3D MFCINST_DECODER) { + __mfc_deinit_dec_ctx(ctx); dev->num_dec_inst--; - else if (ctx->type =3D=3D MFCINST_ENCODER) + } else if (ctx->type =3D=3D MFCINST_ENCODER) { dev->num_enc_inst--; + } =20 MFC_TRACE_CTX_LT("[INFO] Release finished (ctx:%d, total:%d)\n", ctx->num= , dev->num_inst); =20 + /* If ctx is move_ctx in migration worker, clear move_ctx */ + for (i =3D 0; i < dev->move_ctx_cnt; i++) { + move_ctx =3D dev->move_ctx[i]; + if (move_ctx && move_ctx->num =3D=3D ctx->num) { + dev->move_ctx[i] =3D NULL; + break; + } + } + spin_lock_irqsave(&dev->ctx_list_lock, flags); dev->ctx[ctx->num] =3D NULL; kfree(ctx); @@ -238,17 +492,49 @@ static int mfc_release(struct file *file) =20 mfc_dev_info("mfc driver release finished [%d]\n", dev->num_inst); =20 + queue_work(dev->butler_wq, &dev->butler_work); + end_release: + mutex_unlock(&dev->mfc_migrate_mutex); mutex_unlock(&dev->mfc_mutex); return ret; } =20 +/* Poll */ +static __poll_t mfc_poll(struct file *file, struct poll_table_struct *wait) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + unsigned long req_events =3D poll_requested_events(wait); + __poll_t ret =3D 0; + + mfc_ctx_debug_enter(); + + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_ERROR)) { + if (req_events & (POLLOUT | POLLWRNORM)) + mfc_ctx_err("SRC: Call on POLL after unrecoverable error\n"); + else + mfc_ctx_err("DST: Call on POLL after unrecoverable error\n"); + return EPOLLERR; + } + + if (req_events & (POLLOUT | POLLWRNORM)) { + mfc_ctx_debug(2, "wait source buffer\n"); + ret =3D vb2_poll(&ctx->vq_src, file, wait); + } else if (req_events & (POLLIN | POLLRDNORM)) { + mfc_ctx_debug(2, "wait destination buffer\n"); + ret =3D vb2_poll(&ctx->vq_dst, file, wait); + } + + mfc_ctx_debug_leave(); + return ret; +} + /* v4l2 ops */ static const struct v4l2_file_operations mfc_fops =3D { .owner =3D THIS_MODULE, .open =3D mfc_open, .release =3D mfc_release, - .poll =3D NULL, + .poll =3D mfc_poll, .unlocked_ioctl =3D video_ioctl2, }; =20 @@ -278,6 +564,9 @@ static void __mfc_parse_dt_resource(struct mfc_dev *dev= , struct device_node *np) of_property_read_u32_index(np_tmp, "info", idx++, &codec_mode); resource =3D &dev->pdata->mfc_resource[codec_mode]; of_property_read_u32_index(np_tmp, "info", idx++, (u32 *)&resource->op_c= ore_type); + of_property_read_u32_index(np_tmp, "info", idx++, &resource->max_kbps); + if (resource->max_kbps > dev->max_kbps) + dev->max_kbps =3D resource->max_kbps; } } =20 @@ -309,17 +598,104 @@ static int __mfc_parse_dt(struct device_node *np, st= ruct mfc_dev *mfc) /* Resource of standard */ __mfc_parse_dt_resource(mfc, np); =20 + /* Features */ + of_property_read_u32_array(np, "skype", &pdata->skype.support, 2); + of_property_read_u32_array + (np, "black_bar", + &pdata->black_bar.support, 2); + of_property_read_u32_array + (np, "color_aspect_dec", + &pdata->color_aspect_dec.support, 2); + of_property_read_u32_array + (np, "static_info_dec", + &pdata->static_info_dec.support, 2); + of_property_read_u32_array + (np, "vp9_stride_align", + &pdata->vp9_stride_align.support, 2); of_property_read_u32_array (np, "mem_clear", &pdata->mem_clear.support, 2); of_property_read_u32_array (np, "wait_fw_status", &pdata->wait_fw_status.support, 2); + of_property_read_u32_array + (np, "hevc_pic_output_flag", + &pdata->hevc_pic_output_flag.support, 2); + + /* H/W limitation or option */ + of_property_read_u32(np, "stride_align", &pdata->stride_align); + of_property_read_u32(np, "stride_type", &pdata->stride_type); + of_property_read_u32(np, "stream_buf_limit", &pdata->stream_buf_limit); + of_property_read_u32(np, "support_8K_cavlc", &pdata->support_8K_cavlc); + + /* Formats */ + of_property_read_u32(np, "support_422", &pdata->support_422); + + /* Resolution */ + of_property_read_u32(np, "support_check_res", &pdata->support_check_res); + + /* HWACG */ + of_property_read_u32(np, "support_hwacg", &pdata->support_hwacg); + + /* display_err_type */ + of_property_read_u32(np, "display_err_type", &pdata->display_err_type); + + /* output buffer Q framerate */ + of_property_read_u32(np, "display_framerate", &pdata->display_framerate); + + of_property_read_u32_array + (np, "bw_dec_h264", + &pdata->mfc_bw_info.bw_dec_h264.peak, 3); + + /* QoS weight */ + of_property_read_u32(np, "dynamic_weight", &pdata->dynamic_weight); + of_property_read_u32 + (np, "qos_weight_h264_hevc", + &pdata->qos_weight.weight_h264_hevc); + of_property_read_u32 + (np, "qos_weight_3plane", + &pdata->qos_weight.weight_3plane); + of_property_read_u32 + (np, "qos_weight_num_of_tile", + &pdata->qos_weight.weight_num_of_tile); + of_property_read_u32 + (np, "qos_weight_mbaff", + &pdata->qos_weight.weight_mbaff); + + /* Bitrate control for QoS */ + of_property_read_u32(np, "num_mfc_freq", &pdata->num_mfc_freq); + if (pdata->num_mfc_freq) + of_property_read_u32_array + (np, "mfc_freqs", + pdata->mfc_freqs, pdata->num_mfc_freq); + + /* Core balance(%) for resource managing */ + of_property_read_u32(np, "core_balance", &pdata->core_balance); + + /* MFC IOVA threshold */ + of_property_read_u32(np, "iova_threshold", &pdata->iova_threshold); + + /* MFC idle clock control */ + of_property_read_u32(np, "idle_clk_ctrl", &pdata->idle_clk_ctrl); + + /* QoS level for pm_qos dynamic control */ + of_property_read_u32(np, "qos_ctrl_level", &pdata->qos_ctrl_level); + + /* Memlog size */ + of_property_read_u32(np, "memlog_size", &pdata->memlog_size); + of_property_read_u32(np, "memlog_sfr_size", &pdata->memlog_sfr_size); + + /* offset for saving result of regression */ + of_property_read_u32 + (np, "reg_h264_loop_filter_disable", + &pdata->reg_h264_loop_filter_disable); =20 /* Scheduler */ of_property_read_u32(np, "scheduler", &pdata->scheduler); of_property_read_u32(np, "pbs_num_prio", &pdata->pbs_num_prio); =20 + of_property_read_u32(np, "support_mv_hevc", &pdata->support_mv_hevc); + return 0; } =20 @@ -341,6 +717,11 @@ static struct video_device *__mfc_video_device_register vfd->minor =3D -1; vfd->release =3D video_device_release; =20 + if (IS_DEC_NODE(node_num)) + vfd->ioctl_ops =3D mfc_get_dec_v4l2_ioctl_ops(); + else if (IS_ENC_NODE(node_num)) + vfd->ioctl_ops =3D NULL; + vfd->lock =3D &dev->mfc_mutex; vfd->v4l2_dev =3D &dev->v4l2_dev; vfd->vfl_dir =3D VFL_DIR_M2M; @@ -413,6 +794,7 @@ static int mfc_probe(struct platform_device *pdev) dma_set_mask(&pdev->dev, DMA_BIT_MASK(dev->pdata->dma_bit_mask)); =20 mutex_init(&dev->mfc_mutex); + mutex_init(&dev->mfc_migrate_mutex); =20 ret =3D v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) @@ -437,6 +819,24 @@ static int mfc_probe(struct platform_device *pdev) } /* end of node setting*/ =20 + /* instance migration worker */ + dev->migration_wq =3D alloc_workqueue("mfc/inst_migration", WQ_UNBOUND + | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!dev->migration_wq) { + dev_err(&pdev->dev, "failed to create workqueue for migration wq\n"); + goto err_migration_work; + } + INIT_WORK(&dev->migration_work, mfc_rm_migration_worker); + + /* main butler worker */ + dev->butler_wq =3D alloc_workqueue("mfc/butler", WQ_UNBOUND + | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!dev->butler_wq) { + dev_err(&pdev->dev, "failed to create workqueue for butler\n"); + goto err_butler_wq; + } + INIT_WORK(&dev->butler_work, mfc_butler_worker); + /* for DVA reservation */ if (dev->pdata->reserved_start) { dev->domain =3D iommu_get_domain_for_dev(dev->device); @@ -461,6 +861,10 @@ static int mfc_probe(struct platform_device *pdev) =20 /* Deinit MFC if probe had failed */ err_iova_reserve: + destroy_workqueue(dev->butler_wq); +err_butler_wq: + destroy_workqueue(dev->migration_wq); +err_migration_work: video_unregister_device(dev->vfd_enc); alloc_vdev_enc: video_unregister_device(dev->vfd_dec); @@ -468,6 +872,7 @@ static int mfc_probe(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); err_v4l2_dev: mutex_destroy(&dev->mfc_mutex); + mutex_destroy(&dev->mfc_migrate_mutex); err_res_mem: return ret; } @@ -482,6 +887,10 @@ static void mfc_remove(struct platform_device *pdev) platform_driver_unregister(&mfc_core_driver); mfc_unconfigure_dma_memory(dev); of_reserved_mem_device_release(dev->device); + flush_workqueue(dev->butler_wq); + destroy_workqueue(dev->butler_wq); + flush_workqueue(dev->migration_wq); + destroy_workqueue(dev->migration_wq); =20 mfc_deinit_debugfs(dev); video_unregister_device(dev->vfd_enc); @@ -665,6 +1074,7 @@ static const struct dev_pm_ops mfc_pm_ops =3D { =20 struct mfc_ctx_buf_size mfc_ctx_buf_size =3D { .dev_ctx =3D PAGE_ALIGN(0x7800), /* 30KB */ + .h264_dec_ctx =3D PAGE_ALIGN(0x200000), /* 1.6MB */ .dbg_info_buf =3D PAGE_ALIGN(0x1000), /* 4KB for DEBUG INFO */ }; =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_debugfs.c index 5baa76a6b405..59cd6f3945ff 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c @@ -14,18 +14,34 @@ =20 #include "mfc_debugfs.h" =20 +#include "base/mfc_queue.h" + static int __mfc_info_show(struct seq_file *s, void *unused) { struct mfc_dev *dev =3D s->private; struct mfc_core *core =3D NULL; + struct mfc_ctx *ctx =3D NULL; struct mfc_core_ctx *core_ctx =3D NULL; int i, j; + char *codec_name =3D NULL, *fmt_name =3D NULL; =20 seq_puts(s, ">>> MFC common device information\n"); seq_printf(s, " [DEBUG MODE] dt: %s sysfs: %s\n", dev->pdata->debug_mode ? "enabled" : "disabled", dev->debugfs.debug_mode_en ? "enabled" : "disabled"); - + seq_printf(s, " [FEATURES] skype: %d(0x%x), black_bar: %d(0x%x)\n", + dev->pdata->skype.support, dev->pdata->skype.version, + dev->pdata->black_bar.support, + dev->pdata->black_bar.version); + seq_printf(s, " color_aspect_dec: %d(0x%x)\n", + dev->pdata->color_aspect_dec.support, + dev->pdata->color_aspect_dec.version); + seq_printf(s, " static_info_dec: %d(0x%x)\n", + dev->pdata->static_info_dec.support, + dev->pdata->static_info_dec.version); + seq_printf(s, " [FORMATS] 10bit: %s, 422: %s\n", + dev->pdata->support_10bit ? "supported" : "not supported", + dev->pdata->support_422 ? "supported" : "not supported"); seq_printf(s, " [LOWMEM] is_low_mem: %d\n", IS_LOW_MEM); =20 for (j =3D 0; j < dev->num_core; j++) { @@ -54,9 +70,46 @@ static int __mfc_info_show(struct seq_file *s, void *unu= sed) for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { core_ctx =3D core->core_ctx[i]; if (core_ctx) { - seq_printf(s, " [CORECTX:%d] state: %d\n", - i, core_ctx->state); + seq_printf(s, " [CORECTX:%d] state: %d, queue(src: %d, dst: %d)\n", + i, core_ctx->state, + mfc_get_queue_count(&core_ctx->ctx->buf_queue_lock, + &core_ctx->src_buf_queue), + mfc_get_queue_count(&core_ctx->ctx->buf_queue_lock, + &core_ctx->dst_buf_queue)); + } + } + } + seq_puts(s, ">>> MFC instance information\n"); + for (i =3D 0; i < MFC_NUM_CONTEXTS; i++) { + ctx =3D dev->ctx[i]; + if (ctx) { + if (ctx->type =3D=3D MFCINST_DECODER) { + codec_name =3D ctx->src_fmt->name; + fmt_name =3D ctx->dst_fmt->name; + } else { + codec_name =3D ctx->dst_fmt->name; + fmt_name =3D ctx->src_fmt->name; } + + seq_printf(s, " [CTX:%d] %s%s %s, size: %dx%d@%ldfps(ts: %lufps, tmu: = %dfps, op: %lufps), crop: %d %d %d %d\n", + ctx->num, codec_name, ctx->multi_view_enable ? "(MV-HEVC)" : "", + fmt_name, + ctx->img_width, ctx->img_height, + ctx->framerate / 1000, + ctx->last_framerate / 1000, + ctx->dev->tmu_fps, + ctx->operating_framerate, + ctx->crop_width, ctx->crop_height, + ctx->crop_left, ctx->crop_top); + seq_printf(s, " main core: %d, op_mode: %d(stream: %d), idle_mod= e: %d, prio %d, rt %d, queue(src: %d, dst: %d, ref: %d)\n", + ctx->op_core_num[MFC_CORE_MAIN], + ctx->op_mode, ctx->stream_op_mode, ctx->idle_mode, + ctx->prio, ctx->rt, + mfc_get_queue_count(&ctx->buf_queue_lock, + &ctx->src_buf_ready_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue)); + seq_puts(s, "\n"); } } =20 @@ -175,6 +228,7 @@ void mfc_init_debugfs(struct mfc_dev *dev) debugfs_create_u32("sfr_dump", 0644, debugfs->root, &dev->debugfs.sfr_dum= p); =20 debugfs_create_u32("feature_option", 0644, debugfs->root, &dev->debugfs.f= eature_option); + debugfs_create_u32("core_balance", 0644, debugfs->root, &dev->debugfs.cor= e_balance); debugfs_create_u32("logging_option", 0644, debugfs->root, &dev->debugfs.l= ogging_option); debugfs_create_u32("sched_perf_disable", 0644, debugfs->root, &dev->debugfs.sched_perf_disable); --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 35DA425A623 for ; Tue, 30 Sep 2025 03:56:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204617; cv=none; b=oBLdZVZa0wkn4AkYjr7j88G/H+9th2YpmnJoQ4iOKztrpmAdPt9spoBaEmoDJQNM3s3F2jNPcJGKPyuWMdhEk9Ri/0sr98UDvX8kFiBB9wMkh+jvdmtdPcX0simJtpbexWD+44KVA23kFnEwcRZ1swfTRd62xHnYvSzwG08KXKI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204617; c=relaxed/simple; bh=dnuiF9taoSpEVXutekDkhOjcISTQ4v8OhWetOwZyKNk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=tOwIlpDagMciqIKKbqTPK/JgpaZf2Xp3X6b0Hh9nIgyckSBYsIvQayRsYsJP5J6YJK+SWVWwOsemV8KyN9E3hMMPViakTDUUjhwAG1JPbkofFIntjipdqB0d32af7n2CmCMtUJ5wPw9hPeAzzcBXh8vNIvt0qqtFX2WJcdce0PE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=D7JNlpT1; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="D7JNlpT1" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035650epoutp04a62b7be99fbc499c86edfdefc2862f5a~p80XQQSzl2088220882epoutp04M for ; Tue, 30 Sep 2025 03:56:50 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035650epoutp04a62b7be99fbc499c86edfdefc2862f5a~p80XQQSzl2088220882epoutp04M DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204610; bh=FRlzfMbQXPkh6PtdyInbjSItki1kicdByfqh3BQZpvQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=D7JNlpT1Vo/uKExsPIAQ0vj8rOv/7gy1FyJvXGIoNXM0VAqQsU7//+oJF6cxpYH8c Nhokfz3B3nDXki1dW5KaDrvtfLaJg5a26nR6xpJLl5nKW36fznOk80jKoPSVDcT/RF qn2DekTRWYcOxCFWRAL0nJUaXpzr+C2qXKBCw9vk= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035648epcas5p1bcfc137b71de0107d684089a0db0af02~p80VrfTLi2306123061epcas5p1p; Tue, 30 Sep 2025 03:56:48 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.94]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPPq64Zqz6B9mB; Tue, 30 Sep 2025 03:56:47 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPA id 20250930035646epcas5p2f4a9694c7f89ffe27384f8b7b8227802~p80Tw1LC70812408124epcas5p2i; Tue, 30 Sep 2025 03:56:46 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035644epsmtip1ba8c3ef96b0eca6504d23b66b64064d6~p80RVraPo2931929319epsmtip10; Tue, 30 Sep 2025 03:56:44 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 21/29] =?UTF-8?q?media:=20mfc:=20Add=20multi=E2=80=91codec?= =?UTF-8?q?=20support=20&=20QoS=20improvements?= Date: Tue, 30 Sep 2025 09:33:40 +0530 Message-Id: <20250930040348.3702923-22-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035646epcas5p2f4a9694c7f89ffe27384f8b7b8227802 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035646epcas5p2f4a9694c7f89ffe27384f8b7b8227802 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Enable HEVC, AV1, VP8/9, MPEG=E2=80=914 (incl. MVC, VC=E2=80=911=E2=80=AF= RCV) . - Extend DPB/buffer structures for AV1 and high=E2=80=91resolution/multifra= me streams. - Add codec=E2=80=91specific QoS weight parameters (10=E2=80=91bit, 4:2:2, = B=E2=80=91frames) and update DT bandwidth entries. - Enhance format tables with colour=E2=80=91range/space and HDR parsing for= VP9. - Detect display=E2=80=91resolution changes and multiframe flags for VP9/AV= 1. - Introduce utility helpers (CRC, aspect=E2=80=91ratio, colour primaries) and minor safety fixes. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_buf.c | 70 +++++++++ .../samsung/exynos-mfc/base/mfc_common.h | 42 ++++- .../samsung/exynos-mfc/base/mfc_data_struct.h | 20 +++ .../samsung/exynos-mfc/base/mfc_format.h | 136 ++++++++++++++++ .../samsung/exynos-mfc/base/mfc_qos.c | 58 +++++++ .../samsung/exynos-mfc/base/mfc_queue.c | 5 + .../samsung/exynos-mfc/base/mfc_utils.c | 8 +- .../media/platform/samsung/exynos-mfc/mfc.c | 53 +++++++ .../platform/samsung/exynos-mfc/mfc_core.c | 2 + .../samsung/exynos-mfc/mfc_core_buf_ctrl.c | 9 ++ .../samsung/exynos-mfc/mfc_core_cmd.c | 11 ++ .../samsung/exynos-mfc/mfc_core_isr.c | 147 ++++++++++++++++-- .../samsung/exynos-mfc/mfc_core_reg_api.c | 46 +++++- .../samsung/exynos-mfc/mfc_core_reg_api.h | 75 ++++++++- .../samsung/exynos-mfc/mfc_core_run.c | 4 + .../platform/samsung/exynos-mfc/mfc_dec_vb2.c | 1 + 16 files changed, 665 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.c index bd1baf34e0b0..84f97ca230bb 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c @@ -116,8 +116,25 @@ int mfc_alloc_instance_context(struct mfc_core_ctx *co= re_ctx) switch (ctx->codec_mode) { case MFC_REG_CODEC_H264_DEC: case MFC_REG_CODEC_H264_MVC_DEC: + case MFC_REG_CODEC_HEVC_DEC: core_ctx->instance_ctx_buf.size =3D buf_size->h264_dec_ctx; break; + case MFC_REG_CODEC_MPEG4_DEC: + case MFC_REG_CODEC_H263_DEC: + case MFC_REG_CODEC_VC1_RCV_DEC: + case MFC_REG_CODEC_VC1_DEC: + case MFC_REG_CODEC_MPEG2_DEC: + case MFC_REG_CODEC_VP8_DEC: + case MFC_REG_CODEC_VP9_DEC: + case MFC_REG_CODEC_FIMV1_DEC: + case MFC_REG_CODEC_FIMV2_DEC: + case MFC_REG_CODEC_FIMV3_DEC: + case MFC_REG_CODEC_FIMV4_DEC: + core_ctx->instance_ctx_buf.size =3D buf_size->other_dec_ctx; + break; + case MFC_REG_CODEC_AV1_DEC: + core_ctx->instance_ctx_buf.size =3D buf_size->av1_dec_ctx; + break; default: core_ctx->instance_ctx_buf.size =3D 0; mfc_err("Codec type(%d) should be checked!\n", ctx->codec_mode); @@ -155,6 +172,59 @@ static void __mfc_dec_calc_codec_buffer_size(struct mf= c_core_ctx *core_ctx) /* Codecs have different memory requirements */ switch (ctx->codec_mode) { case MFC_REG_CODEC_H264_DEC: + case MFC_REG_CODEC_H264_MVC_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D ctx->scratch_buf_size; + ctx->mv_buf.size =3D dec->mv_count * ctx->mv_size; + break; + case MFC_REG_CODEC_MPEG4_DEC: + case MFC_REG_CODEC_FIMV1_DEC: + case MFC_REG_CODEC_FIMV2_DEC: + case MFC_REG_CODEC_FIMV3_DEC: + case MFC_REG_CODEC_FIMV4_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + if (dec->loop_filter_mpeg4) { + ctx->loopfilter_luma_size =3D ALIGN(ctx->raw_buf.plane_size[0], SZ_256); + ctx->loopfilter_chroma_size =3D ALIGN(ctx->raw_buf.plane_size[1] + + ctx->raw_buf.plane_size[2], SZ_256); + core_ctx->codec_buf.size =3D ctx->scratch_buf_size + + (NUM_MPEG4_LF_BUF * (ctx->loopfilter_luma_size + + ctx->loopfilter_chroma_size)); + } else { + core_ctx->codec_buf.size =3D ctx->scratch_buf_size; + } + break; + case MFC_REG_CODEC_VC1_RCV_DEC: + case MFC_REG_CODEC_VC1_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D ctx->scratch_buf_size; + break; + case MFC_REG_CODEC_MPEG2_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D ctx->scratch_buf_size; + break; + case MFC_REG_CODEC_H263_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D ctx->scratch_buf_size; + break; + case MFC_REG_CODEC_VP8_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D ctx->scratch_buf_size; + break; + case MFC_REG_CODEC_VP9_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D + ctx->scratch_buf_size + + DEC_STATIC_BUFFER_SIZE; + break; + case MFC_REG_CODEC_AV1_DEC: + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D + ctx->scratch_buf_size + + DEC_AV1_STATIC_BUFFER_SIZE(ctx->img_width, ctx->img_height); + ctx->mv_buf.size =3D dec->mv_count * ctx->mv_size; + break; + case MFC_REG_CODEC_HEVC_DEC: ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); core_ctx->codec_buf.size =3D ctx->scratch_buf_size; ctx->mv_buf.size =3D dec->mv_count * ctx->mv_size; diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h index de22c28d1625..5392c8566e42 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h @@ -153,7 +153,47 @@ =20 /* Decoder codec mode check */ #define IS_H264_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_H264_DEC) +#define IS_H264_MVC_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_H264_= MVC_DEC) +#define IS_MPEG4_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_MPEG4_DE= C) +#define IS_FIMV1_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_FIMV1_DE= C) +#define IS_FIMV2_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_FIMV2_DE= C) +#define IS_FIMV3_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_FIMV3_DE= C) +#define IS_FIMV4_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_FIMV4_DE= C) +#define IS_VC1_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_VC1_DEC) +#define IS_VC1_RCV_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_VC1_RC= V_DEC) +#define IS_MPEG2_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_MPEG2_DE= C) +#define IS_HEVC_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_HEVC_DEC) +#define IS_VP8_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_VP8_DEC) +#define IS_VP9_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_VP9_DEC) +#define IS_AV1_DEC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_AV1_DEC) +#define IS_MV_HEVC_DEC(ctx, profile) \ + ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_HEVC_DEC && \ + (profile) =3D=3D MFC_REG_D_PROFILE_MULTIVIEW_HEVC_MAIN) + +#define CODEC_NOT_CODED(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_MPEG4_DEC(_ctx) || IS_VC1_DEC(_ctx) || IS_VC1_RCV_DEC(_ctx) || \ + IS_VP9_DEC(_ctx)); \ +}) +#define CODEC_INTERLACED(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_H264_DEC(_ctx) || IS_H264_MVC_DEC(_ctx) || IS_MPEG2_DEC(_ctx) || \ + IS_MPEG4_DEC(_ctx) || IS_VC1_DEC(_ctx) || IS_VC1_RCV_DEC(_ctx)); \ +}) +#define CODEC_MBAFF(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_H264_DEC(_ctx) || IS_H264_MVC_DEC(_ctx)); \ +}) +#define CODEC_MULTIFRAME(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_MPEG4_DEC(_ctx) || IS_VP9_DEC(_ctx) || IS_FIMV2_DEC(_ctx) || \ + IS_FIMV3_DEC(_ctx) || IS_FIMV4_DEC(_ctx) || IS_AV1_DEC(_ctx)); \ +}) =20 +#define CODEC_422FORMAT(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_HEVC_DEC(_ctx) || IS_VP9_DEC(_ctx)); \ +}) #define ON_RES_CHANGE(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ ((_ctx->state >=3D MFCINST_RES_CHANGE_INIT) && \ @@ -177,7 +217,7 @@ }) #define CODEC_HAS_IDR(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ - (IS_H264_DEC(_ctx)); \ + (IS_H264_DEC(_ctx) || IS_H264_MVC_DEC(_ctx) || IS_HEVC_DEC(_ctx)); \ }) =20 /* diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct= .h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h index 34b4b13b4f01..6b93fe3ab138 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h @@ -503,6 +503,8 @@ struct mfc_fw { struct mfc_ctx_buf_size { size_t dev_ctx; size_t h264_dec_ctx; + size_t av1_dec_ctx; + size_t other_dec_ctx; size_t dbg_info_buf; }; =20 @@ -710,6 +712,14 @@ struct mfc_bw_data { =20 struct mfc_bw_info { struct mfc_bw_data bw_dec_h264; + struct mfc_bw_data bw_dec_hevc; + struct mfc_bw_data bw_dec_hevc_10bit; + struct mfc_bw_data bw_dec_vp8; + struct mfc_bw_data bw_dec_vp9; + struct mfc_bw_data bw_dec_vp9_10bit; + struct mfc_bw_data bw_dec_av1; + struct mfc_bw_data bw_dec_av1_10bit; + struct mfc_bw_data bw_dec_mpeg4; }; =20 /* @@ -723,6 +733,7 @@ struct mfc_qos { unsigned int freq_int; unsigned int freq_mif; unsigned int mo_value; + unsigned int mo_10bit_value; unsigned int time_fw; unsigned int bts_scen_idx; const char *name; @@ -747,8 +758,17 @@ struct mfc_qos_ctrl { =20 struct mfc_qos_weight { unsigned int weight_h264_hevc; + unsigned int weight_vp8_vp9; + unsigned int weight_av1; + unsigned int weight_other_codec; unsigned int weight_3plane; + unsigned int weight_10bit; + unsigned int weight_422; + unsigned int weight_bframe; + unsigned int weight_num_of_ref; + unsigned int weight_gpb; unsigned int weight_num_of_tile; + unsigned int weight_super64_bframe; unsigned int weight_mbaff; }; =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h index 3307c2eeaebb..0d48f2373e8d 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h @@ -127,6 +127,142 @@ static struct mfc_fmt mfc_formats[] =3D { .num_planes =3D 1, .mem_planes =3D 1, }, + { + .name =3D "DEC H264/MVC", + .fourcc =3D V4L2_PIX_FMT_H264_MVC, + .codec_mode =3D MFC_REG_CODEC_H264_MVC_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC H263", + .fourcc =3D V4L2_PIX_FMT_H263, + .codec_mode =3D MFC_REG_CODEC_H263_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC MPEG1", + .fourcc =3D V4L2_PIX_FMT_MPEG1, + .codec_mode =3D MFC_REG_CODEC_MPEG2_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC MPEG2", + .fourcc =3D V4L2_PIX_FMT_MPEG2, + .codec_mode =3D MFC_REG_CODEC_MPEG2_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC MPEG4", + .fourcc =3D V4L2_PIX_FMT_MPEG4, + .codec_mode =3D MFC_REG_CODEC_MPEG4_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC FIMV", + .fourcc =3D V4L2_PIX_FMT_FIMV, + .codec_mode =3D MFC_REG_CODEC_MPEG4_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC FIMV1", + .fourcc =3D V4L2_PIX_FMT_FIMV1, + .codec_mode =3D MFC_REG_CODEC_FIMV1_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC FIMV2", + .fourcc =3D V4L2_PIX_FMT_FIMV2, + .codec_mode =3D MFC_REG_CODEC_FIMV2_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC FIMV3", + .fourcc =3D V4L2_PIX_FMT_FIMV3, + .codec_mode =3D MFC_REG_CODEC_FIMV3_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC FIMV4", + .fourcc =3D V4L2_PIX_FMT_FIMV4, + .codec_mode =3D MFC_REG_CODEC_FIMV4_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC XviD", + .fourcc =3D V4L2_PIX_FMT_XVID, + .codec_mode =3D MFC_REG_CODEC_MPEG4_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC VC1", + .fourcc =3D V4L2_PIX_FMT_VC1_ANNEX_G, + .codec_mode =3D MFC_REG_CODEC_VC1_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC VC1 RCV", + .fourcc =3D V4L2_PIX_FMT_VC1_ANNEX_L, + .codec_mode =3D MFC_REG_CODEC_VC1_RCV_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC VP8", + .fourcc =3D V4L2_PIX_FMT_VP8, + .codec_mode =3D MFC_REG_CODEC_VP8_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC VP9", + .fourcc =3D V4L2_PIX_FMT_VP9, + .codec_mode =3D MFC_REG_CODEC_VP9_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC AV1", + .fourcc =3D V4L2_PIX_FMT_AV1, + .codec_mode =3D MFC_REG_CODEC_AV1_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "DEC HEVC", + .fourcc =3D V4L2_PIX_FMT_HEVC, + .codec_mode =3D MFC_REG_CODEC_HEVC_DEC, + .type =3D MFC_FMT_STREAM | MFC_FMT_DEC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, }; =20 #endif /* __MFC_FORMAT_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_qos.c index f6548543f07c..9922c2396b94 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c @@ -38,6 +38,7 @@ static inline unsigned long __mfc_qos_add_weight(struct m= fc_ctx *ctx, unsigned l =20 switch (ctx->codec_mode) { case MFC_REG_CODEC_H264_DEC: + case MFC_REG_CODEC_H264_MVC_DEC: weight =3D (weight * 100) / qos_weight->weight_h264_hevc; mfc_ctx_debug(3, "[QoS] h264, hevc codec, weight: %d\n", weight / 10); if (num_planes =3D=3D 3) { @@ -45,6 +46,63 @@ static inline unsigned long __mfc_qos_add_weight(struct = mfc_ctx *ctx, unsigned l mfc_ctx_debug(3, "[QoS] 3 plane, weight: %d\n", weight / 10); } break; + + case MFC_REG_CODEC_VP8_DEC: + weight =3D (weight * 100) / qos_weight->weight_vp8_vp9; + mfc_ctx_debug(3, "[QoS] vp8, vp9 codec, weight: %d\n", weight / 10); + if (num_planes =3D=3D 3) { + weight =3D (weight * 100) / qos_weight->weight_3plane; + mfc_ctx_debug(3, "[QoS] 3 plane, weight: %d\n", weight / 10); + } + break; + + case MFC_REG_CODEC_HEVC_DEC: + weight =3D (weight * 100) / qos_weight->weight_h264_hevc; + mfc_ctx_debug(3, "[QoS] h264, hevc codec, weight: %d\n", weight / 10); + if (num_planes =3D=3D 3) { + weight =3D (weight * 100) / qos_weight->weight_3plane; + mfc_ctx_debug(3, "[QoS] 3 plane, weight: %d\n", weight / 10); + } else { + if (ctx->is_422) { + weight =3D (weight * 100) / qos_weight->weight_422; + mfc_ctx_debug(3, "[QoS] 422foramt, weight: %d\n", weight / 10); + } + } + break; + + case MFC_REG_CODEC_AV1_DEC: + weight =3D (weight * 100) / qos_weight->weight_av1; + mfc_ctx_debug(3, "[QoS] av1 codec, weight: %d\n", weight / 10); + break; + + case MFC_REG_CODEC_VP9_DEC: + weight =3D (weight * 100) / qos_weight->weight_vp8_vp9; + mfc_ctx_debug(3, "[QoS] vp8, vp9 codec, weight: %d\n", weight / 10); + + if (num_planes =3D=3D 3) { + weight =3D (weight * 100) / qos_weight->weight_3plane; + mfc_ctx_debug(3, "[QoS] 3 plane, weight: %d\n", weight / 10); + } else { + if (ctx->is_422) { + weight =3D (weight * 100) / qos_weight->weight_422; + mfc_ctx_debug(3, "[QoS] 422foramt, weight: %d\n", weight / 10); + } + } + break; + + case MFC_REG_CODEC_MPEG4_DEC: + case MFC_REG_CODEC_FIMV1_DEC: + case MFC_REG_CODEC_FIMV2_DEC: + case MFC_REG_CODEC_FIMV3_DEC: + case MFC_REG_CODEC_FIMV4_DEC: + case MFC_REG_CODEC_H263_DEC: + case MFC_REG_CODEC_VC1_RCV_DEC: + case MFC_REG_CODEC_VC1_DEC: + case MFC_REG_CODEC_MPEG2_DEC: + weight =3D (weight * 100) / qos_weight->weight_other_codec; + mfc_ctx_debug(3, "[QoS] other codec, weight: %d\n", weight / 10); + break; + default: mfc_ctx_err("[QoS] wrong codec_mode (%d), no weight\n", ctx->codec_mode); } diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c index f56e800c55f0..6dc9bc7a1873 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c @@ -154,6 +154,11 @@ struct mfc_buf *mfc_get_del_if_consumed(struct mfc_ctx= *ctx, consumed, strm_size); } =20 + if (remained && IS_MULTI_MODE(ctx) && !CODEC_MULTIFRAME(ctx)) { + mfc_ctx_info("[2CORE][MULTIFRAME] multicore mode couldn't handle multifr= ame\n"); + remained =3D 0; + } + if (consumed > 0 && remained > min_bytes && IS_NO_ERROR(error) && !exceed) { /* do not delete from queue */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c index b0698b2bb0c0..83cdae3dee57 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c @@ -187,9 +187,15 @@ void mfc_dec_calc_dpb_size(struct mfc_ctx *ctx, struct= mfc_raw_info *raw, struct } mfc_ctx_debug(2, "[FRAME] total plane size: %d\n", raw->total_plane_size); =20 - if (IS_H264_DEC(ctx)) { + if (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx)) { ctx->mv_size =3D DEC_MV_SIZE_MB(ctx->img_width, ctx->img_height); ctx->mv_size =3D ALIGN(ctx->mv_size, 32); + } else if (IS_HEVC_DEC(ctx)) { + ctx->mv_size =3D DEC_HEVC_MV_SIZE(ctx->img_width, ctx->img_height); + ctx->mv_size =3D ALIGN(ctx->mv_size, 32); + } else if (IS_AV1_DEC(ctx)) { + ctx->mv_size =3D DEC_AV1_MV_SIZE(ctx->img_width, ctx->img_height); + ctx->mv_size =3D ALIGN(ctx->mv_size, 32); } else { ctx->mv_size =3D 0; } diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc.c b/drivers/medi= a/platform/samsung/exynos-mfc/mfc.c index fb9a7317e812..293a353c49fa 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc.c @@ -646,18 +646,69 @@ static int __mfc_parse_dt(struct device_node *np, str= uct mfc_dev *mfc) of_property_read_u32_array (np, "bw_dec_h264", &pdata->mfc_bw_info.bw_dec_h264.peak, 3); + of_property_read_u32_array + (np, "bw_dec_hevc", + &pdata->mfc_bw_info.bw_dec_hevc.peak, 3); + of_property_read_u32_array + (np, "bw_dec_hevc_10bit", + &pdata->mfc_bw_info.bw_dec_hevc_10bit.peak, 3); + of_property_read_u32_array + (np, "bw_dec_vp8", + &pdata->mfc_bw_info.bw_dec_vp8.peak, 3); + of_property_read_u32_array + (np, "bw_dec_vp9", + &pdata->mfc_bw_info.bw_dec_vp9.peak, 3); + of_property_read_u32_array + (np, "bw_dec_vp9_10bit", + &pdata->mfc_bw_info.bw_dec_vp9_10bit.peak, 3); + of_property_read_u32_array + (np, "bw_dec_av1", + &pdata->mfc_bw_info.bw_dec_av1.peak, 3); + of_property_read_u32_array + (np, "bw_dec_av1_10bit", + &pdata->mfc_bw_info.bw_dec_av1_10bit.peak, 3); + of_property_read_u32_array + (np, "bw_dec_mpeg4", + &pdata->mfc_bw_info.bw_dec_mpeg4.peak, 3); =20 /* QoS weight */ of_property_read_u32(np, "dynamic_weight", &pdata->dynamic_weight); of_property_read_u32 (np, "qos_weight_h264_hevc", &pdata->qos_weight.weight_h264_hevc); + of_property_read_u32 + (np, "qos_weight_vp8_vp9", + &pdata->qos_weight.weight_vp8_vp9); + of_property_read_u32 + (np, "qos_weight_av1", + &pdata->qos_weight.weight_av1); + of_property_read_u32 + (np, "qos_weight_other_codec", + &pdata->qos_weight.weight_other_codec); of_property_read_u32 (np, "qos_weight_3plane", &pdata->qos_weight.weight_3plane); + of_property_read_u32 + (np, "qos_weight_10bit", + &pdata->qos_weight.weight_10bit); + of_property_read_u32 + (np, "qos_weight_422", + &pdata->qos_weight.weight_422); + of_property_read_u32 + (np, "qos_weight_bframe", + &pdata->qos_weight.weight_bframe); + of_property_read_u32 + (np, "qos_weight_num_of_ref", + &pdata->qos_weight.weight_num_of_ref); + of_property_read_u32 + (np, "qos_weight_gpb", + &pdata->qos_weight.weight_gpb); of_property_read_u32 (np, "qos_weight_num_of_tile", &pdata->qos_weight.weight_num_of_tile); + of_property_read_u32 + (np, "qos_weight_super64_bframe", + &pdata->qos_weight.weight_super64_bframe); of_property_read_u32 (np, "qos_weight_mbaff", &pdata->qos_weight.weight_mbaff); @@ -1075,6 +1126,8 @@ static const struct dev_pm_ops mfc_pm_ops =3D { struct mfc_ctx_buf_size mfc_ctx_buf_size =3D { .dev_ctx =3D PAGE_ALIGN(0x7800), /* 30KB */ .h264_dec_ctx =3D PAGE_ALIGN(0x200000), /* 1.6MB */ + .av1_dec_ctx =3D PAGE_ALIGN(0x19000), /* 100KB */ + .other_dec_ctx =3D PAGE_ALIGN(0xF000), /* 60KB */ .dbg_info_buf =3D PAGE_ALIGN(0x1000), /* 4KB for DEBUG INFO */ }; =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core.c b/drivers= /media/platform/samsung/exynos-mfc/mfc_core.c index af6fd088fad3..aad3273ce2ba 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core.c @@ -77,6 +77,8 @@ static int __mfc_core_parse_mfc_qos_platdata(struct devic= e_node *np, of_property_read_u32(np_qos, "freq_int", &qosdata->freq_int); of_property_read_u32(np_qos, "freq_mif", &qosdata->freq_mif); of_property_read_u32(np_qos, "mo_value", &qosdata->mo_value); + of_property_read_u32(np_qos, "mo_10bit_value", + &qosdata->mo_10bit_value); of_property_read_u32(np_qos, "time_fw", &qosdata->time_fw); =20 of_property_read_string(np_qos, "bts_scen", &qosdata->name); diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c = b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c index 56dc3e734d02..38f09d6ef2dd 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c @@ -63,6 +63,7 @@ static int mfc_core_get_buf_ctrls(struct mfc_core *core, struct mfc_ctx *ctx, struct list_head *head) { struct mfc_buf_ctrl *buf_ctrl; + struct mfc_dec *dec =3D ctx->dec_priv; unsigned int value =3D 0; =20 list_for_each_entry(buf_ctrl, head, list) { @@ -77,6 +78,14 @@ static int mfc_core_get_buf_ctrls(struct mfc_core *core, buf_ctrl->val =3D value; buf_ctrl->has_new =3D 1; =20 + if (IS_VP9_DEC(ctx) && dec) { + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG) + buf_ctrl->val =3D dec->color_range; + else if (buf_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES) + buf_ctrl->val =3D dec->color_space; + } + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_FRAME_ERROR_TYPE) buf_ctrl->val =3D mfc_get_frame_error_type(ctx, value); =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c index fe7946bb49e7..aaf216741575 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c @@ -230,6 +230,10 @@ void mfc_core_cmd_dec_seq_header(struct mfc_core *core= , struct mfc_ctx *ctx) reg |=3D ((dec->idr_decoding & MFC_REG_D_DEC_OPT_IDR_DECODING_MASK) << MFC_REG_D_DEC_OPT_IDR_DECODING_SHIFT); =20 + /* VC1 RCV: Discard to parse additional header as default */ + if (IS_VC1_RCV_DEC(ctx)) + reg |=3D BIT(MFC_REG_D_DEC_OPT_DISCARD_RCV_HEADER_SHIFT); + /* conceal control to specific color */ reg |=3D (0x4 << MFC_REG_D_DEC_OPT_CONCEAL_CONTROL_SHIFT); =20 @@ -248,6 +252,13 @@ void mfc_core_cmd_dec_seq_header(struct mfc_core *core= , struct mfc_ctx *ctx) =20 MFC_CORE_WRITEL(MFC_CONCEAL_COLOR, MFC_REG_D_FORCE_PIXEL_VAL); =20 + if (IS_FIMV1_DEC(ctx)) { + mfc_debug(2, "Setting FIMV1 resolution to %dx%d\n", + ctx->img_width, ctx->img_height); + MFC_CORE_WRITEL(ctx->img_width, MFC_REG_D_SET_FRAME_WIDTH); + MFC_CORE_WRITEL(ctx->img_height, MFC_REG_D_SET_FRAME_HEIGHT); + } + mfc_core_set_pixel_format(core, ctx, ctx->dst_fmt->fourcc); =20 reg =3D 0; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_isr.c index 94cc3c4dfdc5..aa2c0b618c19 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c @@ -126,10 +126,11 @@ static unsigned int __mfc_handle_frame_field(struct m= fc_core *core, unsigned int interlace_type =3D 0, is_interlace =3D 0; unsigned int field; =20 - if (IS_H264_DEC(ctx)) { - dec->is_mbaff =3D mfc_core_is_mbaff_picture(); + if (CODEC_INTERLACED(ctx)) is_interlace =3D mfc_core_is_interlace_picture(); - } + + if (CODEC_MBAFF(ctx)) + dec->is_mbaff =3D mfc_core_is_mbaff_picture(); =20 if (is_interlace) { interlace_type =3D mfc_core_get_interlace_type(); @@ -482,6 +483,27 @@ static struct mfc_buf *__mfc_handle_frame_output_del(s= truct mfc_core *core, } } =20 + if (IS_VP9_DEC(ctx) && + MFC_FEATURE_SUPPORT(dev, dev->pdata->color_aspect_dec)) { + if (dec->color_space !=3D MFC_REG_D_COLOR_UNKNOWN) { + mfc_set_mb_flag(dst_mb, + MFC_FLAG_HDR_COLOUR_DESC); + mfc_ctx_debug(2, "[HDR] color space parsed\n"); + } + mfc_set_mb_flag(dst_mb, MFC_FLAG_HDR_VIDEO_SIGNAL_TYPE); + mfc_ctx_debug(2, "[HDR] color range parsed\n"); + } + + if ((IS_VP9_DEC(ctx) && mfc_core_get_disp_res_change()) || + (IS_AV1_DEC(ctx) && mfc_core_get_disp_res_change_av1())) { + mfc_ctx_info("[FRAME][DRC] display resolution changed\n"); + mutex_lock(&ctx->drc_wait_mutex); + ctx->wait_state =3D WAIT_G_FMT; + mfc_core_get_img_size(core, ctx, MFC_GET_RESOL_SIZE); + mfc_set_mb_flag(dst_mb, MFC_FLAG_DISP_RES_CHANGE); + mutex_unlock(&ctx->drc_wait_mutex); + } + if (dec->black_bar_updated) { mfc_set_mb_flag(dst_mb, MFC_FLAG_BLACKBAR_DETECT); mfc_ctx_debug(3, "[BLACKBAR] black bar detected\n"); @@ -498,6 +520,11 @@ static struct mfc_buf *__mfc_handle_frame_output_del(s= truct mfc_core *core, mfc_ctx_debug(2, "[FRAME] Last display frame\n"); } =20 + if ((IS_VP9_DEC(ctx) || IS_AV1_DEC(ctx)) && dec->has_multiframe) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_MULTIFRAME); + mfc_ctx_debug(2, "[MULTIFRAME] multiframe detected\n"); + } + if (ctx->dst_fmt->mem_planes =3D=3D 1) { vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, raw->total_plane_size); @@ -542,6 +569,8 @@ static struct mfc_buf *__mfc_handle_frame_output_del(st= ruct mfc_core *core, =20 mutex_unlock(&dec->dpb_mutex); } else { + if (IS_AV1_DEC(ctx) && mfc_core_get_multiple_show_frame()) + dec->is_multiple_show =3D 1; mfc_print_dpb_queue_with_lock(core->core_ctx[ctx->num], dec); } =20 @@ -708,11 +737,18 @@ static struct mfc_buf *__mfc_handle_frame_output(stru= ct mfc_core *core, { struct mfc_dec *dec =3D ctx->dec_priv; unsigned int frame_type; + int mvc_view_id; =20 frame_type =3D mfc_core_get_disp_frame_type(); + mvc_view_id =3D mfc_core_get_mvc_disp_view_id(); =20 - if (!ctx->multi_view_enable || ctx->select_view_irq =3D=3D MFC_VIEW_ID_SU= B) - ctx->sequence++; + if (IS_H264_MVC_DEC(ctx)) { + if (mvc_view_id =3D=3D 0) + ctx->sequence++; + } else { + if (!ctx->multi_view_enable || ctx->select_view_irq =3D=3D MFC_VIEW_ID_S= UB) + ctx->sequence++; + } =20 if (dec->immediate_display =3D=3D 1) frame_type =3D mfc_core_get_dec_frame_type(); @@ -721,7 +757,8 @@ static struct mfc_buf *__mfc_handle_frame_output(struct= mfc_core *core, =20 /* If frame is same as previous then skip and do not dequeue */ if (frame_type =3D=3D MFC_REG_DISPLAY_FRAME_NOT_CODED) - return NULL; + if (!CODEC_NOT_CODED(ctx)) + return NULL; =20 /* Dequeued display buffer for user */ return __mfc_handle_frame_output_del(core, ctx, err); @@ -894,6 +931,7 @@ static void __mfc_handle_frame_input(struct mfc_core *c= ore, struct mfc_ctx *ctx, unsigned int err) { + struct mfc_dev *dev =3D ctx->dev; struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; struct mfc_dec *dec =3D ctx->dec_priv; struct mfc_buf *src_mb; @@ -945,6 +983,54 @@ static void __mfc_handle_frame_input(struct mfc_core *= core, =20 mfc_clear_mb_flag(src_mb); =20 + if ((IS_VP9_DEC(ctx) || IS_AV1_DEC(ctx)) && dec->has_multiframe && + mfc_core_get_disp_status() =3D=3D MFC_REG_DEC_STATUS_DECODING_ONLY) { + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + mfc_debug(2, "[STREAM][MULTIFRAME] last frame is decoding only\n"); + } + + /* + * VP8 decoder has decoding only frame, + * it will be used for reference frame only not displayed. + * So, driver inform to user this input has no destination. + */ + if (((IS_VP8_DEC(ctx) || IS_VP9_DEC(ctx)) && + mfc_core_get_disp_status() =3D=3D MFC_REG_DEC_STATUS_DECODING_ONLY) = || + mfc_core_get_int_reason() =3D=3D MFC_REG_R2H_CMD_FIELD_DONE_RET) { + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + mfc_debug(2, "[STREAM] %s decoding only stream has no buffer to DQ\n", + ctx->src_fmt->name); + } + + /* + * Because AV1 has a no show frame, there are two cases that + * driver should inform to user this input has no destination buffer. + * 1) If it's decoding only and it's not showable frame, + * it will be used for reference frame only not displayed. + * 2) If the buffer that has already DQ to display comes to new display, + * it is multiple show frame. + */ + if (IS_AV1_DEC(ctx)) { + if ((mfc_core_get_disp_status() =3D=3D MFC_REG_DEC_STATUS_DECODING_ONLY)= && + !mfc_core_get_showable_frame()) { + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + mfc_debug(2, "[STREAM] AV1 no showable frame has no buffer to DQ\n"); + } + if (dec->is_multiple_show) { + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + dec->is_multiple_show =3D 0; + mfc_info("[STREAM] AV1 multiple show frame has no buffer to DQ\n"); + } + } + + /* If pic_output_flag is 0 in HEVC, it is no destination buffer */ + if (IS_HEVC_DEC(ctx) && + MFC_FEATURE_SUPPORT(dev, dev->pdata->hevc_pic_output_flag) && + !mfc_core_get_hevc_pic_output_flag()) { + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + mfc_debug(2, "[STREAM] HEVC pic_output_flag off has no buffer to DQ\n"); + } + if (mfc_core_get_disp_status() =3D=3D MFC_REG_DEC_STATUS_DECODING_ONLY && mfc_core_get_dec_y_addr() =3D=3D 0) { mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); @@ -955,6 +1041,8 @@ static void __mfc_handle_frame_input(struct mfc_core *= core, mfc_err("failed in core_get_buf_ctrls\n"); =20 dec->consumed =3D 0; + if (IS_VP9_DEC(ctx) || IS_AV1_DEC(ctx)) + dec->has_multiframe =3D 0; =20 if (ctx->multi_view_enable && ctx->select_view =3D=3D 0) mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); @@ -978,7 +1066,7 @@ static void __mfc_handle_frame(struct mfc_core *core, unsigned int res_change, need_dpb_change, need_scratch_change; struct mfc_buf *mfc_buf =3D NULL; bool qos_update =3D false; - int index; + int index, profile; =20 dst_frame_status =3D mfc_core_get_disp_status(); res_change =3D mfc_core_get_res_change(); @@ -1048,6 +1136,15 @@ static void __mfc_handle_frame(struct mfc_core *core, index =3D mfc_buf->vb.vb2_buf.index; call_bop(ctx, core_restore_buf_ctrls, ctx, &ctx->src_ctrls[index]); } + + /* It could because of sub-view header (MV-HEVC) */ + if (!ctx->multi_view_enable) { + profile =3D mfc_core_get_profile(); + if (IS_MV_HEVC_DEC(ctx, profile)) { + mfc_debug(2, "Ready to enable, possibly a sub-view header.\n"); + ctx->ready_to_be_multi_view_enable =3D 1; + } + } return; } =20 @@ -1110,7 +1207,7 @@ static void __mfc_handle_frame(struct mfc_core *core, } =20 /* Detection for QoS weight */ - if (!dec->num_of_tile_over_4 && + if (!dec->num_of_tile_over_4 && !IS_HEVC_DEC(ctx) && mfc_core_get_num_of_tile() >=3D 4) { dec->num_of_tile_over_4 =3D 1; qos_update =3D true; @@ -1185,7 +1282,7 @@ static int __mfc_handle_seq_dec(struct mfc_core *core= , struct mfc_ctx *ctx) struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; struct mfc_dec *dec =3D ctx->dec_priv; struct mfc_buf *src_mb; - int i, is_interlace; + int i, is_interlace, profile; unsigned int strm_size, consumed; =20 if (ctx->src_fmt->fourcc !=3D V4L2_PIX_FMT_FIMV1) { @@ -1196,13 +1293,34 @@ static int __mfc_handle_seq_dec(struct mfc_core *co= re, struct mfc_ctx *ctx) mfc_info("[STREAM] resolution w: %d, h: %d\n", ctx->img_width, ctx->img_= height); } =20 - ctx->dpb_count =3D mfc_core_get_dpb_count(); + if (IS_AV1_DEC(ctx) || (IS_VP9_DEC(ctx) && UNDER_4K_RES(ctx))) + ctx->dpb_count =3D mfc_core_get_dpb_count() + 7 - MFC_EXTRA_DPB; + else + ctx->dpb_count =3D mfc_core_get_dpb_count(); mfc_ctx_debug(2, "dpb_count: %d\n", ctx->dpb_count); =20 ctx->scratch_buf_size =3D mfc_core_get_scratch_size(); =20 mfc_core_dec_get_crop_info(core, ctx); dec->mv_count =3D mfc_core_get_mv_count(); + profile =3D mfc_core_get_profile(); + + if (CODEC_422FORMAT(ctx) && dev->pdata->support_422) { + if (mfc_core_get_chroma_format() =3D=3D MFC_REG_D_CHROMA_422) { + ctx->is_422 =3D 1; + mfc_info("[STREAM] 422 chroma format\n"); + } + } + + if (IS_MV_HEVC_DEC(ctx, profile)) { + if (ctx->ready_to_be_multi_view_enable) { + mfc_debug(2, "It will be enabled later, pending DPB_FLUSH.\n"); + } else { + mfc_debug(2, "[MV-HEVC] enabled\n"); + ctx->multi_view_enable =3D 1; + ctx->select_view =3D MFC_VIEW_ID_MAIN; + } + } =20 if (ctx->img_width =3D=3D 0 || ctx->img_height =3D=3D 0) { mfc_err("[STREAM] wrong resolution w: %d, h: %d\n", @@ -1243,7 +1361,7 @@ static int __mfc_handle_seq_dec(struct mfc_core *core= , struct mfc_ctx *ctx) strm_size =3D mfc_dec_get_strm_size(ctx, src_mb); mfc_debug(2, "[STREAM] header size, %d, %#x, consumed, %d, %#x\n", strm_size, strm_size, consumed, consumed); - if ((IS_H264_DEC(ctx)) && + if ((IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx) || IS_HEVC_DEC(ctx)) && (consumed > 0 && strm_size > consumed)) { dec->consumed +=3D consumed; mfc_debug(2, "[STREAM] there is remained bytes(%d) after header parsing= \n", @@ -1257,6 +1375,13 @@ static int __mfc_handle_seq_dec(struct mfc_core *cor= e, struct mfc_ctx *ctx) mfc_debug(2, "[FRAME] display delay for first frame %d\n", dec->frame_display_delay); =20 + if (IS_VP9_DEC(ctx)) { + dec->color_range =3D mfc_core_get_color_range(); + dec->color_space =3D mfc_core_get_color_space(); + mfc_debug(2, "color range: %d, color space: %d, It's valid for VP9\n", + dec->color_range, dec->color_space); + } + mfc_change_state(core_ctx, MFCINST_HEAD_PARSED); =20 return 0; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c index 6950b8451c3d..0cc5d1d9433e 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c @@ -52,7 +52,8 @@ unsigned int mfc_get_frame_error_type(struct mfc_ctx *ctx= , unsigned int err) return MFC_ERR_FRAME_NO_ERR; } =20 - if (mfc_get_warn(err) =3D=3D MFC_REG_ERR_BROKEN_LINK) { + if ((IS_VC1_RCV_DEC(ctx) && (mfc_get_warn(err) =3D=3D MFC_REG_ERR_SYNC_PO= INT_NOT_RECEIVED)) || + (mfc_get_warn(err) =3D=3D MFC_REG_ERR_BROKEN_LINK)) { mfc_ctx_debug(2, "[FRAME] Broken frame error (%d)\n", mfc_get_warn(err)); return MFC_ERR_FRAME_BROKEN; } else if (mfc_get_warn(err) =3D=3D MFC_REG_ERR_SYNC_POINT_NOT_RECEIVED) { @@ -104,6 +105,7 @@ int mfc_core_set_dec_codec_buffers(struct mfc_core_ctx = *core_ctx) int buf_size; int align_gap; unsigned int reg =3D 0; + unsigned int av1_static_buf_size =3D 0; =20 buf_addr =3D core_ctx->codec_buf.daddr; buf_size =3D core_ctx->codec_buf.size; @@ -124,14 +126,50 @@ int mfc_core_set_dec_codec_buffers(struct mfc_core_ct= x *core_ctx) buf_addr +=3D ctx->scratch_buf_size; buf_size -=3D ctx->scratch_buf_size; =20 - if (IS_H264_DEC(ctx)) + if (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx) || IS_HEVC_DEC(ctx) || IS_AV= 1_DEC(ctx)) MFC_CORE_WRITEL(ctx->mv_size, MFC_REG_D_MV_BUFFER_SIZE); =20 + if (IS_VP9_DEC(ctx)) { + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_D_STATIC_BUFFER_ADDR); + MFC_CORE_WRITEL(DEC_STATIC_BUFFER_SIZE, MFC_REG_D_STATIC_BUFFER_SIZE); + buf_addr +=3D DEC_STATIC_BUFFER_SIZE; + buf_size -=3D DEC_STATIC_BUFFER_SIZE; + } else if (IS_AV1_DEC(ctx)) { + av1_static_buf_size =3D DEC_AV1_STATIC_BUFFER_SIZE(ctx->img_width, ctx->= img_height); + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_D_STATIC_BUFFER_ADDR); + MFC_CORE_WRITEL(av1_static_buf_size, MFC_REG_D_STATIC_BUFFER_SIZE); + buf_addr +=3D av1_static_buf_size; + buf_size -=3D av1_static_buf_size; + } + + if (IS_MPEG4_DEC(ctx) && dec->loop_filter_mpeg4) { + mfc_debug(2, "Add DPB for loop filter of MPEG4\n"); + for (i =3D 0; i < NUM_MPEG4_LF_BUF; i++) { + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_D_POST_FILTER_LUMA_DPB0 + (4 * i)= ); + buf_addr +=3D ctx->loopfilter_luma_size; + buf_size -=3D ctx->loopfilter_luma_size; + + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_D_POST_FILTER_CHROMA_DPB0 + (4 * = i)); + buf_addr +=3D ctx->loopfilter_chroma_size; + buf_size -=3D ctx->loopfilter_chroma_size; + } + reg |=3D ((dec->loop_filter_mpeg4 & MFC_REG_D_INIT_BUF_OPT_LF_CTRL_MASK) + << MFC_REG_D_INIT_BUF_OPT_LF_CTRL_SHIFT); + } + reg |=3D (dec->is_dynamic_dpb << MFC_REG_D_INIT_BUF_OPT_DYNAMIC_DPB_SET_S= HIFT); =20 + if (CODEC_NOT_CODED(ctx)) { + reg |=3D BIT(MFC_REG_D_INIT_BUF_OPT_COPY_NOT_CODED_SHIFT); + mfc_debug(2, "Notcoded frame copy mode start\n"); + } + /* 16byte align, It is valid only for VP9 */ reg &=3D ~BIT(MFC_REG_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN); - + if (IS_VP9_DEC(ctx) && MFC_FEATURE_SUPPORT(dev, dev->pdata->vp9_stride_al= ign)) { + reg &=3D ~(0x3 << MFC_REG_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN); + reg |=3D (0x2 << MFC_REG_D_INIT_BUF_OPT_STRIDE_SIZE_ALIGN); + } reg &=3D ~BIT(MFC_REG_D_INIT_BUF_OPT_TWO_MODE_ENABLE_SHIFT); if (IS_MULTI_MODE(ctx)) { reg |=3D BIT(MFC_REG_D_INIT_BUF_OPT_TWO_MODE_ENABLE_SHIFT); @@ -158,7 +196,7 @@ int mfc_core_set_dec_codec_buffers(struct mfc_core_ctx = *core_ctx) =20 frame_size_mv =3D ctx->mv_size; MFC_CORE_WRITEL(dec->mv_count, MFC_REG_D_NUM_MV); - if (IS_H264_DEC(ctx)) { + if (IS_H264_DEC(ctx) || IS_H264_MVC_DEC(ctx) || IS_HEVC_DEC(ctx) || IS_AV= 1_DEC(ctx)) { if (ctx->mv_buffer_allocated && buf_size && buf_size > ctx->mv_buf.dma_buf->size) { mfc_info("[MEMINFO] Not enough MV buf size %d alloc size %zu\n", diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h index e7c28b2f2b5d..08f74bd56f3f 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h @@ -89,12 +89,15 @@ >> MFC_REG_D_H264_INFO_MBAFF_FRAME_FLAG_SHIFT)\ & MFC_REG_D_H264_INFO_MBAFF_FRAME_FLAG_MASK) =20 +#define mfc_core_get_aspect_ratio() MFC_CORE_READL(MFC_REG_D_DISPLAY_ASPEC= T_RATIO) #define mfc_core_get_img_width() MFC_CORE_READL(MFC_REG_D_DISPLAY_FRAME_W= IDTH) #define mfc_core_get_img_height() MFC_CORE_READL(MFC_REG_D_DISPLAY_FRAME_H= EIGHT) #define mfc_core_get_disp_y_addr() MFC_CORE_DMA_READL(MFC_REG_D_DISPLAY_LU= MA_ADDR) #define mfc_core_get_dec_y_addr() MFC_CORE_DMA_READL(MFC_REG_D_DECODED_LUM= A_ADDR) #define mfc_core_get_crc_luma() MFC_CORE_READL(MFC_REG_D_DISPLAY_FIRST_PL= ANE_CRC) #define mfc_core_get_crc_chroma() MFC_CORE_READL(MFC_REG_D_DISPLAY_SECOND_= PLANE_CRC) +#define mfc_core_get_crc_luma_2bit() MFC_CORE_READL(MFC_REG_D_DISPLAY_FIRS= T_PLANE_2BIT_CRC) +#define mfc_core_get_crc_chroma_2bit() MFC_CORE_READL(MFC_REG_D_DISPLAY_SE= COND_PLANE_2BIT_CRC) =20 /* kind of interrupt */ #define mfc_core_get_int_err() MFC_CORE_READL(MFC_REG_ERROR_CODE) @@ -104,10 +107,13 @@ #define mfc_core_get_dpb_count() MFC_CORE_READL(MFC_REG_D_MIN_NUM_DPB) #define mfc_core_get_min_dpb_size(x) \ MFC_CORE_READL(MFC_REG_D_MIN_FIRST_PLANE_DPB_SIZE + ((x) * 4)) +#define mfc_core_get_min_dpb_size_2bit(x) \ + MFC_CORE_READL(MFC_REG_D_MIN_FIRST_PLANE_2BIT_DPB_SIZE + ((x) * 4)) #define mfc_core_get_scratch_size() MFC_CORE_READL(MFC_REG_D_MIN_SCRATCH_= BUFFER_SIZE) #define mfc_core_get_stride_size(x) \ MFC_CORE_READL(MFC_REG_D_FIRST_PLANE_DPB_STRIDE_SIZE + ((x) * 4)) - +#define mfc_core_get_stride_size_2bit(x) \ + MFC_CORE_READL(MFC_REG_D_FIRST_PLANE_2BIT_DPB_STRIDE_SIZE + ((x) * 4)) #define mfc_core_get_mv_count() MFC_CORE_READL(MFC_REG_D_MIN_NUM_MV) #define mfc_core_get_inst_no() MFC_CORE_READL(MFC_REG_RET_INSTANCE_ID) #define mfc_core_get_sei_avail() MFC_CORE_READL(MFC_REG_D_SEI_AVAIL) @@ -133,14 +139,27 @@ #define mfc_core_get_sei_avail_mastering_display() ((MFC_CORE_READL(MFC_RE= G_D_SEI_AVAIL) \ >> MFC_REG_D_SEI_AVAIL_MASTERING_DISPLAY_SHIFT) \ & MFC_REG_D_SEI_AVAIL_MASTERING_DISPLAY_MASK) - +#define mfc_core_get_sei_avail_st_2094_40() ((MFC_CORE_READL(MFC_REG_D_SE= I_AVAIL) \ + >> MFC_REG_D_SEI_AVAIL_ST_2094_40_SHIFT) \ + & MFC_REG_D_SEI_AVAIL_ST_2094_40_MASK) +#define mfc_core_get_sei_nal_meta_status() ((MFC_CORE_READL(MFC_REG_METADA= TA_STATUS) \ + >> MFC_REG_SEI_NAL_STATUS_SHIFT) \ + & MFC_REG_SEI_NAL_STATUS_MASK) #define mfc_core_get_video_signal_type() ((MFC_CORE_READL(MFC_REG_D_VIDEO_= SIGNAL_TYPE) \ >> MFC_REG_D_VIDEO_SIGNAL_TYPE_FLAG_SHIFT) \ & MFC_REG_D_VIDEO_SIGNAL_TYPE_FLAG_MASK) #define mfc_core_get_colour_description() ((MFC_CORE_READL(MFC_REG_D_VIDEO= _SIGNAL_TYPE) \ >> MFC_REG_D_COLOUR_DESCRIPTION_FLAG_SHIFT) \ & MFC_REG_D_COLOUR_DESCRIPTION_FLAG_MASK) - +#define mfc_core_get_primaries() ((MFC_CORE_READL(MFC_REG_D_VIDEO_SIGNAL_= TYPE) \ + >> MFC_REG_D_COLOUR_PRIMARIES_SHIFT) \ + & MFC_REG_D_COLOUR_PRIMARIES_MASK) +#define mfc_core_get_transfer() ((MFC_CORE_READL(MFC_REG_D_VIDEO_SIGNAL_= TYPE) \ + >> MFC_REG_D_TRANSFER_CHARACTERISTICS_SHIFT) \ + & MFC_REG_D_TRANSFER_CHARACTERISTICS_MASK) +#define mfc_core_get_matrix_coeff() ((MFC_CORE_READL(MFC_REG_D_VIDEO_SIGN= AL_TYPE) \ + >> MFC_REG_D_MATRIX_COEFFICIENTS_SHIFT) \ + & MFC_REG_D_MATRIX_COEFFICIENTS_MASK) #define mfc_core_get_black_bar_pos_x() ((MFC_CORE_READL(MFC_REG_D_BLACK_B= AR_START_POS) \ >> MFC_REG_D_BLACK_BAR_START_X_SHIFT) \ & MFC_REG_D_BLACK_BAR_START_X_MASK) @@ -160,12 +179,25 @@ & MFC_REG_D_MVC_VIEW_ID_DEC_MASK) #define mfc_core_get_mvc_view_id_disp_order() (MFC_CORE_READL(MFC_REG_D_MV= C_VIEW_ID) \ & MFC_REG_D_MVC_VIEW_ID_DISP_MASK) +#define mfc_core_get_mvc_left_view_id() ((MFC_CORE_READL(MFC_REG_D_MVC_VI= EW_ID) \ + >> MFC_REG_D_MVC_LEFT_VIEW_ID_SHIFT) \ + & MFC_REG_D_MVC_LEFT_VIEW_ID_MASK) #define mfc_core_get_mvc_right_view_id() ((MFC_CORE_READL(MFC_REG_D_MVC_VI= EW_ID) \ >> MFC_REG_D_MVC_RIGHT_VIEW_ID_SHIFT) \ & MFC_REG_D_MVC_RIGHT_VIEW_ID_MASK) #define mfc_core_get_profile() (MFC_CORE_READL(MFC_REG_D_DECODED_PICTURE_= PROFILE) \ & MFC_REG_D_DECODED_PIC_PROFILE_MASK) - +#define mfc_core_get_level() ((MFC_CORE_READL(MFC_REG_D_DECODED_PICTURE_P= ROFILE) \ + >> MFC_REG_D_PIC_LEVEL_SHIFT) \ + & MFC_REG_D_PIC_LEVEL_MASK) +#define mfc_core_get_luma_bit_depth_minus8() ((MFC_CORE_READL \ + (MFC_REG_D_DECODED_PICTURE_PROFILE) \ + >> MFC_REG_D_BIT_DEPTH_LUMA_MINUS8_SHIFT) \ + & MFC_REG_D_BIT_DEPTH_LUMA_MINUS8_MASK) +#define mfc_core_get_chroma_bit_depth_minus8() ((MFC_CORE_READL \ + (MFC_REG_D_DECODED_PICTURE_PROFILE) \ + >> MFC_REG_D_BIT_DEPTH_CHROMA_MINUS8_SHIFT) \ + & MFC_REG_D_BIT_DEPTH_CHROMA_MINUS8_MASK) #define mfc_core_get_display_delay() \ ((MFC_CORE_READL(MFC_REG_D_DECODED_PICTURE_PROFILE) \ >> MFC_REG_D_DISPLAY_DELAY_SHIFT) \ @@ -177,10 +209,34 @@ #define mfc_core_get_dec_used_flag() (((unsigned long)(MFC_CORE_READL \ (MFC_REG_D_USED_DPB_FLAG_UPPER)) << 32) | \ MFC_CORE_READL(MFC_REG_D_USED_DPB_FLAG_LOWER)) - +#define mfc_core_get_chroma_format() (MFC_CORE_READL(MFC_REG_D_CHROMA_FOR= MAT) \ + & MFC_REG_D_CHROMA_FORMAT_MASK) +#define mfc_core_get_color_range() ((MFC_CORE_READL(MFC_REG_D_CHROMA_FORM= AT) \ + >> MFC_REG_D_COLOR_RANGE_SHIFT) \ + & MFC_REG_D_COLOR_RANGE_MASK) +#define mfc_core_get_color_space() ((MFC_CORE_READL(MFC_REG_D_CHROMA_FORM= AT) \ + >> MFC_REG_D_COLOR_SPACE_SHIFT) \ + & MFC_REG_D_COLOR_SPACE_MASK) #define mfc_core_get_num_of_tile() ((MFC_CORE_READL(MFC_REG_D_DECODED_STA= TUS) \ >> MFC_REG_DEC_STATUS_NUM_OF_TILE_SHIFT) \ & MFC_REG_DEC_STATUS_NUM_OF_TILE_MASK) +#define mfc_core_get_lcu_size() (MFC_CORE_READL(MFC_REG_D_HEVC_INFO) \ + & MFC_REG_D_HEVC_INFO_LCU_SIZE_MASK) +#define mfc_core_get_disp_res_change() ((MFC_CORE_READL(MFC_REG_D_VP9_INF= O) \ + >> MFC_REG_D_VP9_INFO_DISP_RES_SHIFT) \ + & MFC_REG_D_VP9_INFO_DISP_RES_MASK) +#define mfc_core_get_disp_res_change_av1() ((MFC_CORE_READL(MFC_REG_D_AV1_= INFO) \ + >> MFC_REG_D_AV1_INFO_DISP_RES_SHIFT) \ + & MFC_REG_D_AV1_INFO_DISP_RES_MASK) +#define mfc_core_get_showable_frame() ((MFC_CORE_READL(MFC_REG_D_AV1_INFO= ) \ + >> MFC_REG_D_AV1_INFO_SHOWABLE_FRAME_SHIFT) \ + & MFC_REG_D_AV1_INFO_SHOWABLE_FRAME_MASK) +#define mfc_core_get_multiple_show_frame() ((MFC_CORE_READL(MFC_REG_D_AV1_= INFO) \ + >> MFC_REG_D_AV1_INFO_MULTIPLE_SHOW_SHIFT) \ + & MFC_REG_D_AV1_INFO_MULTIPLE_SHOW_MASK) +#define mfc_core_get_hevc_pic_output_flag() ((MFC_CORE_READL(MFC_REG_D_HEV= C_INFO) \ + >> MFC_REG_D_HEVC_INFO_PIC_OUTPUT_FLAG_SHIFT) \ + & MFC_REG_D_HEVC_INFO_PIC_OUTPUT_FLAG_MASK) =20 static inline void mfc_core_dec_get_crop_info(struct mfc_core *core, struct mfc_ctx *ctx) @@ -201,6 +257,15 @@ static inline void mfc_core_dec_get_crop_info(struct m= fc_core *core, dec->cr_bot =3D bottom; } =20 +static inline void mfc_core_clear_roi_enable(struct mfc_core *core) +{ + unsigned int reg =3D 0; + + reg =3D MFC_CORE_READL(MFC_REG_E_RC_ROI_CTRL); + reg &=3D ~(0x1); + MFC_CORE_WRITEL(reg, MFC_REG_E_RC_ROI_CTRL); +} + static inline void mfc_core_update_tag(struct mfc_core *core, struct mfc_c= tx *ctx, int tag) { diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_run.c index 118108f910e2..127d19c4d1cb 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c @@ -369,6 +369,10 @@ int mfc_core_run_dec_frame(struct mfc_core *core, stru= ct mfc_ctx *ctx) last_frame =3D __mfc_check_last_frame(core_ctx, src_mb); ret =3D mfc_core_cmd_dec_one_frame(core, ctx, last_frame, src_index); =20 + if (dec->consumed && IS_MULTI_MODE(ctx) && !CODEC_MULTIFRAME(ctx)) { + mfc_debug(2, "[STREAM][2CORE] clear consumed for next core\n"); + dec->consumed =3D 0; + } return ret; } =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c index 3097a6c0bf14..626c8db5f93b 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_dec_vb2.c @@ -9,6 +9,7 @@ * Himanshu Dewangan, */ =20 +#include "mfc_dec_v4l2.h" #include "mfc_dec_vb2.h" =20 #include "mfc_rm.h" --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 448CE25BF1B for ; Tue, 30 Sep 2025 03:56:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204620; cv=none; b=au7jaXccxbaIL2kmm63E6az70UzIRPkE35NELcnASMZhUzPjSVBftqInm81FaOpkpA6kGnpmJj0k4BLk6K1GEo0eA3MFmsRYlOmY8y3fIYH/58vNI3Jb/+3lmSIHNMexeP90q55DPDUkuETidKLj37dxF7ioXoEhlfT5oRgh9jo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204620; c=relaxed/simple; bh=DBrV/J9qgqOgwNFuro6IptZjktyBMOwoaHH8YdjROwY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=ghu/PSce6+KHUJHidYQZ0gapeoED1ne6i1zJTROcRJgMqfcs4tbCBc245AbNuFvZEfZIRBFOHo0rJEjMfEZAWNBxpCCLznQQ4et4HA796vhtGRVK66DV8PsDOW0aY49h8XNL9+5WGazJ5tQu3RuZ/0dedvJ0RQgVhUMt9iqgpvs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=k04Ncv83; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="k04Ncv83" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035653epoutp024b9b0a5d23b422516364063e9868d0b4~p80aII3uh2602826028epoutp02A for ; Tue, 30 Sep 2025 03:56:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035653epoutp024b9b0a5d23b422516364063e9868d0b4~p80aII3uh2602826028epoutp02A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204613; bh=VeXF1m2BbK2hp0/O8EEgqvFvywhv7lBUuwNT4zVSRM4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k04Ncv83nI9dm91++oSX4PFEqJFKP3QRxMXBPHTwObnFfDPN5rDVi1AFl/ctcQzZf x//5tcYahrhMIEbDn+9uvlHNTIuYV1se0GAZ8Ed98bepfRhaZAYB2ak9jTIDNxhDBm Bu2sPbIaUdW2lKkfL5xBB+MBJW/82nFml2Ew/uiQ= Received: from epsnrtp01.localdomain (unknown [182.195.42.153]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035652epcas5p4cad3997a587d62d5cf95e8b3f4cdf399~p80ZUdbDH2408224082epcas5p4s; Tue, 30 Sep 2025 03:56:52 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.91]) by epsnrtp01.localdomain (Postfix) with ESMTP id 4cbPPv69h1z6B9m4; Tue, 30 Sep 2025 03:56:51 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPA id 20250930035651epcas5p2a45576829011bbf653c725b352c65140~p80X0_bSQ0812408124epcas5p21; Tue, 30 Sep 2025 03:56:51 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035647epsmtip13198e4ab5dda3d2556f480fed2ee8817~p80UW_bzs2938529385epsmtip1j; Tue, 30 Sep 2025 03:56:47 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 22/29] media: mfc: Add H.264 encoder support with buffer and QoS improvements Date: Tue, 30 Sep 2025 09:33:41 +0530 Message-Id: <20250930040348.3702923-23-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035651epcas5p2a45576829011bbf653c725b352c65140 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035651epcas5p2a45576829011bbf653c725b352c65140 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Integrat H.264 encoding. - Allocate encoder=E2=80=91specific context buffers and ROI memory. - Add detection macro and broadened codec=E2=80=91type validation for QoS utilities. - Introduce encoder=E2=80=91aware QoS tables and refined weight calculations (B=E2=80=91frames, reference count). - Update source=E2=80=91queue cleanup, framerate fallback, and timestamp=E2=80=91delta handling. - Make stride and source=E2=80=91size helpers encoder=E2=80=91aware; expose= d B=E2=80=91frame detection utility. - Expand context structures with encoder buffers, private fields, ROI, and parameters. - Add platform flags for encoder=E2=80=91specific QoS steps and feature support (e.g., RGB). Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_buf.c | 132 ++++++++++ .../samsung/exynos-mfc/base/mfc_buf.h | 3 + .../samsung/exynos-mfc/base/mfc_common.h | 24 +- .../samsung/exynos-mfc/base/mfc_data_struct.h | 231 ++++++++++++++++++ .../samsung/exynos-mfc/base/mfc_qos.c | 34 ++- .../samsung/exynos-mfc/base/mfc_queue.c | 31 +++ .../samsung/exynos-mfc/base/mfc_queue.h | 2 + .../exynos-mfc/base/mfc_rate_calculate.c | 30 ++- .../exynos-mfc/base/mfc_rate_calculate.h | 8 +- .../samsung/exynos-mfc/base/mfc_utils.c | 111 +++++++++ .../samsung/exynos-mfc/base/mfc_utils.h | 52 ++++ .../media/platform/samsung/exynos-mfc/mfc.c | 3 + 12 files changed, 654 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.c index 84f97ca230bb..0186fe3327f1 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c @@ -132,6 +132,9 @@ int mfc_alloc_instance_context(struct mfc_core_ctx *cor= e_ctx) case MFC_REG_CODEC_FIMV4_DEC: core_ctx->instance_ctx_buf.size =3D buf_size->other_dec_ctx; break; + case MFC_REG_CODEC_H264_ENC: + core_ctx->instance_ctx_buf.size =3D buf_size->h264_enc_ctx; + break; case MFC_REG_CODEC_AV1_DEC: core_ctx->instance_ctx_buf.size =3D buf_size->av1_dec_ctx; break; @@ -248,6 +251,64 @@ static void __mfc_dec_calc_codec_buffer_size(struct mf= c_core_ctx *core_ctx) NUM_MPEG4_LF_BUF); } =20 +static void __mfc_enc_calc_codec_buffer_size(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_enc *enc; + unsigned int mb_width, mb_height; + + enc =3D ctx->enc_priv; + enc->tmv_buffer_size =3D 0; + + mb_width =3D WIDTH_MB(ctx->crop_width); + mb_height =3D HEIGHT_MB(ctx->crop_height); + + /* default recon buffer size, it can be changed in case of 422, 10bit */ + enc->luma_dpb_size =3D + ALIGN(ENC_LUMA_DPB_SIZE(ctx->crop_width, ctx->crop_height), SZ_64); + enc->chroma_dpb_size =3D + ALIGN(ENC_CHROMA_DPB_SIZE(ctx->crop_width, ctx->crop_height), SZ_64); + + if (ctx->min_dpb_size[0] > enc->luma_dpb_size || + ctx->min_dpb_size[1] > enc->chroma_dpb_size) { + mfc_debug(2, + "[MEMINFO] recon DPB size changed (Luma: %zu -> %d, Chroma %zu -> %d)= \n", + enc->luma_dpb_size, + ctx->min_dpb_size[0], + enc->chroma_dpb_size, + ctx->min_dpb_size[1]); + enc->luma_dpb_size =3D ctx->min_dpb_size[0]; + enc->chroma_dpb_size =3D ctx->min_dpb_size[1]; + } + + /* Codecs have different memory requirements */ + switch (ctx->codec_mode) { + case MFC_REG_CODEC_H264_ENC: + enc->me_buffer_size =3D + ALIGN(ENC_V100_H264_ME_SIZE(mb_width, mb_height), SZ_256); + + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D + ctx->scratch_buf_size + enc->tmv_buffer_size + + (ctx->dpb_count * (enc->luma_dpb_size + + enc->chroma_dpb_size + enc->me_buffer_size)); + break; + default: + core_ctx->codec_buf.size =3D 0; + mfc_err("invalid codec type: %d\n", ctx->codec_mode); + break; + } + + mfc_debug(2, + "[MEMINFO] scratch: %zu, TMV: %zu, (recon luma: %zu, chroma: %zu, me: = %zu) x count %d\n", + ctx->scratch_buf_size, + enc->tmv_buffer_size, + enc->luma_dpb_size, + enc->chroma_dpb_size, + enc->me_buffer_size, + ctx->dpb_count); +} + /* Allocate codec buffers */ int mfc_alloc_codec_buffers(struct mfc_core_ctx *core_ctx) { @@ -259,6 +320,8 @@ int mfc_alloc_codec_buffers(struct mfc_core_ctx *core_c= tx) =20 if (ctx->type =3D=3D MFCINST_DECODER) { __mfc_dec_calc_codec_buffer_size(core_ctx); + } else if (ctx->type =3D=3D MFCINST_ENCODER) { + __mfc_enc_calc_codec_buffer_size(core_ctx); } else { mfc_err("invalid type: %d\n", ctx->type); return -EINVAL; @@ -401,6 +464,75 @@ void mfc_release_dbg_info_buffer(struct mfc_core *core) mfc_mem_special_buf_free(core->dev, &core->dbg_info_buf); } =20 +/* Allocation buffer of ROI macroblock information */ +static int __mfc_alloc_enc_roi_buffer(struct mfc_core_ctx *core_ctx, + size_t size, + struct mfc_special_buf *roi_buf) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_dev *dev =3D core->dev; + + roi_buf->size =3D size; + roi_buf->buftype =3D MFCBUF_NORMAL; + + if (!roi_buf->dma_buf) { + snprintf(roi_buf->name, MFC_NUM_SPECIAL_BUF_NAME, "ctx%d ROI", core_ctx-= >num); + if (mfc_mem_special_buf_alloc(dev, roi_buf)) { + mfc_err("[ROI] Allocating ROI buffer failed\n"); + return -ENOMEM; + } + } + memset(roi_buf->vaddr, 0, roi_buf->size); + + return 0; +} + +/* Wrapper : allocation ROI buffers */ +int mfc_alloc_enc_roi_buffer(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_enc *enc =3D ctx->enc_priv; + unsigned int mb_width, mb_height; + size_t size; + int i; + + mb_width =3D WIDTH_MB(ctx->crop_width); + mb_height =3D HEIGHT_MB(ctx->crop_height); + + switch (ctx->codec_mode) { + case MFC_REG_CODEC_H264_ENC: + size =3D ((((mb_width * (mb_height + 1) / 2) + 15) / 16) * 16) * 2; + break; + default: + mfc_debug(2, + "ROI not supported codec type(%d). Allocate with default size\n", + ctx->codec_mode); + size =3D mb_width * mb_height; + break; + } + + for (i =3D 0; i < MFC_MAX_EXTRA_BUF; i++) { + if (__mfc_alloc_enc_roi_buffer(core_ctx, size, &enc->roi_buf[i]) < 0) { + mfc_err("[ROI] Allocating remapping buffer[%d] failed\n", i); + return -ENOMEM; + } + } + + return 0; +} + +/* Release buffer of ROI macroblock information */ +void mfc_release_enc_roi_buffer(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_enc *enc =3D ctx->enc_priv; + int i; + + for (i =3D 0; i < MFC_MAX_EXTRA_BUF; i++) + if (enc->roi_buf[i].dma_buf) + mfc_mem_special_buf_free(ctx->dev, &enc->roi_buf[i]); +} + /* Allocate firmware */ int mfc_alloc_firmware(struct mfc_core *core) { diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.h index 6907cf6ac775..67bdfaed5dd6 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.h @@ -26,6 +26,9 @@ void mfc_release_codec_buffers(struct mfc_core_ctx *core_= ctx); int mfc_alloc_scratch_buffer(struct mfc_core_ctx *core_ctx); void mfc_release_scratch_buffer(struct mfc_core_ctx *core_ctx); =20 +int mfc_alloc_enc_roi_buffer(struct mfc_core_ctx *core_ctx); +void mfc_release_enc_roi_buffer(struct mfc_core_ctx *core_ctx); + int mfc_alloc_firmware(struct mfc_core *core); int mfc_load_firmware(struct mfc_core *core, struct mfc_special_buf *fw_buf, diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h index 5392c8566e42..bec6f88d5e44 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h @@ -170,6 +170,9 @@ ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_HEVC_DEC && \ (profile) =3D=3D MFC_REG_D_PROFILE_MULTIVIEW_HEVC_MAIN) =20 +/* Encoder codec mode check */ +#define IS_H264_ENC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_H264_ENC) + #define CODEC_NOT_CODED(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ (IS_MPEG4_DEC(_ctx) || IS_VC1_DEC(_ctx) || IS_VC1_RCV_DEC(_ctx) || \ @@ -217,7 +220,16 @@ }) #define CODEC_HAS_IDR(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ - (IS_H264_DEC(_ctx) || IS_H264_MVC_DEC(_ctx) || IS_HEVC_DEC(_ctx)); \ + (IS_H264_DEC(_ctx) || IS_H264_MVC_DEC(_ctx) || IS_HEVC_DEC(_ctx) || \ + IS_H264_ENC(_ctx)); \ +}) + +// Buffer container +#define IS_BUFFER_BATCH_MODE(ctx) ((ctx)->batch_mode =3D=3D 1) +#define IS_NO_HEADER_GENERATE(ctx, p) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + typeof(p) _p =3D (p); \ + (_p->seq_hdr_mode =3D=3D V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAM= E); \ }) =20 /* @@ -313,6 +325,13 @@ (_fps >=3D 240)); \ }) =20 +#define IS_MULTI_MODE_ENC_RES(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + (IS_MULTI_MODE_RES(_ctx) || \ + (OVER_UHD_RES(_ctx) && _ctx->operating_framerate >=3D 60000) || \ + (OVER_UHD_RES(_ctx) && mfc_is_enc_bframe(_ctx))); \ +}) + #define IS_BLACKBAR_OFF(ctx) ((ctx)->crop_height > 2160) =20 #define IS_SINGLE_FD(ctx, fmt) ((!(ctx)->rgb_bpp) && ((fmt)->mem_planes = =3D=3D 1)) @@ -404,6 +423,9 @@ static inline int mfc_core_get_pwr_ref_cnt(struct mfc_c= ore *core) =20 static inline int mfc_core_get_clk_ref_cnt(struct mfc_core *core) { + if (core->dev->pdata->support_hwacg =3D=3D MFC_HWACG_HWFW_CTRL) + return 1; + return atomic_read(&core->clk_ref); } =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct= .h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h index 6b93fe3ab138..6d34905a1cba 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h @@ -505,6 +505,9 @@ struct mfc_ctx_buf_size { size_t h264_dec_ctx; size_t av1_dec_ctx; size_t other_dec_ctx; + size_t h264_enc_ctx; + size_t hevc_enc_ctx; + size_t other_enc_ctx; size_t dbg_info_buf; }; =20 @@ -711,6 +714,13 @@ struct mfc_bw_data { }; =20 struct mfc_bw_info { + struct mfc_bw_data bw_enc_h264; + struct mfc_bw_data bw_enc_hevc; + struct mfc_bw_data bw_enc_hevc_10bit; + struct mfc_bw_data bw_enc_vp8; + struct mfc_bw_data bw_enc_vp9; + struct mfc_bw_data bw_enc_vp9_10bit; + struct mfc_bw_data bw_enc_mpeg4; struct mfc_bw_data bw_dec_h264; struct mfc_bw_data bw_dec_hevc; struct mfc_bw_data bw_dec_hevc_10bit; @@ -734,6 +744,7 @@ struct mfc_qos { unsigned int freq_mif; unsigned int mo_value; unsigned int mo_10bit_value; + unsigned int mo_uhd_enc60_value; unsigned int time_fw; unsigned int bts_scen_idx; const char *name; @@ -792,6 +803,7 @@ struct mfc_platdata { /* Formats */ unsigned int support_10bit; unsigned int support_422; + unsigned int support_rgb; /* Resolution */ unsigned int support_check_res; =20 @@ -807,11 +819,26 @@ struct mfc_platdata { struct mfc_feature black_bar; struct mfc_feature color_aspect_dec; struct mfc_feature static_info_dec; + struct mfc_feature color_aspect_enc; + struct mfc_feature static_info_enc; struct mfc_feature vp9_stride_align; struct mfc_feature mem_clear; struct mfc_feature wait_fw_status; + struct mfc_feature average_qp; + struct mfc_feature mv_search_mode; + struct mfc_feature enc_idr_flag; + struct mfc_feature min_quality_mode; + struct mfc_feature enc_capability; + struct mfc_feature enc_ts_delta; + struct mfc_feature wfd_rc_mode; + struct mfc_feature max_i_frame_size; struct mfc_feature hevc_pic_output_flag; =20 + /* Encoder default parameter */ + unsigned int enc_param_num; + unsigned int enc_param_addr[MFC_MAX_DEFAULT_PARAM]; + unsigned int enc_param_val[MFC_MAX_DEFAULT_PARAM]; + struct mfc_bw_info mfc_bw_info; unsigned int dynamic_weight; struct mfc_qos_weight qos_weight; @@ -835,7 +862,14 @@ struct mfc_platdata { =20 unsigned int scheduler; unsigned int pbs_num_prio; + + unsigned int enc_rgb_csc_by_fw; + /* HWAPG */ + unsigned int support_hwapg; + /* HWACG */ enum mfc_hwacg_type support_hwacg; + + unsigned int support_enc_mode1; unsigned int support_mv_hevc; }; =20 @@ -858,6 +892,7 @@ struct mfc_core_platdata { unsigned int tsmux_masterid; /* QoS */ unsigned int num_default_qos_steps; + unsigned int num_encoder_qos_steps; unsigned int max_mb; unsigned int max_hw_mb; unsigned int mfc_freq_control; @@ -994,6 +1029,9 @@ struct mfc_core_ops { /* for DEC */ void (*instance_dpb_flush)(struct mfc_core *core, struct mfc_ctx *ctx); int (*instance_init_buf)(struct mfc_core *core, struct mfc_ctx *ctx); + /* for ENC */ + void (*instance_q_flush)(struct mfc_core *core, struct mfc_ctx *ctx); + void (*instance_finishing)(struct mfc_core *core, struct mfc_ctx *ctx); }; =20 struct dump_info { @@ -1148,6 +1186,150 @@ struct mfc_core { struct mfc_core_memlog memlog; }; =20 +/** + * + */ +struct mfc_h264_enc_params { + enum v4l2_mpeg_video_h264_profile profile; + u8 level; + u8 interlace; + u16 open_gop_size; + u8 open_gop; + u8 _8x8_transform; + s8 loop_filter_alpha; + s8 loop_filter_beta; + enum v4l2_mpeg_video_h264_loop_filter_mode loop_filter_mode; + enum v4l2_mpeg_video_h264_entropy_mode entropy_mode; + u8 rc_frame_qp; + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_min_qp_p; + u8 rc_max_qp_p; + u8 rc_min_qp_b; + u8 rc_max_qp_b; + u8 rc_mb_dark; + u8 rc_mb_smooth; + u8 rc_mb_static; + u8 rc_mb_activity; + u8 rc_p_frame_qp; + u8 rc_b_frame_qp; + u8 ar_vui; + u8 sei_gen_enable; + u8 sei_fp_curr_frame_0; + + enum v4l2_mpeg_video_h264_vui_sar_idc ar_vui_idc; + u16 ext_sar_width; + u16 ext_sar_height; + + enum v4l2_mpeg_video_h264_hierarchical_coding_type hier_qp_type; + u32 hier_bit_layer[7]; + u8 hier_qp_layer[7]; + u8 hier_qp_enable; + u8 num_hier_layer; + u8 hier_ref_type; + u8 enable_ltr; + u8 num_of_ltr; + u32 set_priority; + u32 base_priority; + + enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_arrangement_type; + u32 fmo_enable; + u32 fmo_slice_map_type; + u32 fmo_slice_num_grp; + u32 fmo_run_length[4]; + u32 fmo_sg_dir; + u32 fmo_sg_rate; + u32 aso_enable; + u32 aso_slice_order[8]; + + u32 prepend_sps_pps_to_idr; + u32 vui_enable; +}; + +/** + * + */ +struct mfc_enc_params { + enum v4l2_mpeg_video_multi_slice_mode slice_mode; + u32 slice_mb; + u32 slice_bit; + u32 slice_mb_row; + + u32 gop_ctrl; + u32 gop_size; + u32 intra_refresh_mb; + u32 i_frm_ctrl_mode; + u32 i_frm_ctrl; + + u8 pad; + u8 pad_luma; + u8 pad_cb; + u8 pad_cr; + + u8 rc_mb; /* H.264: MFCv5, MPEG4/H.263: MFCv6 */ + u8 rc_pvc; + u8 rc_frame; + u8 drop_control; + u32 rc_bitrate; + u32 rc_framerate; + u16 rc_reaction_coeff; + u16 rc_frame_delta; /* MFC6.1 Only */ + u32 rc_framerate_res; + u32 max_i_frame_size; /* when RC_MODE is 5(CBR_I_LIMIT_WFD =3D CBR_BS) */ + + u32 config_qp; + u32 dynamic_qp; + + u8 frame_tag; + u8 ratio_intra; + u8 num_b_frame; /* H.264, HEVC, MPEG4 */ + u8 num_refs_for_p; /* H.264, HEVC, VP8, VP9 */ + enum v4l2_mpeg_video_header_mode seq_hdr_mode; + enum v4l2_mpeg_mfc51_video_frame_skip_mode frame_skip_mode; + u16 vbv_buf_size; + u8 num_hier_max_layer; + u8 hier_bitrate_ctrl; + u8 weighted_enable; + u8 roi_enable; + u8 ivf_header_disable; /* VP8, VP9 */ + u8 fixed_target_bit; + u8 min_quality_mode; /* H.264, HEVC when RC_MODE is 2(VBR) */ + u8 wp_two_pass_enable; + u8 adaptive_gop_enable; + + u32 check_color_range; + u32 color_range; + u32 colour_primaries; + u32 transfer_characteristics; + u32 matrix_coefficients; + + u32 static_info_enable; + u32 max_pic_average_light; + u32 max_content_light; + u32 max_display_luminance; + u32 min_display_luminance; + u32 white_point; + u32 display_primaries_0; + u32 display_primaries_1; + u32 display_primaries_2; + u32 chroma_qp_offset_cb; /* H.264, HEVC */ + u32 chroma_qp_offset_cr; /* H.264, HEVC */ + + u32 mv_search_mode; + u32 mv_hor_pos_l0; + u32 mv_hor_pos_l1; + u32 mv_ver_pos_l0; + u32 mv_ver_pos_l1; + u32 mv_hor_range; + u32 mv_ver_range; + + u8 timing_info_enable; /* H.264, HEVC */ + + union { + struct mfc_h264_enc_params h264; + } codec; +}; + struct mfc_ctx_ctrl_val { int has_new; int val; @@ -1234,6 +1416,14 @@ struct temporal_layer_info { unsigned int temporal_layer_bitrate[VIDEO_MAX_TEMPORAL_LAYERS]; }; =20 +struct mfc_enc_roi_info { + char *addr; + int size; + int upper_qp; + int lower_qp; + bool enable; +}; + struct mfc_user_shared_handle { int fd; struct dma_buf *dma_buf; @@ -1379,6 +1569,43 @@ struct mfc_dec { unsigned char frame_cnt; }; =20 +struct mfc_enc { + unsigned int dst_buf_size; + unsigned int header_size; + + enum v4l2_mpeg_mfc51_video_frame_type frame_type; + enum v4l2_mpeg_mfc51_video_force_frame_type force_frame_type; + + unsigned int idr_flag; + + size_t luma_dpb_size; + size_t chroma_dpb_size; + size_t me_buffer_size; + size_t tmv_buffer_size; + + unsigned int slice_mode; + unsigned int slice_size_mb; + unsigned int slice_size_bits; + unsigned int in_slice; + unsigned int buf_full; + + int config_qp; + + struct mfc_fmt *uncomp_fmt; + + int fake_src; + int empty_data; + + int roi_index; + struct mfc_special_buf roi_buf[MFC_MAX_EXTRA_BUF]; + struct mfc_enc_roi_info roi_info[MFC_MAX_EXTRA_BUF]; + + struct mfc_enc_params params; + + struct mfc_user_shared_handle sh_handle_svc; + struct mfc_user_shared_handle sh_handle_roi; +}; + struct mfc_resolution { int width; int height; @@ -1414,6 +1641,7 @@ struct mfc_multi_view_buf_info { struct mfc_ctx { struct mfc_dev *dev; struct mfc_dec *dec_priv; + struct mfc_enc *enc_priv; =20 int num; int prio; @@ -1542,6 +1770,9 @@ struct mfc_ctx { dma_addr_t last_src_addr; dma_addr_t last_dst_addr[MFC_MAX_PLANES]; =20 + /* buffer container */ + int batch_mode; + bool mem_type_10bit; =20 unsigned long gdc_ready_buf_ino; diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_qos.c index 9922c2396b94..40541e2d626f 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c @@ -20,17 +20,25 @@ =20 static inline int __mfc_core_get_qos_steps(struct mfc_core *core, int tabl= e_type) { - return core->core_pdata->num_default_qos_steps; + if (table_type =3D=3D MFC_QOS_TABLE_TYPE_ENCODER) + return core->core_pdata->num_encoder_qos_steps; + else + return core->core_pdata->num_default_qos_steps; } =20 static inline struct mfc_qos *__mfc_core_get_qos_table(struct mfc_core *co= re, int table_type) { - return core->core_pdata->default_qos_table; + if (table_type =3D=3D MFC_QOS_TABLE_TYPE_ENCODER) + return core->core_pdata->encoder_qos_table; + else + return core->core_pdata->default_qos_table; } =20 static inline unsigned long __mfc_qos_add_weight(struct mfc_ctx *ctx, unsi= gned long mb) { + struct mfc_enc *enc =3D ctx->enc_priv; struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_enc_params *p; struct mfc_qos_weight *qos_weight =3D &ctx->dev->pdata->qos_weight; u32 num_planes =3D ctx->dst_fmt->num_planes; int weight =3D 1000; @@ -39,6 +47,8 @@ static inline unsigned long __mfc_qos_add_weight(struct m= fc_ctx *ctx, unsigned l switch (ctx->codec_mode) { case MFC_REG_CODEC_H264_DEC: case MFC_REG_CODEC_H264_MVC_DEC: + case MFC_REG_CODEC_H264_ENC: + case MFC_REG_CODEC_H264_MVC_ENC: weight =3D (weight * 100) / qos_weight->weight_h264_hevc; mfc_ctx_debug(3, "[QoS] h264, hevc codec, weight: %d\n", weight / 10); if (num_planes =3D=3D 3) { @@ -107,6 +117,16 @@ static inline unsigned long __mfc_qos_add_weight(struc= t mfc_ctx *ctx, unsigned l mfc_ctx_err("[QoS] wrong codec_mode (%d), no weight\n", ctx->codec_mode); } =20 + if (enc) { + p =3D &enc->params; + if (mfc_is_enc_bframe(ctx)) { + weight =3D (weight * 100) / qos_weight->weight_bframe; + mfc_ctx_debug(3, "[QoS] B frame encoding, weight: %d\n", weight / 10); + } else if (IS_H264_ENC(ctx) && (p->num_refs_for_p >=3D 2)) { + weight =3D (weight * 100) / qos_weight->weight_num_of_ref; + mfc_ctx_debug(3, "[QoS] num of ref >=3D 2, weight: %d\n", weight / 10); + } + } if (dec) { if (dec->num_of_tile_over_4) { weight =3D (weight * 100) / qos_weight->weight_num_of_tile; @@ -223,7 +243,10 @@ void mfc_qos_get_portion(struct mfc_core *core, struct= mfc_ctx *ctx) if (!ctx->mfc_qos_portion) return; =20 - table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; + if (ctx->type =3D=3D MFCINST_ENCODER) + table_type =3D MFC_QOS_TABLE_TYPE_ENCODER; + else + table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; =20 num_qos_steps =3D __mfc_core_get_qos_steps(core, table_type); qos_table =3D __mfc_core_get_qos_table(core, table_type); @@ -390,7 +413,8 @@ bool mfc_qos_mb_calculate(struct mfc_core *core, struct= mfc_core_ctx *core_ctx, =20 if (ctx->type =3D=3D MFCINST_DECODER) table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; - + else + table_type =3D MFC_QOS_TABLE_TYPE_ENCODER; num_qos_steps =3D __mfc_core_get_qos_steps(core, table_type); =20 if (atomic_read(&core->qos_req_cur)) { @@ -652,6 +676,8 @@ void __mfc_qos_calculate(struct mfc_core *core, struct = mfc_ctx *ctx, int delete) =20 if (dec_found) table_type =3D MFC_QOS_TABLE_TYPE_DEFAULT; + else + table_type =3D MFC_QOS_TABLE_TYPE_ENCODER; =20 num_qos_steps =3D __mfc_core_get_qos_steps(core, table_type); qos_table =3D __mfc_core_get_qos_table(core, table_type); diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c index 6dc9bc7a1873..81e649bb4e1e 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.c @@ -539,6 +539,37 @@ void mfc_cleanup_queue(spinlock_t *plock, struct mfc_b= uf_queue *queue) spin_unlock_irqrestore(plock, flags); } =20 +void mfc_cleanup_enc_src_queue(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + unsigned long flags; + struct mfc_buf *mfc_buf =3D NULL; + struct mfc_buf_queue *queue =3D &core_ctx->src_buf_queue; + int i; + + spin_lock_irqsave(&ctx->buf_queue_lock, flags); + + while (!list_empty(&queue->head)) { + mfc_buf =3D list_entry(queue->head.next, struct mfc_buf, list); + + for (i =3D 0; i < mfc_buf->vb.vb2_buf.num_planes; i++) { + if (IS_BUFFER_BATCH_MODE(ctx)) + mfc_bufcon_put_daddr(ctx, mfc_buf, i); + vb2_set_plane_payload(&mfc_buf->vb.vb2_buf, i, 0); + } + + vb2_buffer_done(&mfc_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + list_del(&mfc_buf->list); + queue->count--; + } + + INIT_LIST_HEAD(&queue->head); + queue->count =3D 0; + ctx->batch_mode =3D 0; + + spin_unlock_irqrestore(&ctx->buf_queue_lock, flags); +} + void mfc_print_dpb_queue(struct mfc_core_ctx *core_ctx, struct mfc_dec *de= c) { struct mfc_ctx *ctx =3D core_ctx->ctx; diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_queue.h index 055e7a23a527..cfe889e0dcc9 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_queue.h @@ -145,6 +145,8 @@ void mfc_return_buf_to_ready_queue(struct mfc_ctx *ctx, =20 void mfc_cleanup_queue(spinlock_t *plock, struct mfc_buf_queue *queue); =20 +void mfc_cleanup_enc_src_queue(struct mfc_core_ctx *core_ctx); + void mfc_print_dpb_queue(struct mfc_core_ctx *core_ctx, struct mfc_dec *de= c); void mfc_print_dpb_queue_with_lock(struct mfc_core_ctx *core_ctx, struct m= fc_dec *dec); int mfc_get_available_dpb_count(struct mfc_core_ctx *core_ctx); diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calcul= ate.c b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.c index 94a555c900d7..c88101d1d1c1 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.c @@ -293,6 +293,7 @@ static unsigned long __mfc_rate_get_fps_by_timestamp(st= ruct mfc_ctx *ctx, struct mfc_ts_control *ts, struct timespec64 *time, int type) { + struct list_head *head =3D &ts->ts_list; struct mfc_timestamp *temp_ts; int found; int index =3D 0; @@ -304,6 +305,13 @@ static unsigned long __mfc_rate_get_fps_by_timestamp(s= truct mfc_ctx *ctx, unsigned long flags; u64 current_time; =20 + if (IS_BUFFER_BATCH_MODE(ctx)) { + if (ctx->dev->debugfs.debug_ts =3D=3D 1) + mfc_ctx_info("[BUFCON][TS] Keep framerate if buffer batch mode is used,= %ldfps\n", + ctx->framerate); + return ctx->framerate; + } + spin_lock_irqsave(&ts->ts_lock, flags); if (list_empty(&ts->ts_list)) { __mfc_rate_add_timestamp(ctx, ts, time, &ts->ts_list); @@ -362,6 +370,19 @@ static unsigned long __mfc_rate_get_fps_by_timestamp(s= truct mfc_ctx *ctx, spin_unlock_irqrestore(&ts->ts_lock, flags); } =20 + /* Calculation the last frame fps for drop control */ + if (ctx->type =3D=3D MFCINST_ENCODER) { + temp_ts =3D list_entry(head->prev, struct mfc_timestamp, list); + if (temp_ts->interval > USEC_PER_SEC) { + if (ts->ts_is_full) + mfc_ctx_info("[TS] ts interval(%d) couldn't over 1sec(1fps)\n", + temp_ts->interval); + ts->ts_last_interval =3D 0; + } else { + ts->ts_last_interval =3D temp_ts->interval; + } + } + if (!ts->ts_is_full) { if (ctx->dev->debugfs.debug_ts =3D=3D 1) mfc_ctx_info("[TS] ts doesn't full, keep %ld fps\n", ctx->framerate); @@ -581,7 +602,14 @@ int mfc_rate_check_perf_ctx(struct mfc_ctx *ctx, int m= ax_runtime) =20 op_fps =3D ctx->operating_framerate; if (op_fps =3D=3D 0) { - op_fps =3D ctx->src_q_framerate; + if (ctx->type =3D=3D MFCINST_ENCODER && ctx->enc_priv->params.rc_framera= te) + op_fps =3D ctx->enc_priv->params.rc_framerate; + else + /* + * In case of non-real-time, check the buffer queueing rate + * because the non-real-time is best effort scenario. (ex. video editin= g) + */ + op_fps =3D ctx->src_q_framerate; mfc_ctx_debug(2, "[PRIO][rt %d] use fps: %d\n", ctx->rt, op_fps); } =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calcul= ate.h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.h index 2452e6ee56dd..530ad6fed489 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_rate_calculate.h @@ -45,6 +45,8 @@ static inline void mfc_rate_reset_framerate(struct mfc_ct= x *ctx) { if (ctx->type =3D=3D MFCINST_DECODER) ctx->framerate =3D DEC_DEFAULT_FPS; + else if (ctx->type =3D=3D MFCINST_ENCODER) + ctx->framerate =3D ENC_DEFAULT_FPS; =20 mfc_ctx_debug(3, "[QoS] reset ctx->framrate: %lu\n", ctx->framerate); } @@ -80,7 +82,11 @@ static inline unsigned long mfc_rate_get_rt_framerate(st= ruct mfc_ctx *ctx, enum =20 framerate =3D ctx->operating_framerate; =20 - if (rt =3D=3D MFC_RT_UNDEFINED || rt =3D=3D MFC_NON_RT) { + if (rt =3D=3D MFC_RT) { + if (framerate =3D=3D 0 && ctx->type =3D=3D MFCINST_ENCODER && + ctx->enc_priv->params.rc_framerate) + framerate =3D ctx->enc_priv->params.rc_framerate; + } else if (rt =3D=3D MFC_RT_UNDEFINED || rt =3D=3D MFC_NON_RT) { framerate =3D ctx->framerate; } else { if (ctx->src_ts.ts_is_full) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c index 83cdae3dee57..6dba87fb951b 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.c @@ -108,6 +108,59 @@ static void __mfc_set_dec_stride(struct mfc_ctx *ctx, } } =20 +static void __mfc_set_enc_stride(struct mfc_ctx *ctx, + struct mfc_raw_info *raw, + struct mfc_fmt *fmt) +{ + int i, y_stride, stride_align =3D 16; + + y_stride =3D ctx->bytesperline[0]; + if (!y_stride) + y_stride =3D ALIGN(ctx->img_width, stride_align); + + for (i =3D 0; i < MFC_MAX_PLANES; i++) { + raw->stride[i] =3D 0; + raw->stride_2bits[i] =3D 0; + } + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + case V4L2_PIX_FMT_NV12MT_16X16: + case V4L2_PIX_FMT_NV12MT: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + /* use user stride */ + for (i =3D 0; i < ctx->src_fmt->num_planes; i++) { + raw->stride[i] =3D ctx->bytesperline[i]; + if (!raw->stride[i]) + raw->stride[i] =3D ALIGN(ctx->img_width, stride_align); + } + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB32X: + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_RGB32: + raw->stride[0] =3D y_stride * (ctx->rgb_bpp / 8); + break; + default: + mfc_ctx_err("Invalid pixelformat : %s\n", fmt->name); + break; + } + + for (i =3D 0; i < ctx->src_fmt->num_planes; i++) { + if ((raw->stride[i] % stride_align) !=3D 0) { + mfc_ctx_err("[FRAME] Forced to change stride[%d] %d for %dbyte alignmen= t\n", + i, raw->stride[i], stride_align); + raw->stride[i] =3D ALIGN(raw->stride[i], stride_align); + } + } +} + void mfc_set_linear_stride_size(struct mfc_ctx *ctx, struct mfc_raw_info *= raw, struct mfc_fmt *fmt) { /* @@ -117,6 +170,8 @@ void mfc_set_linear_stride_size(struct mfc_ctx *ctx, st= ruct mfc_raw_info *raw, s */ if (ctx->type =3D=3D MFCINST_DECODER) __mfc_set_dec_stride(ctx, raw, fmt); + else + __mfc_set_enc_stride(ctx, raw, fmt); } =20 void mfc_dec_calc_dpb_size(struct mfc_ctx *ctx, struct mfc_raw_info *raw, = struct mfc_fmt *fmt) @@ -201,6 +256,62 @@ void mfc_dec_calc_dpb_size(struct mfc_ctx *ctx, struct= mfc_raw_info *raw, struct } } =20 +void mfc_enc_calc_src_size(struct mfc_ctx *ctx) +{ + struct mfc_raw_info *raw; + int i, extra; + + raw =3D &ctx->raw_buf; + raw->total_plane_size =3D 0; + extra =3D MFC_LINEAR_BUF_SIZE; + + mfc_set_linear_stride_size(ctx, raw, ctx->src_fmt); + + for (i =3D 0; i < raw->num_planes; i++) { + raw->plane_size[i] =3D 0; + raw->plane_size_2bits[i] =3D 0; + } + + switch (ctx->src_fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: + raw->plane_size[0] =3D raw->stride[0] * ALIGN(ctx->img_height, 16) + ext= ra; + raw->plane_size[1] =3D raw->stride[1] * ALIGN(ctx->img_height, 16) / 2 += extra; + raw->plane_size[2] =3D raw->stride[2] * ALIGN(ctx->img_height, 16) / 2 += extra; + break; + case V4L2_PIX_FMT_NV12MT_16X16: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + raw->plane_size[0] =3D raw->stride[0] * ALIGN(ctx->img_height, 16) + ext= ra; + raw->plane_size[1] =3D raw->stride[1] * ALIGN(ctx->img_height, 16) / 2 += extra; + break; + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + raw->plane_size[0] =3D raw->stride[0] * ALIGN(ctx->img_height, 16) + ext= ra; + raw->plane_size[1] =3D raw->stride[1] * ALIGN(ctx->img_height, 16) + ext= ra; + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB32X: + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_RGB32: + raw->plane_size[0] =3D raw->stride[0] * ctx->img_height; + break; + default: + mfc_ctx_err("Invalid pixel format(%d)\n", ctx->src_fmt->fourcc); + break; + } + + for (i =3D 0; i < raw->num_planes; i++) { + raw->total_plane_size +=3D raw->plane_size[i]; + mfc_ctx_debug(2, "[FRAME] Plane[%d] size =3D %d, stride =3D %d\n", + i, raw->plane_size[i], raw->stride[i]); + } + + mfc_ctx_debug(2, "[FRAME] total plane size: %d\n", raw->total_plane_size); +} + void mfc_calc_base_addr(struct mfc_ctx *ctx, struct vb2_buffer *vb, struct mfc_fmt *fmt) { diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h index dedfb049e6fc..a127f330fe16 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h @@ -212,10 +212,37 @@ static inline int mfc_check_mb_flag(struct mfc_buf *m= fc_buf, enum mfc_mb_flag f) return 0; } =20 +static inline int mfc_is_enc_bframe(struct mfc_ctx *ctx) +{ + struct mfc_enc *enc; + struct mfc_enc_params *p; + int hier_qp_type =3D -EINVAL; + u8 num_hier_layer =3D 0; + + if (ctx->type !=3D MFCINST_ENCODER) + return 0; + enc =3D ctx->enc_priv; + if (!enc) + return 0; + + p =3D &enc->params; + if (IS_H264_ENC(ctx)) { + num_hier_layer =3D p->codec.h264.num_hier_layer; + hier_qp_type =3D (int)p->codec.h264.hier_qp_type; + } + if (enc->params.num_b_frame || + (num_hier_layer >=3D 2 && + hier_qp_type =3D=3D V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B)) + return 1; + + return 0; +} + int mfc_check_vb_with_fmt(struct mfc_fmt *fmt, struct vb2_buffer *vb); int mfc_check_resolution(struct mfc_ctx *ctx); void mfc_set_linear_stride_size(struct mfc_ctx *ctx, struct mfc_raw_info *= raw, struct mfc_fmt *fmt); void mfc_dec_calc_dpb_size(struct mfc_ctx *ctx, struct mfc_raw_info *raw, = struct mfc_fmt *fmt); +void mfc_enc_calc_src_size(struct mfc_ctx *ctx); void mfc_calc_base_addr(struct mfc_ctx *ctx, struct vb2_buffer *vb, struct= mfc_fmt *fmt); void mfc_set_view_buf_info(struct mfc_ctx *ctx, int mem_planes, int num_fd_depth_map, int num_fd_sub_view_meta); @@ -355,6 +382,31 @@ static inline void mfc_ctx_change_idle_mode(struct mfc= _ctx *ctx, ctx->idle_mode =3D idle_mode; } =20 +static inline int mfc_enc_get_ts_delta(struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + int ts_delta =3D 0; + + if (!ctx->src_ts.ts_last_interval) { + ts_delta =3D p->rc_framerate_res / p->rc_framerate; + mfc_ctx_debug(3, "[DFR] default delta: %d\n", ts_delta); + } else { + /* + * FRAME_DELTA specifies the amount of + * increment of frame modulo base time. + * - delta unit =3D framerate resolution / fps + * - fps =3D 1000000(usec per sec) / timestamp interval + * For the sophistication of calculation, we will divide later. + * Excluding H.263, resolution is fixed to 10000, + * so thie is also divided into pre-calculated 100. + * (Preventing both overflow and calculation duplication) + */ + ts_delta =3D ctx->src_ts.ts_last_interval / 100; + } + return ts_delta; +} + static inline void mfc_print_ctx_info(struct mfc_ctx *ctx) { struct mfc_fmt *codec =3D NULL; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc.c b/drivers/medi= a/platform/samsung/exynos-mfc/mfc.c index 293a353c49fa..db17448eae13 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc.c @@ -1128,6 +1128,9 @@ struct mfc_ctx_buf_size mfc_ctx_buf_size =3D { .h264_dec_ctx =3D PAGE_ALIGN(0x200000), /* 1.6MB */ .av1_dec_ctx =3D PAGE_ALIGN(0x19000), /* 100KB */ .other_dec_ctx =3D PAGE_ALIGN(0xF000), /* 60KB */ + .h264_enc_ctx =3D PAGE_ALIGN(0x19000), /* 100KB */ + .hevc_enc_ctx =3D PAGE_ALIGN(0xC800), /* 50KB */ + .other_enc_ctx =3D PAGE_ALIGN(0xC800), /* 50KB */ .dbg_info_buf =3D PAGE_ALIGN(0x1000), /* 4KB for DEBUG INFO */ }; =20 --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) (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 3947727E071 for ; Tue, 30 Sep 2025 03:56:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204624; cv=none; b=UIGL6MZq5JAJ7koQT32MNlSa+bVEu9/BW5RPShkxwEplXgDNp3NUrw1KmcfhsI7brU1cPUzqa7JXZ8KlzBvHx8Pf3GwvzpFEsqMGRKH6J2JXr6f+H5M8kZaNeNfSd0I8UTs7FGvtb+Gc/HEkYFkEZkeCfglWWy2r42djV8LkZWU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204624; c=relaxed/simple; bh=lKh/aujwQc86B3wWTLTo74qOfkou6uPIN9jIjUdlqCY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=l1z9XAzLLN2+eIrw1JRe9vEjX9R4GAhrDf/KDSTWGboF223NrIzOu6Z+iYxaoG+ypHK/5KL6aF/MRxv/xMQrGwMOfoK+nCSGRSue3NvK6eOcfJIOxNw/WRcSVzzsJsonKS5bXoaY/fiB0dEBPYx13KCvUg+VLQ9ENMkqFIKjhRI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=HKeTS5a8; arc=none smtp.client-ip=203.254.224.24 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="HKeTS5a8" Received: from epcas5p1.samsung.com (unknown [182.195.41.39]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250930035656epoutp01aa0ef60d3bd081f26465299db55f562b~p80c3SAa33202032020epoutp01j for ; Tue, 30 Sep 2025 03:56:56 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250930035656epoutp01aa0ef60d3bd081f26465299db55f562b~p80c3SAa33202032020epoutp01j DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204616; bh=ETNz1qi8jzlIkhnJIGvK2dh55eK+c1AzYhvuRmECjc4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HKeTS5a8LJLYe6PMFk2uh5NSYdYkEtZX42+XyrlKYo4xNRCVRNjhHhXZGi0zZbmWk fHXC5m49myfEoOHg9mFCWrtUmhCW7i1/jcHxQTqSluGfdcWXr+tfUhCUjNNu1qXwHu YtDMh25SwGm61fRSSZfQQbTDlfyBZCzsDhRtVUCA= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035656epcas5p1e07ca6fe54967c2515c169f609a4e41d~p80cakZGG2306123061epcas5p18; Tue, 30 Sep 2025 03:56:56 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.86]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPPz0rxjz2SSKf; Tue, 30 Sep 2025 03:56:55 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035654epcas5p1cbb6f3d0fe0dd25758085d041f020846~p80a4O6FG1575615756epcas5p1b; Tue, 30 Sep 2025 03:56:54 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035651epsmtip111276e49f91b36a99e9a1ea266ec2280~p80YQC8ka2938529385epsmtip1k; Tue, 30 Sep 2025 03:56:51 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 23/29] media: mfc: Add encoder parameters, ROI & QoS support Date: Tue, 30 Sep 2025 09:33:42 +0530 Message-Id: <20250930040348.3702923-24-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035654epcas5p1cbb6f3d0fe0dd25758085d041f020846 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035654epcas5p1cbb6f3d0fe0dd25758085d041f020846 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Introduce a generic encoder=E2=80=91parameter framework (slice mode, ASO order, QP config, timestamp delta). - Expose full V4L2 encoder controls (frame tag, RC limits, level/profile, ROI, weighted prediction, hierarchical coding). - Implemente ROI handling by building ROI registers from per=E2=80=91buffer data. - Update encoder resource manager: fixed core integration, excluded from migration/load=E2=80=91balancing, ensured proper finalization per buffer type, and add encoder=E2=80=91specific QoS step table. - Disable bitrate=E2=80=91driven load=E2=80=91balancing for encoders; framerate updates now apply only to non=E2=80=91encoder contexts. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 2 +- .../samsung/exynos-mfc/mfc_core_enc_param.c | 848 ++++++++++++++++++ .../samsung/exynos-mfc/mfc_core_enc_param.h | 23 + .../samsung/exynos-mfc/mfc_ctx_ctrl.c | 803 +++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_rm.c | 89 +- .../platform/samsung/exynos-mfc/mfc_rm.h | 2 + 6 files changed, 1755 insertions(+), 12 deletions(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_= param.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_= param.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index b6b312ae7f22..a257d5b0a576 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -13,7 +13,7 @@ exynos_mfc-y +=3D mfc_core_hwlock.o mfc_core_intlock.o mf= c_core_run.o exynos_mfc-y +=3D mfc_core_pm.o exynos_mfc-y +=3D mfc_core_sync.o mfc_core_sched_prio.o #Core HW access layer -exynos_mfc-y +=3D mfc_core_buf_ctrl.o mfc_core_cmd.o +exynos_mfc-y +=3D mfc_core_enc_param.o mfc_core_buf_ctrl.o mfc_core_cmd.o exynos_mfc-y +=3D mfc_core_hw_reg_api.o mfc_core_reg_api.o #Plugin interface layer #Plugin control layer diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.c= b/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.c new file mode 100644 index 000000000000..9ff949df04ab --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.c @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_core_enc_param.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_core_enc_param.h" + +#include "mfc_core_reg_api.h" + +/* Definition */ +#define VBR_BIT_SAVE 20 +#define CBR_FIX_MAX 10 +#define CBR_I_LIMIT_WFD 6 +#define CBR_I_LIMIT_MAX 5 + +static int mfc_transfer_to_rgb_format_ctrl[][2] =3D { + { MFC_TRANSFER_RESERVED, 1}, + { MFC_TRANSFER_BT709, 1}, + { MFC_TRANSFER_UNSPECIFIED, 1}, + { MFC_TRANSFER_RESERVED, 1}, + { MFC_TRANSFER_GAMMA_22, 1}, + { MFC_TRANSFER_GAMMA_28, 1}, + { MFC_TRANSFER_SMPTE_170M, 0}, + { MFC_TRANSFER_SMPTE_240M, 1}, + { MFC_TRANSFER_LINEAR, 1}, + { MFC_TRANSFER_LOGARITHMIC, 1}, + { MFC_TRANSFER_LOGARITHMIC_S, 1}, + { MFC_TRANSFER_XvYCC, 1}, + { MFC_TRANSFER_BT1361, 1}, + { MFC_TRANSFER_SRGB, 1}, + { MFC_TRANSFER_BT2020_1, 1}, + { MFC_TRANSFER_BT2020_2, 1}, + { MFC_TRANSFER_ST2084, 1}, + { MFC_TRANSFER_ST428, 1}, + { MFC_TRANSFER_HLG, 1}, +}; + +void mfc_core_set_slice_mode(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + + /* multi-slice control */ + if (enc->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) + MFC_CORE_RAW_WRITEL((enc->slice_mode + 0x4), MFC_REG_E_MSLICE_MODE); + else if (enc->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_R= OW) + MFC_CORE_RAW_WRITEL((enc->slice_mode - 0x2), MFC_REG_E_MSLICE_MODE); + else if (enc->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXE= D_BYTES) + MFC_CORE_RAW_WRITEL((enc->slice_mode + 0x3), MFC_REG_E_MSLICE_MODE); + else + MFC_CORE_RAW_WRITEL(enc->slice_mode, MFC_REG_E_MSLICE_MODE); + + /* multi-slice MB number or bit size */ + if (enc->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB || + enc->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_ROW) { + MFC_CORE_RAW_WRITEL(enc->slice_size_mb, MFC_REG_E_MSLICE_SIZE_MB); + } else if (enc->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BY= TES || + enc->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYT= ES) { + MFC_CORE_RAW_WRITEL(enc->slice_size_bits, MFC_REG_E_MSLICE_SIZE_BITS); + } else { + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_MSLICE_SIZE_MB); + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_MSLICE_SIZE_BITS); + } +} + +void mfc_core_set_aso_slice_order_h264(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_h264_enc_params *p_264 =3D &p->codec.h264; + int i; + + if (p_264->aso_enable) { + for (i =3D 0; i < 8; i++) + MFC_CORE_RAW_WRITEL + (p_264->aso_slice_order[i], + MFC_REG_E_H264_ASO_SLICE_ORDER_0 + i * 4); + } +} + +void mfc_core_set_enc_config_qp(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + unsigned int reg =3D 0; + + if (!p->rc_frame && !p->rc_mb && p->dynamic_qp) { + reg =3D MFC_CORE_READL(MFC_REG_E_FIXED_PICTURE_QP); + reg &=3D ~(0xFF000000); + reg |=3D (enc->config_qp & 0xFF) << 24; + MFC_CORE_WRITEL(reg, MFC_REG_E_FIXED_PICTURE_QP); + } +} + +void mfc_core_set_enc_ts_delta(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + unsigned int reg =3D 0; + int ts_delta; + + ts_delta =3D mfc_enc_get_ts_delta(ctx); + + reg =3D MFC_CORE_READL(MFC_REG_E_TIME_STAMP_DELTA); + reg &=3D ~(0xFFFF); + reg |=3D (ts_delta & 0xFFFF); + MFC_CORE_WRITEL(reg, MFC_REG_E_TIME_STAMP_DELTA); + if (ctx->src_ts.ts_last_interval) + mfc_ctx_debug(3, "[DFR] fps %d -> %ld, delta: %d, reg: %#x\n", + p->rc_framerate, USEC_PER_SEC / ctx->src_ts.ts_last_interval, + ts_delta, reg); + else + mfc_ctx_debug(3, "[DFR] fps %d -> 0, delta: %d, reg: %#x\n", + p->rc_framerate, ts_delta, reg); +} + +static void __mfc_set_gop_size(struct mfc_core *core, + struct mfc_ctx *ctx, + int ctrl_mode) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + unsigned int reg =3D 0; + + if (ctrl_mode) { + p->i_frm_ctrl_mode =3D 1; + /* + * gop_ctrl 1: gop_size means the I frame interval + * gop_ctrl 0: gop_size means the number of P frames. + */ + if (p->gop_ctrl) { + p->i_frm_ctrl =3D p->gop_size; + } else { + p->i_frm_ctrl =3D p->gop_size * (p->num_b_frame + 1); + if (p->i_frm_ctrl >=3D 0x3FFFFFFF) { + mfc_ctx_info("I frame interval is bigger than max: %d\n", + p->i_frm_ctrl); + p->i_frm_ctrl =3D 0x3FFFFFFF; + } + } + } else { + p->i_frm_ctrl_mode =3D 0; + p->i_frm_ctrl =3D p->gop_size; + } + + mfc_ctx_debug(2, "I frame interval: %d, (P: %d, B: %d), ctrl mode: %d, go= p ctrl: %d\n", + p->i_frm_ctrl, + p->gop_ctrl ? (p->gop_size / (p->num_b_frame + 1)) : p->gop_size, + p->num_b_frame, p->i_frm_ctrl_mode, p->gop_ctrl); + + /* pictype : IDR period, number of B */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_GOP_CONFIG); + mfc_clear_set_bits(reg, 0xFFFF, 0, p->i_frm_ctrl); + mfc_clear_set_bits(reg, 0x1, 19, p->i_frm_ctrl_mode); + /* if B frame is used, the performance falls by half */ + mfc_clear_set_bits(reg, 0x3, 16, p->num_b_frame); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_GOP_CONFIG); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_GOP_CONFIG2); + mfc_clear_set_bits(reg, 0x3FFF, 0, (p->i_frm_ctrl >> 16)); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_GOP_CONFIG2); +} + +static void __mfc_set_default_params(struct mfc_core *core, struct mfc_ctx= *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + int i; + + mfc_ctx_debug(2, "Set default param - enc_param_num: %d\n", + dev->pdata->enc_param_num); + for (i =3D 0; i < dev->pdata->enc_param_num; i++) { + if (i >=3D MFC_MAX_DEFAULT_PARAM) { + mfc_ctx_err("enc_param_num(%d) is over max number(%d)\n", + dev->pdata->enc_param_num, + MFC_MAX_DEFAULT_PARAM); + break; + } + MFC_CORE_RAW_WRITEL + (dev->pdata->enc_param_val[i], dev->pdata->enc_param_addr[i]); + mfc_ctx_debug(2, "Set default param[%d] - addr:0x%x, val:0x%x\n", + i, dev->pdata->enc_param_addr[i], + dev->pdata->enc_param_val[i]); + } +} + +static void __mfc_init_regs(struct mfc_core *core, struct mfc_ctx *ctx) +{ + /* Register initialization */ + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_FRAME_INSERTION); + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_ROI_BUFFER_ADDR); + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_PARAM_CHANGE); + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_PICTURE_TAG); + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_METADATA_BUFFER_ADDR); + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_METADATA_BUFFER_SIZE); +} + +static int __mfc_get_rgb_format_ctrl(struct mfc_ctx *ctx, struct mfc_enc_p= arams *p) +{ + int ret =3D 0; + + /* + * User set color VUI information as below regardless of the standard. + * --------------------------------------------- + * VP9 | others + * ----------------------|---------------------- + * color space only | primaries, transfer + * (primaries interface) | ,matrix + * --------------------------------------------- + * However, in case of RGB encoding, the F/W need to know + * which to use RGB pixel format transform characteristic. + * So, driver converts it based on the user's VUI information. + * Return value + * 0: ITU-R BT.601 + * 1: ITU-R BT.709 + * If Set to 3, use the coefficients of CSC formula determined by firmware + * on COLOR_SPACE and COLOUR_PRIMARIES of E_VIDEO_SIGNAL_TYPE. + * 3: Determined by firmware + */ + + if (ctx->dev->pdata->enc_rgb_csc_by_fw) { + ret =3D 3; + mfc_ctx_debug(2, "[RGB] coefficients of CSC formula using VUI by F/W\n"); + } else { + ret =3D mfc_transfer_to_rgb_format_ctrl[p->transfer_characteristics][1]; + mfc_ctx_debug(2, "[RGB] transfer %d converts to RGB format ctrl %s\n", + p->transfer_characteristics, ret ? "BT.709" : "BT.601"); + } + + return ret; +} + +static void __mfc_set_video_signal_type(struct mfc_core *core, struct mfc_= ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + unsigned int reg =3D 0; + + if ((ctx->src_fmt->type & MFC_FMT_RGB) && !dev->pdata->enc_rgb_csc_by_fw)= { + /* VIDEO_SIGNAL_TYPE_FLAG */ + mfc_set_bits(reg, 0x1, 31, 0x1); + /* COLOUR_DESCRIPTION_PRESENT_FLAG */ + mfc_set_bits(reg, 0x1, 24, 0x1); + } else if (MFC_FEATURE_SUPPORT(dev, dev->pdata->color_aspect_enc) && + p->check_color_range) { + /* VIDEO_SIGNAL_TYPE_FLAG */ + mfc_set_bits(reg, 0x1, 31, 0x1); + /* COLOR_RANGE */ + if (!(ctx->src_fmt->type & MFC_FMT_RGB)) + mfc_set_bits(reg, 0x1, 25, p->color_range); + + if (p->colour_primaries && + p->transfer_characteristics && + p->matrix_coefficients !=3D 3) { + /* COLOUR_DESCRIPTION_PRESENT_FLAG */ + mfc_set_bits(reg, 0x1, 24, 0x1); + /* COLOUR_PRIMARIES */ + mfc_set_bits(reg, 0xFF, 16, p->colour_primaries); + /* TRANSFER_CHARACTERISTICS */ + mfc_set_bits(reg, 0xFF, 8, p->transfer_characteristics); + /* MATRIX_COEFFICIENTS */ + mfc_set_bits(reg, 0xFF, 0, p->matrix_coefficients); + } + mfc_ctx_debug(2, "[HDR] %s ENC Color aspect: range(%s), pri(%d), trans(%= d), mat(%d)\n", + "H264", + p->color_range ? "Full" : "Limited", p->colour_primaries, + p->transfer_characteristics, p->matrix_coefficients); + } + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VIDEO_SIGNAL_TYPE); +} + +static void __mfc_set_enc_params(struct mfc_core *core, struct mfc_ctx *ct= x) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + unsigned int reg =3D 0; + unsigned int fps =3D ctx->operating_framerate / 1000; + + mfc_ctx_debug_enter(); + + __mfc_init_regs(core, ctx); + __mfc_set_default_params(core, ctx); + + /* width */ + MFC_CORE_RAW_WRITEL(ctx->crop_width, MFC_REG_E_CROPPED_FRAME_WIDTH); + /* height */ + MFC_CORE_RAW_WRITEL(ctx->crop_height, MFC_REG_E_CROPPED_FRAME_HEIGHT); + /* cropped offset */ + mfc_set_bits(reg, MFC_REG_E_FRAME_CROP_OFFSET_MASK, + MFC_REG_E_FRAME_CROP_OFFSET_LEFT, ctx->crop_left); + mfc_set_bits(reg, MFC_REG_E_FRAME_CROP_OFFSET_MASK, + MFC_REG_E_FRAME_CROP_OFFSET_TOP, ctx->crop_top); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_FRAME_CROP_OFFSET); + + /* multi-slice control */ + /* multi-slice MB number or bit size */ + enc->slice_mode =3D p->slice_mode; + + if (p->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB) { + enc->slice_size_mb =3D p->slice_mb; + } else if (p->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTE= S || + p->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES= ) { + enc->slice_size_bits =3D p->slice_bit; + } else if (p->slice_mode =3D=3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB_R= OW) { + enc->slice_size_mb =3D p->slice_mb_row * ((ctx->crop_width + 15) / 16); + } else { + enc->slice_size_mb =3D 0; + enc->slice_size_bits =3D 0; + } + + mfc_core_set_slice_mode(core, ctx); + + /* config qp */ + enc->config_qp =3D p->config_qp; + + /* cyclic intra refresh */ + MFC_CORE_RAW_WRITEL(p->intra_refresh_mb, MFC_REG_E_IR_SIZE); + + mfc_core_set_pixel_format(core, ctx, ctx->src_fmt->fourcc); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_ENC_OPTIONS); + /* frame skip mode */ + mfc_clear_set_bits(reg, 0x3, 0, p->frame_skip_mode); + /* seq header ctrl */ + mfc_clear_set_bits(reg, 0x1, 2, p->seq_hdr_mode); + /* cyclic intra refresh */ + mfc_clear_bits(reg, 0x1, 4); + if (p->intra_refresh_mb) + mfc_set_bits(reg, 0x1, 4, 0x1); + /* disable seq header generation if OTF mode */ + mfc_clear_bits(reg, 0x1, 6); + + /* 'NON_REFERENCE_STORE_ENABLE' for debugging */ + mfc_clear_bits(reg, 0x1, 9); + + /* Predict motion search mode */ + mfc_clear_set_bits(reg, 0x3, 22, p->mv_search_mode); + + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_ENC_OPTIONS); + + if (p->mv_hor_range) { + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_MV_HOR_RANGE); + mfc_clear_set_bits(reg, 0x3fff, 0, p->mv_hor_range); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_MV_HOR_RANGE); + } + if (p->mv_ver_range) { + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_MV_VER_RANGE); + mfc_clear_set_bits(reg, 0x3fff, 0, p->mv_ver_range); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_MV_VER_RANGE); + } + + if (p->mv_search_mode =3D=3D 2) { + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_MV_HOR_RANGE); + mfc_clear_set_bits(reg, 0xff, 16, p->mv_hor_pos_l0); + mfc_clear_set_bits(reg, 0xff, 24, p->mv_hor_pos_l1); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_MV_HOR_RANGE); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_MV_VER_RANGE); + mfc_clear_set_bits(reg, 0xff, 16, p->mv_ver_pos_l0); + mfc_clear_set_bits(reg, 0xff, 24, p->mv_ver_pos_l1); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_MV_VER_RANGE); + mfc_ctx_debug(2, "MV search mode(%d), HOR (L0: %d, L1: %d), VER (L0: %d,= L1: %d)\n", + p->mv_search_mode, + p->mv_hor_pos_l0, p->mv_hor_pos_l1, + p->mv_ver_pos_l0, p->mv_ver_pos_l1); + } + + if (ctx->src_fmt->type & MFC_FMT_RGB) { + reg =3D MFC_CORE_RAW_READL(MFC_REG_PIXEL_FORMAT); + mfc_clear_set_bits(reg, 0x1, 8, p->color_range); + mfc_clear_set_bits(reg, 0x3, 6, __mfc_get_rgb_format_ctrl(ctx, p)); + mfc_ctx_debug(2, "[RGB] enc color_range %d, primaries %d, transfer %d\n", + p->color_range, p->colour_primaries, + p->transfer_characteristics); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_PIXEL_FORMAT); + } + + /* padding control & value */ + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_PADDING_CTRL); + if (p->pad) { + reg =3D 0; + /** enable */ + mfc_set_bits(reg, 0x1, 31, 0x1); + /** cr value */ + mfc_set_bits(reg, 0xFF, 16, p->pad_cr); + /** cb value */ + mfc_set_bits(reg, 0xFF, 8, p->pad_cb); + /** y value */ + mfc_set_bits(reg, 0xFF, 0, p->pad_luma); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_PADDING_CTRL); + } + + /* rate control config. */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG); + /* macroblock level rate control */ + mfc_clear_set_bits(reg, 0x1, 8, p->rc_mb); + /* frame-level rate control */ + mfc_clear_set_bits(reg, 0x1, 9, p->rc_frame); + /* drop control */ + mfc_clear_set_bits(reg, 0x1, 10, p->drop_control); + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->enc_ts_delta)) + mfc_clear_set_bits(reg, 0x1, 20, 1); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_CONFIG); + + /* + * Delta value for framerate is timestamp(ms * 10) diff. + * ex) 30fps: 333, 60fps: 166 + * Resolution unit is most sophisticated value + * that can be determined within 16bit. + * F/W calculates fps through resolution / delta. + * ex) 10000 / 166 =3D 60fps + */ + p->rc_frame_delta =3D p->rc_framerate_res / p->rc_framerate; + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_FRAME_RATE); + mfc_clear_set_bits(reg, 0xFFFF, 16, p->rc_framerate_res); + mfc_clear_set_bits(reg, 0xFFFF, 0, p->rc_frame_delta); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_FRAME_RATE); + + /* bit rate */ + ctx->kbps =3D p->rc_bitrate / SZ_1K; + MFC_CORE_RAW_WRITEL(p->rc_bitrate, MFC_REG_E_RC_BIT_RATE); + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->max_i_frame_size)) { + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_OPTIONS); + mfc_clear_set_bits(reg, 0xFFFF, 0, p->max_i_frame_size); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_OPTIONS); + } + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_MODE); + mfc_clear_bits(reg, 0x7, 0); + mfc_clear_bits(reg, 0x3, 4); + mfc_clear_bits(reg, 0xFF, 8); + if (p->rc_frame) { + if (p->rc_reaction_coeff <=3D CBR_I_LIMIT_MAX) { + mfc_set_bits(reg, 0x7, 0, MFC_REG_E_RC_CBR_I_LIMIT_VT); + /* + * Ratio of intra for max frame size + * is controlled when only CBR_I_LIMIT_VT mode. + * And CBR_I_LIMIT_VT mode is valid for H.264, HEVC codec + */ + if (p->ratio_intra) + mfc_set_bits(reg, 0xFF, 8, p->ratio_intra); + } else if (p->rc_reaction_coeff <=3D CBR_FIX_MAX) { + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->wfd_rc_mode) && + p->rc_reaction_coeff <=3D CBR_I_LIMIT_WFD) { + mfc_set_bits(reg, 0x7, 0, MFC_REG_E_RC_CBR_I_LIMIT_WFD); + } else { + mfc_set_bits(reg, 0x7, 0, MFC_REG_E_RC_CBR_FIX); + } + } else { + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->wfd_rc_mode) && + p->rc_reaction_coeff <=3D VBR_BIT_SAVE) { + mfc_set_bits(reg, 0x7, 0, MFC_REG_E_RC_VBR_BS); + } else { + mfc_set_bits(reg, 0x7, 0, MFC_REG_E_RC_VBR); + } + } + + if (p->rc_mb) + mfc_set_bits(reg, 0x3, 4, p->rc_pvc); + } + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->min_quality_mode) && p->min_qual= ity_mode) { + mfc_set_bits(reg, 0x1, 7, p->min_quality_mode); + mfc_ctx_debug(2, "MIN quality mode is enabled\n"); + } + + mfc_ctx_debug(3, "RC_MODE) rc coeff: %d, wfd_rc_mode: %d, rc_mode: %#x, m= ax I size: %d\n", + p->rc_reaction_coeff, + MFC_FEATURE_SUPPORT(dev, dev->pdata->wfd_rc_mode), + reg, p->max_i_frame_size); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_MODE); + + /* high quality mode */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_HIGH_QUALITY_MODE); + if (p->wp_two_pass_enable) { + mfc_clear_set_bits(reg, 0x1, 0, p->wp_two_pass_enable); + mfc_ctx_debug(2, "WP two pass encoding is enabled\n"); + } + if (p->adaptive_gop_enable) { + mfc_clear_set_bits(reg, 0x1, 4, p->adaptive_gop_enable); + mfc_ctx_debug(2, "Adaptive gop is enabled\n"); + } + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_HIGH_QUALITY_MODE); + + /* extended encoder ctrl */ + /** vbv buffer size */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_VBV_BUFFER_SIZE); + mfc_clear_bits(reg, 0xFF, 0); + if (p->frame_skip_mode =3D=3D V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_L= IMIT) + mfc_set_bits(reg, 0xFF, 0, p->vbv_buf_size); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VBV_BUFFER_SIZE); + + /* Video signal type */ + __mfc_set_video_signal_type(core, ctx); + + /* Check performance decrease options */ + if (!fps) + fps =3D p->rc_framerate; + if (IS_MFC_MAX_PERF(ctx, fps)) { + if (mfc_is_enc_bframe(ctx)) { + p->num_b_frame =3D 0; + if (IS_H264_ENC(ctx)) + p->codec.h264.hier_qp_type =3D + V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P; + mfc_ctx_info("forcely can't be use B frame for 8K or 4K %d fps\n", fps); + } + if (p->num_refs_for_p > 1) { + p->num_refs_for_p =3D 1; + mfc_ctx_info("forcely use 1-ref frame for 8K or 4K %d fps\n", fps); + } + } + + mfc_ctx_debug_leave(); +} + +static void __mfc_set_temporal_svc_h264(struct mfc_core *core, + struct mfc_ctx *ctx, + struct mfc_h264_enc_params *p_264) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + unsigned int reg =3D 0, reg2 =3D 0; + int i; + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_H264_OPTIONS_2); + /* pic_order_cnt_type =3D 0 for backward compatibilities */ + mfc_clear_bits(reg, 0x3, 0); + /* Enable LTR */ + mfc_clear_bits(reg, 0x1, 2); + if ((p_264->enable_ltr & 0x1) || p_264->num_of_ltr > 0) + mfc_set_bits(reg, 0x1, 2, 0x1); + /* Number of LTR */ + mfc_clear_bits(reg, 0x3, 7); + if (p_264->num_of_ltr > 2) + mfc_set_bits(reg, 0x3, 7, (p_264->num_of_ltr - 2)); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_OPTIONS_2); + + /* Temporal SVC - qp type, layer number */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_NUM_T_LAYER); + mfc_clear_set_bits(reg, 0x1, 3, p_264->hier_qp_type); + mfc_clear_set_bits(reg, 0x7, 0, p_264->num_hier_layer); + mfc_clear_bits(reg, 0x7, 4); + if (p_264->hier_ref_type) { + mfc_set_bits(reg, 0x1, 7, 0x1); + mfc_set_bits(reg, 0x7, 4, p->num_hier_max_layer); + } else { + mfc_clear_bits(reg, 0x1, 7); + mfc_set_bits(reg, 0x7, 4, p_264->num_hier_layer); + } + mfc_clear_set_bits(reg, 0x1, 8, p->hier_bitrate_ctrl); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_NUM_T_LAYER); + mfc_ctx_debug(3, "[HIERARCHICAL] hier_qp_enable %d, enable_ltr %d", + p_264->hier_qp_enable, p_264->enable_ltr); + mfc_ctx_debug(3, "num_hier_layer %d, max_layer %d, hier_ref_type %d, NUM_= T_LAYER 0x%x\n", + p_264->num_hier_layer, p->num_hier_max_layer, p_264->hier_ref_type= , reg); + + /* QP & Bitrate for each layer */ + for (i =3D 0; i < 7; i++) { + MFC_CORE_RAW_WRITEL + (p_264->hier_qp_layer[i], + MFC_REG_E_HIERARCHICAL_QP_LAYER0 + i * 4); + /* If hier_bitrate_ctrl is set to 1, this is meaningless */ + MFC_CORE_RAW_WRITEL + (p_264->hier_bit_layer[i], + MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4); + mfc_ctx_debug(3, "[HIERARCHICAL] layer[%d] QP: %#x, bitrate: %d(FW ctrl:= %d)\n", + i, p_264->hier_qp_layer[i], + p_264->hier_bit_layer[i], p->hier_bitrate_ctrl); + } + if (p_264->set_priority) { + reg =3D 0; + reg2 =3D 0; + for (i =3D 0; i < (p_264->num_hier_layer & 0x7); i++) { + if (i <=3D 4) + mfc_set_bits(reg, 0x3F, (6 * i), (p_264->base_priority + i)); + else + mfc_set_bits(reg2, 0x3F, (6 * (i - 5)), (p_264->base_priority + i)); + } + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_HD_SVC_EXTENSION_0); + MFC_CORE_RAW_WRITEL(reg2, MFC_REG_E_H264_HD_SVC_EXTENSION_1); + mfc_ctx_debug(3, "[HIERARCHICAL] priority EXTENSION0: %#x, EXTENSION1: %= #x\n", + reg, reg2); + } +} + +static void __mfc_set_fmo_slice_map_h264(struct mfc_core *core, + struct mfc_ctx *ctx, + struct mfc_h264_enc_params *p_264) +{ + int i; + + if (p_264->fmo_enable) { + switch (p_264->fmo_slice_map_type) { + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES: + if (p_264->fmo_slice_num_grp > 4) + p_264->fmo_slice_num_grp =3D 4; + for (i =3D 0; i < (p_264->fmo_slice_num_grp & 0xF); i++) + MFC_CORE_RAW_WRITEL + (p_264->fmo_run_length[i] - 1, + MFC_REG_E_H264_FMO_RUN_LENGTH_MINUS1_0 + i * 4); + break; + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES: + if (p_264->fmo_slice_num_grp > 4) + p_264->fmo_slice_num_grp =3D 4; + break; + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN: + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN: + if (p_264->fmo_slice_num_grp > 2) + p_264->fmo_slice_num_grp =3D 2; + MFC_CORE_RAW_WRITEL + (p_264->fmo_sg_dir & 0x1, MFC_REG_E_H264_FMO_SLICE_GRP_CHANGE_DIR); + /* the valid range is 0 ~ number of macroblocks -1 */ + MFC_CORE_RAW_WRITEL(p_264->fmo_sg_rate, + MFC_REG_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1); + break; + default: + mfc_ctx_err("Unsupported map type for FMO: %d\n", + p_264->fmo_slice_map_type); + p_264->fmo_slice_map_type =3D 0; + p_264->fmo_slice_num_grp =3D 1; + break; + } + + MFC_CORE_RAW_WRITEL + (p_264->fmo_slice_map_type, MFC_REG_E_H264_FMO_SLICE_GRP_MAP_TYPE); + MFC_CORE_RAW_WRITEL + (p_264->fmo_slice_num_grp - 1, MFC_REG_E_H264_FMO_NUM_SLICE_GRP_MINUS1); + } else { + MFC_CORE_RAW_WRITEL(0, MFC_REG_E_H264_FMO_NUM_SLICE_GRP_MINUS1); + } +} + +static void __mfc_set_enc_params_h264(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_h264_enc_params *p_264 =3D &p->codec.h264; + unsigned int mb =3D 0; + unsigned int reg =3D 0; + + mfc_ctx_debug_enter(); + + p->rc_framerate_res =3D FRAME_RATE_RESOLUTION; + __mfc_set_enc_params(core, ctx); + + if (p_264->num_hier_layer & 0x7) { + /* set gop_size without i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 0); + } else { + /* set gop_size with i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 1); + } + + mb =3D WIDTH_MB((ctx)->crop_width) * HEIGHT_MB((ctx)->crop_height); + /* Level 6.0 case */ + if (IS_LV60_MB(mb)) { + if (p_264->level < 60) + mfc_ctx_info("This resolution(mb: %d) recommends level6.0\n", mb); + /* In case of profile is baseline or constrained baseline */ + if (p_264->profile =3D=3D 0x0 || p_264->profile =3D=3D 0x3) + mfc_ctx_info("This resolution(mb: %d) recommends high profile\n", mb); + if (!dev->pdata->support_8K_cavlc && p_264->entropy_mode !=3D 0x1) { + mfc_ctx_info("Set Entropy mode CABAC\n"); + p_264->entropy_mode =3D 1; + } + } + + /* Level 5.1 case */ + if (IS_LV51_MB(mb)) { + if (p_264->level < 51) + mfc_ctx_info("This resolution(mb: %d) recommends level5.1\n", mb); + /* In case of profile is baseline or constrained baseline */ + if (p_264->profile =3D=3D 0x0 || p_264->profile =3D=3D 0x3) + mfc_ctx_info("This resolution(mb: %d) recommends high profile\n", mb); + } + + /* profile & level */ + reg =3D 0; + /** level */ + mfc_clear_set_bits(reg, 0xFF, 8, p_264->level); + /** profile - 0 ~ 3 */ + mfc_clear_set_bits(reg, 0x3F, 0, p_264->profile); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_PICTURE_PROFILE); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_H264_OPTIONS); + /* entropy coding mode */ + mfc_clear_set_bits(reg, 0x1, 0, p_264->entropy_mode); + /* loop filter ctrl */ + mfc_clear_set_bits(reg, 0x3, 1, p_264->loop_filter_mode); + /* interlace */ + mfc_clear_set_bits(reg, 0x1, 3, p_264->interlace); + /* intra picture period for H.264 open GOP */ + mfc_clear_set_bits(reg, 0x1, 4, p_264->open_gop); + /* extended encoder ctrl */ + mfc_clear_set_bits(reg, 0x1, 5, p_264->ar_vui); + /* ASO enable */ + mfc_clear_set_bits(reg, 0x1, 6, p_264->aso_enable); + /* if num_refs_for_p is 2, the performance falls by half */ + mfc_clear_set_bits(reg, 0x1, 7, (p->num_refs_for_p - 1)); + /* Temporal SVC - hier qp enable */ + mfc_clear_set_bits(reg, 0x1, 8, p_264->hier_qp_enable); + /* Weighted Prediction enable */ + mfc_clear_set_bits(reg, 0x3, 9, p->weighted_enable); + if (p->weighted_enable) + mfc_ctx_debug(2, "WP mode is %d\n", p->weighted_enable); + /* 8x8 transform enable [12]: INTER_8x8_TRANS_ENABLE */ + mfc_clear_set_bits(reg, 0x1, 12, p_264->_8x8_transform); + /* 8x8 transform enable [13]: INTRA_8x8_TRANS_ENABLE */ + mfc_clear_set_bits(reg, 0x1, 13, p_264->_8x8_transform); + /* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */ + mfc_clear_bits(reg, 0x1, 14); + /* sps pps control */ + mfc_clear_set_bits(reg, 0x1, 29, p_264->prepend_sps_pps_to_idr); + /* VUI parameter disable */ + mfc_clear_set_bits(reg, 0x1, 30, p_264->vui_enable); + /* Timing info */ + mfc_set_bits(reg, 0x1, 31, p->timing_info_enable); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_OPTIONS); + + /* cropped height */ + if (p_264->interlace) + MFC_CORE_RAW_WRITEL(ctx->crop_height >> 1, MFC_REG_E_CROPPED_FRAME_HEIGH= T); + + /* loopfilter alpha offset */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_H264_LF_ALPHA_OFFSET); + mfc_clear_set_bits(reg, 0x1F, 0, p_264->loop_filter_alpha); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_LF_ALPHA_OFFSET); + + /* loopfilter beta offset */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_H264_LF_BETA_OFFSET); + mfc_clear_set_bits(reg, 0x1F, 0, p_264->loop_filter_beta); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_LF_BETA_OFFSET); + + /* rate control config. */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG); + /** frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_264->rc_frame_qp); + mfc_clear_bits(reg, 0x1, 11); + if (!p->rc_frame && !p->rc_mb && p->dynamic_qp) + mfc_set_bits(reg, 0x1, 11, 0x1); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_CONFIG); + + /* max & min value of QP for I frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND); + /** max I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_264->rc_max_qp); + /** min I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_264->rc_min_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND); + + /* max & min value of QP for P/B frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND_PB); + /** max B frame QP */ + mfc_clear_set_bits(reg, 0xFF, 24, p_264->rc_max_qp_b); + /** min B frame QP */ + mfc_clear_set_bits(reg, 0xFF, 16, p_264->rc_min_qp_b); + /** max P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_264->rc_max_qp_p); + /** min P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_264->rc_min_qp_p); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND_PB); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_FIXED_PICTURE_QP); + mfc_clear_set_bits(reg, 0xFF, 24, p->config_qp); + mfc_clear_set_bits(reg, 0xFF, 16, p_264->rc_b_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 8, p_264->rc_p_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 0, p_264->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_FIXED_PICTURE_QP); + + /* chroma QP offset */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_H264_CHROMA_QP_OFFSET); + mfc_clear_set_bits(reg, 0x1F, 5, p->chroma_qp_offset_cr); + mfc_clear_set_bits(reg, 0x1F, 0, p->chroma_qp_offset_cb); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_CHROMA_QP_OFFSET); + + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_ASPECT_RATIO); + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_EXTENDED_SAR); + if (p_264->ar_vui) { + /* aspect ration IDC */ + reg =3D 0; + mfc_set_bits(reg, 0xFF, 0, p_264->ar_vui_idc); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_ASPECT_RATIO); + if (p_264->ar_vui_idc =3D=3D 0xFF) { + /* sample AR info. */ + reg =3D 0; + mfc_set_bits(reg, 0xFFFF, 16, p_264->ext_sar_width); + mfc_set_bits(reg, 0xFFFF, 0, p_264->ext_sar_height); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_EXTENDED_SAR); + } + } + /* intra picture period for H.264 open GOP, value */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_H264_REFRESH_PERIOD); + mfc_clear_bits(reg, 0xFFFF, 0); + if (p_264->open_gop) + mfc_set_bits(reg, 0xFFFF, 0, p_264->open_gop_size); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_REFRESH_PERIOD); + + /* Temporal SVC */ + __mfc_set_temporal_svc_h264(core, ctx, p_264); + + /* set frame pack sei generation */ + if (p_264->sei_gen_enable) { + /* frame packing enable */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_H264_OPTIONS); + mfc_set_bits(reg, 0x1, 25, 0x1); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_OPTIONS); + + /* set current frame0 flag & arrangement type */ + reg =3D 0; + /** current frame0 flag */ + mfc_set_bits(reg, 0x1, 2, p_264->sei_fp_curr_frame_0); + /** arrangement type */ + mfc_set_bits(reg, 0x3, 0, (p_264->sei_fp_arrangement_type - 3)); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_H264_FRAME_PACKING_SEI_INFO); + } + + __mfc_set_fmo_slice_map_h264(core, ctx, p_264); + + mfc_ctx_debug_leave(); +} + +int mfc_core_set_enc_params(struct mfc_core *core, struct mfc_ctx *ctx) +{ + if (IS_H264_ENC(ctx)) { + __mfc_set_enc_params_h264(core, ctx); + } else { + mfc_ctx_err("Unknown codec for encoding (%x)\n", ctx->codec_mode); + return -EINVAL; + } + + mfc_ctx_debug(5, "RC) Bitrate: %d / framerate: %#x / config %#x / mode %#= x\n", + MFC_CORE_RAW_READL(MFC_REG_E_RC_BIT_RATE), + MFC_CORE_RAW_READL(MFC_REG_E_RC_FRAME_RATE), + MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG), + MFC_CORE_RAW_READL(MFC_REG_E_RC_MODE)); + + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.h= b/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.h new file mode 100644 index 000000000000..ce15ae29abba --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * mfc_enc_param.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_ENC_PARAM_H +#define __MFC_ENC_PARAM_H __FILE__ + +#include "base/mfc_common.h" + +void mfc_core_set_slice_mode(struct mfc_core *core, struct mfc_ctx *ctx); +void mfc_core_set_aso_slice_order_h264(struct mfc_core *core, + struct mfc_ctx *ctx); +void mfc_core_set_enc_config_qp(struct mfc_core *core, struct mfc_ctx *ctx= ); +void mfc_core_set_enc_ts_delta(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_set_enc_params(struct mfc_core *core, struct mfc_ctx *ctx); +#endif /* __MFC_ENC_PARAM_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c index 8846230f1e20..4580aa3498cc 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_ctx_ctrl.c @@ -379,6 +379,768 @@ static struct mfc_ctrl_cfg mfc_dec_ctrl_list[] =3D { =20 #define NUM_DEC_CTRL_CFGS ARRAY_SIZE(mfc_dec_ctrl_list) =20 +static struct mfc_ctrl_cfg mfc_enc_ctrl_list[] =3D { + { /* set frame tag */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_PICTURE_TAG, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* get frame tag */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RET_PICTURE_TAG, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* encoded y physical addr */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_LUMA_ADDR, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_ENCODED_SOURCE_FIRST_ADDR, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* encoded c physical addr */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_CHROMA_ADDR, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_ENCODED_SOURCE_SECOND_ADDR, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* I, not coded frame insertion */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_FRAME_INSERTION, + .mask =3D 0x3, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* I period change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_GOP_CONFIG, + .mask =3D 0xFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 0, + }, + { /* frame rate change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_FRAME_RATE, + .mask =3D 0x0000FFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 1, + }, + { /* bit rate change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_BIT_RATE, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 2, + }, + { /* frame status (in slice or not) */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS, + .is_volatile =3D 0, + .mode =3D MFC_CTRL_MODE_NONE, + .addr =3D 0, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* H.264 I frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.264 I frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.263 I frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H263_MAX_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.263 I frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H263_MIN_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* MPEG4 I frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* MPEG4 I frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP8 I frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MAX_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP8 I frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MIN_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP9 I frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MAX_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP9 I frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MIN_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* HEVC I frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* HEVC I frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.264 P frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.264 P frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.263 P frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.263 P frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* MPEG4 P frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* MPEG4 P frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP8 P frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP8 P frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP9 P frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* VP9 P frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* HEVC P frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* HEVC P frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.264 B frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 24, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.264 B frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 16, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* MPEG4 B frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 24, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* MPEG4 B frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 16, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* HEVC B frame QP Max change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 24, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* HEVC B frame QP Min change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_QP_BOUND_PB, + .mask =3D 0xFF, + .shft =3D 16, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 4, + }, + { /* H.264 Dynamic Temporal Layer & bitrate change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 10, + }, + { /* HEVC Dynamic Temporal Layer & bitrate change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 10, + }, + { /* VP8 Dynamic Temporal Layer change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 10, + }, + { /* VP9 Dynamic Temporal Layer change */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 10, + }, + { /* set level */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_PICTURE_PROFILE, + .mask =3D 0x000000FF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 5, + }, + { /* set profile */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_PICTURE_PROFILE, + .mask =3D 0x0000000F, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 5, + }, + { /* set store LTR */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC_H264_MARK_LTR, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_H264_NAL_CONTROL, + .mask =3D 0x00000003, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* set use LTR */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC_H264_USE_LTR, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_H264_NAL_CONTROL, + .mask =3D 0x00000003, + .shft =3D 2, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* set base layer priority */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_H264_HD_SVC_EXTENSION_0, + .mask =3D 0x0000003F, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 12, + }, + { /* set QP per each frame */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_MFC_CONFIG_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_FIXED_PICTURE_QP, + .mask =3D 0x000000FF, + .shft =3D 24, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* Region-Of-Interest control */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_ROI_CONTROL, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_ROI_CTRL, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* set YSUM for weighted prediction */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_YSUM, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_WEIGHT_FOR_WEIGHTED_PREDICTION, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_shft =3D 0, + }, + { /* set base layer priority */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_MODE, + .mask =3D 0x000000FF, + .shft =3D 8, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 13, + }, + { /* sync the timestamp for drop control */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_DROP_CONTROL, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_FRAME_RATE, + .mask =3D 0x0000FFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* average QP of current frame */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_AVERAGE_QP, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_NAL_DONE_INFO, + .mask =3D 0x000000FF, + .shft =3D 12, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* HOR range position of current frame */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L0, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_MV_HOR_RANGE, + .mask =3D 0x000000FF, + .shft =3D 16, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* HOR range position of current frame */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L1, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_MV_HOR_RANGE, + .mask =3D 0x000000FF, + .shft =3D 24, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* VER range position of current frame */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L0, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_MV_VER_RANGE, + .mask =3D 0x000000FF, + .shft =3D 16, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* VER range position of current frame */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L1, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_MV_VER_RANGE, + .mask =3D 0x000000FF, + .shft =3D 24, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* Max I frame size */ + .type =3D MFC_CTRL_TYPE_SET_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_MAX_IFRAME_SIZE, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_RC_OPTIONS, + .mask =3D 0xFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_SFR, + .flag_addr =3D MFC_REG_E_PARAM_CHANGE, + .flag_shft =3D 20, + }, + { /* The number of skip MB */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SUM_SKIP_MB, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_SUM_SKIP_MB, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* The number of intra MB */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SUM_INTRA_MB, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_SUM_INTRA_MB, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* The number of intra MB */ + .type =3D MFC_CTRL_TYPE_GET_DST, + .id =3D V4L2_CID_MPEG_VIDEO_SUM_ZERO_MV_MB, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_SFR, + .addr =3D MFC_REG_E_SUM_ZERO_MV_MB, + .mask =3D 0xFFFFFFFF, + .shft =3D 0, + .flag_mode =3D MFC_CTRL_MODE_NONE, + .flag_addr =3D 0, + .flag_shft =3D 0, + }, + { /* buffer additional information */ + .type =3D MFC_CTRL_TYPE_SRC, + .id =3D V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_NONE, + .flag_mode =3D MFC_CTRL_MODE_NONE, + }, + { /* buffer additional information */ + .type =3D MFC_CTRL_TYPE_DST, + .id =3D V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG, + .is_volatile =3D 1, + .mode =3D MFC_CTRL_MODE_NONE, + .flag_mode =3D MFC_CTRL_MODE_NONE, + } +}; + +#define NUM_ENC_CTRL_CFGS ARRAY_SIZE(mfc_enc_ctrl_list) + static void mfc_ctrl_cleanup_ctx(struct mfc_ctx *ctx) { struct mfc_ctx_ctrl *ctx_ctrl; @@ -426,6 +1188,8 @@ static int mfc_ctrl_init_ctx(struct mfc_ctx *ctx) { if (ctx->type =3D=3D MFCINST_DECODER) return __mfc_ctrl_init_ctx(ctx, mfc_dec_ctrl_list, NUM_DEC_CTRL_CFGS); + else if (ctx->type =3D=3D MFCINST_ENCODER) + return __mfc_ctrl_init_ctx(ctx, mfc_enc_ctrl_list, NUM_ENC_CTRL_CFGS); =20 mfc_ctx_err("[CTRLS] invalid type %d\n", ctx->type); return -EINVAL; @@ -560,11 +1324,47 @@ static int mfc_ctrl_init_buf(struct mfc_ctx *ctx, en= um mfc_ctrl_type type, unsig { if (ctx->type =3D=3D MFCINST_DECODER) return __mfc_ctrl_init_buf(ctx, mfc_dec_ctrl_list, type, index, NUM_DEC_= CTRL_CFGS); + else if (ctx->type =3D=3D MFCINST_ENCODER) + return __mfc_ctrl_init_buf(ctx, mfc_enc_ctrl_list, type, index, NUM_ENC_= CTRL_CFGS); =20 mfc_ctx_err("[CTRLS] invalid type %d\n", ctx->type); return -EINVAL; } =20 +static void __mfc_enc_set_roi(struct mfc_ctx *ctx, struct mfc_buf_ctrl *bu= f_ctrl) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + int index =3D 0; + unsigned int reg =3D 0; + + index =3D enc->roi_index; + if (enc->roi_info[index].enable) { + enc->roi_index =3D (index + 1) % MFC_MAX_EXTRA_BUF; + reg |=3D enc->roi_info[index].enable; + reg &=3D ~(0xFF << 8); + reg |=3D (enc->roi_info[index].lower_qp << 8); + reg &=3D ~(0xFFFF << 16); + reg |=3D (enc->roi_info[index].upper_qp << 16); + /* + * 2bit ROI + * - All codec type: upper_qp and lower_qp is valid + * 8bit ROI + * - H.264/HEVC/MPEG4: upper_qp and lower_qp is invalid + * - VP8/VP9: upper_qp and lower_qp is valid + */ + mfc_ctx_debug(3, "[ROI] buffer[%d] en %d QP lower %d upper %d reg %#x\n", + index, enc->roi_info[index].enable, + enc->roi_info[index].lower_qp, + enc->roi_info[index].upper_qp, + reg); + } else { + mfc_ctx_debug(3, "[ROI] buffer[%d] is not enabled\n", index); + } + + buf_ctrl->val =3D reg; + buf_ctrl->old_val2 =3D index; +} + static void mfc_ctrl_to_buf(struct mfc_ctx *ctx, struct list_head *head) { struct mfc_ctx_ctrl *ctx_ctrl; @@ -588,6 +1388,9 @@ static void mfc_ctrl_to_buf(struct mfc_ctx *ctx, struc= t list_head *head) =20 ctx_ctrl->set.has_new =3D 0; =20 + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_ROI_CONTROL) + __mfc_enc_set_roi(ctx, buf_ctrl); + break; } } diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c b/drivers/m= edia/platform/samsung/exynos-mfc/mfc_rm.c index a7db47e58589..4083d76640fd 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.c @@ -685,12 +685,22 @@ static void __mfc_rm_migrate_all_to_one_core(struct m= fc_dev *dev) spin_unlock_irqrestore(&dev->ctx_list_lock, flags); return; } - - if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_0) - op_core_fixed0++; - else if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_1) - op_core_fixed1++; - + /* op core type */ + if (tmp_ctx->type =3D=3D MFCINST_ENCODER) { + /* + * instance of encoder cannot be moved to another core. + * So, treat main core as the fixed core. + */ + if (tmp_ctx->op_core_num[MFC_CORE_MAIN] =3D=3D MFC_DEC_DEFAULT_CORE) + op_core_fixed0++; + else if (tmp_ctx->op_core_num[MFC_CORE_MAIN] =3D=3D MFC_SURPLUS_CORE) + op_core_fixed1++; + } else { + if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_0) + op_core_fixed0++; + else if (tmp_ctx->op_core_type =3D=3D MFC_OP_CORE_FIXED_1) + op_core_fixed1++; + } /* op main core */ if (tmp_ctx->op_core_num[MFC_CORE_MAIN] =3D=3D MFC_DEC_DEFAULT_CORE) op_core0++; @@ -1344,6 +1354,14 @@ void mfc_rm_load_balancing(struct mfc_ctx *ctx, int = load_add) __mfc_rm_update_core_load(tmp_ctx, 0, 1); continue; } + + if (tmp_ctx->type =3D=3D MFCINST_ENCODER) { + mfc_ctx_debug(3, "[RMLB] encoder ctx[%d] can't be moved\n", + tmp_ctx->num); + __mfc_rm_update_core_load(tmp_ctx, 0, IS_MULTI_MODE(tmp_ctx)); + continue; + } + core_num =3D __mfc_rm_get_core_num_by_load(dev, tmp_ctx, MFC_DEC_DEFAULT= _CORE); if (IS_SWITCH_SINGLE_MODE(tmp_ctx) || core_num =3D=3D tmp_ctx->op_core_num[MFC_CORE_MAIN]) { @@ -1440,8 +1458,10 @@ int mfc_rm_instance_init(struct mfc_dev *dev, struct= mfc_ctx *ctx) * QoS portion data should be allocated * only once per instance after maincore is determined. */ - num_qos_steps =3D core->core_pdata->num_default_qos_steps; - + if (ctx->type =3D=3D MFCINST_ENCODER) + num_qos_steps =3D core->core_pdata->num_encoder_qos_steps; + else + num_qos_steps =3D core->core_pdata->num_default_qos_steps; ctx->mfc_qos_portion =3D vmalloc(sizeof(unsigned int) * num_qos_steps); if (!ctx->mfc_qos_portion) ret =3D -ENOMEM; @@ -1916,6 +1936,46 @@ void mfc_rm_instance_dec_stop(struct mfc_dev *dev, s= truct mfc_ctx *ctx, mfc_ctx_debug_leave(); } =20 +void mfc_rm_instance_enc_stop(struct mfc_dev *dev, struct mfc_ctx *ctx, + unsigned int type) +{ + struct mfc_core *core; + struct mfc_core *subcore; + + mfc_ctx_debug_enter(); + + mfc_get_corelock_ctx(ctx); + + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] There is no main core\n"); + mfc_release_corelock_ctx(ctx); + return; + } + + if (type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + core->core_ops->instance_q_flush(core, ctx); + } else if (type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (IS_SWITCH_SINGLE_MODE(ctx)) { + subcore =3D mfc_get_sub_core(dev, ctx); + if (!subcore) { + mfc_ctx_err("[RM] There is no sub core for switch single\n"); + goto err_subcore_stop; + } + + core->core_ops->instance_finishing(core, ctx); + subcore->core_ops->instance_finishing(subcore, ctx); + } else { + core->core_ops->instance_finishing(core, ctx); + } + } + +err_subcore_stop: + mfc_release_corelock_ctx(ctx); + + mfc_ctx_debug_leave(); +} + int mfc_rm_subcore_seq_start(struct mfc_dev *dev, struct mfc_ctx *ctx) { struct mfc_core *core; @@ -2284,6 +2344,8 @@ void mfc_rm_qos_control(struct mfc_ctx *ctx, enum mfc= _qos_control qos_control) mfc_rm_load_balancing(ctx, MFC_RM_LOAD_ADD); =20 ctx->update_bitrate =3D false; + if (ctx->type =3D=3D MFCINST_ENCODER) + ctx->update_framerate =3D false; break; default: mfc_ctx_err("[RM] not supported QoS control type: %#x\n", @@ -2528,9 +2590,14 @@ void mfc_rm_update_real_time(struct mfc_ctx *ctx) } } else { if (new_prio =3D=3D 0) { - /* In case of google photo app, user sets priority 0 */ - new_prio =3D 1; - new_rt =3D MFC_RT_LOW; + if (ctx->type =3D=3D MFCINST_ENCODER && + ctx->enc_priv->params.rc_framerate) { + new_rt =3D MFC_RT; + } else { + /* In case of google photo app, user sets priority 0 */ + new_prio =3D 1; + new_rt =3D MFC_RT_LOW; + } } else if (new_prio >=3D 1) { new_rt =3D MFC_NON_RT; } else { diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h b/drivers/m= edia/platform/samsung/exynos-mfc/mfc_rm.h index b73ef905718a..39ec63392db3 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_rm.h @@ -94,6 +94,8 @@ int mfc_rm_instance_deinit(struct mfc_dev *dev, struct mf= c_ctx *ctx); int mfc_rm_instance_open(struct mfc_dev *dev, struct mfc_ctx *ctx); void mfc_rm_instance_dec_stop(struct mfc_dev *dev, struct mfc_ctx *ctx, unsigned int type); +void mfc_rm_instance_enc_stop(struct mfc_dev *dev, struct mfc_ctx *ctx, + unsigned int type); int mfc_rm_subcore_seq_start(struct mfc_dev *dev, struct mfc_ctx *ctx); int mfc_rm_instance_setup(struct mfc_dev *dev, struct mfc_ctx *ctx); void mfc_rm_request_work(struct mfc_dev *dev, enum mfc_request_work work, --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 6C3DA27FD49 for ; Tue, 30 Sep 2025 03:57:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204624; cv=none; b=eLMwpxavunssSvQLsT3OwttluavZnt1xUHlUON2yNbnsskSQLw+oX6RyYbkSO0FvA5dl7CC4Y6iAS48rafSaTMLH+ThJndOYoReByEeTH8LyKgVH/cJiMuhdl/3LWfwJpyySizzW8KAk9+uxLaB347+92WEaVcXc0XGoS5ibNmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204624; c=relaxed/simple; bh=/J59iFRvwaBgA6PUGWRDu+OLlx8yfYvWCRctdokSgmQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=pORc3nCK9/9LBtSJV5bOpumdjYU2F7bePn8dijnQEWuzu5DWq+O/zBgGhfTFw+2KRy6mosQnTNTWA0HBejcMxTN9Gl/r6cXieGsiAHWRPuntZS6ckLRHUzgKJJjQOoP3+P7U6Nqa0zBCE1++ko8pC2DovZ93IAukQmVvMswQW7o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=GZn+rXXb; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="GZn+rXXb" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035659epoutp0466ea931a1d5a0cd5dcad428089111280~p80fuIfD82087020870epoutp04M for ; Tue, 30 Sep 2025 03:56:59 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035659epoutp0466ea931a1d5a0cd5dcad428089111280~p80fuIfD82087020870epoutp04M DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204619; bh=Opmcn1eBkxs4yvNdgaabKkETHQwug3qB+xi5yQ+tmfo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GZn+rXXbXo3Xqw0kLH4uZDwh9IeHlDm4ZlTocjQbwNkC7oK4WOAdmDxXti0/Vae6j kKCwtsBMAWKoLpw0turFqbOeY20dZEsyxqFHFEhtRAkzNEw71ZtlzJbv94Gsh9OowW ySkHl/WC2AKj9NNfVEjmfIcGsW/QAzzNJKqtLn9E= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPS id 20250930035658epcas5p14da69819905bbbb3d9eea3c25b88f97a~p80fB-F5M1532615326epcas5p1p; Tue, 30 Sep 2025 03:56:58 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.87]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cbPQ203tZz6B9mF; Tue, 30 Sep 2025 03:56:58 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035657epcas5p17d3e94c7c1ba5eeace97b0ff81053653~p80daA7Jw2306123061epcas5p1-; Tue, 30 Sep 2025 03:56:57 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035654epsmtip159f5d79a4cd8ce016a4981d692a91c06~p80a56pxn2938429384epsmtip1P; Tue, 30 Sep 2025 03:56:54 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 24/29] media: mfc: Add encoder VB2 support to driver Date: Tue, 30 Sep 2025 09:33:43 +0530 Message-Id: <20250930040348.3702923-25-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035657epcas5p17d3e94c7c1ba5eeace97b0ff81053653 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035657epcas5p17d3e94c7c1ba5eeace97b0ff81053653 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni Introduce encoder VB2 support. This enables proper V4L2 output for the Exynos MFC encoder, handling queue setup, buffers, and streaming control. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 2 +- .../platform/samsung/exynos-mfc/mfc_enc_vb2.c | 443 ++++++++++++++++++ .../platform/samsung/exynos-mfc/mfc_enc_vb2.h | 19 + 3 files changed, 463 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index a257d5b0a576..dad94a7c468c 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_VIDEO_EXYNOS_MFC) :=3D exynos_mfc.o ccflags-y +=3D -I$(srctree)/$(src) =20 #Dev interface layer -exynos_mfc-y +=3D mfc.o mfc_dec_v4l2.o mfc_dec_vb2.o +exynos_mfc-y +=3D mfc.o mfc_dec_v4l2.o mfc_dec_vb2.o mfc_enc_vb2.o #Dev control layer exynos_mfc-y +=3D mfc_rm.o mfc_ctx_ctrl.o mfc_debugfs.o #Core interface layer diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.c new file mode 100644 index 000000000000..7164c334585b --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_enc_vb2.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_rm.h" + +#include "base/mfc_queue.h" +#include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" +#include "mfc_enc_vb2.h" + +static int mfc_enc_queue_setup(struct vb2_queue *vq, + unsigned int *buf_count, unsigned int *plane_count, + unsigned int psize[], struct device *alloc_devs[]) +{ + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int i; + + mfc_ctx_debug_enter(); + + /* Encoder works only single core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + if (core_ctx->state !=3D MFCINST_GOT_INST && + vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_err("invalid state: %d\n", core_ctx->state); + return -EINVAL; + } + if (core_ctx->state >=3D MFCINST_FINISHING && + vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_err("invalid state: %d\n", core_ctx->state); + return -EINVAL; + } + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_debug(4, "enc dst\n"); + if (ctx->dst_fmt) + *plane_count =3D ctx->dst_fmt->mem_planes; + else + *plane_count =3D MFC_ENC_CAP_PLANE_COUNT; + + if (*buf_count < 1) + *buf_count =3D 1; + if (*buf_count > MFC_MAX_BUFFERS) + *buf_count =3D MFC_MAX_BUFFERS; + + psize[0] =3D enc->dst_buf_size; + alloc_devs[0] =3D dev->device; + /* In case of VP8/VP9 encoder, part of stream buffer should be read */ + vq->dma_dir =3D DMA_BIDIRECTIONAL; + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "enc src\n"); + + if (ctx->src_fmt) + *plane_count =3D ctx->num_fd_frame; + else + *plane_count =3D MFC_ENC_OUT_PLANE_COUNT; + + if (*buf_count < 1) + *buf_count =3D 1; + if (*buf_count > MFC_MAX_BUFFERS) + *buf_count =3D MFC_MAX_BUFFERS; + + /* need to use minimum size to prevent qbuf fail */ + if (*plane_count =3D=3D 1) { + psize[0] =3D 1; + alloc_devs[0] =3D dev->device; + } else { + for (i =3D 0; i < *plane_count; i++) { + psize[i] =3D 1; + alloc_devs[i] =3D dev->device; + } + } + } else { + mfc_err("invalid queue type: %d\n", vq->type); + return -EINVAL; + } + + mfc_debug(2, "buf_count: %d, plane_count: %d, type: %#x\n", + *buf_count, *plane_count, vq->type); + for (i =3D 0; i < *plane_count; i++) + mfc_debug(2, "plane[%d] size: %d\n", i, psize[i]); + + mfc_ctx_debug_leave(); + + return 0; +} + +static void mfc_enc_unlock(struct vb2_queue *q) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + + mutex_unlock(&dev->mfc_mutex); +} + +static void mfc_enc_lock(struct vb2_queue *q) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + + mutex_lock(&dev->mfc_mutex); +} + +static int mfc_enc_buf_init(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + int ret; + + mfc_ctx_debug_enter(); + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret =3D mfc_check_vb_with_fmt(ctx->dst_fmt, vb); + if (ret < 0) + return ret; + + if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_DST, + vb->index) < 0) + mfc_ctx_err("failed in init_buf_ctrls\n"); + + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret =3D mfc_check_vb_with_fmt(ctx->src_fmt, vb); + if (ret < 0) + return ret; + + if (call_cop(ctx, init_buf_ctrls, ctx, MFC_CTRL_TYPE_SRC, + vb->index) < 0) + mfc_ctx_err("failed in init_buf_ctrls\n"); + } else { + mfc_ctx_err("invalid queue type: %d\n", vq->type); + return -EINVAL; + } + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_enc_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_raw_info *raw; + unsigned int index =3D vb->index; + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + struct dma_buf *bufcon_dmabuf[MFC_MAX_PLANES]; + int i, mem_get_count =3D 0; + size_t buf_size; + + mfc_ctx_debug_enter(); + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + buf_size =3D vb2_plane_size(vb, 0); + mfc_ctx_debug(2, "[STREAM] vb size: %lu, calc size: %u\n", + buf_size, enc->dst_buf_size); + + if (buf_size < enc->dst_buf_size) { + mfc_ctx_err("[STREAM] size(%lu) is smaller than (%d)\n", + buf_size, enc->dst_buf_size); + return -EINVAL; + } + + buf->addr[0][0] =3D mfc_mem_get_daddr_vb(vb, 0); + + /* Copy dst buffer flag to buf_ctrl */ + buf->flag =3D call_cop(ctx, get_buf_ctrl_val, ctx, + &ctx->dst_ctrls[index], + V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + raw =3D &ctx->raw_buf; + if (ctx->src_fmt->mem_planes =3D=3D 1) { + buf_size =3D vb2_plane_size(vb, 0); + mfc_ctx_debug(2, "[FRAME] single plane vb size: %lu, calc size: %d\n", + buf_size, raw->total_plane_size); + if (buf_size < raw->total_plane_size) { + mfc_ctx_err("[FRAME] single plane size(%lu) is smaller than (%d)\n", + buf_size, raw->total_plane_size); + return -EINVAL; + } + } else { + for (i =3D 0; i < ctx->src_fmt->mem_planes; i++) { + buf_size =3D vb2_plane_size(vb, i); + mfc_ctx_debug(2, "[FRAME] plane[%d] vb size: %lu, calc size: %d\n", + i, buf_size, raw->plane_size[i]); + if (buf_size < raw->plane_size[i]) { + mfc_ctx_err("[FRAME] plane[%d] size(%lu) is smaller than (%d)\n", + i, buf_size, raw->plane_size[i]); + return -EINVAL; + } + } + } + + for (i =3D 0; i < ctx->src_fmt->mem_planes; i++) { + bufcon_dmabuf[i] =3D dma_buf_get(vb->planes[i].m.fd); + if (IS_ERR(bufcon_dmabuf[i])) { + mfc_ctx_err("failed to get bufcon dmabuf\n"); + goto err_mem_put; + } + mem_get_count++; + + dma_buf_put(bufcon_dmabuf[i]); + mfc_calc_base_addr(ctx, vb, ctx->src_fmt); + } + + call_cop(ctx, to_buf_ctrls, ctx, &ctx->src_ctrls[index]); + + /* Copy src buffer flag to buf_ctrl */ + buf->flag =3D call_cop(ctx, get_buf_ctrl_val, ctx, + &ctx->src_ctrls[index], + V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG); + } else { + mfc_ctx_err("invalid queue type: %d\n", vq->type); + return -EINVAL; + } + + mfc_mem_buf_prepare(vb, 0); + + mfc_ctx_debug_leave(); + return 0; + +err_mem_put: + for (i =3D 0; i < mem_get_count; i++) + dma_buf_put(bufcon_dmabuf[i]); + + return -ENOMEM; +} + +static void mfc_enc_buf_finish(struct vb2_buffer *vb) +{ + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + unsigned int index =3D vb->index; + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + /* Copy to dst buffer flag */ + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG, buf->flag); + mfc_ctx_debug(4, "[FLAG] dst update buf[%d] flag =3D %#x\n", + index, buf->flag); + + call_cop(ctx, to_ctx_ctrls, ctx, &ctx->dst_ctrls[index]); + + mfc_mem_buf_finish(vb, 1); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* Copy to src buffer flag */ + call_cop(ctx, update_buf_val, ctx, &ctx->src_ctrls[index], + V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG, buf->flag); + mfc_ctx_debug(4, "[FLAG] src update buf[%d] flag =3D %#x\n", + index, buf->flag); + + call_cop(ctx, to_ctx_ctrls, ctx, &ctx->src_ctrls[index]); + } +} + +static void mfc_enc_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_ctx *ctx =3D vq->drv_priv; + unsigned int index =3D vb->index; + + mfc_ctx_debug_enter(); + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (call_cop(ctx, cleanup_buf_ctrls, ctx, + MFC_CTRL_TYPE_DST, index) < 0) + mfc_ctx_err("failed in cleanup_buf_ctrls\n"); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (call_cop(ctx, cleanup_buf_ctrls, ctx, + MFC_CTRL_TYPE_SRC, index) < 0) + mfc_ctx_err("failed in cleanup_buf_ctrls\n"); + } else { + mfc_ctx_err("unknown queue type\n"); + } + + mfc_ctx_debug_leave(); +} + +static int mfc_enc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + + /* Encoder works only single core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + if (q->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + core_ctx->state =3D=3D MFCINST_FINISHED) { + mfc_change_state(core_ctx, MFCINST_GOT_INST); + mfc_info("enc start_streaming changes state %d\n", + core_ctx->state); + MFC_TRACE_CTX("** ENC streamon, state: %d\n", + core_ctx->state); + } + + mfc_rm_update_real_time(ctx); + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + + return 0; +} + +static void mfc_enc_stop_streaming(struct vb2_queue *q) +{ + struct mfc_ctx *ctx =3D q->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + + mfc_ctx_info("enc stop_streaming is called, type : %d\n", q->type); + MFC_TRACE_CTX("** ENC streamoff(type:%d)\n", q->type); + + mfc_rm_instance_enc_stop(dev, ctx, q->type); +} + +static void mfc_enc_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_queue *vq =3D vb->vb2_queue; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_ctx *ctx =3D vq->drv_priv; + struct mfc_dev *dev =3D ctx->dev; + struct mfc_buf *buf =3D vb_to_mfc_buf(vb); + int i; + int is_dst_buf_ready; + + mfc_ctx_debug_enter(); + + buf->next_index =3D 0; + buf->done_index =3D 0; + + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(2, "[BUFINFO] ctx[%d] add dst index: %d, addr: 0x%08llx\n", + ctx->num, vb->index, buf->addr[0][0]); + + /* Mark destination as available for use by MFC */ + mfc_add_tail_buf(ctx, &ctx->dst_buf_queue, buf); + mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER); + } else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + buf->src_index =3D ctx->serial_src_index++; + if (ctx->multi_view_enable) { + for (i =3D 0; i < ctx->raw_buf.num_planes; i++) + mfc_ctx_debug(2, "%s[%d] %s: %d(%d), addr[0][%d]: 0x%08llx\n", + "[BUFINFO-view0] ctx", ctx->num, + "add src index", vb->index, + buf->src_index, i, + buf->addr[0][i]); + for (i =3D 0; i < ctx->raw_buf.num_planes; i++) + mfc_ctx_debug(2, "%s[%d] %s: %d(%d), addr[2][%d]: 0x%08llx\n", + "[BUFINFO-view1] ctx", ctx->num, + "add src index", vb->index, + buf->src_index, i, + buf->addr[2][i]); + } else { + if (ctx->num_fd_frame > 3) { + mfc_ctx_err("if not multi_view_enable, num_fd_frame must be <=3D 3\n"); + } else { + for (i =3D 0; i < ctx->num_fd_frame; i++) { + mfc_ctx_debug(2, "%s[%d] %s: %d(%d), addr[%d]: 0x%08llx\n", + "[BUFINFO] ctx", ctx->num, + "add src index", vb->index, + buf->src_index, i, buf->addr[0][i]); + } + } + } + mfc_add_tail_buf(ctx, &ctx->src_buf_ready_queue, buf); + + if (dev->debugfs.debug_ts =3D=3D 1) + mfc_ctx_info("[TS] framerate: %ld, timestamp: %lld\n", + ctx->framerate, buf->vb.vb2_buf.timestamp); + + mfc_rate_update_last_framerate(ctx, buf->vb.vb2_buf.timestamp); + mfc_rm_qos_control(ctx, MFC_QOS_TRIGGER); + } else { + mfc_ctx_err("unsupported buffer type (%d)\n", vq->type); + } + + if (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1) + is_dst_buf_ready =3D + mfc_is_queue_count_greater(&ctx->buf_queue_lock, + &ctx->dst_buf_queue, 0); + + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + + if (!mfc_rm_query_state(ctx, EQUAL_BIGGER, MFCINST_HEAD_PARSED) && + ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1 && is_dst_buf_ready) { + core =3D mfc_get_main_core(dev, ctx); + if (!core) { + mfc_ctx_err("[RM] main core is NULL\n"); + return; + } + core_ctx =3D core->core_ctx[ctx->num]; + + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_SEQ_DONE_RET)) { + mfc_ctx_err("[RM] sub core header parsing failed\n"); + return; + } + + mfc_ctx_info("[2CORE] start the sub core\n"); + if (ctx->op_core_num[MFC_CORE_SUB] =3D=3D MFC_CORE_INVALID) { + if (mfc_rm_instance_setup(dev, ctx)) + mfc_ctx_err("[2CORE] failed to setup sub core\n"); + } else { + if (mfc_rm_subcore_seq_start(dev, ctx)) + mfc_ctx_err("[2CORE] failed to seq_start sub core\n"); + } + } + + mfc_ctx_debug_leave(); +} + +static const struct vb2_ops mfc_enc_qops =3D { + .queue_setup =3D mfc_enc_queue_setup, + .wait_prepare =3D mfc_enc_unlock, + .wait_finish =3D mfc_enc_lock, + .buf_init =3D mfc_enc_buf_init, + .buf_prepare =3D mfc_enc_buf_prepare, + .buf_finish =3D mfc_enc_buf_finish, + .buf_cleanup =3D mfc_enc_buf_cleanup, + .start_streaming =3D mfc_enc_start_streaming, + .stop_streaming =3D mfc_enc_stop_streaming, + .buf_queue =3D mfc_enc_buf_queue, +}; + +const struct vb2_ops *mfc_get_enc_vb2_ops(void) +{ + return &mfc_enc_qops; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.h b/driv= ers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.h new file mode 100644 index 000000000000..d3cb99f0bf84 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_vb2.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_enc_vb2.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_ENC_VB2_H +#define __MFC_ENC_VB2_H __FILE__ + +#include "base/mfc_common.h" + +const struct vb2_ops *mfc_get_enc_vb2_ops(void); + +#endif /* __MFC_ENC_VB2_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) (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 4DFCA2820B9 for ; Tue, 30 Sep 2025 03:57:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204634; cv=none; b=Gze5akMNGB7WWYJ5Bs9dIbQB6zNAabDisPINQqainrpocYqEC8bYi2KTMQEKkxhlkSRQ7uJaqYMGjstdFLQ2kunr7Xrw+Zwg5f1Qx/i7yc9yR3r9V89MYI2aQbWnBttYUcsexEW5+DqUHZSpCaSBsUhtRHCtJHicA9tCQ/ThnTA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204634; c=relaxed/simple; bh=UykV0YbRgexabfo0j1+gEGyJDJhsWfzunTHyfcJy9ls=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=ETjgE6+NM0VLlpETkp+fYNqmOmgnIrE/LCzamiMiIbbEDaVsispNWsxlMYA7cyWzp5PYsPrhb52aWkMzPBAFEIM/k/WtG1r5ZvX0QTtiBkUDGR15I41C7ZtUpuIJZ2AXpIOQFG7UuUduGDWoLfXqMibf722PNsgNGeZrBl1HwTU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=SJMNpDdL; arc=none smtp.client-ip=203.254.224.24 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="SJMNpDdL" Received: from epcas5p3.samsung.com (unknown [182.195.41.41]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250930035703epoutp0188b811bb823051653e9509ea07b7f5d9~p80jPa2yu3124431244epoutp01E for ; Tue, 30 Sep 2025 03:57:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250930035703epoutp0188b811bb823051653e9509ea07b7f5d9~p80jPa2yu3124431244epoutp01E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204623; bh=JstdFQyZsPfRGGPii8utLZ7Ue2RyZeHPK2kiM0pQXwM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SJMNpDdLqXMkAvS+AQJQP45SzpNzSdwAx2/TV1LhFhfeyUIbV4ffUYdGlom3trzPu +zwcCl5f2ZRO+5dzZzBu6woqyPeJJE5W2EW2LVlQDWTl+pYsn5DUIeQv25b4rzxQ7u g/2e7870Nze3x84HmJe6Kb7xf/wJHdJZfUPmil/k= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035702epcas5p4bb78ee0dffdffd53a0c3c808ac58a9c5~p80is2ftW3091130911epcas5p4G; Tue, 30 Sep 2025 03:57:02 +0000 (GMT) Received: from epcas5p1.samsung.com (unknown [182.195.38.89]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPQ566Phz2SSKk; Tue, 30 Sep 2025 03:57:01 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035700epcas5p1860af28e8a9e99d32b94fa1e97ab5456~p80g4DRTh1549615496epcas5p1x; Tue, 30 Sep 2025 03:57:00 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035657epsmtip1a49f305b4660ed0519b861d5fa0f2f2a~p80diLnNE2908429084epsmtip1K; Tue, 30 Sep 2025 03:56:57 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 25/29] media: mfc: Add encoder v4l2 driver interface Date: Tue, 30 Sep 2025 09:33:44 +0530 Message-Id: <20250930040348.3702923-26-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035700epcas5p1860af28e8a9e99d32b94fa1e97ab5456 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035700epcas5p1860af28e8a9e99d32b94fa1e97ab5456 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Add encoder V4L2 ioctls, format handling, control operations, buffer management, and driver initialization. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../platform/samsung/exynos-mfc/Makefile | 2 +- .../samsung/exynos-mfc/mfc_enc_v4l2.c | 4179 +++++++++++++++++ .../samsung/exynos-mfc/mfc_enc_v4l2.h | 20 + 3 files changed, 4200 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c create mode 100644 drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.h diff --git a/drivers/media/platform/samsung/exynos-mfc/Makefile b/drivers/m= edia/platform/samsung/exynos-mfc/Makefile index dad94a7c468c..96d70f9b790d 100644 --- a/drivers/media/platform/samsung/exynos-mfc/Makefile +++ b/drivers/media/platform/samsung/exynos-mfc/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_VIDEO_EXYNOS_MFC) :=3D exynos_mfc.o ccflags-y +=3D -I$(srctree)/$(src) =20 #Dev interface layer -exynos_mfc-y +=3D mfc.o mfc_dec_v4l2.o mfc_dec_vb2.o mfc_enc_vb2.o +exynos_mfc-y +=3D mfc.o mfc_dec_v4l2.o mfc_dec_vb2.o mfc_enc_v4l2.o mfc_en= c_vb2.o #Dev control layer exynos_mfc-y +=3D mfc_rm.o mfc_ctx_ctrl.o mfc_debugfs.o #Core interface layer diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c new file mode 100644 index 000000000000..84edebac82b2 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c @@ -0,0 +1,4179 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_enc_v4l2.c file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#include "mfc_enc_v4l2.h" +#include "mfc_enc_vb2.h" +#include "mfc_rm.h" + +#include "base/mfc_queue.h" +#include "base/mfc_utils.h" +#include "base/mfc_buf.h" +#include "base/mfc_mem.h" +static struct v4l2_queryctrl enc_controls[] =3D { + { + .id =3D V4L2_CID_MPEG_VIDEO_GOP_SIZE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The period of intra frame", + .minimum =3D 0, + .maximum =3D BIT(30) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The slice partitioning method", + .minimum =3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, + .maximum =3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_FIXED_BYTES, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of MB in a slice", + .minimum =3D 1, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The maximum bits per slices", + .minimum =3D 350, + .maximum =3D INT_MAX / 8, + .step =3D 1, + .default_value =3D 350, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB_ROW, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of MB row in a slice", + .minimum =3D 1, + .maximum =3D INT_MAX / SZ_256, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of intra refresh MBs", + .minimum =3D 0, + .maximum =3D BIT(18) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_PADDING, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Padding control enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Padding Color YUV Value", + .minimum =3D 0, + .maximum =3D BIT(24) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Frame level rate control enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_BITRATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Target bit rate rate-control", + .minimum =3D 1, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Rate control reaction coeff.", + .minimum =3D 1, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_STREAM_SIZE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Encoded stream size", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + .flags =3D V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_COUNT, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Encoded frame count", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + .flags =3D V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Encoded frame type", + .minimum =3D 0, + .maximum =3D 5, + .step =3D 1, + .default_value =3D 0, + .flags =3D V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Force frame type", + .minimum =3D V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED, + .maximum =3D V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED, + .step =3D 1, + .default_value =3D V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VBV_SIZE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VBV buffer size (1Kbits)", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEADER_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Sequence header mode", + .minimum =3D V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, + .maximum =3D V4L2_MPEG_VIDEO_HEADER_MODE_AT_THE_READY, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frame skip enable", + .minimum =3D V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED, + .maximum =3D V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT, + .step =3D 1, + .default_value =3D V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Fixed target bit enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_B_FRAMES, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of B frames", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 profile", + .minimum =3D V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .maximum =3D V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 level", + .minimum =3D V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .maximum =3D V4L2_MPEG_VIDEO_H264_LEVEL_6_0, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H264_INTERLACE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "H264 interlace mode", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 loop filter mode", + .minimum =3D V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED, + .maximum =3D V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_S_B, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 loop filter alpha offset", + .minimum =3D -12, + .maximum =3D 12, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 loop filter beta offset", + .minimum =3D -12, + .maximum =3D 12, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 entorpy mode", + .minimum =3D V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .maximum =3D V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of ref. picture of P", + .minimum =3D 1, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "H264 8x8 transform enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "MB level rate control", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Frame rate", + .minimum =3D 1, + .maximum =3D FRAME_RATE_RESOLUTION, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Frame QP value", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "H264 dark region adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "H264 smooth region adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "H264 static region adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "H264 MB activity adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 P frame QP value", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 B frame QP value", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Aspect ratio VUI enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VUI aspect ratio IDC", + .minimum =3D V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED, + .maximum =3D V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Horizontal size of SAR", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Vertical size of SAR", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "GOP closure", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 I period", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Hierarchical Coding", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Type", + .minimum =3D V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B, + .maximum =3D V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer QP", + .minimum =3D 0, + .maximum =3D ((6 << 16) | 0xFFFF), /* (index << 16) | value */ + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "frame pack sei generation flag", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Current frame is frame 0 flag", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frame packing arrangement type", + .minimum =3D V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_SIDE, + .maximum =3D V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TEMPORAL, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_SIDE_BY_= SIDE, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_FMO, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Flexible Macroblock Order", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Map type for FMO", + .minimum =3D V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES, + .maximum =3D V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Number of slice groups for FMO", + .minimum =3D 1, + .maximum =3D 4, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "FMO Run Length", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Direction of the slice group", + .minimum =3D V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT, + .maximum =3D V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_LEFT, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Size of the first slice group", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_ASO, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Arbitrary Slice Order", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "ASO Slice order", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_PREPEND_SPSPPS_TO_IDR, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Prepend SPS/PPS to every IDR", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 profile", + .minimum =3D V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, + .maximum =3D V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 level", + .minimum =3D V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, + .maximum =3D V4L2_MPEG_VIDEO_MPEG4_LEVEL_6, + .step =3D 1, + .default_value =3D V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 Frame QP value", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_QPEL, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Quarter pixel search enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 P frame QP value", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 B frame QP value", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_TIME_RES, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 vop time resolution", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_FRM_DELTA, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 frame delta", + .minimum =3D 1, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_H263_RC_FRAME_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H263 Frame rate", + .minimum =3D 1, + .maximum =3D BIT(8) - 1, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H263 Frame QP value", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H263 P frame QP value", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frame Tag", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frame Status", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_QOS_RATIO, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "QoS ratio value", + .minimum =3D 20, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 100, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_VERSION, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 version", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_I_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Frame QP value", + .minimum =3D 0, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_P_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Frame QP value", + .minimum =3D 0, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_RC_FRAME_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Frame rate", + .minimum =3D 1, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_OF_PARTITIONS, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 number of partitions", + .minimum =3D 0, + .maximum =3D 8, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_LEVEL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 loop filter level", + .minimum =3D 0, + .maximum =3D 63, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_SHARPNESS, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 loop filter sharpness", + .minimum =3D 0, + .maximum =3D 7, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_GOLDEN_FRAMESEL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 indication of golden frame", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_GF_REFRESH_PERIOD, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 indication of golden frame", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "VP8 hierarchy QP enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 layer0 QP value", + .minimum =3D 0, + .maximum =3D ((2 << 16) | 0xFFFF), /* (index << 16) | value */ + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 layer1 QP value", + .minimum =3D 0, + .maximum =3D ((2 << 16) | 0xFFFF), /* (index << 16) | value */ + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 layer2 QP value", + .minimum =3D 0, + .maximum =3D ((2 << 16) | 0xFFFF), /* (index << 16) | value */ + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_REF_NUMBER_FOR_PFRAMES, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Number of reference picture", + .minimum =3D 1, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_DISABLE_INTRA_MD4X4, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "VP8 intra 4x4 mode disable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_TEMPORAL_LAYER, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "VP8 number of hierarchical layer", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_VERSION, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 version", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_PROFILE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 profile", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_LEVEL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 level", + .minimum =3D 10, + .maximum =3D 62, + .step =3D 1, + .default_value =3D 10, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_I_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Frame QP value", + .minimum =3D 1, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_P_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Frame QP value", + .minimum =3D 1, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_RC_FRAME_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Frame rate", + .minimum =3D 1, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_GOLDEN_FRAMESEL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 indication of golden frame", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_GF_REFRESH_PERIOD, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 indication of golden frame", + .minimum =3D 0, + .maximum =3D (BIT(16) - 1), + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHY_QP_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "VP9 hierarchy QP enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 layer0 QP value", + .minimum =3D 0, + .maximum =3D ((2 << 16) | 0xFFFF), /* (index << 16) | value */ + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_REF_NUMBER_FOR_PFRAMES, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Number of reference picture", + .minimum =3D 1, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "VP9 number of hierarchical layer", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MAX_PARTITION_DEPTH, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "VP9 Maximum coding unit depth", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_DISABLE_INTRA_PU_SPLIT, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "VP9 disable intra pu split", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit0", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit1", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit2", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Change", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DISABLE_IVF_HEADER, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "IVF header generation", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Frame QP value", + .minimum =3D -12, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC P frame QP value", + .minimum =3D -12, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC B frame QP value", + .minimum =3D -12, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_DARK, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC dark region adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_SMOOTH, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC smooth region adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_STATIC, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC static region adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_ACTIVITY, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC activity adaptive", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC Profile", + .minimum =3D 0, + .maximum =3D 4, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC level", + .minimum =3D 10, + .maximum =3D 62, + .step =3D 1, + .default_value =3D 10, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TIER_FLAG, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC tier_flag default is Main", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_RC_FRAME_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Frame rate", + .minimum =3D 1, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_PARTITION_DEPTH, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Maximum coding unit depth", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 2, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REF_NUMBER_FOR_PFRAMES, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Number of reference picture", + .minimum =3D 1, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Number of reference picture", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_CONST_INTRA_PRED_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC refresh type", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LOSSLESS_CU_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC lossless encoding select", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WAVEFRONT_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC Wavefront enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_DISABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC Filter disable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_SLICE_BOUNDARY, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "across or not slice boundary", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LTR_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "long term reference enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_QP_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "QP values for temporal layer", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_TYPE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Hierarchical Coding Type", + .minimum =3D V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B, + .maximum =3D V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer", + .minimum =3D 0, + .maximum =3D 7, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer QP", + .minimum =3D 0, + .maximum =3D ((6 << 16) | 0xFFFF), /* (index << 16) | value */ + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer BIT0", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer BIT1", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer BIT2", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT3, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer BIT3", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT4, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer BIT4", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT5, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer BIT5", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT6, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer BIT6", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Change", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIGN_DATA_HIDING, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC Sign data hiding", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_GENERAL_PB_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC General pb enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TEMPORAL_ID_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC Temporal id enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STRONG_SMOTHING_FLAG, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC Strong intra smoothing flag", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_INTRA_PU_SPLIT, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC disable intra pu split", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_TMV_PREDICTION, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "HEVC disable tmv prediction", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "max number of candidate MVs", + .minimum =3D 0, + .maximum =3D 4, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WITHOUT_STARTCODE_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "ENC without startcode enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_PERIOD, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Number of reference picture", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_BETA_OFFSET_DIV2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC loop filter beta offset", + .minimum =3D -6, + .maximum =3D 6, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_TC_OFFSET_DIV2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC loop filter tc offset", + .minimum =3D -6, + .maximum =3D 6, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC size of length field", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_USER_REF, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "user long term reference frame", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STORE_REF, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "store long term reference frame", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 0, /* need to check default value */ + }, + { + .id =3D V4L2_CID_MPEG_MFC_GET_VERSION_INFO, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Get MFC version information", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_GET_EXTRA_BUFFER_SIZE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Get extra buffer size", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_GET_EXT_INFO, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Get extra information", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit0", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit1", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit2", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT3, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit3", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT4, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit4", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT5, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit5", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT6, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit6", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Change", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit0", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit1", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Bit2", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding Layer Change", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_H264_ENABLE_LTR, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Enable LTR", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_H264_NUM_OF_LTR, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Number of LTR", + .minimum =3D 0, + .maximum =3D 4, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_H264_MARK_LTR, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Set the frame as a LTRP", + .minimum =3D 0, + .maximum =3D 4, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_H264_USE_LTR, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Specify a LTRP for encoding", + .minimum =3D 0, + .maximum =3D 0xF, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Base Layer Priority", + .minimum =3D 0, + .maximum =3D 63, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_CONFIG_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "QP control per each frame", + .minimum =3D -12, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_H264_VUI_RESTRICTION_ENABLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 vui generation enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_PREPEND_SPSPPS_TO_IDR, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Prepend SPS/PPS to every IDR", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_CONFIG_QP_ENABLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "set dynamic qp controls", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_ROI_CONTROL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Region-Of-Interest control", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_ROI_ENABLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Region-Of-Interest enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Min QP value for I frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Max QP value for I frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Min QP value for I frame", + .minimum =3D -12, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Max QP value for I frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 Min QP value for I frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 Max QP value for I frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H263_MIN_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H263 Min QP value for I frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H263_MAX_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H263 Max QP value for I frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MIN_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Min QP value for I frame", + .minimum =3D 0, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MAX_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Max QP value for I frame", + .minimum =3D 0, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MIN_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Min QP value for I frame", + .minimum =3D 1, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MAX_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Max QP value for I frame", + .minimum =3D 1, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Min QP value for P frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Max QP value for P frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Min QP value for P frame", + .minimum =3D -12, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Max QP value for P frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 Min QP value for P frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 Max QP value for P frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H263 Min QP value for P frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H263 Max QP value for P frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Min QP value for P frame", + .minimum =3D 0, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP8 Max QP value for P frame", + .minimum =3D 0, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Min QP value for P frame", + .minimum =3D 1, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "VP9 Max QP value for P frame", + .minimum =3D 1, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Min QP value for B frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "H264 Max QP value for B frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Min QP value for B frame", + .minimum =3D -12, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "HEVC Max QP value for B frame", + .minimum =3D 0, + .maximum =3D 51, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 Min QP value for B frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MPEG4 Max QP value for B frame", + .minimum =3D 1, + .maximum =3D 31, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_RC_PVC_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Perceptual Video Coding enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_TEMPORAL_SHORTTERM_MAX_LAYER, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Hierarchical Coding max layer", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_WEIGHTED_ENABLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Weighted Prediction enable", + .minimum =3D 0, + .maximum =3D 3, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_YSUM, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "YSUM for weighted Prediction", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "ratio of intra encoded size", + .minimum =3D 15, + .maximum =3D 50, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Color range", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Colour primaries", + .minimum =3D 0, + .maximum =3D 22, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_TRANSFER_CHARACTERISTICS, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Transfer characteristics", + .minimum =3D 0, + .maximum =3D 18, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MATRIX_COEFFICIENTS, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Matrix coefficients", + .minimum =3D 0, + .maximum =3D 10, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_HIERARCHICAL_BITRATE_CTRL, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Hierarchical bitrate control", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 1, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_STATIC_INFO_ENABLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Static info enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MAX_PIC_AVERAGE_LIGHT, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Max pic average light", + .minimum =3D 0, + .maximum =3D 0xFFFF, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MAX_CONTENT_LIGHT, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Max content light", + .minimum =3D 0, + .maximum =3D 0xFFFF, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MAX_DISPLAY_LUMINANCE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Max display luminance", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_MIN_DISPLAY_LUMINANCE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Min display luminance", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_WHITE_POINT, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "White point", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Display primaries 0", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Display primaries 1", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_2, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Display primaries 2", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_HDR_USER_SHARED_HANDLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Dynamic HDR10+ SEI metadata", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DROP_CONTROL, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Drop control", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_CHROMA_QP_OFFSET_CB, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Chroma QP index for Cb", + .minimum =3D -12, + .maximum =3D 12, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_CHROMA_QP_OFFSET_CR, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Chroma QP index for Cr", + .minimum =3D -12, + .maximum =3D 12, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Buffer flag", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Buffer flag", + .minimum =3D INT_MIN, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_GDC_VOTF, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "GDC vOTF", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SKIP_LAZY_UNMAP, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "skip lazy unmap", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frames per second in 1000x scale", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 60000, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_GOP_CTRL, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Meaning of GOP_SIZE", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_AVERAGE_QP, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Frames average QP", + .minimum =3D 0, + .maximum =3D U8_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MV_SEARCH_MODE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MV search mode", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MV HOR position L0", + .minimum =3D -128, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MV HOR position L1", + .minimum =3D -128, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L0, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MV VER position L0", + .minimum =3D -128, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L1, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MV VER position L1", + .minimum =3D -128, + .maximum =3D 127, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_PRIORITY, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "priority", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MIN_QUALITY, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "MIN quality mode enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MAX_IFRAME_SIZE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "max I frame size", + .minimum =3D 0, + .maximum =3D BIT(16) - 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_WP_TWO_PASS_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "WP two pass encoding enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_TIMING_INFO_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Timing info enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_ADAPTIVE_GOP_ENABLE, + .type =3D V4L2_CTRL_TYPE_BOOLEAN, + .name =3D "Adaptive GOP enable", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SUM_SKIP_MB, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of skip MB", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SUM_INTRA_MB, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of intra MB", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_SUM_ZERO_MV_MB, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "The number of zero MV MB", + .minimum =3D 0, + .maximum =3D INT_MAX, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MV_HOR_RANGE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MV HOR Range", + .minimum =3D 16, + .maximum =3D 128, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_VIDEO_MV_VER_RANGE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "MV VER Range", + .minimum =3D 16, + .maximum =3D 128, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_MULTI_VIEW_ENABLE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Multi-View Enable", + .minimum =3D 0, + .maximum =3D 2, + .step =3D 1, + .default_value =3D 0, + }, + { + .id =3D V4L2_CID_MPEG_MFC_MULTI_VIEW_MAIN_TYPE, + .type =3D V4L2_CTRL_TYPE_INTEGER, + .name =3D "Main-View Type", + .minimum =3D 0, + .maximum =3D 1, + .step =3D 1, + .default_value =3D 0, + }, +}; + +#define ENC_NUM_CTRLS ARRAY_SIZE(enc_controls) +/* Find selected format description */ +static struct mfc_fmt *__mfc_enc_find_format(struct mfc_ctx *ctx, + unsigned int pixelformat) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_fmt *fmt =3D NULL; + unsigned long i; + + for (i =3D 0; i < MFC_NUM_FORMATS; i++) { + if ((mfc_formats[i].type & MFC_FMT_STREAM) && + !(mfc_formats[i].type & MFC_FMT_ENC)) { + continue; + } + if (mfc_formats[i].fourcc =3D=3D pixelformat) { + fmt =3D (struct mfc_fmt *)&mfc_formats[i]; + break; + } + } + + if (fmt && !dev->pdata->support_10bit && (fmt->type & MFC_FMT_10BIT)) { + mfc_ctx_err("[FRAME] 10bit is not supported\n"); + fmt =3D NULL; + } + if (fmt && !dev->pdata->support_422 && (fmt->type & MFC_FMT_422)) { + mfc_ctx_err("[FRAME] 422 is not supported\n"); + fmt =3D NULL; + } + if (fmt && !dev->pdata->support_rgb && (fmt->type & MFC_FMT_RGB)) { + mfc_ctx_err("[FRAME] RGB is not supported\n"); + fmt =3D NULL; + } + + if (fmt && (fmt->type & MFC_FMT_STREAM) && + dev->pdata->mfc_resource[fmt->codec_mode].op_core_type =3D=3D MFC_OP_= CORE_NOT_FIXED) { + mfc_ctx_err("[STREAM] %s is not supported\n", fmt->name); + fmt =3D NULL; + } + + return fmt; +} + +static struct v4l2_queryctrl *__mfc_enc_get_ctrl(int id) +{ + unsigned long i; + + for (i =3D 0; i < ENC_NUM_CTRLS; ++i) + if (id =3D=3D enc_controls[i].id) + return &enc_controls[i]; + return NULL; +} + +static int __mfc_enc_check_ctrl_val(struct mfc_ctx *ctx, struct v4l2_contr= ol *ctrl) +{ + struct v4l2_queryctrl *c; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + + c =3D __mfc_enc_get_ctrl(ctrl->id); + if (!c) { + mfc_ctx_err("[CTRLS] not supported control id (%#x)\n", ctrl->id); + return -EINVAL; + } + + if (ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_GOP_SIZE && ctrl->value > c->maxi= mum) { + mfc_ctx_info("GOP_SIZE is changed to max(%d -> %d)\n", + ctrl->value, c->maximum); + ctrl->value =3D c->maximum; + } + + if (ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER || + ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER) { + if ((ctrl->value & ~BIT(16)) < c->minimum || + (ctrl->value & ~BIT(16)) > c->maximum || + (c->step !=3D 0 && (ctrl->value & ~BIT(16)) % c->step !=3D 0)) { + mfc_ctx_err("[CTRLS][%s] Invalid control id: %#x, value: %d (%#x)\n", + c->name, ctrl->id, ctrl->value, ctrl->value); + return -ERANGE; + } else { + return 0; + } + } + + if (ctrl->id =3D=3D V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY) { + if (ctrl->value < c->minimum || + (ctrl->value + p->num_hier_max_layer - 1) > c->maximum || + (c->step !=3D 0 && (ctrl->value & ~BIT(16)) % c->step !=3D 0)) { + mfc_ctx_err("[CTRLS][%s] Invalid control id: %#x, value: %d (%#x)\n", + c->name, ctrl->id, ctrl->value, ctrl->value); + return -ERANGE; + } else { + return 0; + } + } + + if (ctrl->value < c->minimum || ctrl->value > c->maximum || + (c->step !=3D 0 && ctrl->value % c->step !=3D 0)) { + mfc_ctx_err("[CTRLS][%s] id: %#x, invalid value (%d)\n", + c->name, ctrl->id, ctrl->value); + return -ERANGE; + } + + mfc_ctx_debug(5, "[CTRLS][%s] id: %#x, value: %d (%#x)\n", + c->name, ctrl->id, ctrl->value, ctrl->value); + + return 0; +} + +static inline int __mfc_enc_h264_profile(struct mfc_ctx *ctx, int profile) +{ + int ret =3D 0; + + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + ret =3D MFC_REG_E_PROFILE_H264_MAIN; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + ret =3D MFC_REG_E_PROFILE_H264_HIGH; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + ret =3D MFC_REG_E_PROFILE_H264_BASELINE; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + ret =3D MFC_REG_E_PROFILE_H264_CONSTRAINED_BASELINE; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH: + ret =3D MFC_REG_E_PROFILE_H264_CONSTRAINED_HIGH; + break; + default: + ret =3D -EINVAL; + } + + return ret; +} + +/* Query capabilities of the device */ +static int mfc_enc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "MFC", sizeof(cap->driver)); + strscpy(cap->card, "encoder", sizeof(cap->card)); + + return 0; +} + +static int __mfc_enc_enum_fmt(struct mfc_dev *dev, struct v4l2_fmtdesc *f, + unsigned int type) +{ + struct mfc_fmt *fmt; + unsigned long i, j =3D 0; + + for (i =3D 0; i < MFC_NUM_FORMATS; ++i) { + if (!(mfc_formats[i].type & type)) + continue; + if (!dev->pdata->support_10bit && (mfc_formats[i].type & MFC_FMT_10BIT)) + continue; + if (!dev->pdata->support_422 && (mfc_formats[i].type & MFC_FMT_422)) + continue; + if (!dev->pdata->support_rgb && (mfc_formats[i].type & MFC_FMT_RGB)) + continue; + + if (j =3D=3D f->index) { + fmt =3D &mfc_formats[i]; + strscpy(f->description, fmt->name, + sizeof(f->description)); + f->pixelformat =3D fmt->fourcc; + + return 0; + } + + ++j; + } + + return -EINVAL; +} + +static int mfc_enc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) +{ + struct mfc_dev *dev =3D video_drvdata(file); + + return __mfc_enc_enum_fmt(dev, f, MFC_FMT_STREAM); +} + +static int mfc_enc_enum_fmt_vid_out_mplane(struct file *file, void *prov, + struct v4l2_fmtdesc *f) +{ + struct mfc_dev *dev =3D video_drvdata(file); + + return __mfc_enc_enum_fmt(dev, f, MFC_FMT_FRAME); +} + +static int mfc_enc_g_fmt(struct file *file, void *priv, struct v4l2_format= *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_enc *enc =3D ctx->enc_priv; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + struct mfc_raw_info *raw; + int i; + + mfc_ctx_debug_enter(); + + if (f->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(4, "enc dst g_fmt\n"); + /* This is run on output (encoder dest) */ + pix_fmt_mp->width =3D 0; + pix_fmt_mp->height =3D 0; + pix_fmt_mp->field =3D V4L2_FIELD_NONE; + pix_fmt_mp->pixelformat =3D ctx->dst_fmt->fourcc; + pix_fmt_mp->num_planes =3D ctx->dst_fmt->mem_planes; + + pix_fmt_mp->plane_fmt[0].bytesperline =3D enc->dst_buf_size; + pix_fmt_mp->plane_fmt[0].sizeimage =3D enc->dst_buf_size; + } else if (f->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "enc src g_fmt\n"); + /* This is run on capture (encoder src) */ + raw =3D &ctx->raw_buf; + + pix_fmt_mp->width =3D ctx->img_width; + pix_fmt_mp->height =3D ctx->img_height; + pix_fmt_mp->field =3D V4L2_FIELD_NONE; + pix_fmt_mp->pixelformat =3D ctx->src_fmt->fourcc; + pix_fmt_mp->num_planes =3D ctx->src_fmt->mem_planes; + for (i =3D 0; i < ctx->src_fmt->mem_planes; i++) { + pix_fmt_mp->plane_fmt[i].bytesperline =3D raw->stride[i]; + pix_fmt_mp->plane_fmt[i].sizeimage =3D raw->plane_size[i]; + } + } else { + mfc_ctx_err("invalid buf type (%d)\n", f->type); + return -EINVAL; + } + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_enc_try_fmt(struct file *file, void *priv, struct v4l2_form= at *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_fmt *fmt; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + + fmt =3D __mfc_enc_find_format(ctx, pix_fmt_mp->pixelformat); + if (!fmt) { + mfc_ctx_err("Unsupported format for %s\n", + V4L2_TYPE_IS_OUTPUT(f->type) ? "source" : "destination"); + return -EINVAL; + } + if (f->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + fmt->codec_mode =3D=3D MFC_FORMATS_NO_CODEC) { + mfc_ctx_err("MFC_FORMATS_NO_CODEC is invalid to dst(fmt is %s)\n", + fmt->name); + return -EINVAL; + } + + /* For resource reservation */ + if ((!ctx->img_width && !ctx->img_height) && + pix_fmt_mp->width > 0 && pix_fmt_mp->height > 0) { + ctx->img_width =3D pix_fmt_mp->width; + ctx->img_height =3D pix_fmt_mp->height; + } + + if (f->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ctx->dst_fmt =3D fmt; + ctx->codec_mode =3D ctx->dst_fmt->codec_mode; + ctx->op_core_type =3D ctx->dev->pdata->mfc_resource[ctx->codec_mode].op_= core_type; + } + + mfc_ctx_debug(2, "[%s] resolution %dx%d, %s : %s\n", + V4L2_TYPE_IS_OUTPUT(f->type) ? "FRAME" : "STREAM", + ctx->img_width, ctx->img_height, + V4L2_TYPE_IS_OUTPUT(f->type) ? "pixelformat" : "codectype", fmt->n= ame); + + return 0; +} + +static void __mfc_enc_check_format(struct mfc_ctx *ctx) +{ + ctx->is_422 =3D 0; + ctx->rgb_bpp =3D 0; + + switch (ctx->src_fmt->fourcc) { + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + mfc_ctx_debug(2, "[FRAME] is 422 format\n"); + ctx->is_422 =3D 1; + break; + case V4L2_PIX_FMT_RGB24: + ctx->rgb_bpp =3D 24; + break; + case V4L2_PIX_FMT_RGB565: + ctx->rgb_bpp =3D 16; + break; + case V4L2_PIX_FMT_RGB32X: + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_ARGB32: + case V4L2_PIX_FMT_RGB32: + ctx->rgb_bpp =3D 32; + break; + default: + break; + } + mfc_ctx_debug(2, "[FRAME] 422: %d, rgb: %d\n", + ctx->is_422, ctx->rgb_bpp); +} + +static int __mfc_enc_check_resolution(struct mfc_ctx *ctx) +{ + int max_width =3D 0, max_height =3D 0, min_width =3D 0, min_height =3D 0,= swap_check =3D 0; + + /* Check max resolution */ + switch (ctx->codec_mode) { + case MFC_REG_CODEC_H264_ENC: + max_width =3D 8192; + max_height =3D 8192; + break; + default: + mfc_ctx_err("Not supported codec(%d)\n", ctx->codec_mode); + return -EINVAL; + } + + if (swap_check) { + if (!((ctx->crop_width < max_width && ctx->crop_height < max_height) || + (ctx->crop_width < max_height && ctx->crop_height < max_width))) { + mfc_ctx_err("Resolution is too big(%dx%d > %dxi%d or %dx%d\n", + ctx->crop_width, ctx->crop_height, max_width, max_height, + max_height, max_width); + return -EINVAL; + } + } else { + if (ctx->crop_width > max_width || ctx->crop_height > max_height) { + mfc_ctx_err("Resolution is too big(%dx%d > %dx%d)\n", + ctx->crop_width, ctx->crop_height, max_width, max_height); + return -EINVAL; + } + } + + /* Check min resolution */ + switch (ctx->codec_mode) { + case MFC_REG_CODEC_H264_ENC: + min_width =3D 32; + min_height =3D 32; + break; + default: + mfc_ctx_err("Not supported codec(%d)\n", ctx->codec_mode); + return -EINVAL; + } + + if (ctx->crop_width < min_width || ctx->crop_height < min_height) { + mfc_ctx_err("Resolution is too small(%dx%d < %dx%d)\n", + ctx->crop_width, ctx->crop_height, min_width, min_height); + return -EINVAL; + } + + return 0; +} + +static int mfc_enc_s_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mfc_dev *dev =3D video_drvdata(file); + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_enc *enc =3D ctx->enc_priv; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + struct mfc_fmt *fmt =3D NULL; + int ret =3D 0; + + mfc_ctx_debug_enter(); + + if (ctx->vq_dst.streaming) { + mfc_ctx_err("dst queue busy\n"); + return -EBUSY; + } + + fmt =3D __mfc_enc_find_format(ctx, pix_fmt_mp->pixelformat); + if (!fmt) { + mfc_ctx_err("Unsupported format for destination\n"); + return -EINVAL; + } + ctx->dst_fmt =3D fmt; + + if ((!ctx->img_width && !ctx->img_height) && + pix_fmt_mp->width > 0 && pix_fmt_mp->height > 0) { + ctx->img_width =3D pix_fmt_mp->width; + ctx->img_height =3D pix_fmt_mp->height; + ctx->crop_width =3D ctx->img_width; + ctx->crop_height =3D ctx->img_height; + } + + ctx->codec_mode =3D ctx->dst_fmt->codec_mode; + mfc_ctx_info("[STREAM] codectype: %s(%d), %d x %d\n", + ctx->dst_fmt->name, ctx->codec_mode, + ctx->img_width, ctx->img_height); + + if (__mfc_enc_check_resolution(ctx)) { + mfc_ctx_err("Unsupported MFC resolution\n"); + return -EINVAL; + } + + if (mfc_check_resolution(ctx)) { + mfc_ctx_err("Unsupported product resolution\n"); + return -EINVAL; + } + + enc->dst_buf_size =3D pix_fmt_mp->plane_fmt[0].sizeimage; + pix_fmt_mp->plane_fmt[0].bytesperline =3D 0; + + ret =3D mfc_rm_instance_open(dev, ctx); + if (ret) + mfc_ctx_err("Failed to instance open\n"); + + if (dev->pdata->support_enc_mode1 && + (dev->debugfs.feature_option & MFC_OPTION_SET_MULTI_CORE_FORCE)) { + if (dev->debugfs.feature_option & MFC_OPTION_MULTI_CORE_DISABLE) { + mfc_ctx_info("[2CORE] multi core mode disabled\n"); + } else { + if (dev->num_inst > 1) + mfc_ctx_debug(2, "[2CORE] multi core bits: %#lx, num inst: %d\n", + dev->multi_core_inst_bits, dev->num_inst); + ctx->stream_op_mode =3D MFC_OP_TWO_MODE1; + mfc_ctx_info("[2CORE] This stream need to multi core stream_op_mode(%d)= \n", + ctx->stream_op_mode); + } + } + + mfc_ctx_debug_leave(); + return ret; +} + +static int __mfc_enc_set_num_fd_frame(struct mfc_ctx *ctx, + struct v4l2_pix_format_mplane *pix_fmt_mp) +{ + int calc_num_planes; + int num_fd_depth_map =3D 0; + int num_view =3D 1; + int num_fd_sub_view_meta =3D 0; + + if (ctx->multi_view_enable) { + if (pix_fmt_mp->flags & MFC_FMT_FLAG_MULTI_VIEW) { + num_view =3D MFC_NUM_MULTI_VIEW; + num_fd_sub_view_meta =3D MFC_NUM_FD_SUB_VIEW_META; + } + if (pix_fmt_mp->flags & MFC_FMT_FLAG_DEPTH_MAP) + num_fd_depth_map =3D MFC_NUM_FD_DEPTH_MAP; + + calc_num_planes =3D + (ctx->src_fmt->mem_planes + num_fd_depth_map) * num_view + + num_fd_sub_view_meta; + } else { + calc_num_planes =3D ctx->src_fmt->mem_planes; + } + + if (calc_num_planes !=3D pix_fmt_mp->num_planes) + return -EINVAL; + + mfc_set_view_buf_info(ctx, ctx->src_fmt->mem_planes, + num_fd_depth_map, num_fd_sub_view_meta); + + ctx->num_fd_frame =3D calc_num_planes; + + return 0; +} + +static int mfc_enc_s_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + struct v4l2_pix_format_mplane *pix_fmt_mp =3D &f->fmt.pix_mp; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + struct mfc_fmt *prev_src_fmt =3D NULL; + struct mfc_fmt *fmt =3D NULL; + int i, shift; + + mfc_ctx_debug_enter(); + + if (ctx->vq_src.streaming) { + mfc_ctx_err("src queue busy\n"); + return -EBUSY; + } + + /* Backup previous format */ + prev_src_fmt =3D ctx->src_fmt; + fmt =3D __mfc_enc_find_format(ctx, pix_fmt_mp->pixelformat); + if (!fmt) { + mfc_ctx_err("Unsupported format for source\n"); + return -EINVAL; + } + ctx->src_fmt =3D fmt; + + if (__mfc_enc_set_num_fd_frame(ctx, pix_fmt_mp)) { + mfc_ctx_err("[FRAME] enc src plane number is different (%d !=3D %d)\n", + ctx->num_fd_frame, pix_fmt_mp->num_planes); + return -EINVAL; + } + + ctx->raw_buf.num_planes =3D ctx->src_fmt->num_planes; + shift =3D ctx->src_fmt->num_planes - 2; + ctx->img_width =3D pix_fmt_mp->width; + ctx->img_height =3D pix_fmt_mp->height; + for (i =3D 0; i < ctx->src_fmt->mem_planes; i++) + ctx->bytesperline[i] =3D pix_fmt_mp->plane_fmt[i].bytesperline; + + __mfc_enc_check_format(ctx); + + /* Encoder works only single core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + /* Dynamic Resolution & Format Changes */ + if (core_ctx->state =3D=3D MFCINST_FINISHED) { + mfc_change_state(core_ctx, MFCINST_GOT_INST); + if (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1) { + core =3D mfc_get_sub_core_lock(dev, ctx); + if (core) { + core_ctx =3D core->core_ctx[ctx->num]; + mfc_change_state(core_ctx, MFCINST_GOT_INST); + mfc_clean_core_ctx_int_flags(core_ctx); + } + } + if (ctx->src_fmt->fourcc !=3D prev_src_fmt->fourcc) + mfc_info("[DFC] Enc Dynamic Format Changed %s -> %s\n", + prev_src_fmt->name, ctx->src_fmt->name); + else + mfc_info("[DRC] Enc Dynamic Resolution Changed\n"); + } + + /* When single fd format, use luma stride for chroma stride */ + if (IS_SINGLE_FD(ctx, ctx->src_fmt)) + for (i =3D 1; i < ctx->src_fmt->num_planes; i++) + ctx->bytesperline[i] =3D ctx->bytesperline[0] >> shift; + + mfc_info("[FRAME] enc src pixelformat : %s\n", ctx->src_fmt->name); + mfc_info("[FRAME] resolution w: %d, h: %d, Y stride: %d, C stride: %d\n", + pix_fmt_mp->width, pix_fmt_mp->height, + ctx->bytesperline[0], ctx->bytesperline[1]); + + /* + * It should be keep till buffer size and stride was calculated. + * And it can be changed to real encoding size, if user call the s_crop. + */ + ctx->crop_width =3D ctx->img_width; + ctx->crop_height =3D ctx->img_height; + mfc_enc_calc_src_size(ctx); + + ctx->output_state =3D QUEUE_FREE; + + mfc_ctx_debug_leave(); + return 0; +} + +static int mfc_enc_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + + mfc_ctx_debug_enter(); + + s->r.left =3D ctx->crop_left; + s->r.top =3D ctx->crop_top; + s->r.width =3D ctx->crop_width; + s->r.height =3D ctx->crop_height; + + mfc_ctx_debug(2, "[FRAME] enc crop w: %d, h: %d, offset l: %d t: %d\n", + ctx->crop_width, ctx->crop_height, ctx->crop_left, ctx->crop_top); + + mfc_ctx_debug_leave(); + + return 0; +} + +static int mfc_enc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + + mfc_ctx_debug_enter(); + + if (!V4L2_TYPE_IS_OUTPUT(s->type)) { + mfc_ctx_err("not supported type (It can only in the source)\n"); + return -EINVAL; + } + + if (s->r.left < 0 || s->r.top < 0) { + mfc_ctx_err("[FRAME] crop position is negative\n"); + return -EINVAL; + } + + if (s->r.height > ctx->img_height || s->r.top > ctx->img_height || + s->r.width > ctx->img_width || s->r.left > ctx->img_width || + (s->r.left > (ctx->img_width - s->r.width)) || + (s->r.top > (ctx->img_height - s->r.height))) { + mfc_ctx_err("[FRAME] Out of crop range: (%d,%d,%d,%d) from %dx%d\n", + s->r.left, s->r.top, s->r.width, s->r.height, + ctx->img_width, ctx->img_height); + return -EINVAL; + } + + ctx->crop_top =3D s->r.top; + ctx->crop_left =3D s->r.left; + ctx->crop_height =3D s->r.height; + ctx->crop_width =3D s->r.width; + + mfc_ctx_debug(3, "[FRAME] enc original: %dx%d, crop: %dx%d, offset l: %d = t: %d\n", + ctx->img_width, ctx->img_height, + ctx->crop_width, ctx->crop_height, + ctx->crop_left, ctx->crop_top); + + return 0; +} + +static int mfc_enc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + int ret =3D 0; + + mfc_ctx_debug_enter(); + + if (reqbufs->memory =3D=3D V4L2_MEMORY_MMAP) { + mfc_ctx_err("Not supported memory type (%d)\n", reqbufs->memory); + return -EINVAL; + } + + if (reqbufs->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_ctx_debug(4, "enc dst reqbuf(%d)\n", reqbufs->count); + + if (reqbufs->count =3D=3D 0) { + ret =3D vb2_reqbufs(&ctx->vq_dst, reqbufs); + ctx->capture_state =3D QUEUE_FREE; + return ret; + } + + if (ctx->capture_state !=3D QUEUE_FREE) { + mfc_ctx_err("invalid capture state: %d\n", ctx->capture_state); + return -EINVAL; + } + + ret =3D vb2_reqbufs(&ctx->vq_dst, reqbufs); + if (ret) { + mfc_ctx_err("error in vb2_reqbufs() for E(D)\n"); + return ret; + } + + ctx->capture_state =3D QUEUE_BUFS_REQUESTED; + } else if (reqbufs->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "enc src reqbuf(%d)\n", reqbufs->count); + if (reqbufs->count =3D=3D 0) { + ret =3D vb2_reqbufs(&ctx->vq_src, reqbufs); + ctx->output_state =3D QUEUE_FREE; + return ret; + } + + if (ctx->output_state !=3D QUEUE_FREE) { + mfc_ctx_err("invalid output state: %d\n", ctx->output_state); + return -EINVAL; + } + + ret =3D vb2_reqbufs(&ctx->vq_src, reqbufs); + if (ret) { + mfc_ctx_err("error in vb2_reqbufs() for E(S)\n"); + return ret; + } + + ctx->output_state =3D QUEUE_BUFS_REQUESTED; + } else { + mfc_ctx_err("invalid buf type (%d)\n", reqbufs->type); + return -EINVAL; + } + + mfc_ctx_debug_leave(); + + return ret; +} + +static int mfc_enc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int ret =3D 0; + + mfc_ctx_debug_enter(); + + /* Encoder works only single core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + mfc_debug(4, "enc dst querybuf, state: %d\n", core_ctx->state); + ret =3D vb2_querybuf(&ctx->vq_dst, buf); + if (ret !=3D 0) { + mfc_err("enc dst: error in vb2_querybuf()\n"); + return ret; + } + } else if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_debug(4, "enc src querybuf, state: %d\n", core_ctx->state); + ret =3D vb2_querybuf(&ctx->vq_src, buf); + if (ret !=3D 0) { + mfc_err("enc src: error in vb2_querybuf()\n"); + return ret; + } + } else { + mfc_err("invalid buf type (%d)\n", buf->type); + return -EINVAL; + } + + mfc_ctx_debug_leave(); + + return ret; +} + +/* Queue a buffer */ +static int mfc_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer = *buf) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int i, ret =3D -EINVAL; + + mfc_ctx_debug_enter(); + + /* Encoder works only single core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + if (core_ctx->state =3D=3D MFCINST_ERROR) { + mfc_err("Call on QBUF after unrecoverable error\n"); + return -EIO; + } + + if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { + mfc_err("Invalid V4L2 Buffer for driver: type(%d)\n", buf->type); + return -EINVAL; + } + + if (!buf->length) { + mfc_err("multiplanar but length is zero\n"); + return -EIO; + } + + if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_debug(4, "enc src buf[%d] Q\n", buf->index); + + if (ctx->num_fd_frame !=3D buf->length) { + mfc_err("number of memory container miss-match between Src%s planes(%d)= and buffer length(%d)\n", + ctx->multi_view_enable ? "(Multi-View)" : "", + ctx->num_fd_frame, buf->length); + return -EINVAL; + } + + mfc_idle_update_queued(dev, ctx); + mfc_rate_update_bufq_framerate(ctx, MFC_TS_SRC_Q); + mfc_rate_update_framerate(ctx); + + for (i =3D 0; i < ctx->num_fd_frame; i++) { + if (!buf->m.planes[i].bytesused) { + mfc_debug(2, "[FRAME] enc src[%d] %s %d\n", + i, + "size zero, changed to buf size", + buf->m.planes[i].length); + buf->m.planes[i].bytesused =3D buf->m.planes[i].length; + } else { + mfc_debug(2, "[FRAME] enc src[%d] size %d\n", + i, buf->m.planes[i].bytesused); + } + } + + ret =3D vb2_qbuf(&ctx->vq_src, NULL, buf); + } else { + mfc_idle_update_queued(dev, ctx); + mfc_rate_update_bufq_framerate(ctx, MFC_TS_DST_Q); + mfc_rate_update_framerate(ctx); + + mfc_debug(4, "enc dst buf[%d] Q\n", buf->index); + ret =3D vb2_qbuf(&ctx->vq_dst, NULL, buf); + } + + mfc_ctx_debug_leave(); + return ret; +} + +/* Dequeue a buffer */ +static int mfc_enc_dqbuf(struct file *file, void *priv, struct v4l2_buffer= *buf) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *core; + struct mfc_core_ctx *core_ctx; + int ret; + + mfc_ctx_debug_enter(); + + /* Encoder works only single core */ + core =3D mfc_get_main_core_lock(dev, ctx); + core_ctx =3D core->core_ctx[ctx->num]; + + if (core_ctx->state =3D=3D MFCINST_ERROR) { + mfc_err("Call on DQBUF after unrecoverable error\n"); + return -EIO; + } + + if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { + mfc_err("Invalid V4L2 Buffer for driver: type(%d)\n", buf->type); + return -EINVAL; + } + + if (buf->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret =3D vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK); + mfc_debug(4, "enc src buf[%d] DQ\n", buf->index); + } else { + ret =3D vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK); + mfc_debug(4, "enc dst buf[%d] DQ\n", buf->index); + } + mfc_ctx_debug_leave(); + return ret; +} + +/* Stream on */ +static int mfc_enc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + int ret =3D -EINVAL; + + mfc_ctx_debug_enter(); + + if (!V4L2_TYPE_IS_MULTIPLANAR(type)) { + mfc_ctx_err("Invalid V4L2 Buffer for driver: type(%d)\n", type); + return -EINVAL; + } + + if (type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "enc src streamon\n"); + ret =3D vb2_streamon(&ctx->vq_src, type); + if (!ret) + mfc_rm_qos_control(ctx, MFC_QOS_ON); + } else { + mfc_ctx_debug(4, "enc dst streamon\n"); + ret =3D vb2_streamon(&ctx->vq_dst, type); + } + + mfc_ctx_debug(2, "src: %d, dst: %d, dpb_count =3D %d\n", + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->src_buf_ready_queu= e), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue), + ctx->dpb_count); + mfc_ctx_debug_leave(); + return ret; +} + +/* Stream off, which equals to a pause */ +static int mfc_enc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + int ret =3D -EINVAL; + + mfc_ctx_debug_enter(); + + if (!V4L2_TYPE_IS_MULTIPLANAR(type)) { + mfc_ctx_err("Invalid V4L2 Buffer for driver: type(%d)\n", type); + return -EINVAL; + } + + if (type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + mfc_ctx_debug(4, "enc src streamoff\n"); + mfc_rate_reset_last_framerate(ctx); + + ret =3D vb2_streamoff(&ctx->vq_src, type); + if (!ret) + mfc_rm_qos_control(ctx, MFC_QOS_OFF); + } else { + mfc_ctx_debug(4, "enc dst streamoff\n"); + ret =3D vb2_streamoff(&ctx->vq_dst, type); + } + + mfc_ctx_debug_leave(); + + return ret; +} + +static int __mfc_enc_ext_info(struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + int val =3D 0; + + val |=3D ENC_SET_SPARE_SIZE; + val |=3D ENC_SET_TEMP_SVC_CH; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->skype)) + val |=3D ENC_SET_SKYPE_FLAG; + + val |=3D ENC_SET_ROI_CONTROL; + val |=3D ENC_SET_QP_BOUND_PB; + val |=3D ENC_SET_FIXED_SLICE; + val |=3D ENC_SET_PVC_MODE; + val |=3D ENC_SET_RATIO_OF_INTRA; + val |=3D ENC_SET_DROP_CONTROL; + val |=3D ENC_SET_CHROMA_QP_CONTROL; + val |=3D ENC_SET_BUF_FLAG_CTRL; + val |=3D ENC_SET_OPERATING_FPS; + val |=3D ENC_SET_GOP_CTRL; + val |=3D ENC_SET_PRIORITY; + val |=3D ENC_SET_TIMING_INFO_ENABLE; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->color_aspect_enc)) + val |=3D ENC_SET_COLOR_ASPECT; + + val |=3D ENC_SET_HP_BITRATE_CONTROL; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->static_info_enc)) + val |=3D ENC_SET_STATIC_INFO; + + if (dev->pdata->support_422) + val |=3D ENC_SET_VP9_PROFILE_LEVEL; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->average_qp)) + val |=3D ENC_SET_AVERAGE_QP; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->mv_search_mode)) + val |=3D ENC_SET_MV_SEARCH_MODE; + + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->max_i_frame_size)) + val |=3D ENC_SET_IFRAME_SIZE; + + /* + * This is the integrated meaning of all the function support below. + * V4L2_CID_MPEG_VIDEO_WP_TWO_PASS_ENABLE + * V4L2_CID_MPEG_VIDEO_ADAPTIVE_GOP_ENABLE + * V4L2_CID_MPEG_VIDEO_SUM_SKIP_MB + * V4L2_CID_MPEG_VIDEO_SUM_INTRA_MB + * V4L2_CID_MPEG_VIDEO_SUM_ZERO_MV_MB + * V4L2_CID_MPEG_VIDEO_MV_HOR_RANGE + * V4L2_CID_MPEG_VIDEO_MV_VER_RANGE + * V4L2_CID_MPEG_MFC_H264_SUB_GOP_ENABLE + * V4L2_CID_MPEG_MFC_H264_SUB_GOP_TYPE + * V4L2_CID_MPEG_MFC_HEVC_SUB_GOP_ENABLE + */ + if (MFC_FEATURE_SUPPORT(dev, dev->pdata->enc_capability)) + val |=3D ENC_SET_CAPABILITY; + + mfc_ctx_debug(5, "[CTRLS] ext info val: %#x\n", val); + + return val; +} + +static int __mfc_enc_get_ctrl_val(struct mfc_ctx *ctx, struct v4l2_control= *ctrl) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_ctx_ctrl *ctx_ctrl; + int ret =3D 0; + int found =3D 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_MFC51_VIDEO_STREAM_SIZE: + ctrl->value =3D enc->dst_buf_size; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TYPE: + ctrl->value =3D enc->frame_type; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE: + ctrl->value =3D MFCSTATE_PROCESSING; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG: + case V4L2_CID_MPEG_MFC51_VIDEO_LUMA_ADDR: + case V4L2_CID_MPEG_MFC51_VIDEO_CHROMA_ADDR: + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS: + case V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG: + case V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG: + case V4L2_CID_MPEG_VIDEO_AVERAGE_QP: + case V4L2_CID_MPEG_VIDEO_SUM_SKIP_MB: + case V4L2_CID_MPEG_VIDEO_SUM_INTRA_MB: + case V4L2_CID_MPEG_VIDEO_SUM_ZERO_MV_MB: + list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) { + if (!(ctx_ctrl->type & MFC_CTRL_TYPE_GET)) + continue; + + if (ctx_ctrl->id =3D=3D ctrl->id) { + if (ctx_ctrl->get.has_new) { + ctx_ctrl->get.has_new =3D 0; + ctrl->value =3D ctx_ctrl->get.val; + } else { + mfc_ctx_debug(5, "[CTRLS] %s 0x%08x\n", + "Control value is not up to date:", + ctrl->id); + if (ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG) + ctrl->value =3D IGNORE_TAG; + else + return -EINVAL; + } + + found =3D 1; + break; + } + } + + if (!found) { + mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id); + return -EINVAL; + } + break; + case V4L2_CID_MPEG_MFC_GET_VERSION_INFO: + ctrl->value =3D dev->pdata->ip_ver; + break; + case V4L2_CID_MPEG_MFC_GET_DRIVER_INFO: + ctrl->value =3D MFC_DRIVER_INFO; + break; + case V4L2_CID_MPEG_MFC_GET_EXTRA_BUFFER_SIZE: + ctrl->value =3D MFC_LINEAR_BUF_SIZE; + break; + case V4L2_CID_MPEG_VIDEO_QOS_RATIO: + ctrl->value =3D ctx->qos_ratio; + break; + case V4L2_CID_MPEG_MFC_GET_EXT_INFO: + ctrl->value =3D __mfc_enc_ext_info(ctx); + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE: + ctrl->value =3D mfc_rate_get_framerate(ctx); + break; + case V4L2_CID_MPEG_VIDEO_PRIORITY: + ctrl->value =3D ctx->prio; + mfc_ctx_debug(2, "[PRIO] user get priority: %d(%d)\n", ctrl->value, ctx-= >user_prio); + break; + default: + mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id); + ret =3D -EINVAL; + break; + } + + mfc_ctx_debug(5, "[CTRLS] get id: %#x, value: %d\n", ctrl->id, ctrl->valu= e); + + return ret; +} + +static inline int __mfc_enc_h264_level(enum v4l2_mpeg_video_h264_level lvl) +{ + static unsigned int t[V4L2_MPEG_VIDEO_H264_LEVEL_6_0 + 1] =3D { + /* V4L2_MPEG_VIDEO_H264_LEVEL_1_0 */ 10, + /* V4L2_MPEG_VIDEO_H264_LEVEL_1B */ 9, + /* V4L2_MPEG_VIDEO_H264_LEVEL_1_1 */ 11, + /* V4L2_MPEG_VIDEO_H264_LEVEL_1_2 */ 12, + /* V4L2_MPEG_VIDEO_H264_LEVEL_1_3 */ 13, + /* V4L2_MPEG_VIDEO_H264_LEVEL_2_0 */ 20, + /* V4L2_MPEG_VIDEO_H264_LEVEL_2_1 */ 21, + /* V4L2_MPEG_VIDEO_H264_LEVEL_2_2 */ 22, + /* V4L2_MPEG_VIDEO_H264_LEVEL_3_0 */ 30, + /* V4L2_MPEG_VIDEO_H264_LEVEL_3_1 */ 31, + /* V4L2_MPEG_VIDEO_H264_LEVEL_3_2 */ 32, + /* V4L2_MPEG_VIDEO_H264_LEVEL_4_0 */ 40, + /* V4L2_MPEG_VIDEO_H264_LEVEL_4_1 */ 41, + /* V4L2_MPEG_VIDEO_H264_LEVEL_4_2 */ 42, + /* V4L2_MPEG_VIDEO_H264_LEVEL_5_0 */ 50, + /* V4L2_MPEG_VIDEO_H264_LEVEL_5_1 */ 51, + /* V4L2_MPEG_VIDEO_H264_LEVEL_5_2 */ 52, + /* V4L2_MPEG_VIDEO_H264_LEVEL_6_0 */ 60, + }; + return t[lvl]; +} + +static inline int __mfc_enc_mpeg4_level(enum v4l2_mpeg_video_mpeg4_level l= vl) +{ + static unsigned int t[V4L2_MPEG_VIDEO_MPEG4_LEVEL_6 + 1] =3D { + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 */ 0, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B, Simple */ 9, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 */ 1, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 */ 2, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 */ 3, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B, Advanced */ 7, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 */ 4, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 */ 5, + /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_6, Simple */ 6, + }; + return t[lvl]; +} + +static inline int __mfc_enc_vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_= idc sar) +{ + static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] =3D { + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED */ 0, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 */ 1, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 */ 2, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 */ 3, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 */ 4, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 */ 5, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 */ 6, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 */ 7, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 */ 8, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 */ 9, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 */ 10, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 */ 11, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 */ 12, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 */ 13, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 */ 14, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 */ 15, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 */ 16, + /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED */ 255, + }; + return t[sar]; +} + +static int __mfc_enc_get_roi(struct mfc_ctx *ctx, int value) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + int index =3D 0; + + if (enc->sh_handle_roi.fd =3D=3D -1) { + enc->sh_handle_roi.fd =3D value; + if (mfc_mem_get_user_shared_handle(ctx, &enc->sh_handle_roi, "ROI")) + return -EINVAL; + } + index =3D enc->roi_index; + + /* Copy the ROI info from shared buf */ + memcpy(&enc->roi_info[index], enc->sh_handle_roi.vaddr, + sizeof(struct mfc_enc_roi_info)); + if (enc->roi_info[index].size > enc->roi_buf[index].size) { + mfc_ctx_err("[MEMINFO][ROI] roi info size %d is over\n", + enc->roi_info[index].size); + return -EINVAL; + } + + /* Copy the ROI map buffer from user's map buf */ + if (copy_from_user(enc->roi_buf[index].vaddr, + enc->roi_info[index].addr, + enc->roi_info[index].size)) + return -EFAULT; + + return 0; +} + +static int __mfc_enc_set_param(struct mfc_ctx *ctx, struct v4l2_control *c= trl) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + int ret =3D 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_QOS_RATIO: + ctx->qos_ratio =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_PRIORITY: + mfc_ctx_debug(2, "[PRIO] user set priority: %d\n", ctrl->value); + ctx->user_prio =3D ctrl->value; + mfc_rm_update_real_time(ctx); + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + p->gop_size =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: + p->slice_mode =3D + (enum v4l2_mpeg_video_multi_slice_mode)ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + p->slice_mb =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + p->slice_bit =3D ctrl->value * 8; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB_ROW: + p->slice_mb_row =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: + p->intra_refresh_mb =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_PADDING: + p->pad =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV: + p->pad_luma =3D (ctrl->value >> 16) & 0xff; + p->pad_cb =3D (ctrl->value >> 8) & 0xff; + p->pad_cr =3D (ctrl->value >> 0) & 0xff; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + p->rc_frame =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + p->rc_bitrate =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF: + p->rc_reaction_coeff =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE: + enc->force_frame_type =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VBV_SIZE: + p->vbv_buf_size =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + p->seq_hdr_mode =3D + (enum v4l2_mpeg_video_header_mode)(ctrl->value); + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE: + p->frame_skip_mode =3D + (enum v4l2_mpeg_mfc51_video_frame_skip_mode) + (ctrl->value); + break; + case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT: + p->fixed_target_bit =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + p->num_b_frame =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + p->codec.h264.profile =3D + __mfc_enc_h264_profile(ctx, (enum v4l2_mpeg_video_h264_profile)(ctrl->va= lue)); + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + p->codec.h264.level =3D + __mfc_enc_h264_level((enum v4l2_mpeg_video_h264_level)(ctrl->value)); + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H264_INTERLACE: + p->codec.h264.interlace =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + p->codec.h264.loop_filter_mode =3D + (enum v4l2_mpeg_video_h264_loop_filter_mode)(ctrl->value); + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + p->codec.h264.loop_filter_alpha =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + p->codec.h264.loop_filter_beta =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + p->codec.h264.entropy_mode =3D + (enum v4l2_mpeg_video_h264_entropy_mode)(ctrl->value); + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P: + p->num_refs_for_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + p->codec.h264._8x8_transform =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + p->rc_mb =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H264_RC_FRAME_RATE: + p->rc_framerate =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + p->codec.h264.rc_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + p->codec.h264.rc_min_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + p->codec.h264.rc_max_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P: + p->codec.h264.rc_min_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P: + p->codec.h264.rc_max_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B: + p->codec.h264.rc_min_qp_b =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B: + p->codec.h264.rc_max_qp_b =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK: + p->codec.h264.rc_mb_dark =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH: + p->codec.h264.rc_mb_smooth =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC: + p->codec.h264.rc_mb_static =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY: + p->codec.h264.rc_mb_activity =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + p->codec.h264.rc_p_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + p->codec.h264.rc_b_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: + p->codec.h264.ar_vui =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: + p->codec.h264.ar_vui_idc =3D + __mfc_enc_vui_sar_idc((enum v4l2_mpeg_video_h264_vui_sar_idc)(ctrl->valu= e)); + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: + p->codec.h264.ext_sar_width =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: + p->codec.h264.ext_sar_height =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + p->codec.h264.open_gop =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + p->codec.h264.open_gop_size =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING: + p->codec.h264.hier_qp_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE: + p->codec.h264.hier_qp_type =3D + (enum v4l2_mpeg_video_h264_hierarchical_coding_type)(ctrl->value); + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER: + p->codec.h264.num_hier_layer =3D ctrl->value & 0x7; + p->codec.h264.hier_ref_type =3D (ctrl->value >> 16) & 0x1; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP: + p->codec.h264.hier_qp_layer[(ctrl->value >> 16) & 0x7] =3D + ctrl->value & 0xFF; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT0: + p->codec.h264.hier_bit_layer[0] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT1: + p->codec.h264.hier_bit_layer[1] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT2: + p->codec.h264.hier_bit_layer[2] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT3: + p->codec.h264.hier_bit_layer[3] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT4: + p->codec.h264.hier_bit_layer[4] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT5: + p->codec.h264.hier_bit_layer[5] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_BIT6: + p->codec.h264.hier_bit_layer[6] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING: + p->codec.h264.sei_gen_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0: + p->codec.h264.sei_fp_curr_frame_0 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE: + p->codec.h264.sei_fp_arrangement_type =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_FMO: + p->codec.h264.fmo_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE: + switch ((enum v4l2_mpeg_video_h264_fmo_map_type)(ctrl->value)) { + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES: + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES: + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_RASTER_SCAN: + case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN: + p->codec.h264.fmo_slice_map_type =3D ctrl->value; + break; + default: + ret =3D -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP: + p->codec.h264.fmo_slice_num_grp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH: + p->codec.h264.fmo_run_length[(ctrl->value >> 30) & 0x3] =3D ctrl->value = & 0x3FFFFFFF; + break; + case V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION: + p->codec.h264.fmo_sg_dir =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE: + p->codec.h264.fmo_sg_rate =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_ASO: + p->codec.h264.aso_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER: + p->codec.h264.aso_slice_order[(ctrl->value >> 18) & 0x7] &=3D + ~(0xFF << (((ctrl->value >> 16) & 0x3) << 3)); + p->codec.h264.aso_slice_order[(ctrl->value >> 18) & 0x7] |=3D + (ctrl->value & 0xFF) << (((ctrl->value >> 16) & 0x3) << 3); + break; + case V4L2_CID_MPEG_VIDEO_H264_PREPEND_SPSPPS_TO_IDR: + p->codec.h264.prepend_sps_pps_to_idr =3D ctrl->value ? 1 : 0; + break; + case V4L2_CID_MPEG_MFC_H264_ENABLE_LTR: + p->codec.h264.enable_ltr =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_H264_NUM_OF_LTR: + p->codec.h264.num_of_ltr =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY: + p->codec.h264.base_priority =3D ctrl->value; + p->codec.h264.set_priority =3D 1; + break; + case V4L2_CID_MPEG_VIDEO_ROI_ENABLE: + p->roi_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_H264_VUI_RESTRICTION_ENABLE: + p->codec.h264.vui_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_CONFIG_QP_ENABLE: + p->dynamic_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_CONFIG_QP: + p->config_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_RC_PVC_ENABLE: + /* It is valid for H.264, HEVC, VP8, VP9 */ + p->rc_pvc =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_SHORTTERM_MAX_LAYER: + p->num_hier_max_layer =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIGN_DATA_HIDING: + break; + case V4L2_CID_MPEG_VIDEO_WEIGHTED_ENABLE: + p->weighted_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA: + p->ratio_intra =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_FULL_RANGE_FLAG: + p->check_color_range =3D 1; + p->color_range =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_COLOUR_PRIMARIES: + p->colour_primaries =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_TRANSFER_CHARACTERISTICS: + p->transfer_characteristics =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MATRIX_COEFFICIENTS: + p->matrix_coefficients =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HIERARCHICAL_BITRATE_CTRL: + p->hier_bitrate_ctrl =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_STATIC_INFO_ENABLE: + p->static_info_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_MAX_PIC_AVERAGE_LIGHT: + p->max_pic_average_light =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_MAX_CONTENT_LIGHT: + p->max_content_light =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_MAX_DISPLAY_LUMINANCE: + p->max_display_luminance =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_MIN_DISPLAY_LUMINANCE: + p->min_display_luminance =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_WHITE_POINT: + p->white_point =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_0: + p->display_primaries_0 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_1: + p->display_primaries_1 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SEI_DISPLAY_PRIMARIES_2: + p->display_primaries_2 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_DROP_CONTROL: + p->drop_control =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_CHROMA_QP_OFFSET_CB: + p->chroma_qp_offset_cb =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_CHROMA_QP_OFFSET_CR: + p->chroma_qp_offset_cr =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_SKIP_LAZY_UNMAP: + ctx->skip_lazy_unmap =3D ctrl->value; + mfc_ctx_debug(2, "[LAZY_UNMAP] lazy unmap %s\n", + ctx->skip_lazy_unmap ? "disable" : "enable"); + break; + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE: + mfc_ctx_debug(2, "[QoS] user set the operating frame rate: %d\n", ctrl->= value); + ctx->operating_framerate =3D ctrl->value; + mfc_rm_update_real_time(ctx); + break; + case V4L2_CID_MPEG_VIDEO_GOP_CTRL: + p->gop_ctrl =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MV_SEARCH_MODE: + p->mv_search_mode =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L0: + p->mv_hor_pos_l0 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L1: + p->mv_hor_pos_l1 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L0: + p->mv_ver_pos_l0 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L1: + p->mv_ver_pos_l1 =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MIN_QUALITY: + p->min_quality_mode =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MAX_IFRAME_SIZE: + p->max_i_frame_size =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_WP_TWO_PASS_ENABLE: + p->wp_two_pass_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_ADAPTIVE_GOP_ENABLE: + p->adaptive_gop_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MV_HOR_RANGE: + p->mv_hor_range =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MV_VER_RANGE: + p->mv_ver_range =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_TIMING_INFO_ENABLE: + p->timing_info_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_MULTI_VIEW_ENABLE: + ctx->multi_view_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC_MULTI_VIEW_MAIN_TYPE: + ctx->left_view_id =3D ((ctrl->value & 0x1) =3D=3D 0) ? MFC_VIEW_ID_MAIN = : MFC_VIEW_ID_SUB; + break; + /* These are stored in specific variables */ + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH: + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH: + case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH: + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH: + /* These require control per buffer */ + case V4L2_CID_MPEG_VIDEO_YSUM: + case V4L2_CID_MPEG_VIDEO_ROI_CONTROL: + case V4L2_CID_MPEG_MFC_H264_USE_LTR: + case V4L2_CID_MPEG_MFC_H264_MARK_LTR: + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG: + case V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG: + case V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG: + break; + default: + mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id); + ret =3D -EINVAL; + } + + return ret; +} + +static int __mfc_enc_set_ctrl_val_list(struct mfc_ctx *ctx, + struct v4l2_control *ctrl) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_ctx_ctrl *ctx_ctrl; + int ret =3D 0; + int found =3D 0; + + list_for_each_entry(ctx_ctrl, &ctx->ctrls, list) { + if (!(ctx_ctrl->type & MFC_CTRL_TYPE_SET)) + continue; + + if (ctx_ctrl->id =3D=3D ctrl->id) { + ctx_ctrl->set.has_new =3D 1; + ctx_ctrl->set.val =3D ctrl->value; + if (ctx_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH) { + if (enc->sh_handle_svc.fd =3D=3D -1) { + enc->sh_handle_svc.fd =3D ctrl->value; + if (mfc_mem_get_user_shared_handle + (ctx, &enc->sh_handle_svc, "SVC")) + return -EINVAL; + } + } + if (ctx_ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH && + p->i_frm_ctrl_mode) { + if (!p->gop_ctrl) + ctx_ctrl->set.val =3D ctx_ctrl->set.val * + (p->num_b_frame + 1); + + if (ctx_ctrl->set.val >=3D 0x3FFFFFFF) { + mfc_ctx_info("I frame interval is bigger than max: %d\n", + ctx_ctrl->set.val); + ctx_ctrl->set.val =3D 0x3FFFFFFF; + } + } + if (ctx_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_H264_LEVEL) + ctx_ctrl->set.val =3D __mfc_enc_h264_level + ((enum v4l2_mpeg_video_h264_level)(ctrl->value)); + if (ctx_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_H264_PROFILE) + ctx_ctrl->set.val =3D + __mfc_enc_h264_profile + (ctx, + (enum v4l2_mpeg_video_h264_profile)(ctrl->value)); + if (ctx_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_ROI_CONTROL) { + ret =3D __mfc_enc_get_roi(ctx, ctrl->value); + if (ret) + return ret; + } + + found =3D 1; + break; + } + } + + if (!found) { + mfc_ctx_err("Invalid control: 0x%08x\n", ctrl->id); + return -EINVAL; + } + + return ret; +} + +static int __mfc_enc_set_ctrl_val(struct mfc_ctx *ctx, struct v4l2_control= *ctrl) +{ + int ret =3D 0; + + /* update parameter value */ + ret =3D __mfc_enc_set_param(ctx, ctrl); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_QOS_RATIO: + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P: + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P: + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B: + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B: + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG: + case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE: + case V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH: + case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH: + case V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH: + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH: + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + case V4L2_CID_MPEG_MFC_H264_MARK_LTR: + case V4L2_CID_MPEG_MFC_H264_USE_LTR: + case V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY: + case V4L2_CID_MPEG_MFC_CONFIG_QP: + case V4L2_CID_MPEG_VIDEO_ROI_CONTROL: + case V4L2_CID_MPEG_VIDEO_YSUM: + case V4L2_CID_MPEG_VIDEO_RATIO_OF_INTRA: + case V4L2_CID_MPEG_VIDEO_DROP_CONTROL: + case V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L0: + case V4L2_CID_MPEG_VIDEO_MV_HOR_POSITION_L1: + case V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L0: + case V4L2_CID_MPEG_VIDEO_MV_VER_POSITION_L1: + case V4L2_CID_MPEG_VIDEO_MAX_IFRAME_SIZE: + case V4L2_CID_MPEG_VIDEO_SRC_BUF_FLAG: + case V4L2_CID_MPEG_VIDEO_DST_BUF_FLAG: + ret =3D __mfc_enc_set_ctrl_val_list(ctx, ctrl); + break; + default: + break; + } + + return ret; +} + +static int mfc_enc_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct v4l2_ext_control *ext_ctrl; + struct v4l2_control ctrl; + int i; + int ret =3D 0; + + if (f->which !=3D V4L2_CTRL_CLASS_CODEC && f->which !=3D V4L2_CTRL_CLASS_= USER) + return -EINVAL; + + for (i =3D 0; i < f->count; i++) { + ext_ctrl =3D (f->controls + i); + + ctrl.id =3D ext_ctrl->id; + + ret =3D __mfc_enc_get_ctrl_val(ctx, &ctrl); + if (ret =3D=3D 0) { + ext_ctrl->value =3D ctrl.value; + } else { + f->error_idx =3D i; + break; + } + + mfc_ctx_debug(5, "[CTRLS][%d] id: %#x, value: %d\n", + i, ext_ctrl->id, ext_ctrl->value); + } + + return ret; +} + +static int mfc_enc_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct v4l2_ext_control *ext_ctrl; + struct v4l2_control ctrl; + int i; + int ret =3D 0; + + mfc_ctx_debug_enter(); + + if (f->which !=3D V4L2_CTRL_CLASS_CODEC && f->which !=3D V4L2_CTRL_CLASS_= USER) + return -EINVAL; + + for (i =3D 0; i < f->count; i++) { + ext_ctrl =3D (f->controls + i); + + ctrl.id =3D ext_ctrl->id; + ctrl.value =3D ext_ctrl->value; + + ret =3D __mfc_enc_check_ctrl_val(ctx, &ctrl); + if (ret !=3D 0) { + f->error_idx =3D i; + break; + } + + if (f->which =3D=3D V4L2_CTRL_CLASS_USER) + ret =3D __mfc_enc_set_ctrl_val(ctx, &ctrl); + else + ret =3D __mfc_enc_set_param(ctx, &ctrl); + if (ret !=3D 0) { + f->error_idx =3D i; + break; + } + } + + mfc_ctx_debug_leave(); + + return ret; +} + +static int mfc_enc_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct mfc_ctx *ctx =3D fh_to_mfc_ctx(file->private_data); + struct v4l2_ext_control *ext_ctrl; + struct v4l2_control ctrl; + int i; + int ret =3D 0; + + mfc_ctx_debug_enter(); + + if (f->which !=3D V4L2_CTRL_CLASS_CODEC) + return -EINVAL; + + for (i =3D 0; i < f->count; i++) { + ext_ctrl =3D (f->controls + i); + + ctrl.id =3D ext_ctrl->id; + ctrl.value =3D ext_ctrl->value; + + ret =3D __mfc_enc_check_ctrl_val(ctx, &ctrl); + if (ret !=3D 0) { + f->error_idx =3D i; + break; + } + } + + mfc_ctx_debug_leave(); + + return ret; +} + +/* Initialize for default format */ +void mfc_enc_set_default_format(struct mfc_ctx *ctx) +{ + struct mfc_fmt *fmt =3D NULL; + + /* Set default format for source */ + fmt =3D __mfc_enc_find_format(ctx, V4L2_PIX_FMT_NV12M); + if (!fmt) { + /* NEVER come here */ + mfc_ctx_err("Wrong memory access. Set fmt by mfc_formats[0]\n"); + fmt =3D &mfc_formats[0]; + } + ctx->src_fmt =3D fmt; + + /* Set default format for destination */ + fmt =3D __mfc_enc_find_format(ctx, V4L2_PIX_FMT_H264); + if (!fmt) { + /* NEVER come here */ + mfc_ctx_err("Wrong memory access. Set fmt by mfc_formats[0]\n"); + fmt =3D &mfc_formats[0]; + } + ctx->dst_fmt =3D fmt; +} + +static const struct v4l2_ioctl_ops mfc_enc_ioctl_ops =3D { + .vidioc_querycap =3D mfc_enc_querycap, + .vidioc_enum_fmt_vid_cap =3D mfc_enc_enum_fmt_vid_cap_mplane, + .vidioc_enum_fmt_vid_out =3D mfc_enc_enum_fmt_vid_out_mplane, + .vidioc_g_fmt_vid_cap_mplane =3D mfc_enc_g_fmt, + .vidioc_g_fmt_vid_out_mplane =3D mfc_enc_g_fmt, + .vidioc_try_fmt_vid_cap_mplane =3D mfc_enc_try_fmt, + .vidioc_try_fmt_vid_out_mplane =3D mfc_enc_try_fmt, + .vidioc_s_fmt_vid_cap_mplane =3D mfc_enc_s_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_out_mplane =3D mfc_enc_s_fmt_vid_out_mplane, + .vidioc_g_selection =3D mfc_enc_g_selection, + .vidioc_s_selection =3D mfc_enc_s_selection, + .vidioc_reqbufs =3D mfc_enc_reqbufs, + .vidioc_querybuf =3D mfc_enc_querybuf, + .vidioc_qbuf =3D mfc_enc_qbuf, + .vidioc_dqbuf =3D mfc_enc_dqbuf, + .vidioc_streamon =3D mfc_enc_streamon, + .vidioc_streamoff =3D mfc_enc_streamoff, + .vidioc_g_ext_ctrls =3D mfc_enc_g_ext_ctrls, + .vidioc_s_ext_ctrls =3D mfc_enc_s_ext_ctrls, + .vidioc_try_ext_ctrls =3D mfc_enc_try_ext_ctrls, +}; + +const struct v4l2_ioctl_ops *mfc_get_enc_v4l2_ioctl_ops(void) +{ + return &mfc_enc_ioctl_ops; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.h new file mode 100644 index 000000000000..93cca69ae385 --- /dev/null +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * mfc_enc_v4l2.h file + * + * Nagaraju Siddineni, + * Himanshu Dewangan, + */ + +#ifndef __MFC_ENC_V4L2_H +#define __MFC_ENC_V4L2_H __FILE__ + +#include "base/mfc_common.h" + +const struct v4l2_ioctl_ops *mfc_get_enc_v4l2_ioctl_ops(void); +void mfc_enc_set_default_format(struct mfc_ctx *ctx); + +#endif /* __MFC_ENC_V4L2_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout1.samsung.com (mailout1.samsung.com [203.254.224.24]) (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 14CE5284688 for ; Tue, 30 Sep 2025 03:57:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.24 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204632; cv=none; b=Pc5Xdt80Zlbxsh5zSl9twVCSsD7EZygeP8i6AfX7mgkHNQ3UwdDt2LMSlmj1RILEY7lLY/Lkp2Zg/YQudZTI84W3nvISVN+u464lcVQhJTDWuroHFD/VHipdPnvZAdfRYav1si0pex/O/8DaV7pRJg5zi2MbWwUpxW++M6F9cU8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204632; c=relaxed/simple; bh=nwDGTdMji3KZK78l2KcsGH7AkhXCiKkj0e/FEMG2K40=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=VJ591E6D7XsLhA+maV6tXgbCE0FIo3PQtjOfLcqrRbrw0CwBqKYENmm2bPzqIcpwCE/poxuXJg1mkL+5xaCX3LlyDV+dk4Qt7NGKjRXPvbw000TD5A33dPmoHEPVNVcpLn5yhuRmgpMTfakp5sg4QJaPuDbndRxuPzHAJ9JwMw8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=AS7/ffSU; arc=none smtp.client-ip=203.254.224.24 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="AS7/ffSU" Received: from epcas5p2.samsung.com (unknown [182.195.41.40]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20250930035706epoutp01f528d8738e84e902eebc4cf44d48220d~p80l7jpEv3203032030epoutp01t for ; Tue, 30 Sep 2025 03:57:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20250930035706epoutp01f528d8738e84e902eebc4cf44d48220d~p80l7jpEv3203032030epoutp01t DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204626; bh=LLGsAYmAe+l/wKy2rCHRpwWnMBTKXt8cNThh2lgaqXQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AS7/ffSUWhU2LeRxs3sei1l4lqcA4uGwajT0jObdLvBREKUKIbuTc1ri3SxblKSPI zFt6odoEGDpR3UdGfLzYPykRG7gXTl6MUyMNUj7LFIuRSpuve+oX2DVrvisvIHAObM 6tbb/ejXeTPcvAUUxErJ1rtf6y8Vx8w6jUN081lY= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPS id 20250930035705epcas5p3e3023bc3fa1c082cbecb3f8100c17a85~p80lVIcvU1975219752epcas5p3z; Tue, 30 Sep 2025 03:57:05 +0000 (GMT) Received: from epcas5p3.samsung.com (unknown [182.195.38.90]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cbPQ84Lvgz6B9mG; Tue, 30 Sep 2025 03:57:04 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035703epcas5p18e09c3e9e07401f4b268ea77bc831987~p80joTAXP1549615496epcas5p12; Tue, 30 Sep 2025 03:57:03 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035701epsmtip1ba6a8cafc8ebd7db9415a278f8543985~p80g-n4TH2942629426epsmtip1O; Tue, 30 Sep 2025 03:57:00 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 26/29] media: mfc: Add full encoder support Date: Tue, 30 Sep 2025 09:33:45 +0530 Message-Id: <20250930040348.3702923-27-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035703epcas5p18e09c3e9e07401f4b268ea77bc831987 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035703epcas5p18e09c3e9e07401f4b268ea77bc831987 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Integrate encoder command API into core flow. - Extend hardware=E2=80=91lock handling for encoder state. - Manage encoder resources on instance open/close. - Add APIs for buffers, codec parameters, and status. - Update core ops, macros, headers, and error handling. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/mfc_core_cmd.c | 119 ++++++++++++ .../samsung/exynos-mfc/mfc_core_cmd.h | 6 + .../samsung/exynos-mfc/mfc_core_hwlock.c | 61 +++++++ .../samsung/exynos-mfc/mfc_core_ops.c | 154 ++++++++++++++++ .../samsung/exynos-mfc/mfc_core_reg_api.c | 169 ++++++++++++++++++ .../samsung/exynos-mfc/mfc_core_reg_api.h | 55 ++++++ .../samsung/exynos-mfc/mfc_core_run.c | 127 +++++++++++++ .../samsung/exynos-mfc/mfc_core_run.h | 5 + 8 files changed, 696 insertions(+) diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c index aaf216741575..a1de7920786b 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.c @@ -12,6 +12,7 @@ #include "mfc_core_cmd.h" #include "mfc_core_intlock.h" =20 +#include "mfc_core_enc_param.h" #include "mfc_core_hw_reg_api.h" =20 #include "base/mfc_utils.h" @@ -286,6 +287,28 @@ void mfc_core_cmd_dec_seq_header(struct mfc_core *core= , struct mfc_ctx *ctx) mfc_debug_leave(); } =20 +int mfc_core_cmd_enc_seq_header(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int ret; + + mfc_debug_enter(); + + ret =3D mfc_core_set_enc_params(core, ctx); + if (ret) { + mfc_debug(2, "fail to set enc params\n"); + return ret; + } + + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_SEQ_HEADER); + + mfc_debug_leave(); + + return 0; +} + int mfc_core_cmd_dec_init_buffers(struct mfc_core *core, struct mfc_ctx *c= tx) { struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; @@ -326,6 +349,58 @@ int mfc_core_cmd_dec_init_buffers(struct mfc_core *cor= e, struct mfc_ctx *ctx) return ret; } =20 +int mfc_core_cmd_enc_init_buffers(struct mfc_core *core, struct mfc_ctx *c= tx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + unsigned int reg =3D 0; + int ret; + + /* + * Header was generated now starting processing + * First set the reference frame buffers + */ + if (!core_ctx->codec_buffer_allocated) { + mfc_info("there isn't codec buffer, re-alloc!\n"); + ret =3D mfc_alloc_codec_buffers(core_ctx); + if (ret) { + mfc_err("Failed to allocate encoding buffers\n"); + mfc_change_state(core_ctx, MFCINST_ERROR); + return ret; + } + } + + mfc_clean_core_ctx_int_flags(core_ctx); + ret =3D mfc_core_set_enc_codec_buffers(core_ctx); + if (ret) { + mfc_info("isn't enough codec buffer size, re-alloc!\n"); + + mfc_release_codec_buffers(core_ctx); + ret =3D mfc_alloc_codec_buffers(core_ctx); + if (ret) { + mfc_err("Failed to allocate encoding buffers\n"); + return ret; + } + ret =3D mfc_core_set_enc_codec_buffers(core_ctx); + if (ret) { + mfc_err("Failed to set enc codec buffers\n"); + return ret; + } + } + + if (IS_MULTI_MODE(ctx)) { + reg |=3D ((ctx->subcore_inst_no & MFC_REG_RET_INSTANCE_ID_OF_MFC1_MASK) + << MFC_REG_RET_INSTANCE_ID_OF_MFC1_SHIFT); + reg |=3D (core_ctx->inst_no & MFC_REG_RET_INSTANCE_ID_MASK); + MFC_CORE_WRITEL(reg, MFC_REG_INSTANCE_ID); + } else { + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + } + + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_INIT_BUFFERS); + + return ret; +} + static int __mfc_set_scratch_dpb_buffer(struct mfc_core *core, struct mfc_= ctx *ctx) { struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; @@ -455,3 +530,47 @@ int mfc_core_cmd_dec_one_frame(struct mfc_core *core, = struct mfc_ctx *ctx, mfc_debug(2, "Decoding a usual frame\n"); return 0; } + +/* Encode a single frame */ +void mfc_core_cmd_enc_one_frame(struct mfc_core *core, struct mfc_ctx *ctx, + int last_frame) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + u32 timeout_value =3D MFC_TIMEOUT_VALUE; + unsigned int reg =3D 0; + + mfc_debug_enter(); + + if (core->dev->pdata->support_enc_mode1) { + reg =3D MFC_CORE_READL(MFC_REG_E_HEVC_NAL_CONTROL); + mfc_clear_set_bits(reg, 0x1, 11, IS_MULTI_MODE(ctx)); + MFC_CORE_WRITEL(reg, MFC_REG_E_HEVC_NAL_CONTROL); + } + + reg =3D MFC_CORE_READL(MFC_REG_E_HEVC_NAL_CONTROL); + mfc_clear_set_bits(reg, 0x3, 12, ctx->select_view ? 0x3 : 0x0); + MFC_CORE_WRITEL(reg, MFC_REG_E_HEVC_NAL_CONTROL); + + if (core->last_mfc_freq) + timeout_value =3D (core->last_mfc_freq * MFC_TIMEOUT_VALUE_IN_MSEC); + mfc_debug(2, "Last MFC Freq: %d, Timeout Value: %d\n", + core->last_mfc_freq, timeout_value); + + MFC_CORE_WRITEL(timeout_value, MFC_REG_TIMEOUT_VALUE); + MFC_CORE_WRITEL(core_ctx->inst_no, MFC_REG_INSTANCE_ID); + + /* + * Issue different commands to instance basing on whether it + * is the last frame or not. + */ + switch (last_frame) { + case 0: + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_NAL_START); + break; + case 1: + mfc_core_cmd_host2risc(core, MFC_REG_H2R_CMD_LAST_FRAME); + break; + } + + mfc_debug_leave(); +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h index 216d07c564ae..91da9eeff904 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_cmd.h @@ -28,8 +28,14 @@ void mfc_core_cmd_dpb_flush(struct mfc_core *core, struc= t mfc_ctx *ctx); void mfc_core_cmd_cache_flush(struct mfc_core *core); =20 void mfc_core_cmd_dec_seq_header(struct mfc_core *core, struct mfc_ctx *ct= x); +int mfc_core_cmd_enc_seq_header(struct mfc_core *core, struct mfc_ctx *ctx= ); =20 int mfc_core_cmd_dec_init_buffers(struct mfc_core *core, struct mfc_ctx *c= tx); +int mfc_core_cmd_enc_init_buffers(struct mfc_core *core, struct mfc_ctx *c= tx); + int mfc_core_cmd_dec_one_frame(struct mfc_core *core, struct mfc_ctx *ctx, int last_frame, int src_index); +void mfc_core_cmd_enc_one_frame(struct mfc_core *core, struct mfc_ctx *ctx, + int last_frame); + #endif /* __MFC_CORE_CMD_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c b/= drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c index 0b594429fd59..5456368f5410 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_hwlock.c @@ -508,6 +508,65 @@ static int __mfc_just_run_dec(struct mfc_core *core, s= truct mfc_ctx *ctx) return ret; } =20 +static int __mfc_just_run_enc(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_core *subcore; + struct mfc_core_ctx *subcore_ctx; + int ret =3D 0; + + switch (core_ctx->state) { + case MFCINST_FINISHING: + ret =3D mfc_core_run_enc_last_frames(core, ctx); + break; + case MFCINST_RUNNING: + ret =3D mfc_core_run_enc_frame(core, ctx); + break; + case MFCINST_INIT: + mfc_core_cmd_open_inst(core, ctx); + break; + case MFCINST_RETURN_INST: + ret =3D mfc_core_cmd_close_inst(core, ctx); + break; + case MFCINST_GOT_INST: + ret =3D mfc_core_run_enc_init(core, ctx); + break; + case MFCINST_HEAD_PARSED: + if (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1) { + if (core->id =3D=3D MFC_CORE_SUB) { + mfc_ctx_err("init buffer should be called to main core, so try again\n= "); + return -EAGAIN; + } + + subcore =3D mfc_get_sub_core(ctx->dev, ctx); + if (!subcore) { + mfc_ctx_err("Failed to get subcore before calling INIT_BUF\n"); + return -EAGAIN; + } + subcore_ctx =3D subcore->core_ctx[ctx->num]; + if (mfc_wait_for_done_core_ctx(subcore_ctx, + MFC_REG_R2H_CMD_SEQ_DONE_RET)) { + mfc_ctx_err("sub core header parsing should be done before init buffer= \n"); + return -EAGAIN; + } + } + ret =3D mfc_core_cmd_enc_init_buffers(core, ctx); + break; + case MFCINST_ABORT_INST: + mfc_core_cmd_abort_inst(core, ctx); + break; + case MFCINST_MOVE_INST: + mfc_core_cmd_move_inst(core, ctx); + break; + default: + mfc_info("can't try command(encoder just_run), state : %d\n", + core_ctx->state); + ret =3D -EAGAIN; + } + + return ret; +} + /* Run an operation on hardware */ int mfc_core_just_run(struct mfc_core *core, int new_ctx_index) { @@ -557,6 +616,8 @@ int mfc_core_just_run(struct mfc_core *core, int new_ct= x_index) =20 if (ctx->type =3D=3D MFCINST_DECODER) { ret =3D __mfc_just_run_dec(core, ctx); + } else if (ctx->type =3D=3D MFCINST_ENCODER) { + ret =3D __mfc_just_run_enc(core, ctx); } else { mfc_err("invalid context type: %d\n", ctx->type); ret =3D -EAGAIN; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_ops.c index f8a27548b218..fc90650c228c 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_ops.c @@ -322,6 +322,9 @@ static int mfc_core_instance_deinit(struct mfc_core *co= re, struct mfc_ctx *ctx) mfc_core_release_hwlock_ctx(core_ctx); mfc_core_destroy_listable_wq_ctx(core_ctx); =20 + if (ctx->type =3D=3D MFCINST_ENCODER) + mfc_release_enc_roi_buffer(core_ctx); + mfc_delete_queue(&core_ctx->src_buf_queue); =20 core->core_ctx[core_ctx->num] =3D 0; @@ -361,6 +364,32 @@ static int __mfc_core_instance_open_dec(struct mfc_ctx= *ctx, return 0; } =20 +static int __mfc_core_instance_open_enc(struct mfc_ctx *ctx, + struct mfc_core_ctx *core_ctx) +{ + int ret =3D 0; + + ret =3D mfc_alloc_instance_context(core_ctx); + if (ret) { + mfc_err("Failed to allocate enc instance[%d] buffers\n", + core_ctx->num); + mfc_core_release_hwlock_ctx(core_ctx); + return -ENOMEM; + } + + ctx->capture_state =3D QUEUE_FREE; + + ret =3D mfc_alloc_enc_roi_buffer(core_ctx); + if (ret) { + mfc_err("[ROI] Failed to allocate ROI buffers\n"); + mfc_release_instance_context(core_ctx); + mfc_core_release_hwlock_ctx(core_ctx); + return -ENOMEM; + } + + return 0; +} + static int mfc_core_instance_open(struct mfc_core *core, struct mfc_ctx *c= tx) { struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; @@ -380,6 +409,9 @@ static int mfc_core_instance_open(struct mfc_core *core= , struct mfc_ctx *ctx) if (ctx->type =3D=3D MFCINST_DECODER) { if (__mfc_core_instance_open_dec(ctx, core_ctx)) return -EAGAIN; + } else if (ctx->type =3D=3D MFCINST_ENCODER) { + if (__mfc_core_instance_open_enc(ctx, core_ctx)) + return -EAGAIN; } else { mfc_err("invalid codec type: %d\n", ctx->type); return -EINVAL; @@ -414,6 +446,8 @@ static int mfc_core_instance_open(struct mfc_core *core= , struct mfc_ctx *ctx) mfc_core_release_hwlock_ctx(core_ctx); core->sched->yield_work(core, core_ctx); mfc_release_instance_context(core_ctx); + if (ctx->type =3D=3D MFCINST_ENCODER) + mfc_release_enc_roi_buffer(core_ctx); =20 return ret; } @@ -619,6 +653,124 @@ static int mfc_core_instance_init_buf(struct mfc_core= *core, struct mfc_ctx *ctx return 0; } =20 +static void mfc_core_instance_q_flush(struct mfc_core *core, struct mfc_ct= x *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int index =3D 0; + int ret =3D 0; + + /* If a H/W operation is in progress, wait for it complete */ + if (NEED_TO_WAIT_NAL_ABORT(core_ctx)) { + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_NAL_ABORT_RET))= { + mfc_err("time out during nal abort\n"); + core->sched->yield_work(core, core_ctx); + } + } + + ret =3D mfc_core_get_hwlock_ctx(core_ctx); + if (ret < 0) { + mfc_err("Failed to get hwlock\n"); + MFC_TRACE_CTX_LT("[ERR][Release] failed to get hwlock (shutdown: %d)\n", + core->shutdown); + return; + } + + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->dst_buf_queue); + + while (index < MFC_MAX_BUFFERS) { + index =3D find_next_bit(ctx->dst_ctrls_avail, MFC_MAX_BUFFERS, index); + if (index < MFC_MAX_BUFFERS) + call_cop(ctx, reset_buf_ctrls, &ctx->dst_ctrls[index]); + index++; + } + + if (core_ctx->state =3D=3D MFCINST_FINISHING) + mfc_change_state(core_ctx, MFCINST_FINISHED); + + mfc_debug(2, "encoder destination stop sequence done\n"); + + core->sched->clear_work(core, core_ctx); + mfc_core_release_hwlock_ctx(core_ctx); + + core->sched->enqueue_work(core, core_ctx); + if (core->sched->is_work(core)) + core->sched->queue_work(core); +} + +static void mfc_core_instance_finishing(struct mfc_core *core, struct mfc_= ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int index =3D 0; + int ret =3D 0; + + /* If a H/W operation is in progress, wait for it complete */ + if (NEED_TO_WAIT_NAL_ABORT(core_ctx)) { + if (mfc_wait_for_done_core_ctx(core_ctx, MFC_REG_R2H_CMD_NAL_ABORT_RET))= { + mfc_err("time out during nal abort\n"); + core->sched->yield_work(core, core_ctx); + } + } + + ret =3D mfc_core_get_hwlock_ctx(core_ctx); + if (ret < 0) { + mfc_err("Failed to get hwlock\n"); + MFC_TRACE_CTX_LT("[ERR][Release] failed to get hwlock (shutdown: %d)\n", + core->shutdown); + return; + } + + if (core_ctx->state =3D=3D MFCINST_RUNNING || core_ctx->state =3D=3D MFCI= NST_FINISHING || + (IS_SWITCH_SINGLE_MODE(ctx) && core->id =3D=3D ctx->op_core_num[MFC_C= ORE_SUB])) { + mfc_change_state(core_ctx, MFCINST_FINISHING); + core->sched->set_work(core, core_ctx); + + while (core_ctx->state !=3D MFCINST_FINISHED) { + ret =3D mfc_core_just_run(core, ctx->num); + if (ret) { + mfc_err("Failed to run MFC\n"); + break; + } + if (mfc_wait_for_done_core_ctx(core_ctx, + MFC_REG_R2H_CMD_FRAME_DONE_RET)) { + mfc_err("Waiting for LAST_SEQ timed out\n"); + break; + } + } + } + + ctx->serial_src_index =3D 0; + mfc_move_buf_all(ctx, &core_ctx->src_buf_queue, + &ctx->ref_buf_queue, MFC_QUEUE_ADD_BOTTOM); + mfc_move_buf_all(ctx, &core_ctx->src_buf_queue, + &ctx->src_buf_ready_queue, MFC_QUEUE_ADD_BOTTOM); + mfc_cleanup_enc_src_queue(core_ctx); + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->err_buf_queue); + + while (index < MFC_MAX_BUFFERS) { + index =3D find_next_bit(ctx->src_ctrls_avail, MFC_MAX_BUFFERS, index); + if (index < MFC_MAX_BUFFERS) + call_cop(ctx, reset_buf_ctrls, &ctx->src_ctrls[index]); + index++; + } + + if (core_ctx->state =3D=3D MFCINST_FINISHING || + core_ctx->state =3D=3D MFCINST_GOT_INST || + core_ctx->state =3D=3D MFCINST_HEAD_PARSED) { + mfc_debug(2, "%d status can continue encoding without CLOSE_INSTANCE\n", + core_ctx->state); + mfc_change_state(core_ctx, MFCINST_FINISHED); + } + + mfc_debug(2, "encoder source stop sequence done\n"); + + core->sched->clear_work(core, core_ctx); + mfc_core_release_hwlock_ctx(core_ctx); + + core->sched->enqueue_work(core, core_ctx); + if (core->sched->is_work(core)) + core->sched->queue_work(core); +} + static int mfc_core_request_work(struct mfc_core *core, enum mfc_request_work work, struct mfc_ctx *ctx) @@ -650,6 +802,8 @@ static const struct mfc_core_ops mfc_core_ops =3D { .instance_move_from =3D mfc_core_instance_move_from, .instance_dpb_flush =3D mfc_core_instance_dpb_flush, .instance_init_buf =3D mfc_core_instance_init_buf, + .instance_q_flush =3D mfc_core_instance_q_flush, + .instance_finishing =3D mfc_core_instance_finishing, .request_work =3D mfc_core_request_work, }; =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c index 0cc5d1d9433e..d4e9cb4e4e79 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.c @@ -225,6 +225,63 @@ int mfc_core_set_dec_codec_buffers(struct mfc_core_ctx= *core_ctx) return 0; } =20 +/* Set encoding ref & codec buffer */ +int mfc_core_set_enc_codec_buffers(struct mfc_core_ctx *core_ctx) +{ + struct mfc_core *core =3D core_ctx->core; + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_enc *enc =3D ctx->enc_priv; + dma_addr_t buf_addr; + int buf_size; + int i; + + mfc_debug_enter(); + + buf_addr =3D core_ctx->codec_buf.daddr; + buf_size =3D core_ctx->codec_buf.size; + + mfc_debug(2, "[MEMINFO] codec buf 0x%llx, size: %d\n", buf_addr, buf_size= ); + mfc_debug(2, "DPB COUNT: %d\n", ctx->dpb_count); + + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_E_SCRATCH_BUFFER_ADDR); + MFC_CORE_WRITEL(ctx->scratch_buf_size, MFC_REG_E_SCRATCH_BUFFER_SIZE); + buf_addr +=3D ctx->scratch_buf_size; + buf_size -=3D ctx->scratch_buf_size; + + /* start address of per buffer is aligned */ + for (i =3D 0; i < ctx->dpb_count; i++) { + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_E_LUMA_DPB + (4 * i)); + buf_addr +=3D enc->luma_dpb_size; + buf_size -=3D enc->luma_dpb_size; + } + for (i =3D 0; i < ctx->dpb_count; i++) { + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_E_CHROMA_DPB + (4 * i)); + buf_addr +=3D enc->chroma_dpb_size; + buf_size -=3D enc->chroma_dpb_size; + } + for (i =3D 0; i < ctx->dpb_count; i++) { + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_E_ME_BUFFER + (4 * i)); + buf_addr +=3D enc->me_buffer_size; + buf_size -=3D enc->me_buffer_size; + } + + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_E_TMV_BUFFER0); + buf_addr +=3D enc->tmv_buffer_size >> 1; + MFC_CORE_DMA_WRITEL(buf_addr, MFC_REG_E_TMV_BUFFER1); + buf_addr +=3D enc->tmv_buffer_size >> 1; + buf_size -=3D enc->tmv_buffer_size; + + mfc_debug(2, "[MEMINFO] codec buf 0x%llx, remained size: %d\n", buf_addr,= buf_size); + if (buf_size < 0) { + mfc_debug(2, "[MEMINFO] Not enough memory has been allocated\n"); + return -ENOMEM; + } + + mfc_debug_leave(); + + return 0; +} + /* Set registers for decoding stream buffer */ int mfc_core_set_dec_stream_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, struct mfc_buf *mfc_buf, unsigned int start_num_byte, @@ -273,6 +330,118 @@ int mfc_core_set_dec_stream_buffer(struct mfc_core *c= ore, struct mfc_ctx *ctx, return 0; } =20 +void mfc_core_set_enc_frame_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + struct mfc_buf *mfc_buf, int num_planes) +{ + dma_addr_t addr[3] =3D { 0, 0, 0 }; + int index, i; + int index_view; + + if (!mfc_buf) { + mfc_ctx_debug(3, "enc zero buffer set\n"); + goto buffer_set; + } + + index =3D mfc_buf->vb.vb2_buf.index; + if (mfc_buf->num_valid_bufs > 0) { + for (i =3D 0; i < num_planes; i++) { + addr[i] =3D mfc_buf->addr[mfc_buf->next_index][i]; + mfc_ctx_debug(2, "[BUFCON][BUFINFO] set src index: %d, batch[%d], addr[= %d]: 0x%08llx\n", + index, mfc_buf->next_index, i, addr[i]); + } + mfc_buf->next_index++; + } else { + index_view =3D ctx->select_view =3D=3D MFC_VIEW_ID_MAIN ? MFC_MV_BUF_IDX= _VIEW0 + : MFC_MV_BUF_IDX_VIEW1; + for (i =3D 0; i < num_planes; i++) { + addr[i] =3D mfc_buf->addr[index_view][i]; + mfc_ctx_debug(2, "[BUFINFO] set src index: %d(%d), addr[%d]: 0x%08llx\n= ", + index, mfc_buf->src_index, i, addr[i]); + } + } + +buffer_set: + for (i =3D 0; i < num_planes; i++) + MFC_CORE_DMA_WRITEL(addr[i], MFC_REG_E_SOURCE_FIRST_ADDR + (i * 4)); +} + +/* Set registers for encoding stream buffer */ +int mfc_core_set_enc_stream_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + struct mfc_buf *mfc_buf) +{ + dma_addr_t addr =3D 0; + unsigned int size =3D 0, offset =3D 0, index =3D -1; + + if (mfc_buf) { + index =3D mfc_buf->vb.vb2_buf.index; + addr =3D mfc_buf->addr[0][0]; + offset =3D mfc_buf->vb.vb2_buf.planes[0].data_offset; + size =3D (unsigned int)vb2_plane_size(&mfc_buf->vb.vb2_buf, 0); + size =3D ALIGN(size, STREAM_BUF_ALIGN); + } else { + /* + * When LAST_SEQ of B frame encoding + * if there is no output buffer, set addr and size with 0xffffffff + * and then FW returns COMPLETE_SEQ. + */ + addr =3D 0xffffffff; + size =3D 0xffffffff; + } + + MFC_CORE_DMA_WRITEL(addr, MFC_REG_E_STREAM_BUFFER_ADDR); /* 16B align */ + MFC_CORE_WRITEL(size, MFC_REG_E_STREAM_BUFFER_SIZE); + MFC_CORE_WRITEL(offset, MFC_REG_E_STREAM_BUFFER_OFFSET); + + if (IS_MULTI_MODE(ctx) && + !(ctx->dev->debugfs.feature_option & MFC_OPTION_STREAM_COPY_DISABLE))= { + dma_addr_t tile1_addr =3D 0; + unsigned int tile0_size =3D 0; + unsigned int tile1_size =3D 0; + + tile0_size =3D ALIGN(size / 2, 16); + tile1_addr =3D addr + tile0_size; + tile1_size =3D (size > tile0_size) ? size - tile0_size : 0; + + MFC_CORE_DMA_WRITEL(tile1_addr, MFC_REG_E_TILE1_STREAM_BUFFER_ADDR); /* = 16B align */ + MFC_CORE_WRITEL(tile1_size, MFC_REG_E_TILE1_STREAM_BUFFER_SIZE); + } + + mfc_ctx_debug(2, "[BUFINFO] set dst index: %d, addr: 0x%08llx\n", index, = addr); + mfc_ctx_debug(2, "[STREAM] buf_size: %u, offset: %d\n", size, offset); + + return 0; +} + +void mfc_core_get_enc_frame_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + dma_addr_t addr[], int num_planes) +{ + dma_addr_t enc_recon_y_addr, enc_recon_c_addr; + int i, addr_offset; + + addr_offset =3D MFC_REG_E_ENCODED_SOURCE_FIRST_ADDR; + + for (i =3D 0; i < num_planes; i++) + addr[i] =3D MFC_CORE_DMA_READL(addr_offset + (i * 4)); + + enc_recon_y_addr =3D MFC_CORE_DMA_READL(MFC_REG_E_RECON_LUMA_DPB_ADDR); + enc_recon_c_addr =3D MFC_CORE_DMA_READL(MFC_REG_E_RECON_CHROMA_DPB_ADDR); + + mfc_ctx_debug(2, "[MEMINFO] recon y: %#llx c: %#llx\n", + enc_recon_y_addr, enc_recon_c_addr); +} + +void mfc_core_set_enc_stride(struct mfc_core *core, struct mfc_ctx *ctx) +{ + int i; + + for (i =3D 0; i < ctx->raw_buf.num_planes; i++) { + MFC_CORE_WRITEL(ctx->raw_buf.stride[i], + MFC_REG_E_SOURCE_FIRST_STRIDE + (i * 4)); + mfc_ctx_debug(2, "[FRAME] enc src plane[%d] stride: %d\n", + i, ctx->raw_buf.stride[i]); + } +} + void mfc_core_set_dynamic_dpb(struct mfc_core *core, struct mfc_ctx *ctx, struct mfc_buf *dst_mb) { diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h b= /drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h index 08f74bd56f3f..6042f3a8a6ba 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_reg_api.h @@ -116,6 +116,14 @@ MFC_CORE_READL(MFC_REG_D_FIRST_PLANE_2BIT_DPB_STRIDE_SIZE + ((x) * 4)) #define mfc_core_get_mv_count() MFC_CORE_READL(MFC_REG_D_MIN_NUM_MV) #define mfc_core_get_inst_no() MFC_CORE_READL(MFC_REG_RET_INSTANCE_ID) +#define mfc_core_get_enc_dpb_count() MFC_CORE_READL(MFC_REG_E_NUM_DPB) +#define mfc_core_get_enc_scratch_size() MFC_CORE_READL(MFC_REG_E_MIN_SCRA= TCH_BUFFER_SIZE) +#define mfc_core_get_enc_luma_size() MFC_CORE_READL(MFC_REG_E_MIN_LUMA_DP= B_SIZE) +#define mfc_core_get_enc_chroma_size() MFC_CORE_READL(MFC_REG_E_MIN_CHROM= A_DPB_SIZE) +#define mfc_core_get_enc_strm_size() MFC_CORE_READL(MFC_REG_E_STREAM_SIZE) +#define mfc_core_get_enc_slice_type() (MFC_CORE_READL(MFC_REG_E_SLICE_TYP= E) \ + & MFC_REG_E_SLICE_TYPE_MASK) +#define mfc_core_get_enc_pic_count() MFC_CORE_READL(MFC_REG_E_PICTURE_COU= NT) #define mfc_core_get_sei_avail() MFC_CORE_READL(MFC_REG_D_SEI_AVAIL) #define mfc_core_get_sei_content_light() \ MFC_CORE_READL(MFC_REG_D_CONTENT_LIGHT_LEVEL_INFO_SEI) @@ -209,6 +217,14 @@ #define mfc_core_get_dec_used_flag() (((unsigned long)(MFC_CORE_READL \ (MFC_REG_D_USED_DPB_FLAG_UPPER)) << 32) | \ MFC_CORE_READL(MFC_REG_D_USED_DPB_FLAG_LOWER)) +#define mfc_core_get_enc_idr_flag() \ + ((MFC_CORE_READL(MFC_REG_E_NAL_DONE_INFO) \ + >> MFC_REG_E_NAL_DONE_INFO_IDR_SHIFT) \ + & MFC_REG_E_NAL_DONE_INFO_IDR_MASK) +#define mfc_core_get_enc_comp_err() \ + ((MFC_CORE_READL(MFC_REG_E_NAL_DONE_INFO) \ + >> MFC_REG_E_NAL_DONE_INFO_COMP_ERR_SHIFT) \ + & MFC_REG_E_NAL_DONE_INFO_COMP_ERR_MASK) #define mfc_core_get_chroma_format() (MFC_CORE_READL(MFC_REG_D_CHROMA_FOR= MAT) \ & MFC_REG_D_CHROMA_FORMAT_MASK) #define mfc_core_get_color_range() ((MFC_CORE_READL(MFC_REG_D_CHROMA_FORM= AT) \ @@ -257,6 +273,15 @@ static inline void mfc_core_dec_get_crop_info(struct m= fc_core *core, dec->cr_bot =3D bottom; } =20 +static inline void mfc_core_clear_enc_res_change(struct mfc_core *core) +{ + unsigned int reg =3D 0; + + reg =3D MFC_CORE_READL(MFC_REG_E_PARAM_CHANGE); + reg &=3D ~(0x7 << 6); + MFC_CORE_WRITEL(reg, MFC_REG_E_PARAM_CHANGE); +} + static inline void mfc_core_clear_roi_enable(struct mfc_core *core) { unsigned int reg =3D 0; @@ -276,6 +301,25 @@ static inline void mfc_core_update_tag(struct mfc_core= *core, struct mfc_ctx *ct } } =20 +static inline void mfc_core_set_enc_src_votf(struct mfc_core *core, int on= off) +{ + unsigned int reg =3D 0; + + reg =3D MFC_CORE_READL(MFC_REG_E_PARAM_CHANGE); + reg &=3D ~(0x3 << 18); + reg |=3D (onoff << 18); + MFC_CORE_WRITEL(reg, MFC_REG_E_PARAM_CHANGE); +} + +static inline void mfc_core_clear_enc_src_votf(struct mfc_core *core) +{ + unsigned int reg =3D 0; + + reg =3D MFC_CORE_READL(MFC_REG_E_PARAM_CHANGE); + reg &=3D ~(0x3 << 18); + MFC_CORE_WRITEL(reg, MFC_REG_E_PARAM_CHANGE); +} + static inline void mfc_core_set_migration_addr(struct mfc_core *core, stru= ct mfc_ctx *ctx, dma_addr_t fw_addr, dma_addr_t common_ctx_addr) { @@ -311,10 +355,21 @@ unsigned int mfc_get_frame_error_type(struct mfc_ctx = *ctx, unsigned int err); =20 void mfc_core_set_dec_dpb_and_scratch(struct mfc_core_ctx *core_ctx, dma_a= ddr_t scratch_addr); int mfc_core_set_dec_codec_buffers(struct mfc_core_ctx *core_ctx); +int mfc_core_set_enc_codec_buffers(struct mfc_core_ctx *core_ctx); + int mfc_core_set_dec_stream_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, struct mfc_buf *mfc_buf, unsigned int start_num_byte, unsigned int buf_size); =20 +void mfc_core_set_enc_frame_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + struct mfc_buf *mfc_buf, int num_planes); +int mfc_core_set_enc_stream_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + struct mfc_buf *mfc_buf); + +void mfc_core_get_enc_frame_buffer(struct mfc_core *core, struct mfc_ctx *= ctx, + dma_addr_t addr[], int num_planes); +void mfc_core_set_enc_stride(struct mfc_core *core, struct mfc_ctx *ctx); + void mfc_core_set_dynamic_dpb(struct mfc_core *core, struct mfc_ctx *ctx, struct mfc_buf *dst_vb); =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_run.c index 127d19c4d1cb..6270d22d3806 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.c @@ -16,6 +16,7 @@ =20 #include "mfc_core_cmd.h" #include "mfc_core_hw_reg_api.h" +#include "mfc_core_enc_param.h" =20 #include "base/mfc_queue.h" #include "base/mfc_utils.h" @@ -412,3 +413,129 @@ int mfc_core_run_dec_last_frames(struct mfc_core *cor= e, struct mfc_ctx *ctx) =20 return 0; } + +int mfc_core_run_enc_init(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_buf *dst_mb; + int ret; + + if (!(ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1 && core->id =3D=3D MFC_= CORE_SUB)) { + dst_mb =3D mfc_get_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_SET_USED); + if (!dst_mb) { + mfc_ctx_debug(2, "no dst buffers\n"); + return -EAGAIN; + } + + mfc_core_set_enc_stream_buffer(core, ctx, dst_mb); + mfc_ctx_debug(2, "[BUFINFO] Header addr: 0x%08llx\n", dst_mb->addr[0][0]= ); + } + + mfc_core_set_enc_stride(core, ctx); + + mfc_clean_core_ctx_int_flags(core->core_ctx[ctx->num]); + + ret =3D mfc_core_cmd_enc_seq_header(core, ctx); + return ret; +} + +int mfc_core_run_enc_frame(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_buf *dst_mb; + struct mfc_buf *src_mb; + struct mfc_raw_info *raw; + unsigned int index; + int last_frame =3D 0; + + raw =3D &ctx->raw_buf; + + /* Get the next source buffer */ + src_mb =3D mfc_get_buf(ctx, &core_ctx->src_buf_queue, MFC_BUF_SET_USED); + if (!src_mb) { + mfc_debug(2, "no src buffers\n"); + return -EAGAIN; + } + + if (src_mb->num_valid_bufs > 0) { + /* last image in a buffer container */ + if (src_mb->next_index =3D=3D (src_mb->num_valid_bufs - 1)) { + mfc_debug(4, "[BUFCON] last image in a container\n"); + last_frame =3D __mfc_check_last_frame(core_ctx, src_mb); + } + } else { + last_frame =3D __mfc_check_last_frame(core_ctx, src_mb); + } + + if (mfc_check_mb_flag(src_mb, MFC_FLAG_ENC_SRC_FAKE)) { + enc->fake_src =3D 1; + mfc_debug(2, "src is fake\n"); + } + + index =3D src_mb->vb.vb2_buf.index; + if (mfc_check_mb_flag(src_mb, MFC_FLAG_EMPTY_DATA)) { + enc->empty_data =3D 1; + mfc_debug(2, "[FRAME] src is empty data\n"); + mfc_core_set_enc_frame_buffer(core, ctx, 0, raw->num_planes); + } else { + mfc_core_set_enc_frame_buffer(core, ctx, src_mb, raw->num_planes); + } + + dst_mb =3D mfc_get_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_SET_USED); + if (!dst_mb) { + mfc_debug(2, "no dst buffers\n"); + return -EAGAIN; + } + + mfc_debug(2, "nal start : src index from src_buf_queue:%d\n", + src_mb->vb.vb2_buf.index); + mfc_debug(2, "nal start : dst index from dst_buf_queue:%d\n", + dst_mb->vb.vb2_buf.index); + + mfc_core_set_enc_stream_buffer(core, ctx, dst_mb); + + if (call_bop(ctx, core_set_buf_ctrls, core, ctx, &ctx->src_ctrls[index]) = < 0) + mfc_err("failed in core_set_buf_ctrls\n"); + + mfc_clean_core_ctx_int_flags(core_ctx); + + if (IS_H264_ENC(ctx)) + mfc_core_set_aso_slice_order_h264(core, ctx); + + mfc_core_set_slice_mode(core, ctx); + mfc_core_set_enc_config_qp(core, ctx); + mfc_core_set_enc_ts_delta(core, ctx); + + mfc_core_cmd_enc_one_frame(core, ctx, last_frame); + + return 0; +} + +int mfc_core_run_enc_last_frames(struct mfc_core *core, struct mfc_ctx *ct= x) +{ + struct mfc_buf *dst_mb =3D NULL; + struct mfc_raw_info *raw; + + raw =3D &ctx->raw_buf; + + if (IS_SWITCH_SINGLE_MODE(ctx) && core->id =3D=3D ctx->op_core_num[MFC_CO= RE_SUB]) { + mfc_ctx_info("last-frame of subcore doesn't have dst buffer\n"); + } else { + dst_mb =3D mfc_get_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_SET_USED); + if (!dst_mb) { + mfc_ctx_debug(2, "no dst buffers set to zero\n"); + + if (mfc_is_enc_bframe(ctx)) + mfc_ctx_info("B frame encoding doesn't have dst buffer\n"); + } + } + + mfc_ctx_debug(2, "Set address zero for all planes\n"); + mfc_core_set_enc_frame_buffer(core, ctx, 0, raw->num_planes); + mfc_core_set_enc_stream_buffer(core, ctx, dst_mb); + + mfc_clean_core_ctx_int_flags(core->core_ctx[ctx->num]); + mfc_core_cmd_enc_one_frame(core, ctx, 1); + + return 0; +} diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_run.h index 6b9c9ef91e47..410c2aadcf5d 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_run.h @@ -27,4 +27,9 @@ int mfc_core_run_wakeup(struct mfc_core *core); int mfc_core_run_dec_init(struct mfc_core *core, struct mfc_ctx *ctx); int mfc_core_run_dec_frame(struct mfc_core *core, struct mfc_ctx *ctx); int mfc_core_run_dec_last_frames(struct mfc_core *core, struct mfc_ctx *ct= x); + +int mfc_core_run_enc_init(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_run_enc_frame(struct mfc_core *core, struct mfc_ctx *ctx); +int mfc_core_run_enc_last_frames(struct mfc_core *core, struct mfc_ctx *ct= x); + #endif /* __MFC_CORE_RUN_H */ --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 E4A902848BB for ; Tue, 30 Sep 2025 03:57:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204636; cv=none; b=AGIwKKpmrU5h2HtEVcZRVdQthj2j51cKf0LhDaxIOOfz9IIESn9w24dCu1QLChSXIIvP/2WotMPDC1IjBZySt3aQR1fozfUijCzUQzIGSlfG0cgDQQABv25XJJgeHCqC9NYrfj9xQ1duCnlnlVuE/pe5yArPuEwqH0cysZS1otA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204636; c=relaxed/simple; bh=u7dkwZsISALOkx9pOO/PhWiP/FDJPwjyOnlLAKc6iP8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=KKTMWLtS+ZyDlxK1FbiwFSnaDFPpauELcWKoQfu+TTIy1g3bL9Z/M1w/X2Zwr3GiHvgrssOVUSofHyGcvzvH1RBFY26SnJ7sMWaWzMbO1Glcy/Gmd2ToFv52j8/Ebx3GTOCvRBIXIknlFA89ogxCspJP/TsWsCLDaNzImVAZMdY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=mJ2glgHc; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="mJ2glgHc" Received: from epcas5p1.samsung.com (unknown [182.195.41.39]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035709epoutp04766f35d68984a15e7e57e93ac45a271c~p80ottIuQ2088320883epoutp04W for ; Tue, 30 Sep 2025 03:57:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035709epoutp04766f35d68984a15e7e57e93ac45a271c~p80ottIuQ2088320883epoutp04W DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204629; bh=90Z5+idyqdBJGcoVaccmTCOSKICmqPHbK/q5Tgxv4h0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mJ2glgHchKeFnrkOOpry5ZgjgKgQydQPa2mXcUsjA4rkuJtwJ9DrRxQHN2LVNJU6X t7LfTNu6LYMMGqo63EnbnZJVxQrgf2W/1FdTlsEtE0/NoPoLPNBDdrCEnREjw2mFpy /ReZcWvrDohWjPZqY5Ojl8oymVRIt9WGNFypZFTc= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p3.samsung.com (KnoxPortal) with ESMTPS id 20250930035708epcas5p346f73bcc6109e0ace86871b787727ae1~p80oIEuDq0175701757epcas5p3Q; Tue, 30 Sep 2025 03:57:08 +0000 (GMT) Received: from epcas5p4.samsung.com (unknown [182.195.38.94]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPQC57m1z2SSKX; Tue, 30 Sep 2025 03:57:07 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPA id 20250930035706epcas5p4530de9edacfc86c0ae1ab9de2d2b0695~p80mWWoVc3091130911epcas5p4N; Tue, 30 Sep 2025 03:57:06 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035704epsmtip178a5bdc23e431f79e2cf331e3fe6d553~p80jz6Y7p2938529385epsmtip1u; Tue, 30 Sep 2025 03:57:03 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 27/29] media: mfc: Add H.264 encoder support Date: Tue, 30 Sep 2025 09:33:46 +0530 Message-Id: <20250930040348.3702923-28-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035706epcas5p4530de9edacfc86c0ae1ab9de2d2b0695 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035706epcas5p4530de9edacfc86c0ae1ab9de2d2b0695 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Register H.264 format and V4L2/VB2 headers. - Initialize encoder context, queues, and defaults. - Add buffer=E2=80=91control handling (layers, ROI, frame=E2=80=91rate, dro= p, profile/level) and parse DT properties. - Register encoder ioctl ops, set up QoS table, and extend debugfs. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_format.h | 8 + .../media/platform/samsung/exynos-mfc/mfc.c | 193 ++++++- .../platform/samsung/exynos-mfc/mfc_core.c | 26 + .../samsung/exynos-mfc/mfc_core_buf_ctrl.c | 321 +++++++++++ .../samsung/exynos-mfc/mfc_core_isr.c | 518 +++++++++++++++++- .../samsung/exynos-mfc/mfc_core_sync.c | 58 +- .../platform/samsung/exynos-mfc/mfc_debugfs.c | 17 +- 7 files changed, 1116 insertions(+), 25 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h index 0d48f2373e8d..e8573d6b6005 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h @@ -263,6 +263,14 @@ static struct mfc_fmt mfc_formats[] =3D { .num_planes =3D 1, .mem_planes =3D 1, }, + { + .name =3D "ENC H264", + .fourcc =3D V4L2_PIX_FMT_H264, + .codec_mode =3D MFC_REG_CODEC_H264_ENC, + .type =3D MFC_FMT_STREAM | MFC_FMT_ENC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, }; =20 #endif /* __MFC_FORMAT_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc.c b/drivers/medi= a/platform/samsung/exynos-mfc/mfc.c index db17448eae13..68cba41b45da 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc.c @@ -23,6 +23,8 @@ =20 #include "mfc_dec_v4l2.h" #include "mfc_dec_vb2.h" +#include "mfc_enc_v4l2.h" +#include "mfc_enc_vb2.h" #include "mfc_rm.h" #include "mfc_debugfs.h" =20 @@ -228,6 +230,107 @@ static int __mfc_init_dec_ctx(struct mfc_ctx *ctx) return ret; } =20 +static void __mfc_deinit_enc_ctx(struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + + mfc_delete_queue(&ctx->src_buf_ready_queue); + mfc_delete_queue(&ctx->dst_buf_queue); + mfc_delete_queue(&ctx->ref_buf_queue); + mfc_delete_queue(&ctx->err_buf_queue); + mfc_delete_queue(&ctx->meminfo_inbuf_q); + mfc_delete_queue(&ctx->meminfo_outbuf_q); + + mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_svc); + mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_roi); + + kfree(enc); +} + +static int __mfc_init_enc_ctx(struct mfc_ctx *ctx) +{ + struct mfc_enc *enc; + struct mfc_enc_params *p; + int ret =3D 0; + int i; + + enc =3D kzalloc(sizeof(*enc), GFP_KERNEL); + if (!enc) + return -ENOMEM; + + ctx->enc_priv =3D enc; + ctx->user_prio =3D -1; + + mfc_create_queue(&ctx->src_buf_ready_queue); + mfc_create_queue(&ctx->dst_buf_queue); + mfc_create_queue(&ctx->ref_buf_queue); + mfc_create_queue(&ctx->err_buf_queue); + mfc_create_queue(&ctx->meminfo_inbuf_q); + mfc_create_queue(&ctx->meminfo_outbuf_q); + + for (i =3D 0; i < MFC_MAX_BUFFERS; i++) { + INIT_LIST_HEAD(&ctx->src_ctrls[i]); + INIT_LIST_HEAD(&ctx->dst_ctrls[i]); + } + bitmap_zero(ctx->src_ctrls_avail, MFC_MAX_BUFFERS); + bitmap_zero(ctx->dst_ctrls_avail, MFC_MAX_BUFFERS); + + ctx->type =3D MFCINST_ENCODER; + ctx->c_ops =3D &mfc_ctrls_ops; + ctx->b_ops =3D &mfc_bufs_ops; + + mfc_enc_set_default_format(ctx); + mfc_rate_reset_framerate(ctx); + + ctx->qos_ratio =3D 100; + + /* disable IVF header by default (VP8, VP9) */ + p =3D &enc->params; + p->ivf_header_disable =3D 1; + + INIT_LIST_HEAD(&ctx->bitrate_list); + INIT_LIST_HEAD(&ctx->src_ts.ts_list); + + enc->sh_handle_svc.fd =3D -1; + enc->sh_handle_roi.fd =3D -1; + enc->sh_handle_svc.data_size =3D sizeof(struct temporal_layer_info); + enc->sh_handle_roi.data_size =3D sizeof(struct mfc_enc_roi_info); + + /* Init videobuf2 queue for OUTPUT */ + ctx->vq_src.type =3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + ctx->vq_src.drv_priv =3D ctx; + ctx->vq_src.buf_struct_size =3D (unsigned int)sizeof(struct mfc_buf); + ctx->vq_src.io_modes =3D VB2_USERPTR | VB2_DMABUF; + ctx->vq_src.ops =3D mfc_get_enc_vb2_ops(); + ctx->vq_src.mem_ops =3D mfc_mem_ops(); + ctx->vq_src.timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret =3D vb2_queue_init(&ctx->vq_src); + if (ret) { + mfc_ctx_err("Failed to initialize videobuf2 queue(output)\n"); + goto fail_enc_init; + } + + /* Init videobuf2 queue for CAPTURE */ + ctx->vq_dst.type =3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + ctx->vq_dst.drv_priv =3D ctx; + ctx->vq_dst.buf_struct_size =3D (unsigned int)sizeof(struct mfc_buf); + ctx->vq_dst.io_modes =3D VB2_USERPTR | VB2_DMABUF; + ctx->vq_dst.ops =3D mfc_get_enc_vb2_ops(); + ctx->vq_dst.mem_ops =3D mfc_mem_ops(); + ctx->vq_dst.timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret =3D vb2_queue_init(&ctx->vq_dst); + if (ret) { + mfc_ctx_err("Failed to initialize videobuf2 queue(capture)\n"); + goto fail_enc_init; + } + + return 0; + +fail_enc_init: + __mfc_deinit_enc_ctx(ctx); + return 0; +} + /* Open an MFC node */ static int mfc_open(struct file *file) { @@ -339,6 +442,7 @@ static int mfc_open(struct file *file) ret =3D __mfc_init_dec_ctx(ctx); dev->num_dec_inst++; } else { + ret =3D __mfc_init_enc_ctx(ctx); dev->num_enc_inst++; } if (ret) @@ -351,8 +455,8 @@ static int mfc_open(struct file *file) /* idle mode */ spin_lock_init(&dev->idle_bits_lock); } - if (mfc_is_decoder_node(node)) - ret =3D call_cop(ctx, init_ctx_ctrls, ctx); + + ret =3D call_cop(ctx, init_ctx_ctrls, ctx); if (ret) { mfc_ctx_err("failed in init_ctx_ctrls\n"); goto err_ctx_init; @@ -449,12 +553,10 @@ static int mfc_release(struct file *file) * So, we need to performed stop_streaming * before instance de-init(CLOSE_INSTANCE). */ - if (ctx->type =3D=3D MFCINST_DECODER) { - vb2_queue_release(&ctx->vq_src); - vb2_queue_release(&ctx->vq_dst); + vb2_queue_release(&ctx->vq_src); + vb2_queue_release(&ctx->vq_dst); =20 - call_cop(ctx, cleanup_ctx_ctrls, ctx); - } + call_cop(ctx, cleanup_ctx_ctrls, ctx); =20 ret =3D mfc_rm_instance_deinit(dev, ctx); if (ret) { @@ -471,6 +573,7 @@ static int mfc_release(struct file *file) __mfc_deinit_dec_ctx(ctx); dev->num_dec_inst--; } else if (ctx->type =3D=3D MFCINST_ENCODER) { + __mfc_deinit_enc_ctx(ctx); dev->num_enc_inst--; } =20 @@ -609,6 +712,12 @@ static int __mfc_parse_dt(struct device_node *np, stru= ct mfc_dev *mfc) of_property_read_u32_array (np, "static_info_dec", &pdata->static_info_dec.support, 2); + of_property_read_u32_array + (np, "color_aspect_enc", + &pdata->color_aspect_enc.support, 2); + of_property_read_u32_array + (np, "static_info_enc", + &pdata->static_info_enc.support, 2); of_property_read_u32_array (np, "vp9_stride_align", &pdata->vp9_stride_align.support, 2); @@ -618,6 +727,30 @@ static int __mfc_parse_dt(struct device_node *np, stru= ct mfc_dev *mfc) of_property_read_u32_array (np, "wait_fw_status", &pdata->wait_fw_status.support, 2); + of_property_read_u32_array + (np, "average_qp", + &pdata->average_qp.support, 2); + of_property_read_u32_array + (np, "mv_search_mode", + &pdata->mv_search_mode.support, 2); + of_property_read_u32_array + (np, "enc_idr_flag", + &pdata->enc_idr_flag.support, 2); + of_property_read_u32_array + (np, "min_quality_mode", + &pdata->min_quality_mode.support, 2); + of_property_read_u32_array + (np, "enc_capability", + &pdata->enc_capability.support, 2); + of_property_read_u32_array + (np, "enc_ts_delta", + &pdata->enc_ts_delta.support, 2); + of_property_read_u32_array + (np, "wfd_rc_mode", + &pdata->wfd_rc_mode.support, 2); + of_property_read_u32_array + (np, "max_i_frame_size", + &pdata->max_i_frame_size.support, 2); of_property_read_u32_array (np, "hevc_pic_output_flag", &pdata->hevc_pic_output_flag.support, 2); @@ -630,10 +763,14 @@ static int __mfc_parse_dt(struct device_node *np, str= uct mfc_dev *mfc) =20 /* Formats */ of_property_read_u32(np, "support_422", &pdata->support_422); + of_property_read_u32(np, "support_rgb", &pdata->support_rgb); =20 /* Resolution */ of_property_read_u32(np, "support_check_res", &pdata->support_check_res); =20 + /* HWAPG */ + of_property_read_u32(np, "support_hwapg", &pdata->support_hwapg); + /* HWACG */ of_property_read_u32(np, "support_hwacg", &pdata->support_hwacg); =20 @@ -643,6 +780,39 @@ static int __mfc_parse_dt(struct device_node *np, stru= ct mfc_dev *mfc) /* output buffer Q framerate */ of_property_read_u32(np, "display_framerate", &pdata->display_framerate); =20 + /* Encoder default parameter */ + of_property_read_u32(np, "enc_param_num", &pdata->enc_param_num); + if (pdata->enc_param_num) { + of_property_read_u32_array + (np, "enc_param_addr", + pdata->enc_param_addr, pdata->enc_param_num); + of_property_read_u32_array + (np, "enc_param_val", + pdata->enc_param_val, pdata->enc_param_num); + } + + /* MFC bandwidth information */ + of_property_read_u32_array + (np, "bw_enc_h264", + &pdata->mfc_bw_info.bw_enc_h264.peak, 3); + of_property_read_u32_array + (np, "bw_enc_hevc", + &pdata->mfc_bw_info.bw_enc_hevc.peak, 3); + of_property_read_u32_array + (np, "bw_enc_hevc_10bit", + &pdata->mfc_bw_info.bw_enc_hevc_10bit.peak, 3); + of_property_read_u32_array + (np, "bw_enc_vp8", + &pdata->mfc_bw_info.bw_enc_vp8.peak, 3); + of_property_read_u32_array + (np, "bw_enc_vp9", + &pdata->mfc_bw_info.bw_enc_vp9.peak, 3); + of_property_read_u32_array + (np, "bw_enc_vp9_10bit", + &pdata->mfc_bw_info.bw_enc_vp9_10bit.peak, 3); + of_property_read_u32_array + (np, "bw_enc_mpeg4", + &pdata->mfc_bw_info.bw_enc_mpeg4.peak, 3); of_property_read_u32_array (np, "bw_dec_h264", &pdata->mfc_bw_info.bw_dec_h264.peak, 3); @@ -745,6 +915,11 @@ static int __mfc_parse_dt(struct device_node *np, stru= ct mfc_dev *mfc) of_property_read_u32(np, "scheduler", &pdata->scheduler); of_property_read_u32(np, "pbs_num_prio", &pdata->pbs_num_prio); =20 + /* Encoder RGB CSC formula by VUI from F/W */ + of_property_read_u32(np, "enc_rgb_csc_by_fw", &pdata->enc_rgb_csc_by_fw); + + of_property_read_u32(np, "support_enc_mode1", &pdata->support_enc_mode1); + of_property_read_u32(np, "support_mv_hevc", &pdata->support_mv_hevc); =20 return 0; @@ -771,7 +946,7 @@ static struct video_device *__mfc_video_device_register if (IS_DEC_NODE(node_num)) vfd->ioctl_ops =3D mfc_get_dec_v4l2_ioctl_ops(); else if (IS_ENC_NODE(node_num)) - vfd->ioctl_ops =3D NULL; + vfd->ioctl_ops =3D mfc_get_enc_v4l2_ioctl_ops(); =20 vfd->lock =3D &dev->mfc_mutex; vfd->v4l2_dev =3D &dev->v4l2_dev; @@ -1126,7 +1301,7 @@ static const struct dev_pm_ops mfc_pm_ops =3D { struct mfc_ctx_buf_size mfc_ctx_buf_size =3D { .dev_ctx =3D PAGE_ALIGN(0x7800), /* 30KB */ .h264_dec_ctx =3D PAGE_ALIGN(0x200000), /* 1.6MB */ - .av1_dec_ctx =3D PAGE_ALIGN(0x19000), /* 100KB */ + .av1_dec_ctx =3D PAGE_ALIGN(0x19000), /* 100KB */ .other_dec_ctx =3D PAGE_ALIGN(0xF000), /* 60KB */ .h264_enc_ctx =3D PAGE_ALIGN(0x19000), /* 100KB */ .hevc_enc_ctx =3D PAGE_ALIGN(0xC800), /* 50KB */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core.c b/drivers= /media/platform/samsung/exynos-mfc/mfc_core.c index aad3273ce2ba..2d9c2ffef0d4 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core.c @@ -29,6 +29,7 @@ #include "mfc_core_ops.h" #include "mfc_core_isr.h" #include "mfc_dec_v4l2.h" +#include "mfc_enc_v4l2.h" #include "mfc_debugfs.h" =20 #include "mfc_core_hwlock.h" @@ -79,6 +80,8 @@ static int __mfc_core_parse_mfc_qos_platdata(struct devic= e_node *np, of_property_read_u32(np_qos, "mo_value", &qosdata->mo_value); of_property_read_u32(np_qos, "mo_10bit_value", &qosdata->mo_10bit_value); + of_property_read_u32(np_qos, "mo_uhd_enc60_value", + &qosdata->mo_uhd_enc60_value); of_property_read_u32(np_qos, "time_fw", &qosdata->time_fw); =20 of_property_read_string(np_qos, "bts_scen", &qosdata->name); @@ -214,6 +217,8 @@ static int __mfc_core_parse_dt(struct device_node *np, = struct mfc_core *core) /* QoS */ of_property_read_u32(np, "num_default_qos_steps", &pdata->num_default_qos_steps); + of_property_read_u32(np, "num_encoder_qos_steps", + &pdata->num_encoder_qos_steps); of_property_read_u32(np, "max_mb", &pdata->max_mb); of_property_read_u32(np, "max_hw_mb", &pdata->max_hw_mb); of_property_read_u32(np, "mfc_freq_control", &pdata->mfc_freq_control); @@ -233,6 +238,18 @@ static int __mfc_core_parse_dt(struct device_node *np,= struct mfc_core *core) core); } =20 + pdata->encoder_qos_table =3D devm_kzalloc(core->device, + sizeof(struct mfc_qos) * + pdata->num_encoder_qos_steps, + GFP_KERNEL); + for (i =3D 0; i < pdata->num_encoder_qos_steps; i++) { + snprintf(node_name, sizeof(node_name), "mfc_e_qos_variant_%d", + i); + __mfc_core_parse_mfc_qos_platdata(np, node_name, + &pdata->encoder_qos_table[i], + core); + } + /* performance boost mode */ pdata->qos_boost_table =3D devm_kzalloc(core->device, sizeof(struct mfc_qos_boost), @@ -466,6 +483,15 @@ static int mfc_core_probe(struct platform_device *pdev) core->core_pdata->default_qos_table[i].freq_mif, core->core_pdata->default_qos_table[i].name, core->core_pdata->default_qos_table[i].bts_scen_idx); + mfc_core_info("[QoS]-------------------Encoder only table\n"); + for (i =3D 0; i < core->core_pdata->num_encoder_qos_steps; i++) + mfc_core_info + ("[QoS] table[%d] mfc: %d, int: %d, mif: %d, bts_scen: %s(%d)\n", + i, core->core_pdata->encoder_qos_table[i].freq_mfc, + core->core_pdata->encoder_qos_table[i].freq_int, + core->core_pdata->encoder_qos_table[i].freq_mif, + core->core_pdata->encoder_qos_table[i].name, + core->core_pdata->encoder_qos_table[i].bts_scen_idx); #if IS_ENABLED(CONFIG_SAMSUNG_IOMMU) ret =3D samsung_iommu_register_fault_handler(core->device, mfc_core_sysmmu_fault_handler, diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c = b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c index 38f09d6ef2dd..cc0a20bea33a 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c @@ -11,6 +11,250 @@ =20 #include "mfc_core_reg_api.h" =20 +static void __mfc_enc_store_buf_ctrls_temporal_svc(int id, + struct mfc_enc_params *p, + struct temporal_layer_info + *temporal_LC) +{ + unsigned int num_layer =3D temporal_LC->temporal_layer_count; + int i; + + switch (id) { + case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH: + p->codec.h264.num_hier_layer =3D num_layer & 0x7; + for (i =3D 0; i < (num_layer & 0x7); i++) + p->codec.h264.hier_bit_layer[i] =3D + temporal_LC->temporal_layer_bitrate[i]; + break; + default: + break; + } +} + +static void __mfc_core_enc_set_buf_ctrls_temporal_svc(struct mfc_core *cor= e, + struct mfc_ctx *ctx, + struct mfc_buf_ctrl + *buf_ctrl) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + unsigned int value =3D 0, value2 =3D 0; + struct temporal_layer_info temporal_LC; + unsigned int i; + struct mfc_enc_params *p =3D &enc->params; + + if (buf_ctrl->id + =3D=3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH) { + memcpy(&temporal_LC, + enc->sh_handle_svc.vaddr, + sizeof(struct temporal_layer_info)); + + if ((temporal_LC.temporal_layer_count & 0x7) < 1) { + /* clear NUM_T_LAYER_CHANGE */ + value =3D MFC_CORE_READL(buf_ctrl->flag_addr); + value &=3D ~BIT(10); + MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); + mfc_ctx_err + ("[HIERARCHICAL] layer count is invalid : %d\n", + temporal_LC.temporal_layer_count); + return; + } + + value =3D MFC_CORE_READL(buf_ctrl->flag_addr); + value &=3D ~(0x3 << 21); + + MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); + + /* Store temporal layer information */ + __mfc_enc_store_buf_ctrls_temporal_svc(buf_ctrl->id, p, + &temporal_LC); + + /* enable RC_BIT_RATE_CHANGE */ + value =3D MFC_CORE_READL(buf_ctrl->flag_addr); + if (temporal_LC.temporal_layer_bitrate[0] > 0 || + p->hier_bitrate_ctrl) + /* set RC_BIT_RATE_CHANGE */ + value |=3D BIT(2); + else + /* clear RC_BIT_RATE_CHANGE */ + value &=3D ~BIT(2); + MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); + + mfc_ctx_debug(3, + "[HIERARCHICAL] layer count %d, E_PARAM_CHANGE %#x\n", + temporal_LC.temporal_layer_count & 0x7, value); + + value =3D MFC_CORE_READL(MFC_REG_E_NUM_T_LAYER); + buf_ctrl->old_val2 =3D value; + value &=3D ~(0x7); + value |=3D (temporal_LC.temporal_layer_count & 0x7); + value &=3D ~BIT(8); + value |=3D (p->hier_bitrate_ctrl & 0x1) << 8; + MFC_CORE_WRITEL(value, MFC_REG_E_NUM_T_LAYER); + mfc_ctx_debug(3, "[HIERARCHICAL] E_NUM_T_LAYER %#x\n", value); + for (i =3D 0; i < (temporal_LC.temporal_layer_count & 0x7); i++) { + mfc_ctx_debug(3, + "[HIERARCHICAL] layer bitrate[%d] %d (FW ctrl: %d)\n", + i, temporal_LC.temporal_layer_bitrate[i], + p->hier_bitrate_ctrl); + MFC_CORE_WRITEL(temporal_LC.temporal_layer_bitrate[i], + buf_ctrl->addr + i * 4); + } + /* priority change */ + if (IS_H264_ENC(ctx)) { + value =3D 0; + value2 =3D 0; + for (i =3D 0; i < (p->codec.h264.num_hier_layer & 0x07); + i++) { + if (i <=3D 4) + value |=3D + ((p->codec.h264.base_priority & 0x3F) + i) + << (6 * i); + else + value2 |=3D + ((p->codec.h264.base_priority & 0x3F) + i) + << (6 * (i - 5)); + } + MFC_CORE_WRITEL(value, + MFC_REG_E_H264_HD_SVC_EXTENSION_0); + MFC_CORE_WRITEL(value2, + MFC_REG_E_H264_HD_SVC_EXTENSION_1); + mfc_ctx_debug(3, + "[HIERARCHICAL] EXTENSION0 %#x, EXTENSION1 %#x\n", + value, value2); + + value =3D MFC_CORE_READL(buf_ctrl->flag_addr); + value |=3D BIT(12); + MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); + mfc_ctx_debug(3, "[HIERARCHICAL] E_PARAM_CHANGE %#x\n", + value); + } + } + + /* temproral layer priority */ + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY) { + value =3D MFC_CORE_READL(MFC_REG_E_H264_HD_SVC_EXTENSION_0); + buf_ctrl->old_val |=3D value & 0x3FFFFFC0; + value &=3D ~(0x3FFFFFC0); + value2 =3D MFC_CORE_READL(MFC_REG_E_H264_HD_SVC_EXTENSION_1); + buf_ctrl->old_val2 =3D value2 & 0x0FFF; + value2 &=3D ~(0x0FFF); + for (i =3D 0; i < (p->codec.h264.num_hier_layer & 0x07); i++) { + if (i <=3D 4) + value |=3D + ((buf_ctrl->val & 0x3F) + i) << (6 * i); + else + value2 |=3D + ((buf_ctrl->val & 0x3F) + + i) << (6 * (i - 5)); + } + MFC_CORE_WRITEL(value, MFC_REG_E_H264_HD_SVC_EXTENSION_0); + MFC_CORE_WRITEL(value2, MFC_REG_E_H264_HD_SVC_EXTENSION_1); + mfc_ctx_debug(3, + "[HIERARCHICAL] EXTENSION0 %#x, EXTENSION1 %#x\n", + value, value2); + } +} + +static void __mfc_core_enc_set_buf_ctrls_exception(struct mfc_core *core, + struct mfc_ctx *ctx, + struct mfc_buf_ctrl + *buf_ctrl) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + unsigned int value =3D 0; + + /* temporal layer setting */ + __mfc_core_enc_set_buf_ctrls_temporal_svc(core, ctx, buf_ctrl); + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC_H264_MARK_LTR) { + value =3D MFC_CORE_READL(MFC_REG_E_H264_NAL_CONTROL); + buf_ctrl->old_val2 =3D (value >> 8) & 0x7; + value &=3D ~(0x7 << 8); + value |=3D (buf_ctrl->val & 0x7) << 8; + MFC_CORE_WRITEL(value, MFC_REG_E_H264_NAL_CONTROL); + } + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC_H264_USE_LTR) { + value =3D MFC_CORE_READL(MFC_REG_E_H264_NAL_CONTROL); + buf_ctrl->old_val2 =3D (value >> 11) & 0xF; + value &=3D ~GENMASK(14, 11); + value |=3D (buf_ctrl->val & 0xF) << 11; + MFC_CORE_WRITEL(value, MFC_REG_E_H264_NAL_CONTROL); + } + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH) { + value =3D MFC_CORE_READL(MFC_REG_E_GOP_CONFIG2); + buf_ctrl->old_val |=3D (value << 16) & 0x3FFF0000; + value &=3D ~(0x3FFF); + value |=3D (buf_ctrl->val >> 16) & 0x3FFF; + MFC_CORE_WRITEL(value, MFC_REG_E_GOP_CONFIG2); + } + + /* PROFILE & LEVEL have to be set up together */ + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_H264_LEVEL) { + value =3D MFC_CORE_READL(MFC_REG_E_PICTURE_PROFILE); + buf_ctrl->old_val |=3D (value & 0x000F) << 8; + value &=3D ~(0x000F); + value |=3D p->codec.h264.profile & 0x000F; + MFC_CORE_WRITEL(value, MFC_REG_E_PICTURE_PROFILE); + p->codec.h264.level =3D buf_ctrl->val; + } + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_H264_PROFILE) { + value =3D MFC_CORE_READL(MFC_REG_E_PICTURE_PROFILE); + buf_ctrl->old_val |=3D value & 0xFF00; + value &=3D ~(0x00FF << 8); + value |=3D (p->codec.h264.level << 8) & 0xFF00; + MFC_CORE_WRITEL(value, MFC_REG_E_PICTURE_PROFILE); + p->codec.h264.profile =3D buf_ctrl->val; + } + + /* set the ROI buffer DVA */ + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_ROI_CONTROL) { + MFC_CORE_DMA_WRITEL(enc->roi_buf[buf_ctrl->old_val2].daddr, + MFC_REG_E_ROI_BUFFER_ADDR); + mfc_ctx_debug(3, "[ROI] buffer[%d] addr %#llx, QP val: %#x\n", + buf_ctrl->old_val2, + enc->roi_buf[buf_ctrl->old_val2].daddr, + buf_ctrl->val); + } + + /* set frame rate change with delta */ + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH) { + p->rc_frame_delta =3D p->rc_framerate_res / buf_ctrl->val; + value =3D MFC_CORE_READL(buf_ctrl->addr); + value &=3D ~(buf_ctrl->mask << buf_ctrl->shft); + value |=3D + ((p->rc_frame_delta & buf_ctrl->mask) << buf_ctrl->shft); + MFC_CORE_WRITEL(value, buf_ctrl->addr); + } + + /* set drop control */ + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_DROP_CONTROL) { + p->rc_frame_delta =3D mfc_enc_get_ts_delta(ctx); + value =3D MFC_CORE_READL(MFC_REG_E_RC_FRAME_RATE); + value &=3D ~(0xFFFF); + value |=3D (p->rc_frame_delta & 0xFFFF); + MFC_CORE_WRITEL(value, MFC_REG_E_RC_FRAME_RATE); + if (ctx->src_ts.ts_last_interval) + mfc_ctx_debug(3, + "[DROPCTRL] fps %d -> %ld, delta: %d, reg: %#x\n", + p->rc_framerate, + USEC_PER_SEC / + ctx->src_ts.ts_last_interval, + p->rc_frame_delta, value); + else + mfc_ctx_debug(3, + "[DROPCTRL] fps %d -> 0, delta: %d, reg: %#x\n", + p->rc_framerate, p->rc_frame_delta, + value); + } + + /* store last config qp value in F/W */ + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC_CONFIG_QP) + enc->config_qp =3D p->config_qp; +} + static int mfc_core_set_buf_ctrls(struct mfc_core *core, struct mfc_ctx *ctx, struct list_head *head) { @@ -51,6 +295,10 @@ static int mfc_core_set_buf_ctrls(struct mfc_core *core, if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG) ctx->stored_tag =3D buf_ctrl->val; =20 + if (ctx->type =3D=3D MFCINST_ENCODER) + __mfc_core_enc_set_buf_ctrls_exception(core, ctx, + buf_ctrl); + mfc_ctx_debug(6, "[CTRLS] Set buffer control id: 0x%08x, val: %d (%#x)\n", buf_ctrl->id, buf_ctrl->val, buf_ctrl->val); @@ -64,6 +312,7 @@ static int mfc_core_get_buf_ctrls(struct mfc_core *core, { struct mfc_buf_ctrl *buf_ctrl; struct mfc_dec *dec =3D ctx->dec_priv; + struct mfc_enc *enc =3D ctx->enc_priv; unsigned int value =3D 0; =20 list_for_each_entry(buf_ctrl, head, list) { @@ -89,6 +338,10 @@ static int mfc_core_get_buf_ctrls(struct mfc_core *core, if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_FRAME_ERROR_TYPE) buf_ctrl->val =3D mfc_get_frame_error_type(ctx, value); =20 + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_FRAME_STATUS) + if (enc) + buf_ctrl->val =3D !enc->in_slice; + mfc_ctx_debug(6, "[CTRLS] Get buffer control id: 0x%08x, val: %d (%#x)\n", buf_ctrl->id, buf_ctrl->val, buf_ctrl->val); @@ -97,6 +350,69 @@ static int mfc_core_get_buf_ctrls(struct mfc_core *core, return 0; } =20 +static void __mfc_core_enc_recover_buf_ctrls_exception(struct mfc_core *co= re, + struct mfc_ctx *ctx, + struct mfc_buf_ctrl + *buf_ctrl) +{ + unsigned int value =3D 0; + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH) { + value =3D MFC_CORE_READL(MFC_REG_E_GOP_CONFIG2); + value &=3D ~(0x3FFF); + value |=3D (buf_ctrl->old_val >> 16) & 0x3FFF; + MFC_CORE_WRITEL(value, MFC_REG_E_GOP_CONFIG2); + } + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_H264_LEVEL) { + value =3D MFC_CORE_READL(MFC_REG_E_PICTURE_PROFILE); + value &=3D ~(0x000F); + value |=3D (buf_ctrl->old_val >> 8) & 0x000F; + MFC_CORE_WRITEL(value, MFC_REG_E_PICTURE_PROFILE); + } + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_VIDEO_H264_PROFILE) { + value =3D MFC_CORE_READL(MFC_REG_E_PICTURE_PROFILE); + value &=3D ~(0xFF00); + value |=3D buf_ctrl->old_val & 0xFF00; + MFC_CORE_WRITEL(value, MFC_REG_E_PICTURE_PROFILE); + } + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC_H264_BASE_PRIORITY) { + MFC_CORE_WRITEL(buf_ctrl->old_val, + MFC_REG_E_H264_HD_SVC_EXTENSION_0); + MFC_CORE_WRITEL(buf_ctrl->old_val2, + MFC_REG_E_H264_HD_SVC_EXTENSION_1); + } + + if (buf_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH || + buf_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH || + buf_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) { + MFC_CORE_WRITEL(buf_ctrl->old_val2, MFC_REG_E_NUM_T_LAYER); + /* clear RC_BIT_RATE_CHANGE */ + value =3D MFC_CORE_READL(buf_ctrl->flag_addr); + value &=3D ~BIT(2); + MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); + } + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC_H264_MARK_LTR) { + value =3D MFC_CORE_READL(MFC_REG_E_H264_NAL_CONTROL); + value &=3D ~(0x7 << 8); + value |=3D (buf_ctrl->old_val2 & 0x7) << 8; + MFC_CORE_WRITEL(value, MFC_REG_E_H264_NAL_CONTROL); + } + + if (buf_ctrl->id =3D=3D V4L2_CID_MPEG_MFC_H264_USE_LTR) { + value =3D MFC_CORE_READL(MFC_REG_E_H264_NAL_CONTROL); + value &=3D ~GENMASK(14, 11); + value |=3D (buf_ctrl->old_val2 & 0xF) << 11; + MFC_CORE_WRITEL(value, MFC_REG_E_H264_NAL_CONTROL); + } +} + static int mfc_core_recover_buf_ctrls(struct mfc_core *core, struct mfc_ctx *ctx, struct list_head *head) @@ -125,6 +441,11 @@ static int mfc_core_recover_buf_ctrls(struct mfc_core = *core, MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); } =20 + /* Exception */ + if (ctx->type =3D=3D MFCINST_ENCODER) + __mfc_core_enc_recover_buf_ctrls_exception(core, ctx, + buf_ctrl); + buf_ctrl->updated =3D 0; mfc_ctx_debug(6, "[CTRLS] Recover buffer control id: 0x%08x, old val: %d\n", diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_isr.c index aa2c0b618c19..1a3cf7e76e29 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c @@ -777,6 +777,9 @@ static void __mfc_handle_error_state(struct mfc_ctx *ct= x, struct mfc_core_ctx *c /* Mark all src buffers as having an error */ mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->src_buf_ready_queue); mfc_cleanup_queue(&ctx->buf_queue_lock, &core_ctx->src_buf_queue); + if (ctx->type =3D=3D MFCINST_ENCODER) + mfc_cleanup_queue(&ctx->buf_queue_lock, &ctx->ref_buf_queue); + /* Mark all NAL_Q buffers as having an error */ } =20 void mfc_core_handle_error(struct mfc_core *core) @@ -885,6 +888,11 @@ static void __mfc_handle_frame_error(struct mfc_core *= core, unsigned int index; enum vb2_buffer_state vb2_state; =20 + if (ctx->type =3D=3D MFCINST_ENCODER) { + mfc_info("Encoder Interrupt Error (err: %d)\n", mfc_get_err(err)); + return; + } + dec =3D ctx->dec_priv; if (!dec) { mfc_err("no mfc decoder to run\n"); @@ -1267,12 +1275,428 @@ static void __mfc_handle_frame(struct mfc_core *co= re, mfc_debug(2, "Assesing whether this context should be run again\n"); } =20 -static inline void __mfc_handle_done_frame(struct mfc_core *core, - struct mfc_ctx *ctx, - unsigned int reason, - unsigned int err) +static void __mfc_handle_error_input(struct mfc_core *core, struct mfc_ctx= *ctx) +{ + struct mfc_buf *mfc_buf =3D NULL; + int index; + + while (1) { + mfc_buf =3D mfc_get_del_buf(ctx, &ctx->err_buf_queue, MFC_BUF_NO_TOUCH_U= SED); + if (!mfc_buf) + break; + + index =3D mfc_buf->vb.vb2_buf.index; + + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_ctx_err("failed in core_recover_buf_ctrls\n"); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + mfc_ctx_info("find src buf(fd: %d) in err_queue\n", + mfc_buf->vb.vb2_buf.planes[0].m.fd); + mfc_clear_mb_flag(mfc_buf); + mfc_set_mb_flag(mfc_buf, MFC_FLAG_CONSUMED_ONLY); + vb2_buffer_done(&mfc_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static void __mfc_handle_stream_copy_timestamp(struct mfc_ctx *ctx, struct= mfc_buf *src_mb) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_buf *dst_mb; + u64 interval; + u64 start_timestamp; + u64 new_timestamp; + + start_timestamp =3D src_mb->vb.vb2_buf.timestamp; + interval =3D NSEC_PER_SEC / p->rc_framerate; + if (ctx->dev->debugfs.debug_ts =3D=3D 1) + mfc_ctx_info("[BUFCON][TS] %dfps, start timestamp: %lld, base interval: = %lld\n", + p->rc_framerate, start_timestamp, interval); + + new_timestamp =3D start_timestamp + (interval * src_mb->done_index); + if (ctx->dev->debugfs.debug_ts =3D=3D 1) + mfc_ctx_info("[BUFCON][TS] new timestamp: %lld, interval: %lld\n", + new_timestamp, interval * src_mb->done_index); + + /* Get the destination buffer */ + dst_mb =3D mfc_get_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (dst_mb) + dst_mb->vb.vb2_buf.timestamp =3D new_timestamp; +} + +static void __mfc_handle_stream_input(struct mfc_core *core, struct mfc_ct= x *ctx, + int consumed_only) { - __mfc_handle_frame(core, ctx, reason, err); + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_raw_info *raw; + struct mfc_buf *ref_mb, *src_mb; + dma_addr_t enc_addr[3] =3D { 0, 0, 0 }; + struct mfc_enc *enc =3D ctx->enc_priv; + int i, found_in_src_queue =3D 0; + unsigned int index; + + raw =3D &ctx->raw_buf; + + mfc_core_get_enc_frame_buffer(core, ctx, &enc_addr[0], raw->num_planes); + if (enc_addr[0] =3D=3D 0) { + mfc_debug(3, "no encoded src\n"); + + if (enc->fake_src && enc->params.num_b_frame) { + mfc_change_state(core_ctx, MFCINST_FINISHING); + enc->fake_src =3D 0; + mfc_debug(2, "clear fake_src and change to FINISHING\n"); + } + + goto move_buf; + } + for (i =3D 0; i < raw->num_planes; i++) + mfc_debug(2, "[BUFINFO] ctx[%d] get src addr[%d]: 0x%08llx\n", + ctx->num, i, enc_addr[i]); + + if (ctx->multi_view_enable && + ctx->select_view =3D=3D MFC_VIEW_ID_MAIN) { + mfc_debug(2, "not handling src_mb to reuse for VIEW_1\n"); + return; + } + + if (IS_BUFFER_BATCH_MODE(ctx)) { + src_mb =3D mfc_find_first_buf(ctx, &core_ctx->src_buf_queue, enc_addr[0]= ); + if (src_mb) { + found_in_src_queue =3D 1; + + __mfc_handle_stream_copy_timestamp(ctx, src_mb); + src_mb->done_index++; + mfc_debug(4, "[BUFCON] batch buf done_index: %d\n", src_mb->done_index); + + index =3D src_mb->vb.vb2_buf.index; + /* single buffer || last image in a buffer container */ + if (!src_mb->num_valid_bufs || + src_mb->done_index =3D=3D src_mb->num_valid_bufs) { + if (consumed_only) { + mfc_clear_mb_flag(src_mb); + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + } + + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_recover_buf_ctrls\n"); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + src_mb =3D mfc_find_del_buf(ctx, + &core_ctx->src_buf_queue, + enc_addr[0]); + if (src_mb) { + for (i =3D 0; i < raw->num_planes; i++) + mfc_bufcon_put_daddr(ctx, src_mb, i); + vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + } + } + } else { + /* normal single buffer */ + src_mb =3D mfc_find_del_buf(ctx, &core_ctx->src_buf_queue, enc_addr[0]); + if (src_mb) { + found_in_src_queue =3D 1; + index =3D src_mb->vb.vb2_buf.index; + if (consumed_only) { + mfc_clear_mb_flag(src_mb); + mfc_set_mb_flag(src_mb, MFC_FLAG_CONSUMED_ONLY); + } + + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_recover_buf_ctrls\n"); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + mfc_debug(3, "find src buf in src_queue\n"); + vb2_buffer_done(&src_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + } else { + mfc_debug(3, "no src buf in src_queue\n"); + ref_mb =3D mfc_find_del_buf(ctx, &ctx->ref_buf_queue, enc_addr[0]); + if (ref_mb) { + index =3D ref_mb->vb.vb2_buf.index; + if (consumed_only) { + mfc_clear_mb_flag(ref_mb); + mfc_set_mb_flag(ref_mb, MFC_FLAG_CONSUMED_ONLY); + } + + if (call_bop(ctx, core_recover_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_recover_buf_ctrls\n"); + + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, + &ctx->src_ctrls[index]) < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + + mfc_debug(3, "find src buf in ref_queue\n"); + vb2_buffer_done(&ref_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + } else { + mfc_err("couldn't find src buffer\n"); + } + } + } + +move_buf: + /* move enqueued src buffer: src queue -> ref queue */ + if (!found_in_src_queue && core_ctx->state !=3D MFCINST_FINISHING) { + mfc_get_move_buf_used(ctx, &ctx->ref_buf_queue, &core_ctx->src_buf_queue= ); + + mfc_debug(2, "enc src_buf_queue(%d) -> ref_buf_queue(%d)\n", + mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_buf_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue)); + } +} + +static void __mfc_handle_stream_output(struct mfc_core *core, + struct mfc_ctx *ctx, + int slice_type, + unsigned int strm_size) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_buf *dst_mb; + unsigned int index, idr_flag =3D 1; + + if (strm_size =3D=3D 0) { + mfc_ctx_debug(3, "no encoded dst (reuse)\n"); + return; + } + + if (ctx->select_view =3D=3D MFC_VIEW_ID_MAIN && + MFC_FEATURE_SUPPORT(dev, dev->pdata->enc_idr_flag)) + enc->idr_flag =3D mfc_core_get_enc_idr_flag(); + idr_flag =3D enc->idr_flag; + + /* at least one more dest. buffers exist always */ + if (ctx->multi_view_enable && + ctx->select_view =3D=3D MFC_VIEW_ID_MAIN) { + mfc_ctx_debug(2, "not handling dst_mb to reuse for VIEW_1\n"); + return; + } + + dst_mb =3D mfc_get_del_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USE= D); + if (!dst_mb) { + mfc_ctx_err("no dst buffers\n"); + return; + } + + mfc_ctx_debug(2, "[BUFINFO] ctx[%d] get dst addr: 0x%08llx\n", + ctx->num, dst_mb->addr[0][0]); + + mfc_clear_mb_flag(dst_mb); + dst_mb->vb.flags &=3D ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME); + switch (slice_type) { + case MFC_REG_E_SLICE_TYPE_I: + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_KEYFRAME; + if (!(CODEC_HAS_IDR(ctx) && !idr_flag)) { + mfc_set_mb_flag(dst_mb, MFC_FLAG_SYNC_FRAME); + mfc_ctx_debug(2, "[STREAM] syncframe IDR\n"); + } + break; + case MFC_REG_E_SLICE_TYPE_P: + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_PFRAME; + break; + case MFC_REG_E_SLICE_TYPE_B: + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_BFRAME; + break; + default: + dst_mb->vb.flags |=3D V4L2_BUF_FLAG_KEYFRAME; + break; + } + mfc_ctx_debug(2, "[STREAM] Slice type flag: %d\n", dst_mb->vb.flags); + + if (IS_MULTI_MODE(ctx) && + !(dev->debugfs.feature_option & MFC_OPTION_STREAM_COPY_DISABLE)) { + unsigned int tile0_size, tile1_size; + unsigned int size; + unsigned char *vaddr; + struct sg_table *sgt; + + tile0_size =3D MFC_CORE_READL(MFC_REG_E_TILE0_STREAM_SIZE); + tile1_size =3D MFC_CORE_READL(MFC_REG_E_TILE1_STREAM_SIZE); + + size =3D (unsigned int)vb2_plane_size(&dst_mb->vb.vb2_buf, 0); + size =3D ALIGN(size, STREAM_BUF_ALIGN); + + sgt =3D vb2_dma_sg_plane_desc(&dst_mb->vb.vb2_buf, 0); + dma_sync_sgtable_for_cpu(dev->device, sgt, DMA_BIDIRECTIONAL); + + vaddr =3D vb2_plane_vaddr(&dst_mb->vb.vb2_buf, 0); + if (!vaddr) + mfc_ctx_err("failed to get vaddr for copying stream\n"); + else + memmove(vaddr + tile0_size, vaddr + ALIGN(size / 2, 16), tile1_size); + + mfc_ctx_debug(3, "memmove done: %d + %d\n", tile0_size, tile1_size); + } + + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, strm_size); + mfc_rate_update_framerate(ctx); + + index =3D dst_mb->vb.vb2_buf.index; + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index]) = < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static void __mfc_handle_stream_last_output(struct mfc_core *core, struct = mfc_ctx *ctx) +{ + struct mfc_buf *dst_mb; + unsigned int index; + + /* at least one more dest. buffers exist always */ + dst_mb =3D mfc_get_del_buf(ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USE= D); + if (!dst_mb) { + mfc_ctx_err("no dst buffers\n"); + return; + } + + mfc_ctx_debug(2, "[BUFINFO] ctx[%d] get dst addr: 0x%08llx\n", + ctx->num, dst_mb->addr[0][0]); + + dst_mb->vb.flags &=3D ~(V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME); + + vb2_set_plane_payload(&dst_mb->vb.vb2_buf, 0, 0); + + index =3D dst_mb->vb.vb2_buf.index; + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, &ctx->dst_ctrls[index]) = < 0) + mfc_ctx_err("failed in core_get_buf_ctrls\n"); + + mfc_ctx_debug(2, "[STREAM] update tag for last stream\n"); + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, ctx->stored_tag); + + vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +/* Handle frame encoding interrupt */ +static int __mfc_handle_stream(struct mfc_core *core, struct mfc_ctx *ctx,= unsigned int reason) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_core *subcore; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + int slice_type, consumed_only =3D 0; + unsigned int strm_size; + unsigned int pic_count; + + slice_type =3D mfc_core_get_enc_slice_type(); + strm_size =3D mfc_core_get_enc_strm_size(); + pic_count =3D mfc_core_get_enc_pic_count(); + + mfc_debug(2, "[STREAM] encoded slice type: %d, size: %d, display order: %= d\n", + slice_type, strm_size, pic_count); + + /* clear vOTF enable */ + mfc_core_clear_enc_src_votf(core); + + /* buffer full handling */ + if (enc->buf_full) { + core_ctx->prev_state =3D core_ctx->state; + mfc_change_state(core_ctx, MFCINST_ABORT_INST); + return 0; + } + if (core_ctx->state =3D=3D MFCINST_RUNNING_BUF_FULL) + mfc_change_state(core_ctx, core_ctx->prev_state); + + /* set encoded frame type */ + if (ctx->select_view =3D=3D MFC_VIEW_ID_MAIN) + enc->frame_type =3D slice_type; + ctx->sequence++; + + if (enc->in_slice) { + if (mfc_is_queue_count_same(&ctx->buf_queue_lock, &ctx->dst_buf_queue, 0= )) + core->sched->clear_work(core, core_ctx); + return 0; + } + + if (mfc_qos_mb_calculate(core, core_ctx, mfc_core_get_processing_cycle(),= slice_type)) { + mfc_qos_on(core, ctx); + if (IS_TWO_MODE1(ctx)) { + subcore =3D mfc_get_sub_core(dev, ctx); + if (subcore) { + subcore->core_ctx[ctx->num]->dynamic_weight_level =3D + core_ctx->dynamic_weight_level; + mfc_qos_on(subcore, ctx); + } + } + } + + if (strm_size =3D=3D 0 && !(enc->empty_data && reason =3D=3D MFC_REG_R2H_= CMD_COMPLETE_SEQ_RET)) { + mfc_debug(2, "[FRAME] dst buffer is not returned\n"); + consumed_only =3D 1; + } + + /* handle source buffer */ + __mfc_handle_stream_input(core, ctx, consumed_only); + + /* handle destination buffer */ + if (enc->empty_data && reason =3D=3D MFC_REG_R2H_CMD_COMPLETE_SEQ_RET) { + enc->empty_data =3D 0; + mfc_debug(2, "[FRAME] handle EOS for empty data\n"); + __mfc_handle_stream_last_output(core, ctx); + } else { + __mfc_handle_stream_output(core, ctx, enc->frame_type, strm_size); + } + mfc_rate_update_bufq_framerate(ctx, MFC_TS_DST_DQ); + + /* handle error buffer */ + __mfc_handle_error_input(core, ctx); + + if (ctx->multi_view_enable) + ctx->select_view =3D (ctx->select_view + 1) % MFC_NUM_MULTI_VIEW; + + return 0; +} + +static inline int __mfc_handle_done_frame(struct mfc_core *core, + struct mfc_ctx *ctx, + unsigned int reason, + unsigned int err) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_enc *enc =3D NULL; + + if (ctx->type =3D=3D MFCINST_DECODER) { + __mfc_handle_frame(core, ctx, reason, err); + } else if (ctx->type =3D=3D MFCINST_ENCODER) { + enc =3D ctx->enc_priv; + if (reason =3D=3D MFC_REG_R2H_CMD_SLICE_DONE_RET) { + core->preempt_core_ctx =3D ctx->num; + enc->buf_full =3D 0; + enc->in_slice =3D 1; + } else if (reason =3D=3D MFC_REG_R2H_CMD_ENC_BUFFER_FULL_RET) { + mfc_err("stream buffer size(%d) isn't enough, (Bitrate: %d)\n", + mfc_core_get_enc_strm_size(), + MFC_CORE_RAW_READL(MFC_REG_E_RC_BIT_RATE)); + + core->preempt_core_ctx =3D ctx->num; + enc->buf_full =3D 1; + enc->in_slice =3D 0; + } else { + enc->buf_full =3D 0; + enc->in_slice =3D 0; + } + __mfc_handle_stream(core, ctx, reason); + } + + return 1; } =20 /* Handle header decoder interrupt */ @@ -1387,13 +1811,82 @@ static int __mfc_handle_seq_dec(struct mfc_core *co= re, struct mfc_ctx *ctx) return 0; } =20 +/* Handle header encoder interrupt */ +static int __mfc_handle_seq_enc(struct mfc_core *core, struct mfc_ctx *ctx) +{ + struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_buf *dst_mb; + int ret, index; + + enc->header_size =3D mfc_core_get_enc_strm_size(); + mfc_debug(2, "[STREAM] encoded slice type: %d, header size: %d, display o= rder: %d\n", + mfc_core_get_enc_slice_type(), enc->header_size, + mfc_core_get_enc_pic_count()); + + /* Initialize select_view */ + ctx->select_view =3D MFC_VIEW_ID_MAIN; + + if (!IS_NO_HEADER_GENERATE(ctx, p) && + !(ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1 && core->id =3D=3D MFC_= CORE_SUB)) { + dst_mb =3D mfc_get_del_buf + (ctx, &ctx->dst_buf_queue, MFC_BUF_NO_TOUCH_USED); + if (!dst_mb) { + mfc_err("no dst buffers\n"); + return -EAGAIN; + } + + vb2_set_plane_payload + (&dst_mb->vb.vb2_buf, 0, mfc_core_get_enc_strm_size()); + + index =3D dst_mb->vb.vb2_buf.index; + if (call_bop(ctx, core_get_buf_ctrls, core, ctx, + &ctx->dst_ctrls[index]) < 0) + mfc_err("failed in core_get_buf_ctrls\n"); + call_cop(ctx, update_buf_val, ctx, &ctx->dst_ctrls[index], + V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, HEADER_TAG); + + vb2_buffer_done(&dst_mb->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + + ctx->dpb_count =3D mfc_core_get_enc_dpb_count(); + ctx->scratch_buf_size =3D mfc_core_get_enc_scratch_size(); + ctx->min_dpb_size[0] =3D mfc_core_get_enc_luma_size(); + ctx->min_dpb_size[1] =3D mfc_core_get_enc_chroma_size(); + + /* If the ROI is enabled at SEQ_START, clear ROI_ENABLE bit */ + mfc_core_clear_roi_enable(core); + + if (core_ctx->codec_buffer_allocated) { + mfc_debug(2, "[DRC] previous codec buffer is exist\n"); + + mfc_release_codec_buffers(core_ctx); + } + + ret =3D mfc_alloc_codec_buffers(core_ctx); + if (ret) + mfc_err("Failed to allocate encoding buffers\n"); + + mfc_change_state(core_ctx, MFCINST_HEAD_PARSED); + + return 0; +} + static inline void __mfc_handle_nal_abort(struct mfc_core *core, struct mfc_ctx *ctx, unsigned int reason) { struct mfc_core_ctx *core_ctx =3D core->core_ctx[ctx->num]; + struct mfc_enc *enc =3D ctx->enc_priv; =20 - mfc_change_state(core_ctx, MFCINST_ABORT); + if (ctx->type =3D=3D MFCINST_ENCODER) { + mfc_change_state(core_ctx, MFCINST_RUNNING_BUF_FULL); + enc->buf_full =3D 0; + __mfc_handle_stream(core, ctx, reason); + } else { + mfc_change_state(core_ctx, MFCINST_ABORT); + } } =20 irqreturn_t mfc_core_top_half_irq(int irq, void *priv) @@ -1479,11 +1972,20 @@ static int __mfc_irq_ctx(struct mfc_core *core, case MFC_REG_R2H_CMD_SLICE_DONE_RET: case MFC_REG_R2H_CMD_FIELD_DONE_RET: case MFC_REG_R2H_CMD_FRAME_DONE_RET: + case MFC_REG_R2H_CMD_ENC_BUFFER_FULL_RET: + return __mfc_handle_done_frame(core, ctx, reason, err); case MFC_REG_R2H_CMD_COMPLETE_SEQ_RET: - __mfc_handle_done_frame(core, ctx, reason, err); + if (ctx->type =3D=3D MFCINST_ENCODER) { + __mfc_handle_stream(core, ctx, reason); + mfc_change_state(core_ctx, MFCINST_FINISHED); + } else if (ctx->type =3D=3D MFCINST_DECODER) { + return __mfc_handle_done_frame(core, ctx, reason, err); + } break; case MFC_REG_R2H_CMD_SEQ_DONE_RET: - if (ctx->type =3D=3D MFCINST_DECODER) + if (ctx->type =3D=3D MFCINST_ENCODER) + __mfc_handle_seq_enc(core, ctx); + else if (ctx->type =3D=3D MFCINST_DECODER) __mfc_handle_seq_dec(core, ctx); break; case MFC_REG_R2H_CMD_OPEN_INSTANCE_RET: diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c b/dr= ivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c index e4b839eda2da..aedb3f56035e 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_sync.c @@ -30,7 +30,8 @@ static inline unsigned int __mfc_r2h_bit_mask(int cmd) R2H_BIT(MFC_REG_R2H_CMD_COMPLETE_SEQ_RET) | R2H_BIT(MFC_REG_R2H_CMD_SLICE_DONE_RET) | R2H_BIT(MFC_REG_R2H_CMD_INIT_BUFFERS_RET) | - R2H_BIT(MFC_REG_R2H_CMD_NAL_ABORT_RET)); + R2H_BIT(MFC_REG_R2H_CMD_NAL_ABORT_RET) | + R2H_BIT(MFC_REG_R2H_CMD_ENC_BUFFER_FULL_RET)); /* FIXME: Temporal mask for S3D SEI processing */ else if (cmd =3D=3D MFC_REG_R2H_CMD_INIT_BUFFERS_RET) mask |=3D (R2H_BIT(MFC_REG_R2H_CMD_FIELD_DONE_RET) | @@ -311,6 +312,56 @@ static int __mfc_dec_ctx_ready_set_bit(struct mfc_core= _ctx *core_ctx) return is_ready; } =20 +static int __mfc_enc_ctx_ready_set_bit(struct mfc_core_ctx *core_ctx) +{ + struct mfc_ctx *ctx =3D core_ctx->ctx; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + int is_ready =3D 0; + int src_buf_cnt, dst_buf_cnt; + + src_buf_cnt =3D mfc_get_queue_count(&ctx->buf_queue_lock, &core_ctx->src_= buf_queue); + dst_buf_cnt =3D mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_q= ueue); + + /* context is ready to make header */ + if (core_ctx->state =3D=3D MFCINST_GOT_INST && dst_buf_cnt) { + if (p->seq_hdr_mode =3D=3D V4L2_MPEG_VIDEO_HEADER_MODE_AT_THE_READY) { + if (src_buf_cnt) + is_ready =3D 1; + } else { + is_ready =3D 1; + } + } + + /* In mode1 encoding, MFC1 will run seq_start of dummy. */ + if (core_ctx->state =3D=3D MFCINST_GOT_INST && + (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1 && + core_ctx->core->id =3D=3D MFC_CORE_SUB)) + is_ready =3D 1; + + /* context is ready to allocate DPB */ + else if (core_ctx->state =3D=3D MFCINST_HEAD_PARSED && dst_buf_cnt && + !(ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1 && + core_ctx->core->id =3D=3D MFC_CORE_SUB)) + is_ready =3D 1; + + /* context is ready to encode a frame */ + else if (core_ctx->state =3D=3D MFCINST_RUNNING && + src_buf_cnt && dst_buf_cnt) + is_ready =3D 1; + + /* context is ready to encode a frame for NAL_ABORT command */ + else if (core_ctx->state =3D=3D MFCINST_ABORT_INST && + src_buf_cnt && dst_buf_cnt) + is_ready =3D 1; + + /* context is ready to encode remain frames */ + else if (core_ctx->state =3D=3D MFCINST_FINISHING && dst_buf_cnt) + is_ready =3D 1; + + return is_ready; +} + int mfc_ctx_ready_set_bit_raw(struct mfc_core_ctx *core_ctx, unsigned long= *bits, bool set) { struct mfc_core *core =3D core_ctx->core; @@ -331,7 +382,10 @@ int mfc_ctx_ready_set_bit_raw(struct mfc_core_ctx *cor= e_ctx, unsigned long *bits &ctx->dst_buf_queue), core_ctx->state, ctx->capture_state, ctx->wait_state); =20 - is_ready =3D __mfc_dec_ctx_ready_set_bit(core_ctx); + if (ctx->type =3D=3D MFCINST_DECODER) + is_ready =3D __mfc_dec_ctx_ready_set_bit(core_ctx); + else if (ctx->type =3D=3D MFCINST_ENCODER) + is_ready =3D __mfc_enc_ctx_ready_set_bit(core_ctx); =20 if (is_ready && set) { /* if the ctx is ready and request set_bit, set the work_bit */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c b/driv= ers/media/platform/samsung/exynos-mfc/mfc_debugfs.c index 59cd6f3945ff..8967744b986b 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c @@ -33,15 +33,20 @@ static int __mfc_info_show(struct seq_file *s, void *un= used) dev->pdata->skype.support, dev->pdata->skype.version, dev->pdata->black_bar.support, dev->pdata->black_bar.version); - seq_printf(s, " color_aspect_dec: %d(0x%x)\n", + seq_printf(s, " color_aspect_dec: %d(0x%x), enc: %d(0x%x)\n", dev->pdata->color_aspect_dec.support, - dev->pdata->color_aspect_dec.version); - seq_printf(s, " static_info_dec: %d(0x%x)\n", + dev->pdata->color_aspect_dec.version, + dev->pdata->color_aspect_enc.support, + dev->pdata->color_aspect_enc.version); + seq_printf(s, " static_info_dec: %d(0x%x), enc: %d(0x%x)\n", dev->pdata->static_info_dec.support, - dev->pdata->static_info_dec.version); - seq_printf(s, " [FORMATS] 10bit: %s, 422: %s\n", + dev->pdata->static_info_dec.version, + dev->pdata->static_info_enc.support, + dev->pdata->static_info_enc.version); + seq_printf(s, " [FORMATS] 10bit: %s, 422: %s, RGB: %s\n", dev->pdata->support_10bit ? "supported" : "not supported", - dev->pdata->support_422 ? "supported" : "not supported"); + dev->pdata->support_422 ? "supported" : "not supported", + dev->pdata->support_rgb ? "supported" : "not supported"); seq_printf(s, " [LOWMEM] is_low_mem: %d\n", IS_LOW_MEM); =20 for (j =3D 0; j < dev->num_core; j++) { --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout2.samsung.com (mailout2.samsung.com [203.254.224.25]) (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 9BB55285CAF for ; Tue, 30 Sep 2025 03:57:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204640; cv=none; b=qrhBFoNgi/QzEUYzp2QAgTGO8s8dFLZq2sbGlzH9wQiCTAE7Lj6GXcZQhk7fzXNjLLuQZN3LxwGzkuzkAsv5gFFZU5nkdDr6H99im6dwcan+d0Xx33hiVfzwbmKTuZ/7V6Hgo9bW5npSM58BIWCahvd5vd+tjMKyJlh20R+1e5Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204640; c=relaxed/simple; bh=OkUvqr5qulJhQgIrYr3xhyu50fuSlmvJI3ESvK9EkDY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=ETh28PhB0fCZ5LngFSTkrCmAgN54tgjQQCKVE1Q11pSxJ9+QbO8DCpgYOxXMJPGE+RtaPNRObuUqvt1R/HEe20WcXQwl6w0IJthCR3Zb6dorS1+1pUHIQWziHMFrircA9Q/H0uJTtZ8xbeLShl2hOPtmkH9jZnPnMl6UgRAjnGQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=G/ayGLZg; arc=none smtp.client-ip=203.254.224.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="G/ayGLZg" Received: from epcas5p2.samsung.com (unknown [182.195.41.40]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20250930035713epoutp025d51086d773c6edfb94370c6ce57ce97~p80tATb7F2603326033epoutp02M for ; Tue, 30 Sep 2025 03:57:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20250930035713epoutp025d51086d773c6edfb94370c6ce57ce97~p80tATb7F2603326033epoutp02M DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204634; bh=tH//wEKkPD0mN/XZ520+YyflOs5F52gB0k8S3h5mZBU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=G/ayGLZgGahhf2uhehlA/hudw6NRUO7uLu/jFYNerpSAx+/wd7K35PsUwZOErbJo+ jC9DUIwmEwqiVJKty9N5+tW6E7ni8UqVRRLd6t+tIR5iy7N+tWfQwzTTeImDUSDY5t PbwVUZiKSOVMCKkL6SPypr7Sh1pD1AJyLu4ky2Uw= Received: from epsnrtp02.localdomain (unknown [182.195.42.154]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPS id 20250930035713epcas5p478f4a650374a4e43bc194fddcf2de024~p80sdri5X2850128501epcas5p4D; Tue, 30 Sep 2025 03:57:13 +0000 (GMT) Received: from epcas5p2.samsung.com (unknown [182.195.38.94]) by epsnrtp02.localdomain (Postfix) with ESMTP id 4cbPQJ2j9Tz2SSKX; Tue, 30 Sep 2025 03:57:12 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p4.samsung.com (KnoxPortal) with ESMTPA id 20250930035711epcas5p437e54e8c277ee98149fccb2d984a0e55~p80rBiQCq2850128501epcas5p4-; Tue, 30 Sep 2025 03:57:11 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035707epsmtip12fab77d7330844e0539970d2f0511476~p80mvuVcm2931929319epsmtip1B; Tue, 30 Sep 2025 03:57:06 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 28/29] media: mfc: Add AVC, VP8, VP9, and HEVC encoding support Date: Tue, 30 Sep 2025 09:33:47 +0530 Message-Id: <20250930040348.3702923-29-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035711epcas5p437e54e8c277ee98149fccb2d984a0e55 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035711epcas5p437e54e8c277ee98149fccb2d984a0e55 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Introduce generic codec=E2=80=91mode macros for MPEG=E2=80=914, VP8, VP9,= HEVC - Extend 4:2:2 format handling and IDR=E2=80=91frame processing - Suppress IVF headers for VP8/VP9 when disabled - Enable multi=E2=80=91core processing for HEVC and refine instance opening= flow - Expand mfc_enc_params with codec=E2=80=91specific fields and raise resolution limits - Add new V4L2 controls (profiles, levels, QP, hierarchical coding, golden frames, etc.) - Implement VP9 HDR, colour=E2=80=91space mapping, HEVC ROI, and extra debug logging Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_common.h | 18 +- .../samsung/exynos-mfc/base/mfc_data_struct.h | 127 ++++ .../samsung/exynos-mfc/mfc_core_enc_param.c | 649 +++++++++++++++++- .../samsung/exynos-mfc/mfc_enc_v4l2.c | 439 +++++++++++- 4 files changed, 1211 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h index bec6f88d5e44..30865588b69a 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_common.h @@ -172,6 +172,11 @@ =20 /* Encoder codec mode check */ #define IS_H264_ENC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_H264_ENC) +#define IS_MPEG4_ENC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_MPEG4_EN= C) +#define IS_H263_ENC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_H263_ENC) +#define IS_VP8_ENC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_VP8_ENC) +#define IS_HEVC_ENC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_HEVC_ENC) +#define IS_VP9_ENC(ctx) ((ctx)->codec_mode =3D=3D MFC_REG_CODEC_VP9_ENC) =20 #define CODEC_NOT_CODED(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ @@ -195,7 +200,8 @@ =20 #define CODEC_422FORMAT(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ - (IS_HEVC_DEC(_ctx) || IS_VP9_DEC(_ctx)); \ + (IS_HEVC_DEC(_ctx) || IS_HEVC_ENC(_ctx) || IS_VP9_DEC(_ctx) || \ + IS_VP9_ENC(_ctx)); \ }) #define ON_RES_CHANGE(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ @@ -221,7 +227,7 @@ #define CODEC_HAS_IDR(ctx) ({ \ typeof(ctx) _ctx =3D (ctx); \ (IS_H264_DEC(_ctx) || IS_H264_MVC_DEC(_ctx) || IS_HEVC_DEC(_ctx) || \ - IS_H264_ENC(_ctx)); \ + IS_H264_ENC(_ctx) || IS_HEVC_ENC(_ctx)); \ }) =20 // Buffer container @@ -229,7 +235,8 @@ #define IS_NO_HEADER_GENERATE(ctx, p) ({ \ typeof(ctx) _ctx =3D (ctx); \ typeof(p) _p =3D (p); \ - (_p->seq_hdr_mode =3D=3D V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAM= E); \ + ((_p->seq_hdr_mode =3D=3D V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRA= ME) || \ + ((IS_VP8_ENC(_ctx) || IS_VP9_ENC(_ctx)) && _p->ivf_header_disable)); \ }) =20 /* @@ -332,6 +339,11 @@ (OVER_UHD_RES(_ctx) && mfc_is_enc_bframe(_ctx))); \ }) =20 +#define IS_MULTI_MODE_ENC_CONDITION(ctx) ({ \ + typeof(ctx) _ctx =3D (ctx); \ + ((IS_HEVC_ENC(_ctx) && IS_MULTI_MODE_ENC_RES(_ctx))); \ +}) + #define IS_BLACKBAR_OFF(ctx) ((ctx)->crop_height > 2160) =20 #define IS_SINGLE_FD(ctx, fmt) ((!(ctx)->rgb_bpp) && ((fmt)->mem_planes = =3D=3D 1)) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct= .h b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h index 6d34905a1cba..cb20b19b75cc 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_data_struct.h @@ -1246,6 +1246,129 @@ struct mfc_h264_enc_params { u32 vui_enable; }; =20 +/** + * + */ +struct mfc_mpeg4_enc_params { + /* MPEG4 Only */ + enum v4l2_mpeg_video_mpeg4_profile profile; + u8 level; + u8 quarter_pixel; + u8 rc_b_frame_qp; + /* Common for MPEG4, H263 */ + u8 rc_frame_qp; + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_min_qp_p; + u8 rc_max_qp_p; + u8 rc_min_qp_b; + u8 rc_max_qp_b; + u8 rc_p_frame_qp; + u16 vop_frm_delta; +}; + +/** + * + */ +struct mfc_vp9_enc_params { + /* VP9 Only */ + u8 profile; + u8 level; + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_min_qp_p; + u8 rc_max_qp_p; + u8 rc_frame_qp; + u8 rc_p_frame_qp; + u16 vp9_gfrefreshperiod; + u8 vp9_goldenframesel; + u8 hier_qp_enable; + u8 num_hier_layer; + u8 hier_qp_layer[3]; + u32 hier_bit_layer[3]; + u8 max_partition_depth; + u8 intra_pu_split_disable; +}; + +/** + * + */ +struct mfc_vp8_enc_params { + /* VP8 Only */ + u8 vp8_version; + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_min_qp_p; + u8 rc_max_qp_p; + u8 rc_frame_qp; + u8 rc_p_frame_qp; + u8 vp8_numberofpartitions; + u8 vp8_filterlevel; + u8 vp8_filtersharpness; + u16 vp8_gfrefreshperiod; + u8 vp8_goldenframesel; + u8 intra_4x4mode_disable; + u8 num_hier_layer; + u8 hier_qp_enable; + u8 hier_qp_layer[3]; + u32 hier_bit_layer[3]; +}; + +/** + * + */ +struct mfc_hevc_enc_params { + u8 profile; + u8 level; + u8 tier_flag; + /* HEVC Only */ + u8 rc_min_qp; + u8 rc_max_qp; + u8 rc_min_qp_p; + u8 rc_max_qp_p; + u8 rc_min_qp_b; + u8 rc_max_qp_b; + u8 rc_lcu_dark; + u8 rc_lcu_smooth; + u8 rc_lcu_static; + u8 rc_lcu_activity; + u8 rc_frame_qp; + u8 rc_p_frame_qp; + u8 rc_b_frame_qp; + u8 max_partition_depth; + u8 refreshtype; + u16 refreshperiod; + s32 lf_beta_offset_div2; + s32 lf_tc_offset_div2; + u8 loopfilter_disable; + u8 loopfilter_across; + u8 nal_control_length_filed; + u8 nal_control_user_ref; + u8 nal_control_store_ref; + u8 const_intra_period_enable; + u8 lossless_cu_enable; + u8 wavefront_enable; + enum v4l2_mpeg_video_hevc_hier_coding_type hier_qp_type; + u8 enable_ltr; + u8 hier_qp_enable; + u8 hier_ref_type; + u8 num_hier_layer; + u32 hier_bit_layer[7]; + u8 hier_qp_layer[7]; + u8 general_pb_enable; + u8 temporal_id_enable; + u8 strong_intra_smooth; + u8 intra_pu_split_disable; + u8 tmv_prediction_disable; + u8 max_num_merge_mv; + u8 eco_mode_enable; + u8 encoding_nostartcode_enable; + u8 size_of_length_field; + u8 user_ref; + u8 store_ref; + u8 prepend_sps_pps_to_idr; +}; + /** * */ @@ -1327,6 +1450,10 @@ struct mfc_enc_params { =20 union { struct mfc_h264_enc_params h264; + struct mfc_mpeg4_enc_params mpeg4; + struct mfc_vp8_enc_params vp8; + struct mfc_vp9_enc_params vp9; + struct mfc_hevc_enc_params hevc; } codec; }; =20 diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.c= b/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.c index 9ff949df04ab..aea8d9c9b7ea 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_enc_param.c @@ -19,6 +19,20 @@ #define CBR_I_LIMIT_WFD 6 #define CBR_I_LIMIT_MAX 5 =20 +static int mfc_colorspace_to_rgb_format_ctrl[][2] =3D { + { MFC_COLORSPACE_UNSPECIFICED, 1}, /* Unknown */ + { MFC_COLORSPACE_BT601, 0}, /* Rec. ITU-R BT.601-7 */ + { MFC_COLORSPACE_BT709, 1}, /* Rec. ITU-R BT.709-6 */ + { MFC_COLORSPACE_SMPTE_170, 0}, /* SMPTE-170 */ + { MFC_COLORSPACE_SMPTE_240, 0}, /* SMPTE-240 */ + { MFC_COLORSPACE_BT2020, 1}, /* Rec. ITU-R BT.2020-2 */ + { MFC_COLORSPACE_RESERVED, 1}, /* Reserved */ + { MFC_COLORSPACE_SRGB, 1}, /* sRGB (IEC 61966-2-1) */ + { MFC_COLORSPACE_UNSPECIFICED, 1}, /* Unknown */ + { MFC_COLORSPACE_UNSPECIFICED, 1}, /* Unknown */ + { MFC_COLORSPACE_UNSPECIFICED, 1}, /* Unknown */ +}; + static int mfc_transfer_to_rgb_format_ctrl[][2] =3D { { MFC_TRANSFER_RESERVED, 1}, { MFC_TRANSFER_BT709, 1}, @@ -226,6 +240,11 @@ static int __mfc_get_rgb_format_ctrl(struct mfc_ctx *c= tx, struct mfc_enc_params if (ctx->dev->pdata->enc_rgb_csc_by_fw) { ret =3D 3; mfc_ctx_debug(2, "[RGB] coefficients of CSC formula using VUI by F/W\n"); + } else if (IS_VP9_ENC(ctx)) { + ret =3D mfc_colorspace_to_rgb_format_ctrl[p->colour_primaries][1]; + mfc_ctx_debug(2, "[RGB] VP9 color space %d converts to RGB format ctrl %= s\n", + p->colour_primaries, ret ? "BT.709" : "BT.601"); + } else { ret =3D mfc_transfer_to_rgb_format_ctrl[p->transfer_characteristics][1]; mfc_ctx_debug(2, "[RGB] transfer %d converts to RGB format ctrl %s\n", @@ -246,7 +265,8 @@ static void __mfc_set_video_signal_type(struct mfc_core= *core, struct mfc_ctx *c /* VIDEO_SIGNAL_TYPE_FLAG */ mfc_set_bits(reg, 0x1, 31, 0x1); /* COLOUR_DESCRIPTION_PRESENT_FLAG */ - mfc_set_bits(reg, 0x1, 24, 0x1); + if (!IS_VP9_ENC(ctx)) + mfc_set_bits(reg, 0x1, 24, 0x1); } else if (MFC_FEATURE_SUPPORT(dev, dev->pdata->color_aspect_enc) && p->check_color_range) { /* VIDEO_SIGNAL_TYPE_FLAG */ @@ -254,23 +274,29 @@ static void __mfc_set_video_signal_type(struct mfc_co= re *core, struct mfc_ctx *c /* COLOR_RANGE */ if (!(ctx->src_fmt->type & MFC_FMT_RGB)) mfc_set_bits(reg, 0x1, 25, p->color_range); - - if (p->colour_primaries && - p->transfer_characteristics && - p->matrix_coefficients !=3D 3) { - /* COLOUR_DESCRIPTION_PRESENT_FLAG */ - mfc_set_bits(reg, 0x1, 24, 0x1); - /* COLOUR_PRIMARIES */ - mfc_set_bits(reg, 0xFF, 16, p->colour_primaries); - /* TRANSFER_CHARACTERISTICS */ - mfc_set_bits(reg, 0xFF, 8, p->transfer_characteristics); - /* MATRIX_COEFFICIENTS */ - mfc_set_bits(reg, 0xFF, 0, p->matrix_coefficients); + if (IS_VP9_ENC(ctx)) { + /* COLOR_SPACE: VP9 uses colour_primaries interface for color space */ + mfc_set_bits(reg, 0x1F, 26, p->colour_primaries); + mfc_ctx_debug(2, "[HDR] VP9 ENC Color aspect: range(%s), space(%d)\n", + p->color_range ? "Full" : "Limited", p->colour_primaries); + } else { + if (p->colour_primaries && + p->transfer_characteristics && + p->matrix_coefficients !=3D 3) { + /* COLOUR_DESCRIPTION_PRESENT_FLAG */ + mfc_set_bits(reg, 0x1, 24, 0x1); + /* COLOUR_PRIMARIES */ + mfc_set_bits(reg, 0xFF, 16, p->colour_primaries); + /* TRANSFER_CHARACTERISTICS */ + mfc_set_bits(reg, 0xFF, 8, p->transfer_characteristics); + /* MATRIX_COEFFICIENTS */ + mfc_set_bits(reg, 0xFF, 0, p->matrix_coefficients); + } + mfc_ctx_debug(2, "[HDR] %s ENC Color aspect: range(%s), pri(%d), trans(= %d), mat(%d)\n", + IS_HEVC_ENC(ctx) ? "HEVC" : "H264", + p->color_range ? "Full" : "Limited", p->colour_primaries, + p->transfer_characteristics, p->matrix_coefficients); } - mfc_ctx_debug(2, "[HDR] %s ENC Color aspect: range(%s), pri(%d), trans(%= d), mat(%d)\n", - "H264", - p->color_range ? "Full" : "Limited", p->colour_primaries, - p->transfer_characteristics, p->matrix_coefficients); } MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VIDEO_SIGNAL_TYPE); } @@ -510,6 +536,9 @@ static void __mfc_set_enc_params(struct mfc_core *core,= struct mfc_ctx *ctx) if (IS_H264_ENC(ctx)) p->codec.h264.hier_qp_type =3D V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P; + else if (IS_HEVC_ENC(ctx)) + p->codec.hevc.hier_qp_type =3D + V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P; mfc_ctx_info("forcely can't be use B frame for 8K or 4K %d fps\n", fps); } if (p->num_refs_for_p > 1) { @@ -829,10 +858,596 @@ static void __mfc_set_enc_params_h264(struct mfc_cor= e *core, mfc_ctx_debug_leave(); } =20 +static void __mfc_set_enc_params_mpeg4(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_mpeg4_enc_params *p_mpeg4 =3D &p->codec.mpeg4; + unsigned int reg =3D 0; + + mfc_ctx_debug_enter(); + + p->rc_framerate_res =3D FRAME_RATE_RESOLUTION; + __mfc_set_enc_params(core, ctx); + + /* set gop_size with I_FRM_CTRL mode */ + __mfc_set_gop_size(core, ctx, 1); + + /* profile & level */ + reg =3D 0; + /** level */ + mfc_set_bits(reg, 0xFF, 8, p_mpeg4->level); + /** profile - 0 ~ 1 */ + mfc_set_bits(reg, 0x3F, 0, p_mpeg4->profile); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_PICTURE_PROFILE); + + /* quarter_pixel */ + /* MFC_CORE_RAW_WRITEL(p_mpeg4->quarter_pixel, MFC_REG_ENC_MPEG4_QUART_PX= L); */ + + /* qp */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_FIXED_PICTURE_QP); + mfc_clear_set_bits(reg, 0xFF, 24, p->config_qp); + mfc_clear_set_bits(reg, 0xFF, 16, p_mpeg4->rc_b_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 8, p_mpeg4->rc_p_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_FIXED_PICTURE_QP); + + /* rate control config. */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG); + /** frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_CONFIG); + + /* max & min value of QP for I frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND); + /** max I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_mpeg4->rc_max_qp); + /** min I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_min_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND); + + /* max & min value of QP for P/B frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND_PB); + /** max B frame QP */ + mfc_clear_set_bits(reg, 0xFF, 24, p_mpeg4->rc_max_qp_b); + /** min B frame QP */ + mfc_clear_set_bits(reg, 0xFF, 16, p_mpeg4->rc_min_qp_b); + /** max P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_mpeg4->rc_max_qp_p); + /** min P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_min_qp_p); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND_PB); + + /* initialize for '0' only setting*/ + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_MPEG4_OPTIONS); /* SEQ_start only */ + MFC_CORE_RAW_WRITEL(0x0, MFC_REG_E_MPEG4_HEC_PERIOD); /* SEQ_start only */ + + mfc_ctx_debug_leave(); +} + +static void __mfc_set_enc_params_h263(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_mpeg4_enc_params *p_mpeg4 =3D &p->codec.mpeg4; + unsigned int reg =3D 0; + + mfc_ctx_debug_enter(); + + /* For H.263 only 8 bit is used and maximum value can be 0xFF */ + p->rc_framerate_res =3D U8_MAX; + __mfc_set_enc_params(core, ctx); + + /* set gop_size with I_FRM_CTRL mode */ + __mfc_set_gop_size(core, ctx, 1); + + /* profile & level: supports only baseline profile Level 70 */ + + /* qp */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_FIXED_PICTURE_QP); + mfc_clear_set_bits(reg, 0xFF, 24, p->config_qp); + mfc_clear_set_bits(reg, 0xFF, 8, p_mpeg4->rc_p_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_FIXED_PICTURE_QP); + + /* rate control config. */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG); + /** frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_CONFIG); + + /* max & min value of QP for I frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND); + /** max I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_mpeg4->rc_max_qp); + /** min I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_min_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND); + + /* max & min value of QP for P/B frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND_PB); + /** max P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_mpeg4->rc_max_qp_p); + /** min P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_mpeg4->rc_min_qp_p); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND_PB); + + mfc_ctx_debug_leave(); +} + +static void __mfc_set_enc_params_vp8(struct mfc_core *core, struct mfc_ctx= *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_vp8_enc_params *p_vp8 =3D &p->codec.vp8; + unsigned int reg =3D 0; + int i; + + mfc_ctx_debug_enter(); + + p->rc_framerate_res =3D FRAME_RATE_RESOLUTION; + __mfc_set_enc_params(core, ctx); + + if (p_vp8->num_hier_layer & 0x3) { + /* set gop_size without i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 0); + } else { + /* set gop_size with i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 1); + } + + /* profile*/ + reg =3D 0; + mfc_set_bits(reg, 0xF, 0, p_vp8->vp8_version); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_PICTURE_PROFILE); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_VP8_OPTION); + /* if num_refs_for_p is 2, the performance falls by half */ + mfc_clear_set_bits(reg, 0x1, 0, (p->num_refs_for_p - 1)); + /* vp8 partition is possible as below value: 1/2/4/8 */ + if (p_vp8->vp8_numberofpartitions & 0x1) { + if (p_vp8->vp8_numberofpartitions > 1) + mfc_ctx_err("partition should be even num (%d)\n", + p_vp8->vp8_numberofpartitions); + p_vp8->vp8_numberofpartitions =3D (p_vp8->vp8_numberofpartitions & ~0x1); + } + mfc_clear_set_bits(reg, 0xF, 3, p_vp8->vp8_numberofpartitions); + mfc_clear_set_bits(reg, 0x1, 10, p_vp8->intra_4x4mode_disable); + /* Temporal SVC - hier qp enable */ + mfc_clear_set_bits(reg, 0x1, 11, p_vp8->hier_qp_enable); + /* Disable IVF header */ + mfc_clear_set_bits(reg, 0x1, 12, p->ivf_header_disable); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VP8_OPTION); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_VP8_GOLDEN_FRAME_OPTION); + mfc_clear_set_bits(reg, 0x1, 0, p_vp8->vp8_goldenframesel); + mfc_clear_set_bits(reg, 0xFFFF, 1, p_vp8->vp8_gfrefreshperiod); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VP8_GOLDEN_FRAME_OPTION); + + /* Temporal SVC - layer number */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_NUM_T_LAYER); + mfc_clear_set_bits(reg, 0x7, 0, p_vp8->num_hier_layer); + mfc_clear_set_bits(reg, 0x7, 4, 0x3); + mfc_clear_set_bits(reg, 0x1, 8, p->hier_bitrate_ctrl); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_NUM_T_LAYER); + mfc_ctx_debug(3, "[HIERARCHICAL] hier_qp_enable %d, num_hier_layer %d, NU= M_T_LAYER 0x%x\n", + p_vp8->hier_qp_enable, p_vp8->num_hier_layer, reg); + + /* QP & Bitrate for each layer */ + for (i =3D 0; i < 3; i++) { + MFC_CORE_RAW_WRITEL(p_vp8->hier_qp_layer[i], + MFC_REG_E_HIERARCHICAL_QP_LAYER0 + i * 4); + /* If hier_bitrate_ctrl is set to 1, this is meaningless */ + MFC_CORE_RAW_WRITEL(p_vp8->hier_bit_layer[i], + MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4); + mfc_ctx_debug(3, "[HIERARCHICAL] layer[%d] QP: %#x, bitrate: %#x(FW ctrl= : %d)\n", + i, p_vp8->hier_qp_layer[i], + p_vp8->hier_bit_layer[i], p->hier_bitrate_ctrl); + } + + reg =3D 0; + mfc_set_bits(reg, 0x7, 0, p_vp8->vp8_filtersharpness); + mfc_set_bits(reg, 0x3F, 8, p_vp8->vp8_filterlevel); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VP8_FILTER_OPTION); + + /* qp */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_FIXED_PICTURE_QP); + mfc_clear_set_bits(reg, 0xFF, 24, p->config_qp); + mfc_clear_set_bits(reg, 0xFF, 8, p_vp8->rc_p_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 0, p_vp8->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_FIXED_PICTURE_QP); + + /* rate control config. */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG); + /** frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_vp8->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_CONFIG); + + /* max & min value of QP for I frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND); + /** max I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_vp8->rc_max_qp); + /** min I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_vp8->rc_min_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND); + + /* max & min value of QP for P/B frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND_PB); + /** max P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_vp8->rc_max_qp_p); + /** min P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_vp8->rc_min_qp_p); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND_PB); + + mfc_ctx_debug_leave(); +} + +static void __mfc_enc_check_vp9_profile(struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_vp9_enc_params *p_vp9 =3D &p->codec.vp9; + + if (!ctx->is_422) { + /* YUV420 8bit format */ + if (p_vp9->profile !=3D MFC_REG_E_PROFILE_VP9_PROFILE0) { + mfc_ctx_err("4:2:0 format is not matched with profile(%d)\n", + p_vp9->profile); + p_vp9->profile =3D MFC_REG_E_PROFILE_VP9_PROFILE0; + } + } else if (ctx->is_422) { + /* YUV422 8bit format */ + if (p_vp9->profile !=3D MFC_REG_E_PROFILE_VP9_PROFILE1) { + mfc_ctx_err("4:2:2 format is not matched with profile(%d)\n", + p_vp9->profile); + p_vp9->profile =3D MFC_REG_E_PROFILE_VP9_PROFILE1; + } + } +} + +static void __mfc_set_enc_params_vp9(struct mfc_core *core, struct mfc_ctx= *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_vp9_enc_params *p_vp9 =3D &p->codec.vp9; + unsigned int reg =3D 0; + int i; + + mfc_ctx_debug_enter(); + + p->rc_framerate_res =3D FRAME_RATE_RESOLUTION; + __mfc_set_enc_params(core, ctx); + __mfc_enc_check_vp9_profile(ctx); + + if (p_vp9->num_hier_layer & 0x3) { + /* set gop_size without i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 0); + } else { + /* set gop_size with i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 1); + } + + /* profile*/ + reg =3D 0; + mfc_set_bits(reg, 0xF, 0, p_vp9->profile); + /* level */ + mfc_set_bits(reg, 0xFF, 8, p_vp9->level); + + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_PICTURE_PROFILE); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_VP9_OPTION); + /* if num_refs_for_p is 2, the performance falls by half */ + mfc_clear_set_bits(reg, 0x1, 0, (p->num_refs_for_p - 1)); + mfc_clear_set_bits(reg, 0x1, 1, p_vp9->intra_pu_split_disable); + mfc_clear_set_bits(reg, 0x1, 3, p_vp9->max_partition_depth); + /* Temporal SVC - hier qp enable */ + mfc_clear_set_bits(reg, 0x1, 11, p_vp9->hier_qp_enable); + /* Disable IVF header */ + mfc_clear_set_bits(reg, 0x1, 12, p->ivf_header_disable); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VP9_OPTION); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_VP9_GOLDEN_FRAME_OPTION); + mfc_clear_set_bits(reg, 0x1, 0, p_vp9->vp9_goldenframesel); + mfc_clear_set_bits(reg, 0xFFFF, 1, p_vp9->vp9_gfrefreshperiod); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_VP9_GOLDEN_FRAME_OPTION); + + /* Temporal SVC - layer number */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_NUM_T_LAYER); + mfc_clear_set_bits(reg, 0x7, 0, p_vp9->num_hier_layer); + mfc_clear_set_bits(reg, 0x7, 4, 0x3); + mfc_clear_set_bits(reg, 0x1, 8, p->hier_bitrate_ctrl); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_NUM_T_LAYER); + mfc_ctx_debug(3, "[HIERARCHICAL] hier_qp_enable %d, num_hier_layer %d, NU= M_T_LAYER 0x%x\n", + p_vp9->hier_qp_enable, p_vp9->num_hier_layer, reg); + + /* QP & Bitrate for each layer */ + for (i =3D 0; i < 3; i++) { + MFC_CORE_RAW_WRITEL(p_vp9->hier_qp_layer[i], + MFC_REG_E_HIERARCHICAL_QP_LAYER0 + i * 4); + /* If hier_bitrate_ctrl is set to 1, this is meaningless */ + MFC_CORE_RAW_WRITEL(p_vp9->hier_bit_layer[i], + MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4); + mfc_ctx_debug(3, "[HIERARCHICAL] layer[%d] QP: %#x, bitrate: %#x (FW ctr= l: %d)\n", + i, p_vp9->hier_qp_layer[i], + p_vp9->hier_bit_layer[i], p->hier_bitrate_ctrl); + } + + /* qp */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_FIXED_PICTURE_QP); + mfc_clear_set_bits(reg, 0xFF, 24, p->config_qp); + mfc_clear_set_bits(reg, 0xFF, 8, p_vp9->rc_p_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 0, p_vp9->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_FIXED_PICTURE_QP); + + /* rate control config. */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG); + /** frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_vp9->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_CONFIG); + + /* max & min value of QP for I frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND); + /** max I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_vp9->rc_max_qp); + /** min I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_vp9->rc_min_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND); + + /* max & min value of QP for P/B frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND_PB); + /** max P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_vp9->rc_max_qp_p); + /** min P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_vp9->rc_min_qp_p); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND_PB); + + mfc_ctx_debug_leave(); +} + +static void __mfc_enc_check_hevc_profile(struct mfc_ctx *ctx) +{ + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_hevc_enc_params *p_hevc =3D &p->codec.hevc; + + if (!ctx->is_422) { + /* YUV420 8bit format */ + if (p_hevc->profile !=3D MFC_REG_E_PROFILE_HEVC_MAIN && + p_hevc->profile !=3D MFC_REG_E_PROFILE_HEVC_MAIN_10) { + mfc_ctx_err("4:2:0 format is not matched with profile(%d)\n", + p_hevc->profile); + p_hevc->profile =3D MFC_REG_E_PROFILE_HEVC_MAIN; + } + } else if (ctx->is_422) { + /* YUV422 8bit format */ + if (p_hevc->profile !=3D MFC_REG_E_PROFILE_HEVC_MAIN_422_10_INTRA && + p_hevc->profile !=3D MFC_REG_E_PROFILE_HEVC_MAIN_422_10) { + mfc_ctx_err("4:2:2 format is not matched with profile(%d)\n", + p_hevc->profile); + p_hevc->profile =3D MFC_REG_E_PROFILE_HEVC_MAIN_422_10; + } + } +} + +static void __mfc_set_enc_params_hevc(struct mfc_core *core, + struct mfc_ctx *ctx) +{ + struct mfc_dev *dev =3D ctx->dev; + struct mfc_enc *enc =3D ctx->enc_priv; + struct mfc_enc_params *p =3D &enc->params; + struct mfc_hevc_enc_params *p_hevc =3D &p->codec.hevc; + unsigned int mb =3D 0; + unsigned int reg =3D 0; + bool enable_stream_copy =3D false; + int i; + + mfc_ctx_debug_enter(); + + p->rc_framerate_res =3D FRAME_RATE_RESOLUTION; + __mfc_set_enc_params(core, ctx); + __mfc_enc_check_hevc_profile(ctx); + + if (p_hevc->num_hier_layer & 0x7) { + /* set gop_size without i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 0); + } else { + /* set gop_size with i_frm_ctrl mode */ + __mfc_set_gop_size(core, ctx, 1); + } + + mb =3D WIDTH_MB((ctx)->crop_width) * HEIGHT_MB((ctx)->crop_height); + /* Level 6.0 case */ + if (IS_LV60_MB(mb) && p_hevc->level < 60) + mfc_ctx_info("This resolution(mb: %d) recommends level6.0\n", mb); + + /* Level 5.1 case */ + if (IS_LV51_MB(mb) && p_hevc->level < 51) + mfc_ctx_info("This resolution(mb: %d) recommends level5.1\n", mb); + + /* tier_flag & level & profile */ + reg =3D 0; + + /* profile */ + if (ctx->multi_view_enable) + mfc_set_bits(reg, 0xF, 0, 0x6); + else + mfc_set_bits(reg, 0xF, 0, p_hevc->profile); + + /* level */ + mfc_set_bits(reg, 0xFF, 8, p_hevc->level); + /* tier_flag - 0 ~ 1 */ + mfc_set_bits(reg, 0x1, 16, p_hevc->tier_flag); + + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_PICTURE_PROFILE); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_HEVC_OPTIONS); + /* max partition depth */ + mfc_clear_set_bits(reg, 0x3, 0, p_hevc->max_partition_depth); + /* if num_refs_for_p is 2, the performance falls by half */ + mfc_clear_set_bits(reg, 0x1, 2, (p->num_refs_for_p - 1)); + mfc_clear_set_bits(reg, 0x3, 3, p_hevc->refreshtype); + mfc_clear_set_bits(reg, 0x1, 5, p_hevc->const_intra_period_enable); + mfc_clear_set_bits(reg, 0x1, 6, p_hevc->lossless_cu_enable); + mfc_clear_set_bits(reg, 0x1, 7, p_hevc->wavefront_enable); + mfc_clear_set_bits(reg, 0x1, 8, p_hevc->loopfilter_disable); + mfc_clear_set_bits(reg, 0x1, 9, p_hevc->loopfilter_across); + mfc_clear_set_bits(reg, 0x1, 10, p_hevc->enable_ltr); + mfc_clear_set_bits(reg, 0x1, 11, p_hevc->hier_qp_enable); + mfc_clear_set_bits(reg, 0x1, 13, p_hevc->general_pb_enable); + mfc_clear_set_bits(reg, 0x1, 14, p_hevc->temporal_id_enable); + mfc_clear_set_bits(reg, 0x1, 15, p_hevc->strong_intra_smooth); + mfc_clear_set_bits(reg, 0x1, 16, p_hevc->intra_pu_split_disable); + mfc_clear_set_bits(reg, 0x1, 17, p_hevc->tmv_prediction_disable); + mfc_clear_set_bits(reg, 0x7, 18, p_hevc->max_num_merge_mv); + mfc_clear_set_bits(reg, 0x1, 23, p_hevc->encoding_nostartcode_enable); + mfc_clear_set_bits(reg, 0x1, 26, p_hevc->prepend_sps_pps_to_idr); + + /* Weighted Prediction enable */ + mfc_clear_set_bits(reg, 0x1, 28, p->weighted_enable); + /* 30bit is 32x32 transform. If it is enabled, the performance falls by h= alf */ + mfc_clear_bits(reg, 0x1, 30); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_HEVC_OPTIONS); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_HEVC_OPTIONS_2); + /* Timing info */ + mfc_set_bits(reg, 0x1, 2, p->timing_info_enable); + + // TODO : replace this magic number to define + mfc_clear_set_bits(reg, 0x1, 9, (ctx->stream_op_mode =3D=3D MFC_OP_TWO_MO= DE1)); + enable_stream_copy =3D ((ctx->stream_op_mode =3D=3D MFC_OP_TWO_MODE1) && + !(dev->debugfs.feature_option & MFC_OPTION_STREAM_COPY_DISABLE)); + mfc_clear_set_bits(reg, 0x1, 13, enable_stream_copy); + + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_HEVC_OPTIONS_2); + + /* refresh period */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_HEVC_REFRESH_PERIOD); + mfc_clear_set_bits(reg, 0xFFFF, 0, p_hevc->refreshperiod); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_HEVC_REFRESH_PERIOD); + /* loop filter setting */ + MFC_CORE_RAW_WRITEL(0, MFC_REG_E_HEVC_LF_BETA_OFFSET_DIV2); + MFC_CORE_RAW_WRITEL(0, MFC_REG_E_HEVC_LF_TC_OFFSET_DIV2); + if (!p_hevc->loopfilter_disable) { + MFC_CORE_RAW_WRITEL + (p_hevc->lf_beta_offset_div2, MFC_REG_E_HEVC_LF_BETA_OFFSET_DIV2); + MFC_CORE_RAW_WRITEL + (p_hevc->lf_tc_offset_div2, MFC_REG_E_HEVC_LF_TC_OFFSET_DIV2); + } + /* long term reference */ + if (p_hevc->enable_ltr) { + reg =3D 0; + mfc_set_bits(reg, 0x3, 0, p_hevc->store_ref); + mfc_set_bits(reg, 0x3, 2, p_hevc->user_ref); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_HEVC_NAL_CONTROL); + } + + /* Temporal SVC - qp type, layer number */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_NUM_T_LAYER); + mfc_clear_set_bits(reg, 0x1, 3, p_hevc->hier_qp_type); + mfc_clear_set_bits(reg, 0x7, 0, p_hevc->num_hier_layer); + mfc_clear_bits(reg, 0x7, 4); + if (p_hevc->hier_ref_type) { + mfc_set_bits(reg, 0x1, 7, 0x1); + mfc_set_bits(reg, 0x7, 4, p->num_hier_max_layer); + } else { + mfc_clear_bits(reg, 0x1, 7); + mfc_set_bits(reg, 0x7, 4, p_hevc->num_hier_layer); + } + mfc_clear_set_bits(reg, 0x1, 8, p->hier_bitrate_ctrl); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_NUM_T_LAYER); + mfc_ctx_debug(3, "[HIERARCHICAL] hier_qp_enable %d, enable_ltr %d, ", + p_hevc->hier_qp_enable, p_hevc->enable_ltr); + mfc_ctx_debug(3, "num_hier_layer %d, max_layer %d, hier_ref_type %d, NUM_= T_LAYER 0x%x\n", + p_hevc->num_hier_layer, p->num_hier_max_layer, p_hevc->hier_ref_ty= pe, reg); + + /* QP & Bitrate for each layer */ + for (i =3D 0; i < 7; i++) { + MFC_CORE_RAW_WRITEL(p_hevc->hier_qp_layer[i], + MFC_REG_E_HIERARCHICAL_QP_LAYER0 + i * 4); + MFC_CORE_RAW_WRITEL(p_hevc->hier_bit_layer[i], + MFC_REG_E_HIERARCHICAL_BIT_RATE_LAYER0 + i * 4); + mfc_ctx_debug(3, "[HIERARCHICAL] layer[%d] QP: %#x, bitrate: %d(FW ctrl:= %d)\n", + i, p_hevc->hier_qp_layer[i], + p_hevc->hier_bit_layer[i], p->hier_bitrate_ctrl); + } + + /* rate control config. */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_CONFIG); + /** frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_hevc->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_CONFIG); + + /* max & min value of QP for I frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND); + /** max I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_hevc->rc_max_qp); + /** min I frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_hevc->rc_min_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND); + + /* max & min value of QP for P/B frame */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_QP_BOUND_PB); + /** max B frame QP */ + mfc_clear_set_bits(reg, 0xFF, 24, p_hevc->rc_max_qp_b); + /** min B frame QP */ + mfc_clear_set_bits(reg, 0xFF, 16, p_hevc->rc_min_qp_b); + /** max P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 8, p_hevc->rc_max_qp_p); + /** min P frame QP */ + mfc_clear_set_bits(reg, 0xFF, 0, p_hevc->rc_min_qp_p); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_QP_BOUND_PB); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_FIXED_PICTURE_QP); + mfc_clear_set_bits(reg, 0xFF, 24, p->config_qp); + mfc_clear_set_bits(reg, 0xFF, 16, p_hevc->rc_b_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 8, p_hevc->rc_p_frame_qp); + mfc_clear_set_bits(reg, 0xFF, 0, p_hevc->rc_frame_qp); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_FIXED_PICTURE_QP); + + /* chroma QP offset */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_HEVC_CHROMA_QP_OFFSET); + mfc_clear_set_bits(reg, 0x1F, 5, p->chroma_qp_offset_cr); + mfc_clear_set_bits(reg, 0x1F, 0, p->chroma_qp_offset_cb); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_HEVC_CHROMA_QP_OFFSET); + + /* ROI enable: it must set on SEQ_START only for HEVC encoder */ + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_RC_ROI_CTRL); + mfc_clear_set_bits(reg, 0x1, 0, p->roi_enable); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_RC_ROI_CTRL); + mfc_ctx_debug(3, "[ROI] HEVC ROI %s\n", p->roi_enable ? "enable" : "disab= le"); + + reg =3D MFC_CORE_RAW_READL(MFC_REG_E_HEVC_OPTIONS_2); + /* HDR_STATIC_INFO_ENABLE */ + mfc_clear_bits(reg, 0x1, 0); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_HEVC_OPTIONS_2); + + reg =3D MFC_CORE_READL(MFC_REG_E_MVC_INTER_VIEW_PREDICTION_ON); + mfc_clear_set_bits(reg, 0x1, 0, ctx->multi_view_enable); + /* LEFT_VIEW_ID */ + mfc_clear_set_bits(reg, 0x3, 1, ctx->left_view_id); + /* RIGHT_VIEW_ID. Always opposite to left. */ + mfc_clear_set_bits(reg, 0x3, 3, ctx->left_view_id ? 0 : 1); + MFC_CORE_RAW_WRITEL(reg, MFC_REG_E_MVC_INTER_VIEW_PREDICTION_ON); + + mfc_ctx_debug_leave(); +} + int mfc_core_set_enc_params(struct mfc_core *core, struct mfc_ctx *ctx) { if (IS_H264_ENC(ctx)) { __mfc_set_enc_params_h264(core, ctx); + } else if (IS_MPEG4_ENC(ctx)) { + __mfc_set_enc_params_mpeg4(core, ctx); + } else if (IS_H263_ENC(ctx)) { + __mfc_set_enc_params_h263(core, ctx); + } else if (IS_VP8_ENC(ctx)) { + __mfc_set_enc_params_vp8(core, ctx); + } else if (IS_VP9_ENC(ctx)) { + __mfc_set_enc_params_vp9(core, ctx); + } else if (IS_HEVC_ENC(ctx)) { + __mfc_set_enc_params_hevc(core, ctx); } else { mfc_ctx_err("Unknown codec for encoding (%x)\n", ctx->codec_mode); return -EINVAL; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c index 84edebac82b2..386262623977 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_enc_v4l2.c @@ -2667,10 +2667,33 @@ static int __mfc_enc_check_resolution(struct mfc_ct= x *ctx) =20 /* Check max resolution */ switch (ctx->codec_mode) { + case MFC_REG_CODEC_HEVC_ENC: + if (ctx->is_422) { + max_width =3D 65536; + max_height =3D 8192; + swap_check =3D 1; + } else { + max_width =3D 8192; + max_height =3D 8192; + } + break; case MFC_REG_CODEC_H264_ENC: + case MFC_REG_CODEC_VP8_ENC: max_width =3D 8192; max_height =3D 8192; break; + case MFC_REG_CODEC_VP9_ENC: + max_width =3D 4096; + max_height =3D 8192; + break; + case MFC_REG_CODEC_MPEG4_ENC: + max_width =3D 2048; + max_height =3D 2048; + break; + case MFC_REG_CODEC_H263_ENC: + max_width =3D 2048; + max_height =3D 1152; + break; default: mfc_ctx_err("Not supported codec(%d)\n", ctx->codec_mode); return -EINVAL; @@ -2694,7 +2717,15 @@ static int __mfc_enc_check_resolution(struct mfc_ctx= *ctx) =20 /* Check min resolution */ switch (ctx->codec_mode) { + case MFC_REG_CODEC_HEVC_ENC: + case MFC_REG_CODEC_VP9_ENC: + min_width =3D 64; + min_height =3D 64; + break; case MFC_REG_CODEC_H264_ENC: + case MFC_REG_CODEC_VP8_ENC: + case MFC_REG_CODEC_MPEG4_ENC: + case MFC_REG_CODEC_H263_ENC: min_width =3D 32; min_height =3D 32; break; @@ -2767,7 +2798,8 @@ static int mfc_enc_s_fmt_vid_cap_mplane(struct file *= file, void *priv, mfc_ctx_err("Failed to instance open\n"); =20 if (dev->pdata->support_enc_mode1 && - (dev->debugfs.feature_option & MFC_OPTION_SET_MULTI_CORE_FORCE)) { + (IS_MULTI_MODE_ENC_CONDITION(ctx) || + (dev->debugfs.feature_option & MFC_OPTION_SET_MULTI_CORE_FORCE))) { if (dev->debugfs.feature_option & MFC_OPTION_MULTI_CORE_DISABLE) { mfc_ctx_info("[2CORE] multi core mode disabled\n"); } else { @@ -3751,12 +3783,382 @@ static int __mfc_enc_set_param(struct mfc_ctx *ctx= , struct v4l2_control *ctrl) p->codec.h264.base_priority =3D ctrl->value; p->codec.h264.set_priority =3D 1; break; + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + switch ((enum v4l2_mpeg_video_mpeg4_profile)(ctrl->value)) { + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE: + p->codec.mpeg4.profile =3D + MFC_REG_E_PROFILE_MPEG4_SIMPLE; + break; + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE: + p->codec.mpeg4.profile =3D + MFC_REG_E_PROFILE_MPEG4_ADVANCED_SIMPLE; + break; + default: + ret =3D -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + p->codec.mpeg4.level =3D + __mfc_enc_mpeg4_level((enum v4l2_mpeg_video_mpeg4_level)(ctrl->value)); + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: + p->codec.mpeg4.rc_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: + p->codec.mpeg4.rc_min_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: + p->codec.mpeg4.rc_max_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P: + p->codec.mpeg4.rc_min_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P: + p->codec.mpeg4.rc_max_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B: + p->codec.mpeg4.rc_min_qp_b =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B: + p->codec.mpeg4.rc_max_qp_b =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: + p->codec.mpeg4.quarter_pixel =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: + p->codec.mpeg4.rc_p_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: + p->codec.mpeg4.rc_b_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_TIME_RES: + p->rc_framerate =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_MPEG4_VOP_FRM_DELTA: + p->codec.mpeg4.vop_frm_delta =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC51_VIDEO_H263_RC_FRAME_RATE: + p->rc_framerate =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: + p->codec.mpeg4.rc_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: + p->codec.mpeg4.rc_min_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: + p->codec.mpeg4.rc_max_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P: + p->codec.mpeg4.rc_min_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P: + p->codec.mpeg4.rc_max_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: + p->codec.mpeg4.rc_p_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_VERSION: + p->codec.vp8.vp8_version =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_RC_FRAME_RATE: + p->rc_framerate =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP: + p->codec.vp8.rc_min_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP: + p->codec.vp8.rc_max_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P: + p->codec.vp8.rc_min_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P: + p->codec.vp8.rc_max_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_I_FRAME_QP: + p->codec.vp8.rc_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_P_FRAME_QP: + p->codec.vp8.rc_p_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_OF_PARTITIONS: + p->codec.vp8.vp8_numberofpartitions =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_LEVEL: + p->codec.vp8.vp8_filterlevel =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_FILTER_SHARPNESS: + p->codec.vp8.vp8_filtersharpness =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_GOLDEN_FRAMESEL: + p->codec.vp8.vp8_goldenframesel =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_GF_REFRESH_PERIOD: + p->codec.vp8.vp8_gfrefreshperiod =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_ENABLE: + p->codec.vp8.hier_qp_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER0: + p->codec.vp8.hier_qp_layer[(ctrl->value >> 16) & 0x3] =3D + ctrl->value & 0xFF; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER1: + p->codec.vp8.hier_qp_layer[(ctrl->value >> 16) & 0x3] =3D + ctrl->value & 0xFF; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_HIERARCHY_QP_LAYER2: + p->codec.vp8.hier_qp_layer[(ctrl->value >> 16) & 0x3] =3D + ctrl->value & 0xFF; + break; + case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT0: + p->codec.vp8.hier_bit_layer[0] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT1: + p->codec.vp8.hier_bit_layer[1] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_BIT2: + p->codec.vp8.hier_bit_layer[2] =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_REF_NUMBER_FOR_PFRAMES: + p->num_refs_for_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_DISABLE_INTRA_MD4X4: + p->codec.vp8.intra_4x4mode_disable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC70_VIDEO_VP8_NUM_TEMPORAL_LAYER: + p->codec.vp8.num_hier_layer =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_VERSION: + p->codec.vp9.profile =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_RC_FRAME_RATE: + p->rc_framerate =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP: + p->codec.vp9.rc_min_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP: + p->codec.vp9.rc_max_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P: + p->codec.vp9.rc_min_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P: + p->codec.vp9.rc_max_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_I_FRAME_QP: + p->codec.vp9.rc_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_P_FRAME_QP: + p->codec.vp9.rc_p_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_GOLDEN_FRAMESEL: + p->codec.vp9.vp9_goldenframesel =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_GF_REFRESH_PERIOD: + p->codec.vp9.vp9_gfrefreshperiod =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHY_QP_ENABLE: + p->codec.vp9.hier_qp_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_QP: + p->codec.vp9.hier_qp_layer[(ctrl->value >> 16) & 0x3] =3D + ctrl->value & 0xFF; + break; + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT0: + p->codec.vp9.hier_bit_layer[0] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT1: + p->codec.vp9.hier_bit_layer[1] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_BIT2: + p->codec.vp9.hier_bit_layer[2] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_REF_NUMBER_FOR_PFRAMES: + p->num_refs_for_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER: + p->codec.vp9.num_hier_layer =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_MAX_PARTITION_DEPTH: + p->codec.vp9.max_partition_depth =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_DISABLE_INTRA_PU_SPLIT: + p->codec.vp9.intra_pu_split_disable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_DISABLE_IVF_HEADER: + p->ivf_header_disable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + p->codec.vp9.profile =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: + p->codec.vp9.level =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: + p->codec.hevc.rc_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: + p->codec.hevc.rc_p_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: + p->codec.hevc.rc_b_frame_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_RC_FRAME_RATE: + p->rc_framerate =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: + p->codec.hevc.rc_min_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: + p->codec.hevc.rc_max_qp =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P: + p->codec.hevc.rc_min_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P: + p->codec.hevc.rc_max_qp_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B: + p->codec.hevc.rc_min_qp_b =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B: + p->codec.hevc.rc_max_qp_b =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + p->codec.hevc.level =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + p->codec.hevc.profile =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_DARK: + p->codec.hevc.rc_lcu_dark =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_SMOOTH: + p->codec.hevc.rc_lcu_smooth =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_STATIC: + p->codec.hevc.rc_lcu_static =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_ADAPTIVE_RC_ACTIVITY: + p->codec.hevc.rc_lcu_activity =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TIER_FLAG: + p->codec.hevc.tier_flag =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_PARTITION_DEPTH: + p->codec.hevc.max_partition_depth =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REF_NUMBER_FOR_PFRAMES: + p->num_refs_for_p =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_TYPE: + p->codec.hevc.refreshtype =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_CONST_INTRA_PRED_ENABLE: + p->codec.hevc.const_intra_period_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LOSSLESS_CU_ENABLE: + p->codec.hevc.lossless_cu_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WAVEFRONT_ENABLE: + p->codec.hevc.wavefront_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_DISABLE: + p->codec.hevc.loopfilter_disable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_SLICE_BOUNDARY: + p->codec.hevc.loopfilter_across =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LTR_ENABLE: + p->codec.hevc.enable_ltr =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_QP_ENABLE: + p->codec.hevc.hier_qp_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_TYPE: + p->codec.hevc.hier_qp_type =3D + (enum v4l2_mpeg_video_hevc_hier_coding_type)(ctrl->value); + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER: + p->codec.hevc.num_hier_layer =3D ctrl->value & 0x7; + p->codec.hevc.hier_ref_type =3D (ctrl->value >> 16) & 0x1; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_QP: + p->codec.hevc.hier_qp_layer[(ctrl->value >> 16) & 0x7] =3D + ctrl->value & 0xFF; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT0: + p->codec.hevc.hier_bit_layer[0] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT1: + p->codec.hevc.hier_bit_layer[1] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT2: + p->codec.hevc.hier_bit_layer[2] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT3: + p->codec.hevc.hier_bit_layer[3] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT4: + p->codec.hevc.hier_bit_layer[4] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT5: + p->codec.hevc.hier_bit_layer[5] =3D ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_BIT6: + p->codec.hevc.hier_bit_layer[6] =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_GENERAL_PB_ENABLE: + p->codec.hevc.general_pb_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_TEMPORAL_ID_ENABLE: + p->codec.hevc.temporal_id_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STRONG_SMOTHING_FLAG: + p->codec.hevc.strong_intra_smooth =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_INTRA_PU_SPLIT: + p->codec.hevc.intra_pu_split_disable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_DISABLE_TMV_PREDICTION: + p->codec.hevc.tmv_prediction_disable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1: + p->codec.hevc.max_num_merge_mv =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_WITHOUT_STARTCODE_ENABLE: + p->codec.hevc.encoding_nostartcode_enable =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_REFRESH_PERIOD: + p->codec.hevc.refreshperiod =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: + p->codec.hevc.lf_beta_offset_div2 =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_LF_TC_OFFSET_DIV2: + p->codec.hevc.lf_tc_offset_div2 =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: + p->codec.hevc.size_of_length_field =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_USER_REF: + p->codec.hevc.user_ref =3D ctrl->value; + break; + case V4L2_CID_MPEG_MFC90_VIDEO_HEVC_STORE_REF: + p->codec.hevc.store_ref =3D ctrl->value; + break; case V4L2_CID_MPEG_VIDEO_ROI_ENABLE: p->roi_enable =3D ctrl->value; break; case V4L2_CID_MPEG_MFC_H264_VUI_RESTRICTION_ENABLE: p->codec.h264.vui_enable =3D ctrl->value; break; + case V4L2_CID_MPEG_VIDEO_HEVC_PREPEND_SPSPPS_TO_IDR: + p->codec.hevc.prepend_sps_pps_to_idr =3D ctrl->value; + break; case V4L2_CID_MPEG_MFC_CONFIG_QP_ENABLE: p->dynamic_qp =3D ctrl->value; break; @@ -3924,7 +4326,13 @@ static int __mfc_enc_set_ctrl_val_list(struct mfc_ct= x *ctx, ctx_ctrl->set.has_new =3D 1; ctx_ctrl->set.val =3D ctrl->value; if (ctx_ctrl->id =3D=3D - V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH) { + V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH || + ctx_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH || + ctx_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH || + ctx_ctrl->id =3D=3D + V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) { if (enc->sh_handle_svc.fd =3D=3D -1) { enc->sh_handle_svc.fd =3D ctrl->value; if (mfc_mem_get_user_shared_handle @@ -3984,17 +4392,44 @@ static int __mfc_enc_set_ctrl_val(struct mfc_ctx *c= tx, struct v4l2_control *ctrl case V4L2_CID_MPEG_VIDEO_QOS_RATIO: break; case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: + case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP: + case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP: + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: + case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP: + case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP: + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_P: + case V4L2_CID_MPEG_VIDEO_H263_MAX_QP_P: + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_P: + case V4L2_CID_MPEG_VIDEO_VP8_MAX_QP_P: + case V4L2_CID_MPEG_VIDEO_VP9_MAX_QP_P: + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_P: case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_P: + case V4L2_CID_MPEG_VIDEO_H263_MIN_QP_P: + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_P: + case V4L2_CID_MPEG_VIDEO_VP8_MIN_QP_P: + case V4L2_CID_MPEG_VIDEO_VP9_MIN_QP_P: + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_P: case V4L2_CID_MPEG_VIDEO_H264_MAX_QP_B: + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP_B: + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP_B: case V4L2_CID_MPEG_VIDEO_H264_MIN_QP_B: + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP_B: + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP_B: case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG: case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE: case V4L2_CID_MPEG_MFC51_VIDEO_I_PERIOD_CH: case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_RATE_CH: case V4L2_CID_MPEG_MFC51_VIDEO_BIT_RATE_CH: case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH: + case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH: + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH: + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH: case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_H264_LEVEL: case V4L2_CID_MPEG_MFC_H264_MARK_LTR: --=20 2.34.1 From nobody Thu Oct 2 00:58:12 2025 Received: from mailout4.samsung.com (mailout4.samsung.com [203.254.224.34]) (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 146592882C8 for ; Tue, 30 Sep 2025 03:57:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=203.254.224.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204645; cv=none; b=nf/5t0zMlEExDD9QMtitZjoYsokQfJMfOOFZjya7UpARNLaA5PYtkYpCxSCyx5LjaMF4wD1MhX4uertFJq9xOjTyPbf/wtf2JoOCSDCqUnoTD1vg0mG28Yhya9Ikqi3dkeEQaEzKGD9U+9fzpGv7MT8NA+SblAN4rL0wdOG/L4U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759204645; c=relaxed/simple; bh=JQx0eGpEsVIg+dfEEzT2oz8GoWLPcwyVwNxPVMuhe+4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:MIME-Version: Content-Type:References; b=NFU5beEecCUhDEK6CUVH7p7T1NZApwSEN9JoR05dPOF50Xbm8P44/20Yi9rmPTdVoHPJwg06gJ2EcQ/C6BYoeoPznRPHaWDa3MA4dci4hApbmFuZEGWANKvbsgJUYfbJj+Vgg9A5MYo2O2DG8K7LM1zvWmHcCvfztIrv9YSZibU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=j3HdkPOv; arc=none smtp.client-ip=203.254.224.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="j3HdkPOv" Received: from epcas5p4.samsung.com (unknown [182.195.41.42]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20250930035720epoutp0410c95e68258c122935c2cec6ffbeef11~p80y7l3Tp2087020870epoutp04W for ; Tue, 30 Sep 2025 03:57:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20250930035720epoutp0410c95e68258c122935c2cec6ffbeef11~p80y7l3Tp2087020870epoutp04W DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1759204640; bh=shSGGSQ/J7/0hvsTymgYvJaWZEtxz03ANsXDedOYjPI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=j3HdkPOvHllrbJB1NuTbsnTPFtWltdSs4Qjj9aSuqQKmD/u2VeN06CkMhIKDORwZn G99yknqVu9cJQR+jaevX2mCmYkkbelm3xzKCL7dlwYt/Da4b++2rahqNZ06I46/8i+ llXGDeLtk2rwY/XonpoPHm7ie1f0GroZ4302b6MY= Received: from epsnrtp04.localdomain (unknown [182.195.42.156]) by epcas5p2.samsung.com (KnoxPortal) with ESMTPS id 20250930035718epcas5p2e2942753026962581113f4d3da88dfe5~p80xqepOh0188301883epcas5p2G; Tue, 30 Sep 2025 03:57:18 +0000 (GMT) Received: from epcas5p4.samsung.com (unknown [182.195.38.89]) by epsnrtp04.localdomain (Postfix) with ESMTP id 4cbPQP66Htz6B9mJ; Tue, 30 Sep 2025 03:57:17 +0000 (GMT) Received: from epsmtip1.samsung.com (unknown [182.195.34.30]) by epcas5p1.samsung.com (KnoxPortal) with ESMTPA id 20250930035717epcas5p1dd561ae382ab7cbab428c528a046e989~p80wFKIng2306123061epcas5p1h; Tue, 30 Sep 2025 03:57:17 +0000 (GMT) Received: from bose.samsungds.net (unknown [107.108.83.9]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250930035712epsmtip1013bd2f40f267c4e3e1c1f4e6fe0a6e6~p80rP3zc62908429084epsmtip1c; Tue, 30 Sep 2025 03:57:11 +0000 (GMT) From: Himanshu Dewangan To: mchehab@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, sumit.semwal@linaro.org, christian.koenig@amd.com, alim.akhtar@samsung.com, manjun@samsung.com, nagaraju.s@samsung.com, ih0206.lee@samsung.com, jehyung.lee@samsung.com Cc: linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Himanshu Dewangan Subject: [PATCH 29/29] =?UTF-8?q?media:=20mfc:=20Hardware=E2=80=91accelera?= =?UTF-8?q?ted=20encoding=20support?= Date: Tue, 30 Sep 2025 09:33:48 +0530 Message-Id: <20250930040348.3702923-30-h.dewangan@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250930040348.3702923-1-h.dewangan@samsung.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CMS-MailID: 20250930035717epcas5p1dd561ae382ab7cbab428c528a046e989 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" CMS-TYPE: 105P cpgsPolicy: CPGSC10-542,Y X-CFilter-Loop: Reflected X-CMS-RootMailID: 20250930035717epcas5p1dd561ae382ab7cbab428c528a046e989 References: <20250930040348.3702923-1-h.dewangan@samsung.com> From: Nagaraju Siddineni - Add for MPEG=E2=80=914, H.263, VP8/VP9, HEVC codec support with extended contexts and buffer allocation. - Implement LCU sizing, motion=E2=80=91estimation buffers, and align scratch buffers. - Unify ROI buffer handling across all encoders. - Register additional pixel formats (`mfc_format.h`). - Enhance QoS weighting and B=E2=80=91frame/reference=E2=80=91count logic. - Introduce hierarchical=E2=80=91coding controls (layer count, per=E2=80=91layer bitrate) and SVC for HEVC, VP8, VP9. - Update timestamp handling for H.263 and improved VP8 buffer=E2=80=91exhau= stion error reporting. Signed-off-by: Nagaraju Siddineni Signed-off-by: Himanshu Dewangan --- .../samsung/exynos-mfc/base/mfc_buf.c | 71 ++++++++++++++++++- .../samsung/exynos-mfc/base/mfc_format.h | 40 +++++++++++ .../samsung/exynos-mfc/base/mfc_qos.c | 23 +++++- .../samsung/exynos-mfc/base/mfc_utils.h | 10 ++- .../samsung/exynos-mfc/mfc_core_buf_ctrl.c | 59 ++++++++++++++- .../samsung/exynos-mfc/mfc_core_isr.c | 2 + 6 files changed, 198 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_buf.c index 0186fe3327f1..164852e83e7d 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_buf.c @@ -133,10 +133,17 @@ int mfc_alloc_instance_context(struct mfc_core_ctx *c= ore_ctx) core_ctx->instance_ctx_buf.size =3D buf_size->other_dec_ctx; break; case MFC_REG_CODEC_H264_ENC: + case MFC_REG_CODEC_AV1_DEC: core_ctx->instance_ctx_buf.size =3D buf_size->h264_enc_ctx; break; - case MFC_REG_CODEC_AV1_DEC: - core_ctx->instance_ctx_buf.size =3D buf_size->av1_dec_ctx; + case MFC_REG_CODEC_HEVC_ENC: + core_ctx->instance_ctx_buf.size =3D buf_size->hevc_enc_ctx; + break; + case MFC_REG_CODEC_MPEG4_ENC: + case MFC_REG_CODEC_H263_ENC: + case MFC_REG_CODEC_VP8_ENC: + case MFC_REG_CODEC_VP9_ENC: + core_ctx->instance_ctx_buf.size =3D buf_size->other_enc_ctx; break; default: core_ctx->instance_ctx_buf.size =3D 0; @@ -256,6 +263,7 @@ static void __mfc_enc_calc_codec_buffer_size(struct mfc= _core_ctx *core_ctx) struct mfc_ctx *ctx =3D core_ctx->ctx; struct mfc_enc *enc; unsigned int mb_width, mb_height; + unsigned int lcu_width =3D 0, lcu_height =3D 0; =20 enc =3D ctx->enc_priv; enc->tmv_buffer_size =3D 0; @@ -263,6 +271,9 @@ static void __mfc_enc_calc_codec_buffer_size(struct mfc= _core_ctx *core_ctx) mb_width =3D WIDTH_MB(ctx->crop_width); mb_height =3D HEIGHT_MB(ctx->crop_height); =20 + lcu_width =3D ENC_LCU_WIDTH(ctx->crop_width); + lcu_height =3D ENC_LCU_HEIGHT(ctx->crop_height); + /* default recon buffer size, it can be changed in case of 422, 10bit */ enc->luma_dpb_size =3D ALIGN(ENC_LUMA_DPB_SIZE(ctx->crop_width, ctx->crop_height), SZ_64); @@ -293,6 +304,47 @@ static void __mfc_enc_calc_codec_buffer_size(struct mf= c_core_ctx *core_ctx) (ctx->dpb_count * (enc->luma_dpb_size + enc->chroma_dpb_size + enc->me_buffer_size)); break; + case MFC_REG_CODEC_MPEG4_ENC: + case MFC_REG_CODEC_H263_ENC: + enc->me_buffer_size =3D + ALIGN(ENC_V100_MPEG4_ME_SIZE(mb_width, mb_height), SZ_256); + + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D + ctx->scratch_buf_size + enc->tmv_buffer_size + + (ctx->dpb_count * (enc->luma_dpb_size + + enc->chroma_dpb_size + enc->me_buffer_size)); + break; + case MFC_REG_CODEC_VP8_ENC: + enc->me_buffer_size =3D + ALIGN(ENC_V100_VP8_ME_SIZE(mb_width, mb_height), SZ_256); + + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D + ctx->scratch_buf_size + enc->tmv_buffer_size + + (ctx->dpb_count * (enc->luma_dpb_size + + enc->chroma_dpb_size + enc->me_buffer_size)); + break; + case MFC_REG_CODEC_VP9_ENC: + enc->me_buffer_size =3D + ALIGN(ENC_V100_VP9_ME_SIZE(lcu_width, lcu_height), SZ_256); + + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D + ctx->scratch_buf_size + enc->tmv_buffer_size + + (ctx->dpb_count * (enc->luma_dpb_size + + enc->chroma_dpb_size + enc->me_buffer_size)); + break; + case MFC_REG_CODEC_HEVC_ENC: + enc->me_buffer_size =3D + ALIGN(ENC_V100_HEVC_ME_SIZE(lcu_width, lcu_height), SZ_256); + + ctx->scratch_buf_size =3D ALIGN(ctx->scratch_buf_size, SZ_256); + core_ctx->codec_buf.size =3D + ctx->scratch_buf_size + enc->tmv_buffer_size + + (ctx->dpb_count * (enc->luma_dpb_size + + enc->chroma_dpb_size + enc->me_buffer_size)); + break; default: core_ctx->codec_buf.size =3D 0; mfc_err("invalid codec type: %d\n", ctx->codec_mode); @@ -493,6 +545,7 @@ int mfc_alloc_enc_roi_buffer(struct mfc_core_ctx *core_= ctx) struct mfc_ctx *ctx =3D core_ctx->ctx; struct mfc_enc *enc =3D ctx->enc_priv; unsigned int mb_width, mb_height; + unsigned int lcu_width =3D 0, lcu_height =3D 0; size_t size; int i; =20 @@ -503,6 +556,20 @@ int mfc_alloc_enc_roi_buffer(struct mfc_core_ctx *core= _ctx) case MFC_REG_CODEC_H264_ENC: size =3D ((((mb_width * (mb_height + 1) / 2) + 15) / 16) * 16) * 2; break; + case MFC_REG_CODEC_MPEG4_ENC: + case MFC_REG_CODEC_VP8_ENC: + size =3D mb_width * mb_height; + break; + case MFC_REG_CODEC_VP9_ENC: + lcu_width =3D (ctx->crop_width + 63) / 64; + lcu_height =3D (ctx->crop_height + 63) / 64; + size =3D lcu_width * lcu_height * 4; + break; + case MFC_REG_CODEC_HEVC_ENC: + lcu_width =3D (ctx->crop_width + 31) / 32; + lcu_height =3D (ctx->crop_height + 31) / 32; + size =3D lcu_width * lcu_height; + break; default: mfc_debug(2, "ROI not supported codec type(%d). Allocate with default size\n", diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h b/= drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h index e8573d6b6005..070c669e1d82 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_format.h @@ -271,6 +271,46 @@ static struct mfc_fmt mfc_formats[] =3D { .num_planes =3D 1, .mem_planes =3D 1, }, + { + .name =3D "ENC MPEG4", + .fourcc =3D V4L2_PIX_FMT_MPEG4, + .codec_mode =3D MFC_REG_CODEC_MPEG4_ENC, + .type =3D MFC_FMT_STREAM | MFC_FMT_ENC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "ENC H263", + .fourcc =3D V4L2_PIX_FMT_H263, + .codec_mode =3D MFC_REG_CODEC_H263_ENC, + .type =3D MFC_FMT_STREAM | MFC_FMT_ENC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "ENC VP8", + .fourcc =3D V4L2_PIX_FMT_VP8, + .codec_mode =3D MFC_REG_CODEC_VP8_ENC, + .type =3D MFC_FMT_STREAM | MFC_FMT_ENC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "ENC VP9", + .fourcc =3D V4L2_PIX_FMT_VP9, + .codec_mode =3D MFC_REG_CODEC_VP9_ENC, + .type =3D MFC_FMT_STREAM | MFC_FMT_ENC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, + { + .name =3D "ENC HEVC", + .fourcc =3D V4L2_PIX_FMT_HEVC, + .codec_mode =3D MFC_REG_CODEC_HEVC_ENC, + .type =3D MFC_FMT_STREAM | MFC_FMT_ENC, + .num_planes =3D 1, + .mem_planes =3D 1, + }, }; =20 #endif /* __MFC_FORMAT_H */ diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c b/dri= vers/media/platform/samsung/exynos-mfc/base/mfc_qos.c index 40541e2d626f..73058ace1fd6 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_qos.c @@ -58,6 +58,7 @@ static inline unsigned long __mfc_qos_add_weight(struct m= fc_ctx *ctx, unsigned l break; =20 case MFC_REG_CODEC_VP8_DEC: + case MFC_REG_CODEC_VP8_ENC: weight =3D (weight * 100) / qos_weight->weight_vp8_vp9; mfc_ctx_debug(3, "[QoS] vp8, vp9 codec, weight: %d\n", weight / 10); if (num_planes =3D=3D 3) { @@ -80,12 +81,26 @@ static inline unsigned long __mfc_qos_add_weight(struct= mfc_ctx *ctx, unsigned l } break; =20 + case MFC_REG_CODEC_HEVC_ENC: + weight =3D (weight * 100) / qos_weight->weight_h264_hevc; + mfc_ctx_debug(3, "[QoS] h264, hevc codec, weight: %d\n", weight / 10); + if (num_planes =3D=3D 3) { + weight =3D (weight * 100) / qos_weight->weight_3plane; + mfc_ctx_debug(3, "[QoS] 3 plane, weight: %d\n", weight / 10); + } + if (ctx->is_422) { + weight =3D (weight * 100) / qos_weight->weight_422; + mfc_ctx_debug(3, "[QoS] 422foramt, weight: %d\n", weight / 10); + } + break; + case MFC_REG_CODEC_AV1_DEC: weight =3D (weight * 100) / qos_weight->weight_av1; mfc_ctx_debug(3, "[QoS] av1 codec, weight: %d\n", weight / 10); break; =20 case MFC_REG_CODEC_VP9_DEC: + case MFC_REG_CODEC_VP9_ENC: weight =3D (weight * 100) / qos_weight->weight_vp8_vp9; mfc_ctx_debug(3, "[QoS] vp8, vp9 codec, weight: %d\n", weight / 10); =20 @@ -109,6 +124,8 @@ static inline unsigned long __mfc_qos_add_weight(struct= mfc_ctx *ctx, unsigned l case MFC_REG_CODEC_VC1_RCV_DEC: case MFC_REG_CODEC_VC1_DEC: case MFC_REG_CODEC_MPEG2_DEC: + case MFC_REG_CODEC_MPEG4_ENC: + case MFC_REG_CODEC_H263_ENC: weight =3D (weight * 100) / qos_weight->weight_other_codec; mfc_ctx_debug(3, "[QoS] other codec, weight: %d\n", weight / 10); break; @@ -122,9 +139,13 @@ static inline unsigned long __mfc_qos_add_weight(struc= t mfc_ctx *ctx, unsigned l if (mfc_is_enc_bframe(ctx)) { weight =3D (weight * 100) / qos_weight->weight_bframe; mfc_ctx_debug(3, "[QoS] B frame encoding, weight: %d\n", weight / 10); - } else if (IS_H264_ENC(ctx) && (p->num_refs_for_p >=3D 2)) { + } else if ((IS_H264_ENC(ctx) || IS_HEVC_ENC(ctx) || IS_VP8_ENC(ctx) || + IS_VP9_ENC(ctx)) && (p->num_refs_for_p >=3D 2)) { weight =3D (weight * 100) / qos_weight->weight_num_of_ref; mfc_ctx_debug(3, "[QoS] num of ref >=3D 2, weight: %d\n", weight / 10); + } else if (IS_HEVC_ENC(ctx) && p->codec.hevc.general_pb_enable) { + weight =3D (weight * 100) / qos_weight->weight_gpb; + mfc_ctx_debug(3, "[QoS] Genaral PB, weight: %d\n", weight / 10); } } if (dec) { diff --git a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h b/d= rivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h index a127f330fe16..8526f5676761 100644 --- a/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h +++ b/drivers/media/platform/samsung/exynos-mfc/base/mfc_utils.h @@ -229,7 +229,11 @@ static inline int mfc_is_enc_bframe(struct mfc_ctx *ct= x) if (IS_H264_ENC(ctx)) { num_hier_layer =3D p->codec.h264.num_hier_layer; hier_qp_type =3D (int)p->codec.h264.hier_qp_type; + } else if (IS_HEVC_ENC(ctx)) { + num_hier_layer =3D p->codec.hevc.num_hier_layer; + hier_qp_type =3D (int)p->codec.hevc.hier_qp_type; } + if (enc->params.num_b_frame || (num_hier_layer >=3D 2 && hier_qp_type =3D=3D V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B)) @@ -402,7 +406,11 @@ static inline int mfc_enc_get_ts_delta(struct mfc_ctx = *ctx) * so thie is also divided into pre-calculated 100. * (Preventing both overflow and calculation duplication) */ - ts_delta =3D ctx->src_ts.ts_last_interval / 100; + if (IS_H263_ENC(ctx)) + ts_delta =3D ctx->src_ts.ts_last_interval * + p->rc_framerate_res / USEC_PER_SEC; + else + ts_delta =3D ctx->src_ts.ts_last_interval / 100; } return ts_delta; } diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c = b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c index cc0a20bea33a..11e9c2622242 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_buf_ctrl.c @@ -11,6 +11,25 @@ =20 #include "mfc_core_reg_api.h" =20 +static int __mfc_enc_check_adaptive_temporal_svc(int id, + struct mfc_enc_params *p, + struct temporal_layer_info + *temporal_LC) +{ + unsigned int new_num_layer =3D temporal_LC->temporal_layer_count; + unsigned int old_num_layer, ref_type; + + if (id =3D=3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) { + old_num_layer =3D p->codec.hevc.num_hier_layer; + ref_type =3D p->codec.hevc.hier_ref_type; + if (ref_type =3D=3D 0 && old_num_layer !=3D new_num_layer) + if (new_num_layer =3D=3D 1 || old_num_layer =3D=3D 1) + return 1; + } + + return 0; +} + static void __mfc_enc_store_buf_ctrls_temporal_svc(int id, struct mfc_enc_params *p, struct temporal_layer_info @@ -26,6 +45,24 @@ static void __mfc_enc_store_buf_ctrls_temporal_svc(int i= d, p->codec.h264.hier_bit_layer[i] =3D temporal_LC->temporal_layer_bitrate[i]; break; + case V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH: + p->codec.hevc.num_hier_layer =3D num_layer & 0x7; + for (i =3D 0; i < (num_layer & 0x7); i++) + p->codec.hevc.hier_bit_layer[i] =3D + temporal_LC->temporal_layer_bitrate[i]; + break; + case V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH: + p->codec.vp8.num_hier_layer =3D num_layer & 0x7; + for (i =3D 0; i < (num_layer & 0x7); i++) + p->codec.vp8.hier_bit_layer[i] =3D + temporal_LC->temporal_layer_bitrate[i]; + break; + case V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH: + p->codec.vp9.num_hier_layer =3D num_layer & 0x7; + for (i =3D 0; i < (num_layer & 0x7); i++) + p->codec.vp9.hier_bit_layer[i] =3D + temporal_LC->temporal_layer_bitrate[i]; + break; default: break; } @@ -43,12 +80,21 @@ static void __mfc_core_enc_set_buf_ctrls_temporal_svc(s= truct mfc_core *core, struct mfc_enc_params *p =3D &enc->params; =20 if (buf_ctrl->id - =3D=3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH) { + =3D=3D V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_CH || + buf_ctrl->id + =3D=3D V4L2_CID_MPEG_VIDEO_VP8_HIERARCHICAL_CODING_LAYER_CH || + buf_ctrl->id + =3D=3D V4L2_CID_MPEG_VIDEO_VP9_HIERARCHICAL_CODING_LAYER_CH || + buf_ctrl->id + =3D=3D V4L2_CID_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_LAYER_CH) { memcpy(&temporal_LC, enc->sh_handle_svc.vaddr, sizeof(struct temporal_layer_info)); =20 - if ((temporal_LC.temporal_layer_count & 0x7) < 1) { + if (((temporal_LC.temporal_layer_count & 0x7) < 1) || + (temporal_LC.temporal_layer_count > 3 && IS_VP8_ENC(ctx)) || + (temporal_LC.temporal_layer_count > 3 && + IS_VP9_ENC(ctx))) { /* clear NUM_T_LAYER_CHANGE */ value =3D MFC_CORE_READL(buf_ctrl->flag_addr); value &=3D ~BIT(10); @@ -61,7 +107,14 @@ static void __mfc_core_enc_set_buf_ctrls_temporal_svc(s= truct mfc_core *core, =20 value =3D MFC_CORE_READL(buf_ctrl->flag_addr); value &=3D ~(0x3 << 21); - + /* Adaptive temporal SVC(layer count 1: IPPP, others: H-B) */ + if (__mfc_enc_check_adaptive_temporal_svc + (buf_ctrl->id, p, &temporal_LC)) { + if (temporal_LC.temporal_layer_count > 1) + value |=3D BIT(21); + else + value |=3D (0x2 << 21); + } MFC_CORE_WRITEL(value, buf_ctrl->flag_addr); =20 /* Store temporal layer information */ diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c b/dri= vers/media/platform/samsung/exynos-mfc/mfc_core_isr.c index 1a3cf7e76e29..9c5be84fcbc6 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_core_isr.c @@ -1883,6 +1883,8 @@ static inline void __mfc_handle_nal_abort(struct mfc_= core *core, if (ctx->type =3D=3D MFCINST_ENCODER) { mfc_change_state(core_ctx, MFCINST_RUNNING_BUF_FULL); enc->buf_full =3D 0; + if (IS_VP8_ENC(ctx)) + mfc_err("stream buffer size isn't enough\n"); __mfc_handle_stream(core, ctx, reason); } else { mfc_change_state(core_ctx, MFCINST_ABORT); --=20 2.34.1