From nobody Tue Dec 2 01:04:48 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DA46523BD1B; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763955155; cv=none; b=NW7crtIdZ7QSCVHMekxLQm1VW6svEhesq1xq1B+FAKoTvXg2w6HhVi+XQw924nzJRDVjuZcCozZQ/Q/SQIOruK+rsNNiosJRmIOrkbJSKytcQbBCkSl99hGsM4Qix1KeUwmL1kF1I5Hl21ZHLvaueLJRHw4wOb0GpvdZM2B/WAo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763955155; c=relaxed/simple; bh=+vUEeQ+oKBW42P4bNYyEWDh9IPPF0c9ajm6FdHUtDgQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=B4O5aBMzf12pfP9R/ojN86tEt6iND2y0NUPUcr+736K5SZLf6vAUoZa1ZlxPxYHjE6LKuUnDRkZKndgorLXjY4028zcuwg3SyM5yz1/JZR2qStIbeHk1nEMe2MikHh/ehPGAegzl0sdPaedVcAAGJfumcQ1GCxyGVTPCxDYovwE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=K2QHG2vg; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="K2QHG2vg" Received: by smtp.kernel.org (Postfix) with ESMTPS id 6B38BC4CEFB; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763955154; bh=+vUEeQ+oKBW42P4bNYyEWDh9IPPF0c9ajm6FdHUtDgQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=K2QHG2vgHxz4OUoqZIJnJvp0v3udBmajr5kDfVQaUPleIqOk6iPHRCevoyK/gt2a8 HI7TKc0JY7QBIozsVWIqHJQOF/DuzV95nakrdG3s7aurQUXWUFhTH0BUOUhaC25GZ4 au6y46lSOnRgFASjFImqu3o6OEsAJOiiJI9nLh49WXR4yHt+KuWt/ZSdPiOWsl6s4f UwtkI14Mopwm4EUmgYM6lxfwza/6uMjz1MzaEuZDOjrx4il2eUsJTpM55NFAlnJGTh /TVYeJBsEdTR46KSui/KfzqFtNlOpvuufI46mAGbzkJhwRgsgSUZXa53sTgjmp1xuH YSwCmI0ELv9ZA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 50CD6CFD317; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) From: Zhentao Guo via B4 Relay Date: Mon, 24 Nov 2025 11:32:17 +0800 Subject: [PATCH RFC v2 1/3] media: dt-bindings: Add Amlogic V4L2 video decoder Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251124-b4-s4-vdec-upstream-v2-1-bdbbce3f11a6@amlogic.com> References: <20251124-b4-s4-vdec-upstream-v2-0-bdbbce3f11a6@amlogic.com> In-Reply-To: <20251124-b4-s4-vdec-upstream-v2-0-bdbbce3f11a6@amlogic.com> To: Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Neil Armstrong , Kevin Hilman , Jerome Brunet , Martin Blumenstingl Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-amlogic@lists.infradead.org, Zhentao Guo X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763955152; l=2874; i=zhentao.guo@amlogic.com; s=20251024; h=from:subject:message-id; bh=ojBHIRmTFbfRhSiNqZUE5wWfU7fFlbxeePD+nSCZSVI=; b=kGEEn3iGlSSJBNB1XpOPFEICT4DGpDfrFEJEg69bILFh9RhP7d7k5fE+S9k2RsjSPo4YIyogm DYyPCIQOM9RA62C8DdE4iCXsXY7/tXicnfpo4P9NNfSKzyGxT68OxzN X-Developer-Key: i=zhentao.guo@amlogic.com; a=ed25519; pk=5yfDKrjreXwcAoEUsdtWafy6YN500upXp/CgtnXjLVU= X-Endpoint-Received: by B4 Relay for zhentao.guo@amlogic.com/20251024 with auth_id=555 X-Original-From: Zhentao Guo Reply-To: zhentao.guo@amlogic.com From: Zhentao Guo Describe the initial support for the V4L2 stateless video decoder driver used with the Amlogic S4 (S805X2) platform. Signed-off-by: Zhentao Guo --- .../bindings/media/amlogic,s4-vcodec-dec.yaml | 87 ++++++++++++++++++= ++++ 1 file changed, 87 insertions(+) diff --git a/Documentation/devicetree/bindings/media/amlogic,s4-vcodec-dec.= yaml b/Documentation/devicetree/bindings/media/amlogic,s4-vcodec-dec.yaml new file mode 100644 index 000000000000..401a5a32902e --- /dev/null +++ b/Documentation/devicetree/bindings/media/amlogic,s4-vcodec-dec.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025 Amlogic, Inc. All rights reserved +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/amlogic,vcodec-dec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic Video Decode Accelerator + +maintainers: + - Zhentao Guo + +description: + The Video Decoder Accelerator present on Amlogic SOCs. + It supports stateless h264 decoding. + +properties: + compatible: + const: amlogic,s4-vcodec-dec + + reg: + maxItems: 2 + + reg-names: + items: + - const: dos + - const: dmc + + interrupts: + maxItems: 3 + + clocks: + maxItems: 3 + + clock-names: + items: + - const: vdec + - const: clk_vdec_mux + - const: clk_hevcf_mux + + power-domains: + maxItems: 2 + + power-domain-names: + items: + - const: vdec + - const: hevc + +required: + - compatible + - reg + - reg-names + - interrupts + - clocks + - clock-names + - power-domains + - power-domain-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + video-codec@fe320000 { + compatible =3D "amlogic,s4-vcodec-dec"; + reg =3D <0xfe320000 0x10000>, + <0xfe036000 0x20>; + amlogic,canvas =3D <&canvas>; + reg-names =3D "dos", + "dmc"; + interrupts =3D , + , + ; + clocks =3D <&clkc_periphs CLKID_DOS>, + <&clkc_periphs CLKID_VDEC_SEL>, + <&clkc_periphs CLKID_HEVCF_SEL>; + clock-names =3D "vdec", + "clk_vdec_mux", + "clk_hevcf_mux"; + power-domains =3D <&pwrc PWRC_S4_DOS_VDEC_ID>, + <&pwrc PWRC_S4_DOS_HEVC_ID>; + power-domain-names =3D "vdec", + "hevc"; + }; --=20 2.42.0 From nobody Tue Dec 2 01:04:48 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DA54327E1DC; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763955155; cv=none; b=Vo4414ZJhGO1ybA58PA/yd7eXc/gsD9LoYDmdDr3tc/5EanI3uoDYPd7c1TcSLxrX3ShOCOQgWH/JPAEalxmMIiFw7EVCQSQVt0KFqQC2/XgTtk2DFEthebzHOSYGqolDVf8WTha/dVienkmWjuWfxUVw2c7weBaioKLV86fUtM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763955155; c=relaxed/simple; bh=z3W0vNekhBQjJYfuihLjGvs3WCxMpRJy/C6AP4dtxxI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=H+ji+qNWHN0cR5J0uvwEXVy9ofKp3bec4ORnGreovW8B7U0mVs0HvyTTubxj1mX9ydt/yGGaAmIyipBCNR3tNisSYnaDjXWudaLCFZolk1F2xjQFBO7humHyHoEU/Yqn2DcDLJKQxFuu4MsCrL0BnVtKwz0i2jsEXmXnhNdBKAg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GbtU8WGT; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GbtU8WGT" Received: by smtp.kernel.org (Postfix) with ESMTPS id 81511C19424; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763955154; bh=z3W0vNekhBQjJYfuihLjGvs3WCxMpRJy/C6AP4dtxxI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=GbtU8WGTxxqryiWxyc9t2BqlAwHMqsrOFMubIihc9GSEHDKGVRUZpHdkB7sw9Flqu vZRigfsqZpQHXSXTnvbhNE2WdtIa8TL735vxepG4VWXktowB/UgH9h1lVafOezKo+M bPOTr0Gq+N76NRKPcpHNK8t11yVSC/UP6DQB5aqO0qQExIpyZ5NksjTFaZjQkheYFG 5c2jjk4vfT9beFLYtCo1qX9rLVXjfM95AApp/BbtyU1jRRNRGA3j4cv+egqDqnIVxn 9TzE9E0mdn+m19+zmWOlEeC0L9HX150gNDOSo/WAd8bCv/VoLNew8R3cITwtF0uAO3 kek8aGDwmGyeQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 75F15CFD2F6; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) From: Zhentao Guo via B4 Relay Date: Mon, 24 Nov 2025 11:32:18 +0800 Subject: [PATCH RFC v2 2/3] decoder: Add V4L2 stateless H.264 decoder driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251124-b4-s4-vdec-upstream-v2-2-bdbbce3f11a6@amlogic.com> References: <20251124-b4-s4-vdec-upstream-v2-0-bdbbce3f11a6@amlogic.com> In-Reply-To: <20251124-b4-s4-vdec-upstream-v2-0-bdbbce3f11a6@amlogic.com> To: Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Neil Armstrong , Kevin Hilman , Jerome Brunet , Martin Blumenstingl Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-amlogic@lists.infradead.org, Zhentao Guo X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763955152; l=153347; i=zhentao.guo@amlogic.com; s=20251024; h=from:subject:message-id; bh=T/77BwS5XaWqcO9ji3vCZb6yGI065cjmRoPvTpT9QUk=; b=K/7QhI6vUEjFOfJXgjfn9jW484GWm2w5Bw6BVfShNGJl99X1cyCeFexvoFmlEuUuIL7cOQMTX Q5gXSkuBiCMBg6O6VKOJIFOYs/JrU7udaVu7gW3EhrmhpbvYiywv8jd X-Developer-Key: i=zhentao.guo@amlogic.com; a=ed25519; pk=5yfDKrjreXwcAoEUsdtWafy6YN500upXp/CgtnXjLVU= X-Endpoint-Received: by B4 Relay for zhentao.guo@amlogic.com/20251024 with auth_id=555 X-Original-From: Zhentao Guo Reply-To: zhentao.guo@amlogic.com From: Zhentao Guo Add initial support for V4L2 stateless video decoder driver on Amlogic S4(S805X2) platform. In phase 1, it supports H.264 bitstreams decoding. Signed-off-by: Zhentao Guo --- MAINTAINERS | 7 + drivers/media/platform/amlogic/Kconfig | 1 + drivers/media/platform/amlogic/Makefile | 1 + drivers/media/platform/amlogic/vdec/Kconfig | 16 + drivers/media/platform/amlogic/vdec/Makefile | 4 + drivers/media/platform/amlogic/vdec/TODO | 7 + drivers/media/platform/amlogic/vdec/aml_vdec.c | 756 ++++++++ drivers/media/platform/amlogic/vdec/aml_vdec.h | 31 + drivers/media/platform/amlogic/vdec/aml_vdec_drv.c | 239 +++ drivers/media/platform/amlogic/vdec/aml_vdec_drv.h | 196 ++ drivers/media/platform/amlogic/vdec/aml_vdec_hw.c | 596 ++++++ drivers/media/platform/amlogic/vdec/aml_vdec_hw.h | 158 ++ .../platform/amlogic/vdec/aml_vdec_platform.c | 37 + .../platform/amlogic/vdec/aml_vdec_platform.h | 62 + drivers/media/platform/amlogic/vdec/h264.c | 1933 ++++++++++++++++= ++++ drivers/media/platform/amlogic/vdec/h264.h | 300 +++ drivers/media/platform/amlogic/vdec/reg_defines.h | 177 ++ 17 files changed, 4521 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ec635515c0c4..371c8b828394 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1365,6 +1365,13 @@ S: Maintained F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml F: drivers/spi/spi-amlogic-spisg.c =20 +AMLOGIC VCODEC DRIVER +M: Zhentao Guo +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/amlogic,s4-vcodec-dec.yaml +F: drivers/media/platform/amlogic/vdec/ + AMPHENOL CHIPCAP 2 DRIVER M: Javier Carrasco L: linux-hwmon@vger.kernel.org diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platfor= m/amlogic/Kconfig index 458acf3d5fa8..7c541ac0d0c3 100644 --- a/drivers/media/platform/amlogic/Kconfig +++ b/drivers/media/platform/amlogic/Kconfig @@ -4,3 +4,4 @@ comment "Amlogic media platform drivers" =20 source "drivers/media/platform/amlogic/c3/Kconfig" source "drivers/media/platform/amlogic/meson-ge2d/Kconfig" +source "drivers/media/platform/amlogic/vdec/Kconfig" diff --git a/drivers/media/platform/amlogic/Makefile b/drivers/media/platfo= rm/amlogic/Makefile index c744afcd1b9e..7409de674c0b 100644 --- a/drivers/media/platform/amlogic/Makefile +++ b/drivers/media/platform/amlogic/Makefile @@ -2,3 +2,4 @@ =20 obj-y +=3D c3/ obj-y +=3D meson-ge2d/ +obj-y +=3D vdec/ diff --git a/drivers/media/platform/amlogic/vdec/Kconfig b/drivers/media/pl= atform/amlogic/vdec/Kconfig new file mode 100644 index 000000000000..95424d64cc1f --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR MIT) + +config VIDEO_AMLOGIC_VCODEC + tristate "Amlogic Video Codec Driver" + depends on ARCH_MESON || COMPILE_TEST + depends on VIDEO_DEV + depends on V4L_MEM2MEM_DRIVERS + select VIDEOBUF2_DMA_CONTIG + select V4L2_H264 + select V4L2_MEM2MEM_DEV + select MESON_CANVAS + help + This is a v4l2 driver for Amlogic video codec driver. + This driver is designed to support V4L2 M2M STATELESS + interface. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/amlogic/vdec/Makefile b/drivers/media/p= latform/amlogic/vdec/Makefile new file mode 100644 index 000000000000..1a7dcf1d7562 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +aml-vdec-drv-objs :=3D aml_vdec.o aml_vdec_drv.o aml_vdec_hw.o aml_vdec_pl= atform.o h264.o\ + +obj-$(CONFIG_VIDEO_AMLOGIC_VCODEC) +=3D aml-vdec-drv.o diff --git a/drivers/media/platform/amlogic/vdec/TODO b/drivers/media/platf= orm/amlogic/vdec/TODO new file mode 100644 index 000000000000..54c60145770e --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/TODO @@ -0,0 +1,7 @@ +TODO list for Amlogic V4L2 stateless decoder driver: + +1. Support decoding for HEVC, VP9, AV1, and MPEG-2. +2. Support more SoCs, including the new T7/S7 series and legacy SoCs (e.g.= , GXL, SM1, G12B). +3. Support 10-bit decoding and P010 output. + Note: P010 output requires hardware support. +4. Support interlaced stream decoding for H.264, HEVC, and MPEG-2. diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec.c b/drivers/media= /platform/amlogic/vdec/aml_vdec.c new file mode 100644 index 000000000000..3ef2d32a7a73 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include +#include +#include + +#include "aml_vdec.h" +#include "aml_vdec_hw.h" +#include "aml_vdec_platform.h" + +#define VCODEC_DRV_NAME "aml-vdec-drv" + +static const struct aml_vdec_v4l2_ctrl controls[] =3D { + { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_STATELESS_H264_DECODE_PARAMS, + }, + }, { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_STATELESS_H264_SPS, + }, + }, { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_STATELESS_H264_PPS, + }, + }, { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_STATELESS_H264_SCALING_MATRIX, + }, + }, { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_STATELESS_H264_DECODE_MODE, + .min =3D V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .def =3D V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .max =3D V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + }, + }, { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_MPEG_VIDEO_H264_LEVEL, + }, + }, { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_STATELESS_H264_START_CODE, + .min =3D V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .def =3D V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .max =3D V4L2_STATELESS_H264_START_CODE_ANNEX_B, + }, + }, { + .codec_type =3D CODEC_TYPE_H264, + .cfg =3D { + .id =3D V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .min =3D V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max =3D V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .def =3D V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + }, + } +}; + +static struct aml_video_fmt aml_video_formats[] =3D { + { + .name =3D "H.264", + .fourcc =3D V4L2_PIX_FMT_H264_SLICE, + .type =3D AML_FMT_DEC, + .codec_type =3D CODEC_TYPE_H264, + .num_planes =3D 1, + .stepwise =3D {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2, + AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2}, + }, + { + .name =3D "NV21M", + .fourcc =3D V4L2_PIX_FMT_NV21M, + .type =3D AML_FMT_FRAME, + .codec_type =3D CODEC_TYPE_FRAME, + .num_planes =3D 2, + .stepwise =3D {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2, + AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2}, + }, + { + .name =3D "NV21", + .fourcc =3D V4L2_PIX_FMT_NV21, + .type =3D AML_FMT_FRAME, + .codec_type =3D CODEC_TYPE_FRAME, + .num_planes =3D 1, + .stepwise =3D {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2, + AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2}, + }, + { + .name =3D "NV12M", + .fourcc =3D V4L2_PIX_FMT_NV12M, + .type =3D AML_FMT_FRAME, + .codec_type =3D CODEC_TYPE_FRAME, + .num_planes =3D 2, + .stepwise =3D {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2, + AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2}, + + }, + { + .name =3D "NV12", + .fourcc =3D V4L2_PIX_FMT_NV12, + .type =3D AML_FMT_FRAME, + .codec_type =3D CODEC_TYPE_FRAME, + .num_planes =3D 1, + .stepwise =3D {AML_VDEC_MIN_W, AML_VDEC_1080P_MAX_W, 2, + AML_VDEC_MIN_H, AML_VDEC_1080P_MAX_H, 2}, + }, +}; + +void aml_vdec_set_default_params(struct aml_vdec_ctx *ctx) +{ + struct aml_q_data *q_data =3D NULL; + + ctx->m2m_ctx->q_lock =3D &ctx->v4l2_intf_lock; + + ctx->pic_info.colorspace =3D V4L2_COLORSPACE_DEFAULT; + ctx->pic_info.ycbcr_enc =3D V4L2_YCBCR_ENC_DEFAULT; + ctx->pic_info.quantization =3D V4L2_QUANTIZATION_DEFAULT; + ctx->pic_info.xfer_func =3D V4L2_XFER_FUNC_DEFAULT; + + q_data =3D &ctx->q_data[AML_Q_DATA_SRC]; + memset(q_data, 0, sizeof(struct aml_q_data)); + q_data->visible_width =3D AML_VDEC_MIN_W; + q_data->visible_height =3D AML_VDEC_MIN_H; + q_data->coded_width =3D AML_VDEC_MIN_W; + q_data->coded_height =3D AML_VDEC_MIN_H; + q_data->filed_flag =3D V4L2_FIELD_NONE; + q_data->bytesperline[0] =3D 0; + q_data->sizeimage[0] =3D (1024 * 1024); + q_data->fmt =3D &aml_video_formats[DEFAULT_OUT_IDX]; + + q_data =3D &ctx->q_data[AML_Q_DATA_DST]; + memset(q_data, 0, sizeof(struct aml_q_data)); + q_data->visible_width =3D AML_VDEC_MIN_W; + q_data->visible_height =3D AML_VDEC_MIN_H; + q_data->coded_width =3D AML_VDEC_MIN_W; + q_data->coded_height =3D AML_VDEC_MIN_H; + q_data->filed_flag =3D V4L2_FIELD_NONE; + q_data->bytesperline[0] =3D q_data->coded_width; + q_data->sizeimage[0] =3D q_data->coded_width * q_data->coded_height; + q_data->bytesperline[1] =3D q_data->coded_width; + q_data->sizeimage[1] =3D q_data->sizeimage[0] / 2; + q_data->fmt =3D &aml_video_formats[DEFAULT_CAP_IDX]; +} + +int aml_vdec_ctrls_setup(struct aml_vdec_ctx *ctx) +{ + int i; + int ctrls_size =3D sizeof(controls) / sizeof(struct aml_vdec_v4l2_ctrl); + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, ctrls_size); + for (i =3D 0; i < ctrls_size; i++) { + v4l2_ctrl_new_custom(&ctx->ctrl_handler, &controls[i].cfg, NULL); + if (ctx->ctrl_handler.error) { + dev_info(&ctx->dev->plat_dev->dev, "add ctrl for (%d) failed%d\n", + controls[i].cfg.id, ctx->ctrl_handler.error); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return ctx->ctrl_handler.error; + } + } + ctx->fh.ctrl_handler =3D &ctx->ctrl_handler; + return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); +} + +static void m2mops_vdec_device_run(void *m2m_priv) +{ + struct aml_vdec_ctx *ctx =3D (struct aml_vdec_ctx *)m2m_priv; + struct aml_vdec_dev *dev =3D ctx->dev; + struct vb2_v4l2_buffer *src, *dst; + struct media_request *src_req; + const char *fw_path =3D dev->pvdec_data->fw_path[ctx->curr_dec_type]; + + src =3D v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst =3D v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + dev_dbg(&dev->plat_dev->dev, "device run : src buf : %d dst buf %d\n", + src->vb2_buf.index, dst->vb2_buf.index); + + src_req =3D src->vb2_buf.req_obj.req; + if (src_req) + v4l2_ctrl_request_setup(src_req, &ctx->ctrl_handler); + + dos_enable(dev->dec_hw); + /* incase of bus hang in stop_streaming */ + ctx->dos_clk_en =3D 1; + aml_vdec_reset_core(dev->dec_hw); + load_firmware(dev->dec_hw, fw_path); + + if (ctx->codec_ops->run) + ctx->codec_ops->run(ctx); + + v4l2_m2m_buf_copy_metadata(src, dst); + if (src_req) + v4l2_ctrl_request_complete(src_req, &ctx->ctrl_handler); + + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, VB2_BUF_= STATE_DONE); +} + +const struct v4l2_m2m_ops aml_vdec_m2m_ops =3D { + .device_run =3D m2mops_vdec_device_run, +}; + +static int vidioc_vdec_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, VCODEC_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, "platform:" VCODEC_DRV_NAME, sizeof(cap->card)); + + return 0; +} + +static int vidioc_vdec_enum_fmt(struct v4l2_fmtdesc *f, bool is_output) +{ + struct aml_video_fmt *fmt; + int fmt_size =3D sizeof(aml_video_formats) / sizeof(struct aml_video_fmt); + int i =3D 0, j =3D 0; + + for (; i < fmt_size; i++) { + fmt =3D &aml_video_formats[i]; + if (is_output && fmt->type !=3D AML_FMT_DEC) + continue; + if (!is_output && fmt->type !=3D AML_FMT_FRAME) + continue; + + if (j =3D=3D f->index) { + f->pixelformat =3D fmt->fourcc; + strscpy(f->description, fmt->name, + sizeof(f->description)); + if (strlen(fmt->name) >=3D sizeof(f->description)) + f->description[sizeof(f->description) - 1] =3D '\0'; + return 0; + } + ++j; + } + return -EINVAL; +} + +static struct aml_q_data *aml_vdec_get_qdata_by_type(struct aml_vdec_ctx *= ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->q_data[AML_Q_DATA_SRC]; + + return &ctx->q_data[AML_Q_DATA_DST]; +} + +static struct aml_video_fmt *aml_vdec_get_video_fmt(u32 format) +{ + struct aml_video_fmt *fmt; + unsigned int k; + + for (k =3D 0; k < (sizeof(aml_video_formats) / sizeof(struct aml_video_fm= t)); k++) { + fmt =3D &aml_video_formats[k]; + if (fmt->fourcc =3D=3D format) + return fmt; + } + + return NULL; +} + +static int aml_vdec_init_dec_inst(struct aml_vdec_ctx *ctx, + struct aml_video_fmt *fmt_out) +{ + struct aml_vdec_dev *dev =3D ctx->dev; + int ret =3D -1; + + if (!fmt_out) + return ret; + + if (fmt_out->codec_type =3D=3D CODEC_TYPE_FRAME) { + dev_dbg(&dev->plat_dev->dev, "capture type no need to set\n"); + return 0; + } + + ctx->codec_ops =3D &dev->pvdec_data->codec_ops[fmt_out->codec_type]; + if (ctx->codec_ops->init) { + ret =3D ctx->codec_ops->init(ctx); + if (ret < 0) + return ret; + } + ctx->curr_dec_type =3D fmt_out->codec_type; + dev_info(&dev->plat_dev->dev, "%s set curr_dec_type =3D %d\n", __func__, = ctx->curr_dec_type); + + return ret; +} + +static void set_pic_info(struct aml_vdec_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp, + enum v4l2_buf_type type) +{ + struct aml_q_data *q_data; + + q_data =3D aml_vdec_get_qdata_by_type(ctx, type); + + ctx->pic_info.colorspace =3D pix_mp->colorspace; + ctx->pic_info.ycbcr_enc =3D pix_mp->ycbcr_enc; + ctx->pic_info.quantization =3D pix_mp->quantization; + ctx->pic_info.xfer_func =3D pix_mp->xfer_func; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + q_data->sizeimage[0] =3D pix_mp->plane_fmt[0].sizeimage; + ctx->pic_info.output_pix_fmt =3D pix_mp->pixelformat; + ctx->pic_info.coded_width =3D ALIGN(pix_mp->width, 64); + ctx->pic_info.coded_height =3D ALIGN(pix_mp->height, 64); + ctx->pic_info.fb_size[0] =3D + ctx->pic_info.coded_width * ctx->pic_info.coded_height; + ctx->pic_info.fb_size[1] =3D ctx->pic_info.fb_size[0] / 2; + ctx->pic_info.plane_num =3D 1; + } else { + ctx->pic_info.plane_num =3D q_data->fmt->num_planes; + ctx->pic_info.cap_pix_fmt =3D pix_mp->pixelformat; + q_data->coded_width =3D ctx->pic_info.coded_width; + q_data->coded_height =3D ctx->pic_info.coded_height; + q_data->sizeimage[0] =3D ctx->pic_info.fb_size[0]; + q_data->bytesperline[0] =3D ctx->pic_info.coded_width; + if (q_data->fmt->num_planes > 1) { + q_data->sizeimage[1] =3D ctx->pic_info.fb_size[1]; + q_data->bytesperline[1] =3D ctx->pic_info.coded_width; + } else { + q_data->sizeimage[0] +=3D ctx->pic_info.fb_size[1]; + } + } +} + +static int vidioc_vdec_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct aml_video_fmt *fmt; + struct aml_vdec_dev *dev =3D video_drvdata(file); + u32 max_h, max_w; + + if (fsize->index !=3D 0) + return -EINVAL; + + max_h =3D dev->pvdec_data->dec_fmt->max_height; + max_w =3D dev->pvdec_data->dec_fmt->max_width; + + fmt =3D aml_vdec_get_video_fmt(fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type =3D V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise =3D fmt->stepwise; + fsize->stepwise.max_height =3D max_h; + fsize->stepwise.max_width =3D max_w; + + return 0; +} + +static int vdec_try_fmt_mp(struct aml_vdec_ctx *ctx, struct v4l2_format *f, + const struct aml_video_fmt *fmt_mp) +{ + struct v4l2_pix_format_mplane *pix_mp =3D &f->fmt.pix_mp; + struct aml_q_data *q_data; + struct aml_vdec_dev *dev =3D ctx->dev; + u32 max_h, max_w; + int i; + + max_h =3D dev->pvdec_data->dec_fmt->max_height; + max_w =3D dev->pvdec_data->dec_fmt->max_width; + + pix_mp->field =3D V4L2_FIELD_NONE; + q_data =3D aml_vdec_get_qdata_by_type(ctx, f->type); + + pix_mp->height =3D clamp(pix_mp->height, AML_VDEC_MIN_H, max_h); + pix_mp->width =3D clamp(pix_mp->width, AML_VDEC_MIN_H, max_w); + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + pix_mp->num_planes =3D q_data->fmt->num_planes; + pix_mp->pixelformat =3D q_data->fmt->fourcc; + pix_mp->plane_fmt[0].bytesperline =3D q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage =3D q_data->sizeimage[0]; + } else { + v4l2_fill_pixfmt_mp(pix_mp, fmt_mp->fourcc, pix_mp->width, + pix_mp->height); + } + + for (i =3D 0; i < pix_mp->num_planes; i++) + memset(&pix_mp->plane_fmt[i].reserved[0], 0x0, + sizeof(pix_mp->plane_fmt[0].reserved)); + + memset(pix_mp->reserved, 0x0, sizeof(pix_mp->reserved)); + pix_mp->flags =3D 0; + + return 0; +} + +static int vdec_s_fmt(struct aml_vdec_ctx *ctx, struct v4l2_format *f) +{ + struct aml_q_data *q_data; + struct v4l2_pix_format_mplane *pix_mp =3D &f->fmt.pix_mp; + struct aml_video_fmt *fmt =3D + aml_vdec_get_video_fmt(f->fmt.pix_mp.pixelformat); + + q_data =3D aml_vdec_get_qdata_by_type(ctx, f->type); + + if (fmt) /* default fmt was set in fopen */ + q_data->fmt =3D fmt; + + vdec_try_fmt_mp(ctx, f, q_data->fmt); + set_pic_info(ctx, pix_mp, f->type); + + return 0; +} + +static int vdec_g_fmt(struct aml_vdec_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp =3D &f->fmt.pix_mp; + struct aml_q_data *q_data; + + q_data =3D aml_vdec_get_qdata_by_type(ctx, f->type); + + pix_mp->field =3D V4L2_FIELD_NONE; + pix_mp->colorspace =3D ctx->pic_info.colorspace; + pix_mp->ycbcr_enc =3D ctx->pic_info.ycbcr_enc; + pix_mp->quantization =3D ctx->pic_info.quantization; + pix_mp->xfer_func =3D ctx->pic_info.xfer_func; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + pix_mp->height =3D q_data->coded_height; + pix_mp->width =3D q_data->coded_width; + pix_mp->pixelformat =3D q_data->fmt->fourcc; + pix_mp->num_planes =3D q_data->fmt->num_planes; + pix_mp->plane_fmt[0].bytesperline =3D q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage =3D q_data->sizeimage[0]; + } else { + if (ctx->pic_info.coded_width !=3D 0 && ctx->pic_info.coded_height !=3D = 0) { + pix_mp->width =3D ctx->pic_info.coded_width; + pix_mp->height =3D ctx->pic_info.coded_height; + } else { + pix_mp->height =3D q_data->coded_height; + pix_mp->width =3D q_data->coded_height; + } + v4l2_fill_pixfmt_mp(pix_mp, q_data->fmt->fourcc, pix_mp->width, + pix_mp->height); + } + + return 0; +} + +static int vidioc_try_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + const struct aml_video_fmt *fmt_mp; + struct aml_q_data *q_data; + + q_data =3D aml_vdec_get_qdata_by_type(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MP= LANE); + + fmt_mp =3D aml_vdec_get_video_fmt(f->fmt.pix_mp.pixelformat); + if (!fmt_mp) + fmt_mp =3D q_data->fmt; + + return vdec_try_fmt_mp(ctx, f, fmt_mp); +} + +static int vidioc_try_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + const struct aml_video_fmt *fmt_mp; + struct aml_q_data *q_data =3D aml_vdec_get_qdata_by_type(ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + fmt_mp =3D aml_vdec_get_video_fmt(f->fmt.pix_mp.pixelformat); + if (!fmt_mp) + fmt_mp =3D q_data->fmt; + + return vdec_try_fmt_mp(ctx, f, fmt_mp); +} + +static int vidioc_vdec_s_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + + return vdec_s_fmt(ctx, f); +} + +static int vidioc_vdec_s_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + + return vdec_s_fmt(ctx, f); +} + +static int vidioc_vdec_g_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + + return vdec_g_fmt(ctx, f); +} + +static int vidioc_vdec_g_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + + return vdec_g_fmt(ctx, f); +} + +static int vidioc_vdec_enum_fmt_out_mplane(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + return vidioc_vdec_enum_fmt(f, 1); +} + +static int vidioc_vdec_enum_fmt_cap_mplane(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + return vidioc_vdec_enum_fmt(f, 0); +} + +const struct v4l2_ioctl_ops aml_vdec_ioctl_ops =3D { + .vidioc_querycap =3D vidioc_vdec_querycap, + .vidioc_enum_framesizes =3D vidioc_vdec_enum_framesizes, + + .vidioc_enum_fmt_vid_cap =3D vidioc_vdec_enum_fmt_cap_mplane, + .vidioc_try_fmt_vid_cap_mplane =3D vidioc_try_fmt_cap_mplane, + .vidioc_s_fmt_vid_cap_mplane =3D vidioc_vdec_s_fmt_cap_mplane, + .vidioc_g_fmt_vid_cap_mplane =3D vidioc_vdec_g_fmt_cap_mplane, + + .vidioc_enum_fmt_vid_out =3D vidioc_vdec_enum_fmt_out_mplane, + .vidioc_try_fmt_vid_out_mplane =3D vidioc_try_fmt_out_mplane, + .vidioc_s_fmt_vid_out_mplane =3D vidioc_vdec_s_fmt_out_mplane, + .vidioc_g_fmt_vid_out_mplane =3D vidioc_vdec_g_fmt_out_mplane, + + .vidioc_reqbufs =3D v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf =3D v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf =3D v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf =3D v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf =3D v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs =3D v4l2_m2m_ioctl_create_bufs, + + .vidioc_expbuf =3D v4l2_m2m_ioctl_expbuf, + + .vidioc_decoder_cmd =3D v4l2_m2m_ioctl_stateless_decoder_cmd, + .vidioc_try_decoder_cmd =3D v4l2_m2m_ioctl_stateless_try_decoder_cmd, + + .vidioc_subscribe_event =3D v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event =3D v4l2_event_unsubscribe, + + .vidioc_streamon =3D v4l2_m2m_ioctl_streamon, + .vidioc_streamoff =3D v4l2_m2m_ioctl_streamoff, +}; + +static void aml_vdec_release_instance(struct aml_vdec_ctx *ctx) +{ + if (ctx->codec_ops && ctx->codec_ops->exit) + ctx->codec_ops->exit(ctx); +} + +static int vb2ops_vdec_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct aml_vdec_ctx *ctx =3D vb2_get_drv_priv(vq); + struct aml_q_data *q_data; + unsigned int i; + + q_data =3D aml_vdec_get_qdata_by_type(ctx, vq->type); + if (!q_data) { + dev_err(&ctx->dev->plat_dev->dev, "not supported vq type\n"); + return -EINVAL; + } + + if (*nplanes) { + if (*nplanes !=3D q_data->fmt->num_planes) + return -EINVAL; + + for (i =3D 0; i < *nplanes; i++) { + if (sizes[i] < q_data->sizeimage[i]) { + dev_err(&ctx->dev->plat_dev->dev, "not supported sizeimage\n"); + return -EINVAL; + } + alloc_devs[i] =3D &ctx->dev->plat_dev->dev; + } + } else { + if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + *nplanes =3D q_data->fmt->num_planes; + else if (vq->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + *nplanes =3D 1; + + for (i =3D 0; i < *nplanes; i++) { + alloc_devs[i] =3D &ctx->dev->plat_dev->dev; + sizes[i] =3D q_data->sizeimage[i]; + } + } + + if (*nplanes) { + dev_dbg(&ctx->dev->plat_dev->dev, "type: %d, plane: %d, buf cnt: %d, siz= e: [Y: %u, C: %u]\n", + vq->type, *nplanes, *nbuffers, sizes[0], sizes[1]); + return 0; + } + + return -EINVAL; +} + +static int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) +{ + struct aml_vdec_ctx *ctx =3D vb2_get_drv_priv(vb->vb2_queue); + struct aml_q_data *q_data; + unsigned int sizeimage =3D 0; + int i; + + q_data =3D aml_vdec_get_qdata_by_type(ctx, vb->type); + if (!q_data) + return -EINVAL; + + for (i =3D 0; i < q_data->fmt->num_planes; i++) { + sizeimage =3D q_data->sizeimage[i]; + if (vb2_plane_size(vb, i) < sizeimage) + return -EINVAL; + + if (V4L2_TYPE_IS_CAPTURE(vb->type)) + vb2_set_plane_payload(vb, i, q_data->sizeimage[i]); + } + + return 0; +} + +static int vb2_ops_vdec_buf_init(struct vb2_buffer *vb) +{ + return 0; +} + +static void vb2_ops_vdec_buf_queue(struct vb2_buffer *vb) +{ + struct aml_vdec_ctx *ctx =3D vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2 =3D to_vb2_v4l2_buffer(vb); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb2_v4l2); +} + +static void vb2_ops_vdec_buf_finish(struct vb2_buffer *vb) +{ +} + +static int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int c= ount) +{ + struct aml_vdec_ctx *ctx =3D vb2_get_drv_priv(q); + struct aml_q_data *q_data; + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + ctx->is_output_streamon =3D 1; + q_data =3D aml_vdec_get_qdata_by_type(ctx, q->type); + if (aml_vdec_init_dec_inst(ctx, q_data->fmt) < 0) + return -EINVAL; + } else { + ctx->is_cap_streamon =3D 1; + } + + return 0; +} + +static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) +{ + struct aml_vdec_ctx *ctx =3D vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *src_buf =3D NULL, *dst_buf =3D NULL; + + aml_vdec_release_instance(ctx); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + while ((src_buf =3D v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + ctx->is_output_streamon =3D 0; + } else { + while ((dst_buf =3D v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + ctx->is_cap_streamon =3D 0; + } +} + +static int vb2ops_vdec_out_buf_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf =3D to_vb2_v4l2_buffer(vb); + + vbuf->field =3D V4L2_FIELD_NONE; + return 0; +} + +static void vb2ops_vdec_buf_request_complete(struct vb2_buffer *vb) +{ + struct aml_vdec_ctx *ctx =3D vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_handler); +} + +static const struct vb2_ops aml_vdec_vb2_ops =3D { + .queue_setup =3D vb2ops_vdec_queue_setup, + .wait_prepare =3D vb2_ops_wait_prepare, + .wait_finish =3D vb2_ops_wait_finish, + .start_streaming =3D vb2ops_vdec_start_streaming, + .stop_streaming =3D vb2ops_vdec_stop_streaming, + + .buf_init =3D vb2_ops_vdec_buf_init, + .buf_prepare =3D vb2ops_vdec_buf_prepare, + .buf_out_validate =3D vb2ops_vdec_out_buf_validate, + .buf_queue =3D vb2_ops_vdec_buf_queue, + .buf_finish =3D vb2_ops_vdec_buf_finish, + .buf_request_complete =3D vb2ops_vdec_buf_request_complete, +}; + +int aml_vdec_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct aml_vdec_ctx *ctx =3D (struct aml_vdec_ctx *)priv; + struct aml_vdec_dev *dev =3D ctx->dev; + int ret =3D 0; + + src_vq->type =3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes =3D VB2_MMAP | VB2_DMABUF; + src_vq->mem_ops =3D &vb2_dma_contig_memops; + src_vq->drv_priv =3D ctx; + src_vq->ops =3D &aml_vdec_vb2_ops; + src_vq->lock =3D &ctx->v4l2_intf_lock; + src_vq->buf_struct_size =3D sizeof(struct v4l2_m2m_buffer); + src_vq->supports_requests =3D true; + src_vq->timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret =3D vb2_queue_init(src_vq); + if (ret) { + v4l2_info(&dev->v4l2_dev, + "Failed to initialize videobuf2 queue(output)"); + return ret; + } + + dst_vq->type =3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes =3D VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv =3D ctx; + dst_vq->mem_ops =3D &vb2_dma_contig_memops; + dst_vq->ops =3D &aml_vdec_vb2_ops; + dst_vq->lock =3D &ctx->v4l2_intf_lock; + dst_vq->buf_struct_size =3D sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret =3D vb2_queue_init(dst_vq); + if (ret) { + v4l2_info(&dev->v4l2_dev, + "Failed to initialize videobuf2 queue(capture)"); + vb2_queue_release(src_vq); + } + + return ret; +} diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec.h b/drivers/media= /platform/amlogic/vdec/aml_vdec.h new file mode 100644 index 000000000000..a9ff93f25043 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#ifndef _AML_VDEC_H_ +#define _AML_VDEC_H_ + +#include "aml_vdec_drv.h" + +#define DEFAULT_OUT_IDX 0 /* set default output format to h264 type */ +#define DEFAULT_CAP_IDX 2 /* set default capture format to NV21 */ + +/** + * struct aml_vdec_v4l2_ctrl - helper type to declare supported ctrls + * @codec_type: codec id this control belong to (CODEC_TYPE_H264, etc.) + * @cfg: control configuration + */ +struct aml_vdec_v4l2_ctrl { + unsigned int codec_type; + struct v4l2_ctrl_config cfg; +}; + +extern const struct v4l2_m2m_ops aml_vdec_m2m_ops; +extern const struct v4l2_ioctl_ops aml_vdec_ioctl_ops; + +int aml_vdec_ctrls_setup(struct aml_vdec_ctx *ctx); +int aml_vdec_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +void aml_vdec_set_default_params(struct aml_vdec_ctx *ctx); +#endif diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_drv.c b/drivers/m= edia/platform/amlogic/vdec/aml_vdec_drv.c new file mode 100644 index 000000000000..7591e2958f42 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include +#include + +#include "aml_vdec.h" +#include "aml_vdec_hw.h" +#include "aml_vdec_platform.h" + +#define AML_VDEC_DRV_NAME "aml-vdec-drv" + +static int fops_vcodec_open(struct file *file) +{ + struct aml_vdec_dev *dec_dev =3D video_drvdata(file); + struct aml_vdec_ctx *ctx =3D NULL; + int ret =3D 0; + + ctx =3D kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_lock(&dec_dev->dev_mutex); + dec_dev->dec_ctx =3D ctx; + ctx->dev =3D dec_dev; + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data =3D &ctx->fh; + v4l2_fh_add(&ctx->fh, file); + dec_dev->filp =3D file; + mutex_init(&ctx->v4l2_intf_lock); + init_waitqueue_head(&ctx->queue); + ctx->int_cond =3D 0; + + ctx->m2m_ctx =3D v4l2_m2m_ctx_init(dec_dev->m2m_dev_dec, ctx, + &aml_vdec_queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + ret =3D PTR_ERR((__force void *)ctx->m2m_ctx); + v4l2_err(&dec_dev->v4l2_dev, "Failed to v4l2_m2m_ctx_init() (%d)", ret); + goto err_m2m_ctx_init; + } + + ctx->fh.m2m_ctx =3D ctx->m2m_ctx; + ret =3D aml_vdec_ctrls_setup(ctx); + if (ret) { + v4l2_err(&dec_dev->v4l2_dev, "Failed to init all ctrls (%d)", ret); + goto err_ctrls_setup; + } + + aml_vdec_set_default_params(ctx); + mutex_unlock(&dec_dev->dev_mutex); + + return ret; + +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_m2m_ctx_init: + v4l2_fh_del(&ctx->fh, file); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&dec_dev->dev_mutex); + + return ret; +} + +static int fops_vcodec_release(struct file *file) +{ + struct aml_vdec_dev *dec_dev =3D video_drvdata(file); + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + + mutex_lock(&dec_dev->dev_mutex); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_fh_del(&ctx->fh, file); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&dec_dev->dev_mutex); + + return 0; +} + +static const struct v4l2_file_operations aml_vdec_fops =3D { + .owner =3D THIS_MODULE, + .open =3D fops_vcodec_open, + .release =3D fops_vcodec_release, + .poll =3D v4l2_m2m_fop_poll, + .unlocked_ioctl =3D video_ioctl2, + .mmap =3D v4l2_m2m_fop_mmap, +}; + +static const struct video_device dec_dev =3D { + .name =3D "aml_dev_drv", + .fops =3D &aml_vdec_fops, + .ioctl_ops =3D &aml_vdec_ioctl_ops, + .release =3D video_device_release, + .vfl_dir =3D VFL_DIR_M2M, + .device_caps =3D V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, +}; + +static const struct media_device_ops aml_m2m_media_ops =3D { + .req_validate =3D vb2_request_validate, + .req_queue =3D v4l2_m2m_request_queue, +}; + +static int aml_vdec_drv_probe(struct platform_device *pdev) +{ + struct aml_vdec_dev *dev; + struct video_device *vfd_dec; + struct aml_vdec_hw *hw; + int ret =3D 0; + + dev =3D devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->plat_dev =3D pdev; + mutex_init(&dev->dev_mutex); + + ret =3D v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "v4l2_device_register err\n"); + + vfd_dec =3D video_device_alloc(); + if (!vfd_dec) { + v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); + ret =3D -ENOMEM; + goto err_device_alloc; + } + *vfd_dec =3D dec_dev; + vfd_dec->v4l2_dev =3D &dev->v4l2_dev; + vfd_dec->lock =3D &dev->dev_mutex; + video_set_drvdata(vfd_dec, dev); + dev->vfd =3D vfd_dec; + platform_set_drvdata(pdev, dev); + + hw =3D devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL); + if (!hw) { + ret =3D -ENOMEM; + goto err_dec_mem_init; + } + dev->dec_hw =3D hw; + + dev->pvdec_data =3D of_device_get_match_data(&pdev->dev); + ret =3D dev->pvdec_data->req_hw_resource(dev); + if (ret < 0) + goto err_hw_init; + + dev->m2m_dev_dec =3D v4l2_m2m_init(&aml_vdec_m2m_ops); + if (IS_ERR(dev->m2m_dev_dec)) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem dec device\n"); + ret =3D PTR_ERR((__force void *)dev->m2m_dev_dec); + goto err_hw_init; + } + + ret =3D video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device"); + goto err_vid_dev_register; + } + + dev->mdev.dev =3D &pdev->dev; + strscpy(dev->mdev.model, AML_VDEC_DRV_NAME, sizeof(dev->mdev.model)); + media_device_init(&dev->mdev); + dev->mdev.ops =3D &aml_m2m_media_ops; + dev->v4l2_dev.mdev =3D &dev->mdev; + + ret =3D v4l2_m2m_register_media_controller(dev->m2m_dev_dec, vfd_dec, + MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto error_m2m_mc_register; + } + + ret =3D media_device_register(&dev->mdev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register media device"); + goto err_media_dev_register; + } + vdec_enable(dev->dec_hw); + return 0; + +err_media_dev_register: + v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec); +error_m2m_mc_register: + media_device_cleanup(&dev->mdev); +err_vid_dev_register: + v4l2_m2m_release(dev->m2m_dev_dec); +err_hw_init: + dev->dec_hw =3D NULL; +err_dec_mem_init: + video_device_release(vfd_dec); +err_device_alloc: + v4l2_device_unregister(&dev->v4l2_dev); + return ret; +} + +static void aml_vdec_drv_remove(struct platform_device *pdev) +{ + struct aml_vdec_dev *dev =3D platform_get_drvdata(pdev); + + vdec_disable(dev->dec_hw); + + if (media_devnode_is_registered(dev->mdev.devnode)) { + media_device_unregister(&dev->mdev); + media_device_cleanup(&dev->mdev); + } + + if (dev->m2m_dev_dec) + v4l2_m2m_release(dev->m2m_dev_dec); + if (dev->vfd) + video_unregister_device(dev->vfd); + if (dev->dec_hw) { + dev->pvdec_data->destroy_hw_resource(dev); + dev->dec_hw =3D NULL; + } + v4l2_device_unregister(&dev->v4l2_dev); +} + +static const struct of_device_id aml_vdec_match[] =3D { + {.compatible =3D "amlogic,s4-vcodec-dec", .data =3D &aml_vdec_s4_pdata}, + {}, +}; + +static struct platform_driver aml_vcodec_dec_driver =3D { + .probe =3D aml_vdec_drv_probe, + .remove =3D aml_vdec_drv_remove, + .driver =3D { + .name =3D AML_VDEC_DRV_NAME, + .of_match_table =3D aml_vdec_match, + }, +}; + +module_platform_driver(aml_vcodec_dec_driver); + +MODULE_DESCRIPTION("Amlogic V4L2 decoder driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_drv.h b/drivers/m= edia/platform/amlogic/vdec/aml_vdec_drv.h new file mode 100644 index 000000000000..18659eef87eb --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#ifndef _AML_VDEC_DRV_H_ +#define _AML_VDEC_DRV_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define AML_VCODEC_MAX_PLANES 3 +#define AML_VDEC_MIN_W 64U +#define AML_VDEC_MIN_H 64U +#define AML_VDEC_1080P_MAX_H 1088U +#define AML_VDEC_1080P_MAX_W 1920U + +struct aml_vdec_ctx; +/** + * enum aml_fmt_type - Type of format type + */ +enum aml_fmt_type { + AML_FMT_DEC =3D 0, + AML_FMT_FRAME =3D 1, +}; + +/** + * enum aml_codec_type - Type of codec format + */ +enum aml_codec_type { + CODEC_TYPE_H264 =3D 0, + CODEC_TYPE_FRAME, +}; + +/** + * enum aml_q_type - Type of queue : cap or output + */ +enum aml_q_type { + AML_Q_DATA_SRC =3D 0, + AML_Q_DATA_DST =3D 1, +}; + +/** + * struct aml_video_fmt - aml video decoder fmt information + * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. + * @type: Curr queue type: capture or output. + * @codec_type: Codec mode related. See aml_codec_type. + * @num_planes: Num planes of the format. + * @name: Name of the format. + * @stepwise: Supported range of frame sizes (only for bitstream formats). + */ +struct aml_video_fmt { + u32 fourcc; + enum aml_fmt_type type; + enum aml_codec_type codec_type; + u32 num_planes; + const u8 *name; + struct v4l2_frmsize_stepwise stepwise; +}; + +/** + * struct aml_q_data - aml video queue information + * @visible_width: Width for display. + * @visible_height: Height for display. + * @coded_width: Width for decode, which is 64/32 aligned. + * @coded_height: Height for decode, which is 64/32 aligned. + * @filed_flag: Field pic flag. + * @bytesperline: Byte num of each pixel line. + * @sizeimage: Size of frame in bytes. + * @fmt: Format for curr queue. See struct aml_video_fmt. + */ +struct aml_q_data { + u32 visible_width; + u32 visible_height; + u32 coded_width; + u32 coded_height; + u32 filed_flag; + u32 bytesperline[AML_VCODEC_MAX_PLANES]; + u32 sizeimage[AML_VCODEC_MAX_PLANES]; + struct aml_video_fmt *fmt; +}; + +/** + * struct aml_vdec_dev - driver data + * @plat_dev: Platform device for the current driver. + * @v4l2_dev: V4L2 device to register video devices for. + * @m2m_dev_dec: Mem2mem device associated to this device. + * @vfd: Video_device associated to this device. + * @mdev: Media_device associated to this device. + * @dec_ctx: Decoder context. See struct aml_vdec_ctx. + * @dec_hw: Decoder hardware resources. See struct aml_vdec_hw. + * @pvdec_data: Decoder platform data. See struct aml_dev_platform_data. + * @dev_mutex: video_device lock. + * @filp: v4l2 file handle pointer. + */ +struct aml_vdec_dev { + struct platform_device *plat_dev; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev_dec; + struct video_device *vfd; + struct media_device mdev; + + struct aml_vdec_ctx *dec_ctx; + struct aml_vdec_hw *dec_hw; + const struct aml_dev_platform_data *pvdec_data; + + struct mutex dev_mutex; + struct file *filp; +}; + +/** + * struct dec_pic_info - pic information description + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + * @quantization: enum v4l2_quantization, colorspace quantization + * @cap_pix_fmt: Pixel format for capture queue. + * @output_pix_fmt: Pixel format for output queue. + * @coded_width: Width for decode. + * @coded_height: Height for decode. + * @fb_size: Frame buffer size for Y or UV. + * @plane_num: Num for planes for curr format. + */ +struct dec_pic_info { + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_xfer_func xfer_func; + enum v4l2_quantization quantization; + u32 cap_pix_fmt; + u32 output_pix_fmt; + u32 coded_width; + u32 coded_height; + u32 fb_size[2]; + u32 plane_num; +}; + +/** + * struct aml_vdec_ctx - driver instance context + * @dev: pointer to the aml_vdec_dev of the device. + * @fh: struct v4l2 fh. + * @m2m_ctx: pointer to v4l2_m2m_ctx context. + * @ctrl_handler: V4L2 ctrl handler. + * @v4l2_intf_lock: Mutex lock for v4l2 interface. + * @codec_ops: Codec operation functions. See struct aml_codec_ops. + * @int_cond: Variable used by the waitqueue. + * @queue: Waitqueue to wait for the current decode context finish. + * @q_data: feature supported by the current decoder instance. + * @is_cap_streamon: indicates if the current capture stream is on. + * @is_output_streamon: indicates if the current output stream is on. + * @dos_clk_en: indicates if dos clk is enabled. + * @pic_info: Pic information for curr decoder context. See struct dec_pic= _info. + * @curr_dec_type: Current decoder type. (CODEC_TYPE_H264, etc.) + * @codec_priv: Pointer to current decoder instance. + */ +struct aml_vdec_ctx { + struct aml_vdec_dev *dev; + struct v4l2_fh fh; + struct v4l2_m2m_ctx *m2m_ctx; + struct v4l2_ctrl_handler ctrl_handler; + struct mutex v4l2_intf_lock; + + const struct aml_codec_ops *codec_ops; + int int_cond; + wait_queue_head_t queue; + struct aml_q_data q_data[2]; + + bool is_cap_streamon; + bool is_output_streamon; + bool dos_clk_en; + + struct dec_pic_info pic_info; + u32 curr_dec_type; + void *codec_priv; +}; + +static inline struct aml_vdec_ctx *fh_to_dec_ctx(struct file *file) +{ + struct v4l2_fh *file_fh =3D file_to_v4l2_fh(file); + + return container_of(file_fh, struct aml_vdec_ctx, fh); +} + +static inline struct aml_vdec_ctx *ctrl_to_dec_ctx(struct v4l2_ctrl_handle= r *ctrl) +{ + return container_of(ctrl, struct aml_vdec_ctx, ctrl_handler); +} + +#endif diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_hw.c b/drivers/me= dia/platform/amlogic/vdec/aml_vdec_hw.c new file mode 100644 index 000000000000..1729076de76c --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aml_vdec_drv.h" +#include "aml_vdec_hw.h" +#include "aml_vdec_platform.h" + +#define MHz (1000000) +#define MC_SIZE (4096 * 16) + +static struct pm_pd_s vdec_domain_data[] =3D { + [VDEC_1] =3D {.name =3D "vdec", }, + [VDEC_HEVC] =3D {.name =3D "hevc", }, +}; + +u32 read_dos_reg(struct aml_vdec_hw *hw, u32 addr) +{ + u32 ret_val; + + regmap_read(hw->map[DOS_BUS], addr, &ret_val); + + return ret_val; +} + +static void dos_reg_write_bits(struct aml_vdec_hw *hw, u32 reg, u32 val, i= nt start, int len) +{ + u32 mask =3D (((1L << (len)) - 1) << (start)); + + regmap_update_bits(hw->map[DOS_BUS], reg, mask, val); +} + +void dos_enable(struct aml_vdec_hw *hw) +{ + dos_reg_write_bits(hw, DOS_GCLK_EN0, 0x3ff, 0, 10); + + regmap_write(hw->map[DOS_BUS], GCLK_EN, 0x3ff); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 31), 0); +} + +void aml_vdec_reset_core(struct aml_vdec_hw *hw) +{ + unsigned int mask =3D 0; + + mask =3D (1 << 21); + + regmap_update_bits(hw->map[DMC_BUS], 0x0, mask, 0); + usleep_range(60, 70); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, + (1 << 3) | (1 << 4) | (1 << 5) | (1 << 7) | + (1 << 8) | (1 << 9)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + regmap_update_bits(hw->map[DOS_BUS], VDEC_ASSIST_MMC_CTRL1, 1 << 3, 0); + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_MUX_CTRL, 1 << 31, 0); + regmap_write(hw->map[DOS_BUS], MDEC_EXTIF_CFG1, 0); + + regmap_update_bits(hw->map[DMC_BUS], 0x0, mask, mask); +} + +void aml_start_vdec_hw(struct aml_vdec_hw *hw) +{ + u32 reg_read_val; + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, (1 << 12) | (1 << 11)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_read_val); + + regmap_write(hw->map[DOS_BUS], MPSR, 0x0001); +} + +void aml_stop_vdec_hw(struct aml_vdec_hw *hw) +{ + u32 reg_val =3D 0; + int ret; + + regmap_write(hw->map[DOS_BUS], MPSR, 0); + regmap_write(hw->map[DOS_BUS], CPSR, 0); + + ret =3D read_poll_timeout_atomic(read_dos_reg, reg_val, + !(reg_val & 0x8000), + 10, 100000, true, + hw, IMEM_DMA_CTRL); + + ret =3D read_poll_timeout_atomic(read_dos_reg, reg_val, + !(reg_val & 0x8000), + 10, 100000, true, + hw, LMEM_DMA_CTRL); + + ret =3D read_poll_timeout_atomic(read_dos_reg, reg_val, + !(reg_val & 0xfff), + 10, 800000, true, + hw, WRRSP_LMEM); + if (ret) + dev_err(hw->dev, "%s, ctrl %x, rsp %x, pc %x status %x,%x\n", + __func__, read_dos_reg(hw, LMEM_DMA_CTRL), + read_dos_reg(hw, WRRSP_LMEM), read_dos_reg(hw, MPC_E), + read_dos_reg(hw, AV_SCRATCH_J), read_dos_reg(hw, AV_SCRATCH_9)); + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, (1 << 12) | (1 << 11)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); + regmap_read(hw->map[DOS_BUS], DOS_SW_RESET0, ®_val); +} + +int load_firmware(struct aml_vdec_hw *hw, const char *path) +{ + const struct firmware *fw; + struct device *dev =3D hw->dev; + static u8 *mc_addr; + static dma_addr_t mc_addr_map; + int fw_head_len; + ulong timeout; + int ret; + + ret =3D request_firmware(&fw, path, dev); + if (ret < 0) { + dev_info(dev, "request_firmware %s failed ret %d\n", path, ret); + return -EINVAL; + } + + if (fw->size > MC_SIZE) { + dev_info(dev, "fw %s oversize\n", path); + ret =3D -EINVAL; + goto release_firmware; + } + + fw_head_len =3D 512; + mc_addr =3D dma_alloc_coherent(dev, MC_SIZE, &mc_addr_map, GFP_KERNEL); + if (!mc_addr) { + dev_info(dev, "no mem for fw %s\n", path); + ret =3D -ENOMEM; + goto release_firmware; + } + memset(mc_addr, 0, MC_SIZE); + memcpy(mc_addr, ((u8 *)fw->data + fw_head_len), + (fw->size - fw_head_len)); + + regmap_write(hw->map[DOS_BUS], MPSR, 0); + regmap_write(hw->map[DOS_BUS], CPSR, 0); + + timeout =3D read_dos_reg(hw, MPSR); + timeout =3D read_dos_reg(hw, MPSR); + + timeout =3D jiffies + HZ; + + regmap_write(hw->map[DOS_BUS], IMEM_DMA_ADR, mc_addr_map); + regmap_write(hw->map[DOS_BUS], IMEM_DMA_COUNT, 0x1000); + regmap_write(hw->map[DOS_BUS], IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (read_dos_reg(hw, IMEM_DMA_CTRL) & 0x8000) { + if (time_before(jiffies, timeout)) { + schedule(); + } else { + dev_info(dev, "vdec load mc error\n"); + ret =3D -EBUSY; + break; + } + } + + /* Only h264 needs this step */ + if (hw->hw_ops.load_firmware_ex) { + ret =3D hw->hw_ops.load_firmware_ex(hw->curr_ctx, + mc_addr, + (fw->size - fw_head_len)); + if (ret < 0) { + ret =3D -EINVAL; + goto free_dma_mem; + } + } + +free_dma_mem: + dma_free_coherent(dev, MC_SIZE, mc_addr, mc_addr_map); +release_firmware: + release_firmware(fw); + return ret; +} + +static int vdec_clock_gate_init(struct aml_vdec_hw *hw) +{ + hw->gates[VDEC].id =3D "vdec"; + hw->gates[VDEC_MUX].id =3D "clk_vdec_mux"; + hw->gates[HEVCF_MUX].id =3D "clk_hevcf_mux"; + + return devm_clk_bulk_get(hw->dev, DOS_CLK_MAX, hw->gates); +} + +static struct clk_bulk_data *vdec_get_clk_by_name(struct aml_vdec_hw *hw, + const char *name) +{ + int i; + + for (i =3D 0; i < DOS_CLK_MAX; i++) { + if (!strcmp(name, hw->gates[i].id)) { + if (hw->gates[i].clk) + return &hw->gates[i]; + } + } + return NULL; +} + +static int pm_vdec_power_domain_init(struct aml_vdec_hw *hw) +{ + int i, err; + const struct power_manager_s *pm =3D hw->pm; + struct pm_pd_s *pd =3D pm->pd_data; + + mutex_init(&hw->pm_mutex); + + for (i =3D 0; i < VDEC_MAX; i++) { + pd[i].dev =3D dev_pm_domain_attach_by_name(hw->dev, pd[i].name); + if (IS_ERR_OR_NULL(pd[i].dev)) { + err =3D PTR_ERR(pd[i].dev); + dev_dbg(hw->dev, "Get %s failed, pm-domain: %d\n", + pd[i].name, err); + continue; + } + + pd[i].link =3D device_link_add(hw->dev, pd[i].dev, + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); + if (IS_ERR_OR_NULL(pd[i].link)) { + dev_err(hw->dev, "Adding %s device link failed!\n", pd[i].name); + return -ENODEV; + } + + dev_dbg(hw->dev, "power domain: name: %s, dev: %p, link: %p\n", + pd[i].name, pd[i].dev, pd[i].link); + } + + return 0; +} + +static void pm_vdec_power_domain_release(struct aml_vdec_hw *hw) +{ + int i; + const struct power_manager_s *pm =3D hw->pm; + struct pm_pd_s *pd =3D pm->pd_data; + + for (i =3D 0; i < VDEC_MAX; i++) { + if (!IS_ERR_OR_NULL(pd[i].link)) + device_link_del(pd[i].link); + + if (!IS_ERR_OR_NULL(pd[i].dev)) + dev_pm_domain_detach(pd[i].dev, true); + } +} + +static void dos_local_config(struct aml_vdec_hw *hw, bool is_on, int id) +{ + if (is_on) { + usleep_range(20, 100); + + switch (id) { + case VDEC_1: + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_VDEC, 0); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0xfffffffc); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_VDEC, 0); + break; + case VDEC_HEVC: + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_HEVC, 0); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0xffffffff); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_HEVC, 0); + break; + default: + dev_info(hw->dev, "%s on, not found id %d\n", __func__, id); + break; + } + } else { + switch (id) { + case VDEC_1: + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0xfffffffc); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_VDEC, 0xffffffffUL); + break; + case VDEC_HEVC: + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0xffffffff); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET3, 0); + usleep_range(20, 100); + regmap_write(hw->map[DOS_BUS], DOS_MEM_PD_HEVC, 0xffffffffUL); + break; + default: + dev_info(hw->dev, "%s off, not found id %d\n", __func__, id); + break; + } + } + + dev_dbg(hw->dev, "%s end, id %d, is_on %d\n", __func__, id, is_on); +} + +static void pm_vdec_power_domain_power_on(struct aml_vdec_hw *hw, int id) +{ + const struct power_manager_s *pm =3D hw->pm; + struct device *dev =3D pm->pd_data[id].dev; + struct clk_bulk_data *gate_node; + + if (id =3D=3D VDEC_1) + gate_node =3D vdec_get_clk_by_name(hw, "clk_vdec_mux"); + else if (id =3D=3D VDEC_HEVC) + gate_node =3D vdec_get_clk_by_name(hw, "clk_hevcf_mux"); + + if (gate_node) { + clk_prepare_enable(gate_node->clk); + if (id =3D=3D VDEC_1) { + clk_set_rate(gate_node->clk, 499999992); + dev_dbg(hw->dev, "after set, vdec mux clock is %lu Hz\n", + clk_get_rate(gate_node->clk)); + } + dev_dbg(hw->dev, "the %-15s clock on\n", gate_node->id); + } else { + dev_info(hw->dev, "clk %d, unreachable\n", id); + } + + if (dev) { + pm_runtime_get_sync(dev); + dev_dbg(dev, "dev: %p link %p the %-15s power on\n", + dev, pm->pd_data[id].link, pm->pd_data[id].name); + } + + dos_local_config(hw, 1, id); +} + +static void pm_vdec_power_domain_power_off(struct aml_vdec_hw *hw, int id) +{ + const struct power_manager_s *pm =3D hw->pm; + struct device *dev =3D pm->pd_data[id].dev; + struct clk_bulk_data *gate_node; + + dos_local_config(hw, 0, id); + + if (dev) { + pm_runtime_put_sync(dev); + dev_dbg(dev, "dev: %p link %p the %-15s power off\n", + dev, pm->pd_data[id].link, pm->pd_data[id].name); + } + + if (id =3D=3D VDEC_1) + gate_node =3D vdec_get_clk_by_name(hw, "clk_vdec_mux"); + else if (id =3D=3D VDEC_HEVC) + gate_node =3D vdec_get_clk_by_name(hw, "clk_hevcf_mux"); + + if (gate_node) { + clk_disable_unprepare(gate_node->clk); + dev_dbg(hw->dev, "the %-15s clock off\n", gate_node->id); + } else { + dev_info(hw->dev, "clk %d, unreachable\n", id); + } +} + +static bool pm_vdec_power_domain_power_state(struct aml_vdec_hw *hw, int i= d) +{ + if (hw->pm->pd_data[id].dev) + return pm_runtime_active(hw->pm->pd_data[id].dev); + else + return false; +} + +static void vdec_poweron(struct aml_vdec_hw *hw, enum vdec_type_e core) +{ + if (core >=3D VDEC_MAX) + return; + + mutex_lock(&hw->pm_mutex); + if (!hw->pm->pd_data[core].dev) + goto out; + + hw->pm->pd_data[core].ref_count++; + if (hw->pm->pd_data[core].ref_count > 1) + goto out; + + if (hw->pm->power_state(hw, core)) + goto out; + + hw->pm->power_on(hw, core); + +out: + mutex_unlock(&hw->pm_mutex); +} + +static void vdec_poweroff(struct aml_vdec_hw *hw, enum vdec_type_e core) +{ + if (core >=3D VDEC_MAX) + return; + + mutex_lock(&hw->pm_mutex); + if (hw->pm->pd_data[core].ref_count =3D=3D 0) + goto out; + + hw->pm->pd_data[core].ref_count--; + if (hw->pm->pd_data[core].ref_count > 0) + goto out; + + hw->pm->power_off(hw, core); + +out: + mutex_unlock(&hw->pm_mutex); +} + +int vdec_enable(struct aml_vdec_hw *hw) +{ + vdec_poweron(hw, VDEC_1); + + return 0; +} + +int vdec_disable(struct aml_vdec_hw *hw) +{ + vdec_poweroff(hw, VDEC_1); + + return 0; +} + +static const struct power_manager_s pm[] =3D { + [AML_PM_PD] =3D { + .pd_data =3D vdec_domain_data, + .init =3D pm_vdec_power_domain_init, + .release =3D pm_vdec_power_domain_release, + .power_on =3D pm_vdec_power_domain_power_on, + .power_off =3D pm_vdec_power_domain_power_off, + .power_state =3D pm_vdec_power_domain_power_state, + }, +}; + +static irqreturn_t vdec_irq_handler(int irq, void *priv) +{ + struct aml_vdec_dev *dev =3D (struct aml_vdec_dev *)priv; + struct aml_vdec_hw *hw =3D dev->dec_hw; + irqreturn_t ret; + + if (hw->hw_ops.irq_handler) + ret =3D hw->hw_ops.irq_handler(irq, priv); + + return ret; +} + +static irqreturn_t vdec_threaded_isr_handler(int irq, void *priv) +{ + struct aml_vdec_dev *dev =3D (struct aml_vdec_dev *)priv; + struct aml_vdec_hw *hw =3D dev->dec_hw; + irqreturn_t ret =3D IRQ_HANDLED; + + if (hw->hw_ops.irq_threaded_func) + ret =3D hw->hw_ops.irq_threaded_func(irq, priv); + + return ret; +} + +struct aml_vdec_hw *vdec_get_hw(void *priv) +{ + struct aml_vdec_dev *dev =3D (struct aml_vdec_dev *)priv; + + return dev->dec_hw; +} + +static const struct regmap_config dos_regmap_conf =3D { + .reg_bits =3D 32, + .val_bits =3D 32, + .reg_stride =3D 4, + .max_register =3D 0x10000, +}; + +static const struct regmap_config dmc_regmap_conf =3D { + .reg_bits =3D 32, + .val_bits =3D 32, + .reg_stride =3D 4, + .max_register =3D 0x20, +}; + +int dev_request_hw_resources(void *priv) +{ + struct aml_vdec_dev *dev =3D (struct aml_vdec_dev *)priv; + struct aml_vdec_hw *hw; + struct platform_device *pdev; + void __iomem *reg_base[MAX_REG_BUS]; + struct resource res; + int i; + int ret =3D -1; + + if (!dev || !dev->dec_hw) + return -1; + + pdev =3D dev->plat_dev; + hw =3D dev->dec_hw; + hw->dev =3D &pdev->dev; + + hw->dec_irq =3D platform_get_irq(pdev, VDEC_IRQ_1); + if (hw->dec_irq < 0) { + dev_err(&pdev->dev, "get irq failed\n"); + return hw->dec_irq; + } + ret =3D devm_request_threaded_irq(&pdev->dev, hw->dec_irq, vdec_irq_handl= er, + vdec_threaded_isr_handler, IRQF_ONESHOT, + "vdec-1", dev); + if (ret) { + dev_err(&pdev->dev, "failed to install irq %d (%d)", + hw->dec_irq, ret); + return -1; + } + + for (i =3D 0; i < MAX_REG_BUS; i++) { + if (of_address_to_resource(pdev->dev.of_node, i, &res)) { + dev_err(&pdev->dev, "of_address_to_resource %d failed\n", i); + return -EINVAL; + } + reg_base[i] =3D devm_ioremap_resource(&pdev->dev, &res); + + if (IS_ERR(reg_base[i])) + return PTR_ERR(reg_base[i]); + + if (i =3D=3D DOS_BUS) { + hw->map[i] =3D devm_regmap_init_mmio(&pdev->dev, reg_base[i], + &dos_regmap_conf); + } else if (i =3D=3D DMC_BUS) { + hw->map[i] =3D devm_regmap_init_mmio(&pdev->dev, reg_base[i], + &dmc_regmap_conf); + } + + if (IS_ERR(hw->map[i])) + return PTR_ERR(hw->map[i]); + + dev_dbg(&pdev->dev, "%s, res start %llx, end %llx, iomap: %p\n", + __func__, (unsigned long long)res.start, + (unsigned long long)res.end, reg_base[i]); + } + hw->canvas =3D meson_canvas_get(&pdev->dev); + if (IS_ERR(&pdev->dev)) + return PTR_ERR(&pdev->dev); + + hw->pm =3D &pm[dev->pvdec_data->power_type]; + if (hw->pm->init) { + ret =3D hw->pm->init(hw); + if (ret < 0) { + dev_err(&pdev->dev, "power mgr init failed!\n"); + return ret; + } + } + + ret =3D vdec_clock_gate_init(hw); + if (ret) { + dev_err(&pdev->dev, "clk bulk init failed!\n"); + return ret; + } + + dev_dbg(&pdev->dev, "##Amlogic hw resource init OK##\n"); + + return 0; +} + +void dev_destroy_hw_resources(void *priv) +{ + struct aml_vdec_dev *dev =3D (struct aml_vdec_dev *)priv; + struct aml_vdec_hw *hw; + + if (!dev || !dev->dec_hw) + return; + + hw =3D dev->dec_hw; + + if (hw->pm->release) + hw->pm->release(hw); + + dev_dbg(hw->dev, "##Amlogic hw resource release OK##\n"); +} diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_hw.h b/drivers/me= dia/platform/amlogic/vdec/aml_vdec_hw.h new file mode 100644 index 000000000000..6aac89a6356c --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_hw.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#ifndef _AML_VDEC_HW_H_ +#define _AML_VDEC_HW_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "reg_defines.h" + +#define VDEC_FIFO_ALIGN 8 +#define VLD_PADDING_SIZE 1024 + +/** + * struct aml_vdec_hw_ops - codec mode specific operations for hw + * @load_firmware_ex: Load firmware for current dec specific. + * @irq_handler: mandatory call when the ISR triggers + * @irq_threaded_func: mandatory call for the threaded ISR + * @canvas_alloc: Alloc canvas for curr frame + * @canvas_free: Release canvas. + * @config_canvas: Config for curr frame, such as w/h, Y/UV start addr etc. + */ +struct aml_vdec_hw_ops { + int (*load_firmware_ex)(void *priv, const u8 *data, u32 len); + irqreturn_t (*irq_handler)(int irq, void *priv); + irqreturn_t (*irq_threaded_func)(int irq, void *priv); + int (*canvas_alloc)(u8 *canvas_index); + void (*canvas_free)(u8 canvas_index); + void (*config_canvas)(u8 canvas_index, + ulong addr, u32 width, u32 height, + u32 wrap, u32 blkmode, u32 endian); +}; + +/** + * enum vdec_type_e - Type of decoder hardware. + */ +enum vdec_type_e { + VDEC_1 =3D 0, + VDEC_HEVC, + VDEC_MAX, +}; + +/** + * enum vdec_irq_num - Definition of the irq. + */ +enum vdec_irq_num { + VDEC_IRQ_0 =3D 0, + VDEC_IRQ_1, + VDEC_IRQ_2, + VDEC_IRQ_MAX, +}; + +/** + * enum vdec_type_e - Type of decoder clock. + */ +enum clk_type_e { + VDEC =3D 0, + VDEC_MUX, + HEVCF_MUX, + DOS_CLK_MAX, +}; + +/** + * enum aml_power_type_e - Type of decoder power. + */ +enum aml_power_type_e { + AML_PM_PD =3D 0, +}; + +/** + * enum mm_bus_e - Type of decoder hardware bus. + */ +enum mm_bus_e { + DOS_BUS =3D 0, + DMC_BUS, + MAX_REG_BUS +}; + +/** + * struct pm_pd_s - power domain definition + * @name: Power domain name. + * @dev: Pointer to device structure. + * @mutex: Pointer to device_link structure. + * @ref_count: Curr power domain instance ref count. + */ +struct pm_pd_s { + u8 *name; + struct device *dev; + struct device_link *link; + int ref_count; +}; + +/** + * struct aml_vdec_hw - decoder hardware resources definition + * @pdev: Pointer to struct platform_device. + * @dev: Pointer to struct device. + * @regs: Reg base for dos/dmc hardware. + * @pm_mutex: Mutex for pm->pd_data. + * @pm: Pointer to struct power_manager_s. + * @hw_ops: Hardware resource operation functions. See struct aml_vdec_hw_= ops. + * @gates: Clk instance used by curr decoder context. + * @dec_irq: Irq registered. + * @curr_ctx: Pointer to curr decoder context. + */ +struct aml_vdec_hw { + struct platform_device *pdev; + struct device *dev; + struct regmap *map[MAX_REG_BUS]; + struct mutex pm_mutex; + struct meson_canvas *canvas; + const struct power_manager_s *pm; + struct aml_vdec_hw_ops hw_ops; + struct clk_bulk_data gates[DOS_CLK_MAX]; + int dec_irq; + void *curr_ctx; +}; + +/** + * struct power_manager_s - Power manager & opertion function + * @pd_data: Pointer to struct pm_pd_s + * @init: Power manager init. + * @release: Power manager release. + * @power_on: Power on for decoder hw. + * @power_off: Power off for decoder hw. + * @power_state: Query the power state. + */ +struct power_manager_s { + struct pm_pd_s *pd_data; + int (*init)(struct aml_vdec_hw *hw); + void (*release)(struct aml_vdec_hw *hw); + void (*power_on)(struct aml_vdec_hw *hw, int id); + void (*power_off)(struct aml_vdec_hw *hw, int id); + bool (*power_state)(struct aml_vdec_hw *hw, int id); +}; + +int dev_request_hw_resources(void *priv); +void dev_destroy_hw_resources(void *priv); +struct aml_vdec_hw *vdec_get_hw(void *priv); +u32 read_dos_reg(struct aml_vdec_hw *hw, u32 reg_addr); +int vdec_enable(struct aml_vdec_hw *hw); +int vdec_disable(struct aml_vdec_hw *hw); +void dos_enable(struct aml_vdec_hw *hw); +void aml_start_vdec_hw(struct aml_vdec_hw *hw); +void aml_stop_vdec_hw(struct aml_vdec_hw *hw); +int load_firmware(struct aml_vdec_hw *hw, const char *path); +void aml_vdec_reset_core(struct aml_vdec_hw *hw); + +#endif diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_platform.c b/driv= ers/media/platform/amlogic/vdec/aml_vdec_platform.c new file mode 100644 index 000000000000..f1f8ba1f4ff6 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#include "aml_vdec_platform.h" +#include "aml_vdec_drv.h" +#include "aml_vdec_hw.h" +#include "h264.h" + +#define VIDEO_DEC_H264 "s4_h264_multi.bin" + +const struct aml_codec_ops aml_S4_dec_ops[] =3D { + [CODEC_TYPE_H264] =3D { + .init =3D aml_h264_init, + .exit =3D aml_h264_exit, + .run =3D aml_h264_dec_run, + }, +}; + +static const struct aml_video_dec_fmt aml_S4_dec_fmts =3D { + .max_height =3D AML_VDEC_1080P_MAX_H, + .max_width =3D AML_VDEC_1080P_MAX_W, + .align =3D 32, + .is_10_bit_support =3D 0, +}; + +const struct aml_dev_platform_data aml_vdec_s4_pdata =3D { + .dec_fmt =3D &aml_S4_dec_fmts, + .codec_ops =3D aml_S4_dec_ops, + .power_type =3D AML_PM_PD, + .req_hw_resource =3D dev_request_hw_resources, + .destroy_hw_resource =3D dev_destroy_hw_resources, + .fw_path =3D { + VIDEO_DEC_H264, + }, +}; diff --git a/drivers/media/platform/amlogic/vdec/aml_vdec_platform.h b/driv= ers/media/platform/amlogic/vdec/aml_vdec_platform.h new file mode 100644 index 000000000000..ff0933f6f074 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#ifndef AML_VDEC_PLATFORM_H_ +#define AML_VDEC_PLATFORM_H_ + +#include + +#define MAX_DEC_FORMAT 3 + +/** + * struct aml_codec_ops - codec mode specific operations + * @init: Used for decoder initialization. + * @exit: If needed, can be used to undo the .init phase. + * @run: Start a single decoding job. Called from atomic context. + * Caller should ensure that a pair of buffers is ready and the + * hardware is powered on and clk is enabled. Returns zero if OK, + * a negative value in error cases. + */ +struct aml_codec_ops { + int (*init)(void *priv); + void (*exit)(void *priv); + int (*run)(void *priv); +}; + +/** + * struct aml_video_dec_fmt - codec format required by platform + * @max_height: Max decode frame height of current platform. + * @max_width: Max decode frame width of current platform. + * @align: Align requirement of the current platform. + * @is_10_bit_support: Whether the platform supports 10 bit. + */ +struct aml_video_dec_fmt { + u32 max_height; + u32 max_width; + int align; + int is_10_bit_support; +}; + +/** + * struct aml_dev_platform_data - compatible data for each chip. + * @dec_fmt: Support dec format. + * @codec_ops: Codec operation function. + * @req_hw_resource: Operation function to request the hardware resource. + * @destroy_hw_resource: Operation function to release the hardware resour= ce. + * @power_type: Type of power that the current chip need. See aml_power_ty= pe_e. + * @fw_path: Path of the firmware.bin. + */ +struct aml_dev_platform_data { + const struct aml_video_dec_fmt *dec_fmt; + const struct aml_codec_ops *codec_ops; + int (*req_hw_resource)(void *priv); + void (*destroy_hw_resource)(void *priv); + int power_type; + const char *fw_path[MAX_DEC_FORMAT]; +}; + +extern const struct aml_dev_platform_data aml_vdec_s4_pdata; + +#endif diff --git a/drivers/media/platform/amlogic/vdec/h264.c b/drivers/media/pla= tform/amlogic/vdec/h264.c new file mode 100644 index 000000000000..d8619e160a6b --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/h264.c @@ -0,0 +1,1933 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#include +#include +#include "aml_vdec.h" +#include "aml_vdec_hw.h" +#include "h264.h" + +#define INVALID_POC 0xffffffff + +#define H264_SLICE_HEADER_DONE 0x1 +#define H264_SLICE_DATA_DONE 0x2 + +#define H264_MAX_COL_BUF 32 +#define H264_MAX_CANVAS_POS 26 + +#define DECODER_TIMEOUT_MS 500 + +#define COL_BUFFER_MARGIN 2 +#define COL_SIZE_FOR_ONE_MB 96 + +struct vdec_h264_stateless_ctrl_ref { + const struct v4l2_ctrl_h264_decode_params *decode; + const struct v4l2_ctrl_h264_scaling_matrix *scaling; + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; +}; + +enum SliceType { + P_SLICE =3D 0, + B_SLICE =3D 1, + I_SLICE =3D 2, + SP_SLICE =3D 3, + SI_SLICE =3D 4, + MAX_SLICE_TYPES =3D 5 +}; + +/* Used by firmware */ +union param { + struct { + unsigned short data[RPM_END - RPM_BEGIN]; + } l; + struct { + unsigned short dump[DPB_OFFSET]; + unsigned short dpb_base[FRAME_IN_DPB << 3]; + + unsigned short dpb_max_buffer_frame; + unsigned short actual_dpb_size; + + unsigned short colocated_buf_status; + + unsigned short num_forward_short_term_reference_pic; + unsigned short num_short_term_reference_pic; + unsigned short num_reference_pic; + + unsigned short current_dpb_index; + unsigned short current_decoded_frame_num; + unsigned short current_reference_frame_num; + + unsigned short l0_size; + unsigned short l1_size; + /** + * [6:5] : nal_ref_idc + * [4:0] : nal_unit_type + */ + unsigned short NAL_info_mmco; + /** + * [1:0] : 00 - top field, 01 - bottom field, + * 10 - frame, 11 - mbaff frame + */ + unsigned short picture_structure_mmco; + unsigned short frame_num; + unsigned short pic_order_cnt_lsb; + + unsigned short num_ref_idx_l0_active_minus1; + unsigned short num_ref_idx_l1_active_minus1; + + unsigned short PrevPicOrderCntLsb; + unsigned short PreviousFrameNum; + + /* 32 bits variables */ + unsigned short delta_pic_order_cnt_bottom[2]; + unsigned short delta_pic_order_cnt_0[2]; + unsigned short delta_pic_order_cnt_1[2]; + + unsigned short PrevPicOrderCntMsb[2]; + unsigned short PrevFrameNumOffset[2]; + + unsigned short frame_pic_order_cnt[2]; + unsigned short top_field_pic_order_cnt[2]; + unsigned short bottom_field_pic_order_cnt[2]; + + unsigned short colocated_mv_addr_start[2]; + unsigned short colocated_mv_addr_end[2]; + unsigned short colocated_mv_wr_addr[2]; + } dpb; + struct { + unsigned short dump[MMCO_OFFSET]; + + /* array base address for offset_for_ref_frame */ + unsigned short offset_for_ref_frame_base[128]; + + /** + * 0 - Index in DPB + * 1 - Picture Flag + * [2] : 0 - short term reference, + * 1 - long term reference + * [1] : bottom field + * [0] : top field + * 2 - Picture Number (short term or long term) low 16 bits + * 3 - Picture Number (short term or long term) high 16 bits + */ + unsigned short reference_base[128]; + + /* command and parameter, until command is 3 */ + unsigned short l0_reorder_cmd[REORDER_CMD_MAX]; + unsigned short l1_reorder_cmd[REORDER_CMD_MAX]; + + /* command and parameter, until command is 0 */ + unsigned short mmco_cmd[44]; + + unsigned short l0_base[40]; + unsigned short l1_base[40]; + } mmco; + struct { + /* from ucode lmem, do not change this struct */ + } p; +}; + +struct h264_decode_buf_spec { + struct v4l2_h264_dpb_entry *dpb; + u32 canvas_pos; + u32 dpb_index; + u32 poc; + int col_buf_index; + u8 y_canvas_index; + u8 u_canvas_index; + u8 v_canvas_index; + u8 used; + u8 long_term_flag; + dma_addr_t y_dma_addr; + dma_addr_t c_dma_addr; +}; + +#define REORDERING_COMMAND_MAX_SIZE 33 +struct slice { + int frame_num; + /*modification */ + int slice_type; + int num_ref_idx_l0; + int num_ref_idx_l1; + int first_mb_in_slice; + int ref_pic_list_reordering_flag[2]; + int modification_of_pic_nums_idc[2][REORDERING_COMMAND_MAX_SIZE]; + int abs_diff_pic_num_minus1[2][REORDERING_COMMAND_MAX_SIZE]; + int long_term_pic_idx[2][REORDERING_COMMAND_MAX_SIZE]; + unsigned char dec_ref_pic_marking_buffer_valid; +}; + +struct aml_h264_ctx { + struct aml_vdec_ctx *v4l2_ctx; + u8 init_flag; + u8 new_pic_flag; + u8 mc_cpu_loaded; + u8 param_set; + u8 colocated_buf_num; + u8 reg_iqidct_control_init_flag; + u32 reg_iqidct_control; + u32 reg_vcop_ctrl_reg; + u32 reg_rv_ai_mb_count; + u32 vld_dec_control; + u32 save_avscratch_f; + u32 seq_info; + u32 decode_pic_count; + union param dpb_param; + u32 dec_status; + struct slice mslice; + struct h264_decode_buf_spec curr_spec; + struct h264_decode_buf_spec ref_list0[V4L2_H264_NUM_DPB_ENTRIES + 1]; + struct h264_decode_buf_spec ref_list1[V4L2_H264_NUM_DPB_ENTRIES + 1]; + struct h264_decode_buf_spec ref_list0_unreordered[V4L2_H264_NUM_DPB_ENTRI= ES + 1]; + struct h264_decode_buf_spec ref_list1_unreordered[V4L2_H264_NUM_DPB_ENTRI= ES + 1]; + u8 list_size[2]; + dma_addr_t lmem_phy_addr; + void *lmem_addr; + dma_addr_t mc_cpu_paddr; + void *mc_cpu_vaddr; + dma_addr_t cma_alloc_addr; + void *cma_alloc_vaddr; + dma_addr_t collated_cma_addr; + dma_addr_t collated_cma_addr_end; + void *collated_cma_vaddr; + dma_addr_t workspace_offset; + void *workspace_vaddr; + u32 col_buf_alloc_size; + u32 one_col_buf_size; + u32 colocated_buf_map; + int colocated_buf_poc[H264_MAX_COL_BUF]; + int canvas_pos_poc[H264_MAX_CANVAS_POS]; + + u32 frame_width; + u32 frame_height; + u32 mb_width; + u32 mb_height; + u32 mb_total; + u32 max_num_ref_frames; + + struct vdec_h264_stateless_ctrl_ref ctrl_ref; +}; + +static inline int get_flag(u32 flag, u32 mask) +{ + return (flag & mask) ? 1 : 0; +} + +static inline void write_lmem(unsigned short *base, u32 offset, u32 value) +{ + base[offset] =3D value; +} + +static inline uint32_t spec2canvas(struct h264_decode_buf_spec *buf_spec) +{ + return (buf_spec->v_canvas_index << 16) | + (buf_spec->u_canvas_index << 8) | + (buf_spec->y_canvas_index << 0); +} + +static struct h264_decode_buf_spec *find_spec_by_dpb_index(struct aml_h264= _ctx *h264_ctx, int index, + int list) +{ + int i; + int size; + struct h264_decode_buf_spec *ref_list; + + size =3D h264_ctx->list_size[list]; + if (list =3D=3D 0) + ref_list =3D &h264_ctx->ref_list0[0]; + else + ref_list =3D &h264_ctx->ref_list1[0]; + + for (i =3D 0; i < size; i++) { + if (index =3D=3D ref_list[i].dpb_index) + return &ref_list[i]; + } + + return NULL; +} + +static int h264_prepare_input(struct aml_vdec_ctx *ctx) +{ + struct aml_vdec_hw *hw =3D vdec_get_hw(ctx->dev); + struct vb2_v4l2_buffer *src; + struct vb2_buffer *vb; + dma_addr_t src_dma; + u32 payload_size; + int dummy; + + src =3D v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + if (!src) { + dev_info(hw->dev, "no input buffer available!\n"); + return -1; + } + vb =3D &src->vb2_buf; + payload_size =3D vb2_get_plane_payload(vb, 0); + src_dma =3D vb2_dma_contig_plane_dma_addr(vb, 0); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 0); + /* reset VLD fifo for all vdec */ + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, (1 << 5) | (1 << 4) | (1 <<= 3)); + regmap_write(hw->map[DOS_BUS], DOS_SW_RESET0, 0); + regmap_write(hw->map[DOS_BUS], POWER_CTL_VLD, 1 << 4); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_START_PTR, src_dma); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_END_PTR, (src_dma + payload= _size)); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CURR_PTR, + round_down(src_dma, VDEC_FIFO_ALIGN)); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 1); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 0); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_BUF_CNTL, 2); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_RP, + round_down(src_dma, VDEC_FIFO_ALIGN)); + dummy =3D payload_size + VLD_PADDING_SIZE; + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_WP, + round_down((src_dma + dummy), VDEC_FIFO_ALIGN)); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_BUF_CNTL, 3); + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_BUF_CNTL, 2); + + regmap_write(hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, + (0x11 << 16) | (1 << 10) | (7 << 3)); + + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_1, 0x0); + regmap_write(hw->map[DOS_BUS], H264_DECODE_INFO, (1 << 13)); + regmap_write(hw->map[DOS_BUS], H264_DECODE_SIZE, payload_size); + regmap_write(hw->map[DOS_BUS], VIFF_BIT_CNT, payload_size * 8); + + return 0; +} + +static void config_sps_params(struct aml_h264_ctx *h264_ctx, + unsigned short *sps_base, + const struct v4l2_ctrl_h264_sps *sps) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + struct aml_vdec_hw *hw =3D vdec_get_hw(ctx->dev); + u32 cfg_tmp =3D 0; + u32 frame_size; + u32 offset =3D 0; + unsigned short data_tmp[0x100]; + int i, ii; + + memset(sps_base, 0, 0x100); + + h264_ctx->frame_width =3D (sps->pic_width_in_mbs_minus1 + 1) << 4; + h264_ctx->frame_height =3D (sps->pic_height_in_map_units_minus1 + 1) << 4; + + data_tmp[offset] =3D PARAM_BASE_VAL; + offset +=3D 2; + + data_tmp[offset++] =3D GET_SPS_PROFILE_IDC(sps->profile_idc); + + data_tmp[offset++] =3D GET_SPS_SEQ_PARAM_SET_ID(sps->seq_parameter_set_id= ) | + GET_SPS_LEVEL_IDC(sps->level_idc); + + if (sps->profile_idc >=3D 100) { + data_tmp[offset++] =3D GET_SPS_CHROMA_FORMAT_IDC(sps->chroma_format_idc); + + data_tmp[offset++] =3D ((sps->chroma_format_idc ^ 1) << 1); + } + + data_tmp[offset++] =3D GET_SPS_LOG2_MAX_FRAME_NUM(sps->log2_max_frame_num= _minus4); + data_tmp[offset++] =3D GET_SPS_PIC_ORDER_TYPE(sps->pic_order_cnt_type); + + if (sps->pic_order_cnt_type =3D=3D 0) { + data_tmp[offset++] =3D + GET_SPS_PIC_ORDER_CNT_LSB(sps->log2_max_pic_order_cnt_lsb_minus4); + } else if (sps->pic_order_cnt_type =3D=3D 1) { + data_tmp[offset++] =3D + get_flag(sps->flags, + V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); + data_tmp[offset++] =3D + GET_SPS_OFFSET_FOR_NONREF_PIC_LOW(sps->offset_for_non_ref_pic); + data_tmp[offset++] =3D + GET_SPS_OFFSET_FOR_NONREF_PIC_HIGH(sps->offset_for_non_ref_pic); + data_tmp[offset++] =3D + GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_LOW(sps->offset_for_top_to_bottom_fiel= d); + data_tmp[offset++] =3D + GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_HIGH(sps->offset_for_top_to_bottom_fie= ld); + data_tmp[offset++] =3D sps->num_ref_frames_in_pic_order_cnt_cycle; + } + + data_tmp[offset++] =3D GET_SPS_NUM_REF_FRAMES(sps->max_num_ref_frames) | + GET_SPS_GAPS_ALLOWED_FLAG(get_flag(sps->flags, + V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED)); + + data_tmp[offset++] =3D GET_SPS_PIC_WIDTH_IN_MBS(sps->pic_width_in_mbs_min= us1); + + data_tmp[offset++] =3D GET_SPS_PIC_HEIGHT_IN_MBS(sps->pic_height_in_map_u= nits_minus1); + data_tmp[offset++] =3D + GET_SPS_DIRECT_8X8_FLAGS + (get_flag(sps->flags, + V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)) | + GET_SPS_MB_ADAPTIVE_FRAME_FIELD_FLAGS + (get_flag(sps->flags, + V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)) | + GET_SPS_FRAME_MBS_ONLY_FLAGS + (get_flag(sps->flags, + V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)); + + for (i =3D 0; i < 0x100; i +=3D 4) { + for (ii =3D 0; ii < 4; ii++) + sps_base[i + 3 - ii] =3D data_tmp[i + ii]; + } + + frame_size =3D (sps->pic_width_in_mbs_minus1 + 1) * (sps->pic_height_in_m= ap_units_minus1 + 1); + cfg_tmp =3D (get_flag(sps->flags, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) << 3= 1) | + (sps->max_num_ref_frames << 24) | (frame_size << 8) | + (sps->pic_width_in_mbs_minus1 + 1); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_1, cfg_tmp); + h264_ctx->seq_info =3D cfg_tmp; + + cfg_tmp =3D 0; + cfg_tmp =3D (get_flag(sps->flags, V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE= ) << 15) | + (sps->chroma_format_idc); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_2, cfg_tmp); + + cfg_tmp =3D 0; + cfg_tmp =3D (sps->max_num_ref_frames << 8) | (sps->level_idc); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_B, cfg_tmp); + + cfg_tmp =3D ((sps->level_idc & 0xff) << 7) | + (get_flag(sps->flags, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) << 2); + regmap_write(hw->map[DOS_BUS], NAL_SEARCH_CTL, + read_dos_reg(hw, NAL_SEARCH_CTL) | cfg_tmp); + + h264_ctx->mb_width =3D (sps->pic_width_in_mbs_minus1 + 4) & 0xfffffffc; + h264_ctx->mb_height =3D (sps->pic_height_in_map_units_minus1 + 4) & 0xfff= ffffc; + h264_ctx->mb_total =3D h264_ctx->mb_width * h264_ctx->mb_height; + h264_ctx->max_num_ref_frames =3D sps->max_num_ref_frames; +} + +static void config_pps_params(struct aml_h264_ctx *h264_ctx, + unsigned short *pps_base, + const struct v4l2_ctrl_h264_pps *pps) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + struct aml_vdec_hw *hw =3D vdec_get_hw(ctx->dev); + u32 offset =3D 0; + unsigned short data_tmp[0x100]; + u32 max_reference_size =3D V4L2_H264_NUM_DPB_ENTRIES; + u32 max_list_size; + int i, ii; + + memset(pps_base, 0, 0x100); + + data_tmp[offset++] =3D PARAM_BASE_VAL; + + data_tmp[offset++] =3D + GET_PPS_PIC_PARAM_SET_ID(pps->pic_parameter_set_id) | + GET_PPS_SEQ_PARAM_SET_ID(pps->seq_parameter_set_id) | + GET_PPS_ENTROPY_CODING_MODE_FLAG + (get_flag(pps->flags, + V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE)) | + GET_PPS_PIC_ORDER_PRESENT_FLAG + (get_flag(pps->flags, + V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT)); + + data_tmp[offset++] =3D + GET_PPS_WEIGHTED_BIPRED_IDC(pps->weighted_bipred_idc) | + GET_PPS_WEIGHTED_PRED_FLAG(get_flag(pps->flags, + V4L2_H264_PPS_FLAG_WEIGHTED_PRED)) | + GET_PPS_NUM_IDX_REF_L1_MINUS1(pps->num_ref_idx_l1_default_active_minus1)= | + GET_PPS_NUM_IDX_REF_L0_MINUS1(pps->num_ref_idx_l0_default_active_minus1); + + data_tmp[offset++] =3D GET_PPS_INIT_QS_MINUS26(pps->pic_init_qs_minus26) | + GET_PPS_INIT_QP_MINUS26(pps->pic_init_qp_minus26); + + data_tmp[offset] =3D + GET_PPS_CHROMA_QP_INDEX_OFFSET(pps->chroma_qp_index_offset) | + GET_PPS_DEBLOCK_FILTER_CTRL_PRESENT_FLAG + (get_flag(pps->flags, + V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)) | + GET_PPS_CONSTRAIN_INTRA_PRED_FLAG + (get_flag(pps->flags, + V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED)) | + GET_PPS_REDUNDANT_PIC_CNT_PRESENT_FLAG + (get_flag(pps->flags, + V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT)); + if (get_flag + (pps->flags, + V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE | + V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) + data_tmp[offset] |=3D (1 << 11); + offset++; + + data_tmp[offset++] =3D + GET_PPS_SCALING_MATRIX_PRESENT_FLAG(get_flag + (pps->flags, + V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) | + GET_PPS_TRANSFORM_8X8_FLAG(get_flag + (pps->flags, + V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE)); + + data_tmp[offset++] =3D + GET_PPS_GET_SECOND_CHROMA_QP_OFFSET(pps->second_chroma_qp_index_offset); + + max_list_size =3D (pps->num_ref_idx_l1_default_active_minus1 + 1) + + (pps->num_ref_idx_l0_default_active_minus1 + 1); + + h264_ctx->max_num_ref_frames =3D max_list_size > h264_ctx->max_num_ref_fr= ames ? + max_list_size : h264_ctx->max_num_ref_frames; + + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_0, + ((h264_ctx->max_num_ref_frames + 1) << 24) | + (max_reference_size << 16) | (max_reference_size << 8)); + + for (i =3D 0; i < 0x100; i +=3D 4) { + for (ii =3D 0; ii < 4; ii++) + pps_base[i + 3 - ii] =3D data_tmp[i + ii]; + } +} + +static void h264_config_params(struct aml_vdec_ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)ctx->codec_priv; + unsigned short *p_sps_base, *p_pps_base; + struct vdec_h264_stateless_ctrl_ref *ctrls =3D &h264_ctx->ctrl_ref; + const struct v4l2_ctrl_h264_sps *sps =3D ctrls->sps; + const struct v4l2_ctrl_h264_pps *pps =3D ctrls->pps; + + p_sps_base =3D (unsigned short *)(h264_ctx->workspace_vaddr + + MEM_SPS_BASE + sps->seq_parameter_set_id * 0x400); + p_pps_base =3D (unsigned short *)(h264_ctx->workspace_vaddr + + MEM_PPS_BASE + pps->pic_parameter_set_id * 0x200); + + config_sps_params(h264_ctx, p_sps_base, sps); + config_pps_params(h264_ctx, p_pps_base, pps); +} + +static void config_decode_canvas(struct aml_vdec_hw *hw, + struct h264_decode_buf_spec *buf_spec, + u32 mb_width, u32 mb_height) +{ + int canvas_alloc_result =3D 0; + int blkmode =3D 0x0; + + canvas_alloc_result =3D meson_canvas_alloc(hw->canvas, &buf_spec->y_canva= s_index); + canvas_alloc_result =3D meson_canvas_alloc(hw->canvas, &buf_spec->u_canva= s_index); + buf_spec->v_canvas_index =3D buf_spec->u_canvas_index; + + if (!canvas_alloc_result) { + /* config y canvas */ + meson_canvas_config(hw->canvas, + buf_spec->y_canvas_index, buf_spec->y_dma_addr, + mb_width << 4, mb_height << 4, + MESON_CANVAS_WRAP_NONE, MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + regmap_write(hw->map[DOS_BUS], VDEC_ASSIST_CANVAS_BLK32, + (1 << 11) | /* canvas_blk32_wr */ + (blkmode << 10) | /* canvas_blk32 */ + (1 << 8) | /* canvas_index_wr */ + (buf_spec->y_canvas_index << 0) /* canvas index */ + ); + + /* config uv canvas */ + meson_canvas_config(hw->canvas, + buf_spec->u_canvas_index, buf_spec->c_dma_addr, + mb_width << 4, mb_height << 3, + MESON_CANVAS_WRAP_NONE, MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + regmap_write(hw->map[DOS_BUS], VDEC_ASSIST_CANVAS_BLK32, + (1 << 11) | /* canvas_blk32_wr */ + (blkmode << 10) | /* canvas_blk32 */ + (1 << 8) | /* canvas_index_wr */ + (buf_spec->u_canvas_index << 0) /* canvas index */ + ); + + regmap_write(hw->map[DOS_BUS], ANC0_CANVAS_ADDR + (buf_spec->canvas_pos = << 2), + spec2canvas(buf_spec)); + } +} + +static int allocate_colocate_buf(struct aml_h264_ctx *h264_ctx, int poc) +{ + int i; + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + + for (i =3D 0; i < h264_ctx->colocated_buf_num; i++) { + if (((h264_ctx->colocated_buf_map >> i) & 0x1) =3D=3D 0) { + h264_ctx->colocated_buf_map |=3D (1 << i); + break; + } + } + + if (i =3D=3D h264_ctx->colocated_buf_num) + return -1; + + h264_ctx->colocated_buf_poc[i] =3D poc; + dev_dbg(&ctx->dev->plat_dev->dev, "%s colocated_buf_index %d poc %d\n", _= _func__, i, + h264_ctx->colocated_buf_poc[i]); + + return i; +} + +static void release_colocate_buf(struct aml_h264_ctx *h264_ctx, int index) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + + if (index >=3D 0) { + if (index >=3D h264_ctx->colocated_buf_num) { + dev_dbg + (&ctx->dev->plat_dev->dev, + "%s error, index %d is bigger than buf count %d\n", + __func__, index, h264_ctx->max_num_ref_frames); + } else { + if (h264_ctx->colocated_buf_poc[index] !=3D INVALID_POC && + ((h264_ctx->colocated_buf_map >> index) & 0x1) =3D=3D 0x1) { + h264_ctx->colocated_buf_map &=3D (~(1 << index)); + dev_dbg + (&ctx->dev->plat_dev->dev, + "%s colocated_buf_index %d released poc %d\n", + __func__, index, + h264_ctx->colocated_buf_poc[index]); + } + h264_ctx->colocated_buf_poc[index] =3D INVALID_POC; + } + } +} + +static int get_col_buf_index_by_poc(struct aml_h264_ctx *h264_ctx, int poc) +{ + int idx; + + for (idx =3D 0; idx < h264_ctx->colocated_buf_num; idx++) { + if (h264_ctx->colocated_buf_poc[idx] =3D=3D poc) + break; + } + + if (idx =3D=3D h264_ctx->colocated_buf_num) + idx =3D -1; + + return idx; +} + +static int alloc_colocate_cma(struct aml_h264_ctx *h264_ctx, + struct aml_vdec_ctx *ctx) +{ + int alloc_size =3D 0; + int i; + struct aml_vdec_hw *hw; + + if (h264_ctx->collated_cma_vaddr) + return 0; + + hw =3D vdec_get_hw(ctx->dev); + if (!hw) + return -1; + + /* 96 :col buf size for each mb */ + h264_ctx->one_col_buf_size =3D h264_ctx->mb_total * 96; + alloc_size =3D PAGE_ALIGN(h264_ctx->one_col_buf_size * + (h264_ctx->max_num_ref_frames + COL_BUFFER_MARGIN)); + h264_ctx->collated_cma_vaddr =3D dma_alloc_coherent(hw->dev, alloc_size, + &h264_ctx->collated_cma_addr, GFP_KERNEL); + if (!h264_ctx->collated_cma_vaddr) + return -ENOMEM; + + dev_dbg + (&ctx->dev->plat_dev->dev, + "collated_cma_addr =3D 0x%llx, one_col_buf_size =3D %x alloc_size =3D= %x\n", + h264_ctx->collated_cma_addr, h264_ctx->one_col_buf_size, + alloc_size); + h264_ctx->collated_cma_addr_end =3D + h264_ctx->collated_cma_addr + alloc_size; + memset(h264_ctx->collated_cma_vaddr, 0, alloc_size); + h264_ctx->col_buf_alloc_size =3D alloc_size; + h264_ctx->colocated_buf_map =3D 0; + h264_ctx->colocated_buf_num =3D h264_ctx->max_num_ref_frames + COL_BUFFER= _MARGIN; + + for (i =3D 0; i < H264_MAX_COL_BUF; i++) + h264_ctx->colocated_buf_poc[i] =3D INVALID_POC; + + return 0; +} + +static void config_p_reflist(struct aml_h264_ctx *h264_ctx, + struct v4l2_h264_reference *v4l2_p0_reflist, + u32 list_size) +{ + struct vdec_h264_stateless_ctrl_ref *ctrls =3D &h264_ctx->ctrl_ref; + struct v4l2_ctrl_h264_decode_params *decode =3D + (struct v4l2_ctrl_h264_decode_params *)ctrls->decode; + struct v4l2_h264_dpb_entry *dpb =3D decode->dpb; + u8 index; + int i; + + for (i =3D 0; i < list_size; i++) { + index =3D v4l2_p0_reflist[i].index; + h264_ctx->ref_list0[i].used =3D 1; + h264_ctx->ref_list0[i].dpb =3D &dpb[index]; + h264_ctx->ref_list0[i].poc =3D dpb[index].top_field_order_cnt; + h264_ctx->ref_list0[i].long_term_flag =3D + dpb[index].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM ? true : false; + h264_ctx->ref_list0[i].dpb_index =3D index; + } + h264_ctx->list_size[0] =3D i; +} + +static void config_b_reflist(struct aml_h264_ctx *h264_ctx, + struct v4l2_h264_reference *v4l2_b0_reflist, + struct v4l2_h264_reference *v4l2_b1_reflist, + u32 list_size) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + struct vdec_h264_stateless_ctrl_ref *ctrls =3D &h264_ctx->ctrl_ref; + struct v4l2_ctrl_h264_decode_params *decode =3D + (struct v4l2_ctrl_h264_decode_params *)ctrls->decode; + struct v4l2_h264_dpb_entry *dpb =3D decode->dpb; + struct slice *curr_slice =3D &h264_ctx->mslice; + u8 index; + int i, j; + + h264_ctx->list_size[0] =3D curr_slice->num_ref_idx_l0; + for (i =3D 0; i < list_size && i < curr_slice->num_ref_idx_l0; i++) { + index =3D v4l2_b0_reflist[i].index; + h264_ctx->ref_list0[i].used =3D 1; + h264_ctx->ref_list0[i].dpb =3D &dpb[index]; + h264_ctx->ref_list0[i].poc =3D dpb[index].top_field_order_cnt; + h264_ctx->ref_list0[i].long_term_flag =3D + dpb[index].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM ? true : false; + h264_ctx->ref_list0[i].col_buf_index =3D + get_col_buf_index_by_poc(h264_ctx, dpb[index].top_field_order_cnt); + h264_ctx->ref_list0[i].dpb_index =3D index; + } + + h264_ctx->list_size[1] =3D curr_slice->num_ref_idx_l1; + for (j =3D 0; j < list_size && j < curr_slice->num_ref_idx_l1; j++) { + index =3D v4l2_b1_reflist[j].index; + h264_ctx->ref_list1[j].used =3D 1; + h264_ctx->ref_list1[j].dpb =3D &dpb[index]; + h264_ctx->ref_list1[j].poc =3D dpb[index].top_field_order_cnt; + h264_ctx->ref_list1[j].long_term_flag =3D + dpb[index].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM ? true : false; + h264_ctx->ref_list1[j].col_buf_index =3D + get_col_buf_index_by_poc(h264_ctx, dpb[index].top_field_order_cnt); + h264_ctx->ref_list1[j].dpb_index =3D index; + } + + if ((h264_ctx->list_size[1] + h264_ctx->list_size[0]) < list_size) + dev_info(&ctx->dev->plat_dev->dev, "ref list incorrect list0 %d list0 %d= list_size%d\n", + h264_ctx->list_size[0], h264_ctx->list_size[1], list_size); +} + +static int poc_is_in_dpb(int poc, const struct v4l2_h264_dpb_entry *dpb) +{ + int i; + int ret =3D 0; + + for (i =3D 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { + if (poc =3D=3D dpb[i].top_field_order_cnt) { + ret =3D 1; + break; + } + } + + return ret; +} + +static int get_ref_list_size(struct aml_h264_ctx *h264_ctx, int cur_list) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + unsigned short override_flag =3D h264_ctx->dpb_param.l.data[REF_IDC_OVERR= IDE_FLAG]; + int num_ref_idx_lx_active_minus1; + + if (cur_list =3D=3D 0) { + num_ref_idx_lx_active_minus1 =3D + h264_ctx->ctrl_ref.pps->num_ref_idx_l0_default_active_minus1; + if (override_flag) + num_ref_idx_lx_active_minus1 =3D + h264_ctx->dpb_param.dpb.num_ref_idx_l0_active_minus1; + } else { + num_ref_idx_lx_active_minus1 =3D + h264_ctx->ctrl_ref.pps->num_ref_idx_l1_default_active_minus1; + } + dev_dbg(&ctx->dev->plat_dev->dev, "%s get list %d size %d\n", + __func__, cur_list, num_ref_idx_lx_active_minus1 + 1); + + return num_ref_idx_lx_active_minus1 + 1; +} + +static int get_refidx_by_picnum(struct aml_h264_ctx *h264_ctx, int pic_num, + int curr_list) +{ + int i; + struct h264_decode_buf_spec *ref_list; + + if (curr_list =3D=3D 0) + ref_list =3D &h264_ctx->ref_list0[0]; + else + ref_list =3D &h264_ctx->ref_list1[0]; + + for (i =3D 0; ref_list[i].dpb; i++) { + if (pic_num =3D=3D ref_list[i].dpb->pic_num) + return i; + } + + return -1; +} + +static struct h264_decode_buf_spec *get_st_refpic_by_num(struct aml_h264_c= tx *h264_ctx, + int pic_num, int curr_list) +{ + int i; + struct h264_decode_buf_spec *ref_list; + + if (curr_list =3D=3D 0) + ref_list =3D &h264_ctx->ref_list0_unreordered[0]; + else + ref_list =3D &h264_ctx->ref_list1_unreordered[0]; + + for (i =3D 0; ref_list[i].dpb; i++) { + if (pic_num =3D=3D ref_list[i].dpb->pic_num && ref_list[i].long_term_fla= g =3D=3D 0) + return &ref_list[i]; + } + + return NULL; +} + +static struct h264_decode_buf_spec *get_lt_refpic_by_num(struct aml_h264_c= tx *h264_ctx, + int pic_num, int curr_list) +{ + int i; + struct h264_decode_buf_spec *ref_list; + + if (curr_list =3D=3D 0) + ref_list =3D &h264_ctx->ref_list0_unreordered[0]; + else + ref_list =3D &h264_ctx->ref_list1_unreordered[0]; + + for (i =3D 0; ref_list[i].dpb; i++) { + if (pic_num =3D=3D ref_list[i].dpb->pic_num && ref_list[i].long_term_fla= g =3D=3D 1) + return &ref_list[i]; + } + + return NULL; +} + +static void reorder_short_term(struct slice *curr_slice, int cur_list, + int pic_num_lx, int *ref_idx_lx) +{ + struct aml_h264_ctx *h264_ctx =3D container_of(curr_slice, struct aml_h26= 4_ctx, mslice); + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + int c_idx, n_idx; + int num_ref_idx_lx_active; + struct h264_decode_buf_spec *pic_lx =3D NULL; + struct h264_decode_buf_spec *ref_list_reordered; + + if (cur_list =3D=3D 0) + ref_list_reordered =3D &h264_ctx->ref_list0[0]; + else + ref_list_reordered =3D &h264_ctx->ref_list1[0]; + + num_ref_idx_lx_active =3D get_ref_list_size(h264_ctx, cur_list); + + /* find short-term ref frame with pic_num is pic_num_lx */ + pic_lx =3D get_st_refpic_by_num(h264_ctx, pic_num_lx, cur_list); + if (!pic_lx) { + dev_dbg(&ctx->dev->plat_dev->dev, "cannot find st pic_lx for %d\n", pic_= num_lx); + return; + } + + if (*ref_idx_lx =3D=3D get_refidx_by_picnum(h264_ctx, pic_num_lx, cur_lis= t)) { + dev_dbg(&ctx->dev->plat_dev->dev, "no need to move pic lx %d\n", *ref_id= x_lx); + *ref_idx_lx =3D *ref_idx_lx + 1; + return; + } + + for (c_idx =3D num_ref_idx_lx_active; c_idx > *ref_idx_lx; c_idx--) + memcpy(&ref_list_reordered[c_idx], &ref_list_reordered[c_idx - 1], + sizeof(struct h264_decode_buf_spec)); + + memcpy(&ref_list_reordered[*ref_idx_lx], pic_lx, sizeof(struct h264_decod= e_buf_spec)); + dev_dbg(&ctx->dev->plat_dev->dev, "%s : RefPicListX[%d ] =3D pic %p pic_n= um(%d)\n", __func__, + *ref_idx_lx, pic_lx, ref_list_reordered[*ref_idx_lx].dpb->pic_num); + *ref_idx_lx =3D *ref_idx_lx + 1; + + n_idx =3D *ref_idx_lx; + for (c_idx =3D *ref_idx_lx; c_idx <=3D num_ref_idx_lx_active; c_idx++) { + if (ref_list_reordered[c_idx].long_term_flag || !ref_list_reordered[c_id= x].dpb || + ref_list_reordered[c_idx].dpb->pic_num !=3D pic_num_lx) + memcpy(&ref_list_reordered[n_idx++], &ref_list_reordered[c_idx], + sizeof(struct h264_decode_buf_spec)); + } + + h264_ctx->list_size[cur_list] =3D num_ref_idx_lx_active; +} + +static void reorder_long_term(struct slice *curr_slice, int cur_list, + int lt_pic_num, int *ref_idx_lx) +{ + struct aml_h264_ctx *h264_ctx =3D container_of(curr_slice, struct aml_h26= 4_ctx, mslice); + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + int num_ref_idx_lx_active; + int c_idx, n_idx; + struct h264_decode_buf_spec *ref_list; + struct h264_decode_buf_spec *pic_lt =3D NULL; + + if (cur_list =3D=3D 0) + ref_list =3D &h264_ctx->ref_list0[0]; + else + ref_list =3D &h264_ctx->ref_list1[0]; + + num_ref_idx_lx_active =3D get_ref_list_size(h264_ctx, cur_list); + + /* find long-term ref frame with pic_num is lt_pic_num */ + pic_lt =3D get_lt_refpic_by_num(h264_ctx, lt_pic_num, cur_list); + if (!pic_lt) { + dev_dbg(&ctx->dev->plat_dev->dev, "cannot find lt pic_lx for %d\n", lt_p= ic_num); + return; + } + + if (*ref_idx_lx =3D=3D get_refidx_by_picnum(h264_ctx, lt_pic_num, cur_lis= t)) { + dev_dbg(&ctx->dev->plat_dev->dev, "no need to move pic lx %d\n", *ref_id= x_lx); + *ref_idx_lx =3D *ref_idx_lx + 1; + return; + } + + for (c_idx =3D num_ref_idx_lx_active; c_idx > *ref_idx_lx; c_idx--) + memcpy(&ref_list[c_idx], &ref_list[c_idx - 1], sizeof(struct h264_decode= _buf_spec)); + + memcpy(&ref_list[*ref_idx_lx], pic_lt, sizeof(struct h264_decode_buf_spec= )); + dev_dbg(&ctx->dev->plat_dev->dev, "%s : RefPicListX[%d ] =3D pic %p pic_n= um(%d)\n", __func__, + *ref_idx_lx, pic_lt, ref_list[*ref_idx_lx].dpb->pic_num); + *ref_idx_lx =3D *ref_idx_lx + 1; + + n_idx =3D *ref_idx_lx; + /* Pointer dpb is NULL means this is a dummy frame store */ + for (c_idx =3D *ref_idx_lx; c_idx <=3D num_ref_idx_lx_active; c_idx++) { + if (!ref_list[c_idx].long_term_flag || !ref_list[c_idx].dpb || + ref_list[c_idx].dpb->pic_num !=3D lt_pic_num) + memcpy(&ref_list[n_idx++], &ref_list[c_idx], + sizeof(struct h264_decode_buf_spec)); + } + + h264_ctx->list_size[cur_list] =3D num_ref_idx_lx_active; +} + +static void get_modification_cmd(unsigned short *reorder_cmd, struct slice= *curr_slice, int list) +{ + int i, j, val; + + val =3D curr_slice->ref_pic_list_reordering_flag[list]; + if (val) { + i =3D 0; + j =3D 0; + do { + curr_slice->modification_of_pic_nums_idc[list][i] =3D + reorder_cmd[j++]; + if (j >=3D REORDER_CMD_MAX) { + curr_slice->modification_of_pic_nums_idc[list][i] =3D 0; + break; + } + + val =3D curr_slice->modification_of_pic_nums_idc[list][i]; + if (val =3D=3D 0 || val =3D=3D 1) + curr_slice->abs_diff_pic_num_minus1[list][i] =3D reorder_cmd[j++]; + else if (val =3D=3D 2) + curr_slice->long_term_pic_idx[list][i] =3D reorder_cmd[j++]; + + i++; + + if (i >=3D REORDERING_COMMAND_MAX_SIZE) { + curr_slice->ref_pic_list_reordering_flag[list] =3D 0; + break; + }; + if (j > REORDER_CMD_MAX) { + curr_slice->ref_pic_list_reordering_flag[list] =3D 0; + break; + }; + } while (val !=3D 3); + } +} + +static void reorder_pics(struct aml_h264_ctx *h264_ctx, struct slice *curr= _slice, int cur_list) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + int *modification_of_pic_nums_idc =3D + curr_slice->modification_of_pic_nums_idc[cur_list]; + int *abs_diff_pic_num_minus1 =3D + curr_slice->abs_diff_pic_num_minus1[cur_list]; + int *long_term_pic_idx =3D curr_slice->long_term_pic_idx[cur_list]; + int pic_num_lx_nowarp, pic_num_lx_pred, pic_num_lx; + int curr_pic_num =3D curr_slice->frame_num; + int max_pic_num =3D 1 << (4 + h264_ctx->ctrl_ref.sps->log2_max_frame_num_= minus4); + int ref_idx_lx =3D 0; + int nowarp_tmp =3D 0; + int i; + + pic_num_lx_pred =3D curr_pic_num; + for (i =3D 0; i < REORDERING_COMMAND_MAX_SIZE && modification_of_pic_nums= _idc[i] !=3D 3; i++) { + if (modification_of_pic_nums_idc[i] > 3) { + dev_info(&ctx->dev->plat_dev->dev, "error, Invalid modification_of_pic_= nums_idc command\n"); + break; + } + + if (modification_of_pic_nums_idc[i] < 2) { + if (modification_of_pic_nums_idc[i] =3D=3D 0) { + nowarp_tmp =3D pic_num_lx_pred - (abs_diff_pic_num_minus1[i] + 1); + pic_num_lx_nowarp =3D nowarp_tmp + (nowarp_tmp < 0 ? max_pic_num : 0); + } else if (modification_of_pic_nums_idc[i] =3D=3D 1) { + nowarp_tmp =3D pic_num_lx_pred + (abs_diff_pic_num_minus1[i] + 1); + pic_num_lx_nowarp =3D nowarp_tmp - + (nowarp_tmp > max_pic_num ? max_pic_num : 0); + } + pic_num_lx_pred =3D pic_num_lx_nowarp; + if (pic_num_lx_nowarp > curr_pic_num) + pic_num_lx =3D pic_num_lx_nowarp - max_pic_num; + else + pic_num_lx =3D pic_num_lx_nowarp; + + reorder_short_term(curr_slice, cur_list, pic_num_lx, &ref_idx_lx); + } else { + reorder_long_term(curr_slice, cur_list, long_term_pic_idx[i], &ref_idx_= lx); + } + } +} + +static void copy_ref_list(struct aml_h264_ctx *h264_ctx, int curr_list) +{ + if (curr_list =3D=3D 0) + memcpy(h264_ctx->ref_list0_unreordered, h264_ctx->ref_list0, + sizeof(h264_ctx->ref_list0)); + else + memcpy(h264_ctx->ref_list1_unreordered, h264_ctx->ref_list0, + sizeof(h264_ctx->ref_list1)); +} + +static void h264_reorder_reflists(struct aml_h264_ctx *h264_ctx) +{ + unsigned short *reorder_cmd; + struct slice *curr_slice =3D &h264_ctx->mslice; + + if (curr_slice->slice_type !=3D I_SLICE && curr_slice->slice_type !=3D SI= _SLICE) { + reorder_cmd =3D &h264_ctx->dpb_param.mmco.l0_reorder_cmd[0]; + /* 3:parsed by ucode, means no reorder needed */ + if (reorder_cmd[0] !=3D 3) + curr_slice->ref_pic_list_reordering_flag[0] =3D 1; + else + curr_slice->ref_pic_list_reordering_flag[0] =3D 0; + + get_modification_cmd(reorder_cmd, curr_slice, 0); + } + + if (curr_slice->slice_type =3D=3D B_SLICE) { + reorder_cmd =3D &h264_ctx->dpb_param.mmco.l1_reorder_cmd[0]; + /* 3:parsed by ucode, means no reorder needed */ + if (reorder_cmd[0] !=3D 3) + curr_slice->ref_pic_list_reordering_flag[1] =3D 1; + else + curr_slice->ref_pic_list_reordering_flag[1] =3D 0; + + get_modification_cmd(reorder_cmd, curr_slice, 1); + } + + if (curr_slice->slice_type !=3D I_SLICE && + curr_slice->slice_type !=3D SI_SLICE && + curr_slice->ref_pic_list_reordering_flag[0] !=3D 0) { + copy_ref_list(h264_ctx, 0); + reorder_pics(h264_ctx, curr_slice, 0); + } + + if (curr_slice->slice_type =3D=3D B_SLICE && curr_slice->ref_pic_list_reo= rdering_flag[1] !=3D 0) { + copy_ref_list(h264_ctx, 1); + reorder_pics(h264_ctx, curr_slice, 1); + } +} + +static void h264_config_ref_lists(struct aml_vdec_ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)ctx->codec_priv; + struct vdec_h264_stateless_ctrl_ref *ctrls =3D &h264_ctx->ctrl_ref; + struct v4l2_ctrl_h264_decode_params *decode =3D + (struct v4l2_ctrl_h264_decode_params *)ctrls->decode; + struct v4l2_ctrl_h264_sps *sps =3D (struct v4l2_ctrl_h264_sps *)ctrls->sp= s; + const struct v4l2_h264_dpb_entry *dpb =3D decode->dpb; + struct v4l2_h264_reflist_builder builder; + struct v4l2_h264_reference v4l2_p0_reflist[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference v4l2_b0_reflist[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference v4l2_b1_reflist[V4L2_H264_REF_LIST_LEN]; + + if (decode->flags =3D=3D V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) + return; + + v4l2_h264_init_reflist_builder(&builder, decode, sps, dpb); + dev_dbg(&ctx->dev->plat_dev->dev, "%s num_valid =3D %d", __func__, builde= r.num_valid); + + if (decode->flags & V4L2_H264_DECODE_PARAM_FLAG_PFRAME) { + v4l2_h264_build_p_ref_list(&builder, v4l2_p0_reflist); + config_p_reflist(h264_ctx, v4l2_p0_reflist, builder.num_valid); + } else if (decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME) { + v4l2_h264_build_b_ref_lists(&builder, v4l2_b0_reflist, v4l2_b1_reflist); + config_b_reflist(h264_ctx, v4l2_b0_reflist, v4l2_b1_reflist, builder.num= _valid); + } +} + +static void clear_unused_col_buf(struct aml_h264_ctx *h264_ctx, + struct v4l2_ctrl_h264_decode_params *decode) +{ + int i, col_poc; + + /* flush all col buffers when IDR */ + if (decode->flags =3D=3D V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) { + /* 32 : max index of co-locate buffer */ + for (i =3D 0; i < 32; i++) + release_colocate_buf(h264_ctx, i); + return; + } + + for (i =3D 0; i < h264_ctx->colocated_buf_num; i++) { + col_poc =3D h264_ctx->colocated_buf_poc[i]; + if (col_poc !=3D INVALID_POC && (poc_is_in_dpb(col_poc, decode->dpb) != =3D 1)) + release_colocate_buf(h264_ctx, i); + } +} + +static void h264_config_decode_spec(struct aml_vdec_hw *hw, struct aml_vde= c_ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)hw->curr_ctx; + struct vdec_h264_stateless_ctrl_ref *ctrls =3D &h264_ctx->ctrl_ref; + struct v4l2_ctrl_h264_decode_params *decode =3D + (struct v4l2_ctrl_h264_decode_params *)ctrls->decode; + struct h264_decode_buf_spec *buf_spec_l0, *buf_spec_l1; + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb2_v4l2; + struct vb2_queue *vq; + int i; + + clear_unused_col_buf(h264_ctx, decode); + + vb2_v4l2 =3D v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + vb =3D &vb2_v4l2->vb2_buf; + + h264_ctx->curr_spec.y_dma_addr =3D vb2_dma_contig_plane_dma_addr(vb, 0); + if (ctx->pic_info.plane_num > 1) + h264_ctx->curr_spec.c_dma_addr =3D vb2_dma_contig_plane_dma_addr(vb, 1); + else + h264_ctx->curr_spec.c_dma_addr =3D + h264_ctx->curr_spec.y_dma_addr + ctx->pic_info.fb_size[0]; + h264_ctx->curr_spec.canvas_pos =3D 0; + if (decode->nal_ref_idc) + h264_ctx->curr_spec.col_buf_index =3D + allocate_colocate_buf(h264_ctx, decode->top_field_order_cnt); + else + h264_ctx->curr_spec.col_buf_index =3D -1; + h264_ctx->curr_spec.poc =3D decode->top_field_order_cnt; + config_decode_canvas(hw, &h264_ctx->curr_spec, h264_ctx->mb_width, h264_c= tx->mb_height); + h264_ctx->canvas_pos_poc[0] =3D decode->top_field_order_cnt; + + h264_config_ref_lists(ctx); + + vq =3D v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + for (i =3D 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { + struct v4l2_h264_dpb_entry *dpb =3D &decode->dpb[i]; + + if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + break; + + buf_spec_l0 =3D find_spec_by_dpb_index(h264_ctx, i, 0); + if (buf_spec_l0) { + buf_spec_l0->canvas_pos =3D i + 1; + vb =3D vb2_find_buffer(vq, dpb->reference_ts); + if (!vb) { + dev_err(&ctx->dev->plat_dev->dev, + "ref pic for ts %llu lost\n", dpb->reference_ts); + continue; + } + + buf_spec_l0->y_dma_addr =3D + vb2_dma_contig_plane_dma_addr(vb, 0); + if (ctx->pic_info.plane_num > 1) + buf_spec_l0->c_dma_addr =3D vb2_dma_contig_plane_dma_addr(vb, 1); + else + buf_spec_l0->c_dma_addr =3D + buf_spec_l0->y_dma_addr + ctx->pic_info.fb_size[0]; + config_decode_canvas(hw, buf_spec_l0, + h264_ctx->mb_width, h264_ctx->mb_height); + if (h264_ctx->canvas_pos_poc[buf_spec_l0->canvas_pos] =3D=3D INVALID_PO= C) + h264_ctx->canvas_pos_poc[buf_spec_l0->canvas_pos] =3D + buf_spec_l0->dpb->top_field_order_cnt; + dev_dbg + (&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx\n", + buf_spec_l0->dpb->top_field_order_cnt, + buf_spec_l0->canvas_pos, buf_spec_l0->y_dma_addr); + } + + buf_spec_l1 =3D find_spec_by_dpb_index(h264_ctx, i, 1); + if (!buf_spec_l0 && buf_spec_l1) { + buf_spec_l1->canvas_pos =3D i + 1; + vb =3D vb2_find_buffer(vq, dpb->reference_ts); + if (!vb) { + dev_err(&ctx->dev->plat_dev->dev, + "ref pic for ts %llu lost\n", dpb->reference_ts); + continue; + } + + buf_spec_l1->y_dma_addr =3D + vb2_dma_contig_plane_dma_addr(vb, 0); + if (ctx->pic_info.plane_num > 1) + buf_spec_l1->c_dma_addr =3D vb2_dma_contig_plane_dma_addr(vb, 1); + else + buf_spec_l1->c_dma_addr =3D + buf_spec_l1->y_dma_addr + ctx->pic_info.fb_size[0]; + config_decode_canvas(hw, buf_spec_l1, h264_ctx->mb_width, + h264_ctx->mb_height); + if (h264_ctx->canvas_pos_poc[buf_spec_l1->canvas_pos] =3D=3D INVALID_PO= C) + h264_ctx->canvas_pos_poc[buf_spec_l1->canvas_pos] =3D + buf_spec_l1->dpb->top_field_order_cnt; + dev_dbg + (&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx\n", + buf_spec_l1->dpb->top_field_order_cnt, + buf_spec_l1->canvas_pos, buf_spec_l1->y_dma_addr); + } else if (buf_spec_l0 && buf_spec_l1) { + memcpy(buf_spec_l1, buf_spec_l0, sizeof(struct h264_decode_buf_spec)); + dev_dbg + (&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx\n", + buf_spec_l1->dpb->top_field_order_cnt, + buf_spec_l1->canvas_pos, buf_spec_l1->y_dma_addr); + } + } + + h264_reorder_reflists(h264_ctx); +} + +static int h264_config_decode_buf(struct aml_vdec_hw *hw, struct aml_vdec_= ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)hw->curr_ctx; + struct vdec_h264_stateless_ctrl_ref *ctrls =3D &h264_ctx->ctrl_ref; + struct v4l2_ctrl_h264_decode_params *decode =3D + (struct v4l2_ctrl_h264_decode_params *)ctrls->decode; + unsigned int canvas_adr; + unsigned int ref_cfg; + unsigned int ref_cfg_once =3D 0; + struct slice *curr_slice =3D &h264_ctx->mslice; + unsigned int type_cfg =3D 0x3; /* 0x3: frame type */ + unsigned int colocate_adr_offset =3D 0; + unsigned int colocate_wr_adr; + unsigned int info0; + unsigned int info1; + unsigned int info2; + int i, j; + int h264_buffer_info_data_write_count; + u8 canvas_pos; + u8 use_mode_8x8_flag; + u32 reg_val; + + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC_IDX_RESET, 0); + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC, decode->top_field_order_= cnt); + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC, decode->top_field_order_= cnt); + regmap_write(hw->map[DOS_BUS], H264_CURRENT_POC, decode->bottom_field_ord= er_cnt); + regmap_write(hw->map[DOS_BUS], CURR_CANVAS_CTRL, h264_ctx->curr_spec.canv= as_pos << 24); + regmap_read(hw->map[DOS_BUS], CURR_CANVAS_CTRL, &canvas_adr); + canvas_adr &=3D 0xffffff; + dev_dbg(hw->dev, "canvas_pos =3D %d canvas_adr 0x%x\n", + h264_ctx->curr_spec.canvas_pos, canvas_adr); + + regmap_write(hw->map[DOS_BUS], REC_CANVAS_ADDR, canvas_adr); + regmap_write(hw->map[DOS_BUS], DBKR_CANVAS_ADDR, canvas_adr); + regmap_write(hw->map[DOS_BUS], DBKW_CANVAS_ADDR, canvas_adr); + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_INDEX, 16); + info0 =3D 0xf480; + info1 =3D decode->top_field_order_cnt; + info2 =3D decode->bottom_field_order_cnt; + if (decode->bottom_field_order_cnt < decode->top_field_order_cnt) + info0 |=3D 0x100; + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info0 | 0xf); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info1); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info2); + + for (j =3D 0; j < V4L2_H264_NUM_DPB_ENTRIES; j++) { + struct v4l2_h264_dpb_entry *dpb =3D &decode->dpb[j]; + + info0 =3D 0; + info1 =3D 0; + info2 =3D 0; + if (dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) { + info0 =3D 0xf480; + if (dpb->bottom_field_order_cnt < dpb->top_field_order_cnt) + info0 |=3D 0x100; + info1 =3D dpb->top_field_order_cnt; + info2 =3D dpb->bottom_field_order_cnt; + if (dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) + info0 |=3D ((1 << 5) | (1 << 4)); + } + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info0); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info1); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, info2); + } + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_INDEX, 0); + /* when frame width <=3D 256, Disable DDR_BYTE64_CACHE */ + if (ctx->pic_info.coded_width <=3D 256) { + regmap_update_bits(hw->map[DOS_BUS], IQIDCT_CONTROL, (1 << 16), (1 << 16= )); + regmap_write(hw->map[DOS_BUS], DCAC_DDR_BYTE64_CTL, + (read_dos_reg(hw, DCAC_DDR_BYTE64_CTL) & (~0xf)) | 0xa); + } else { + regmap_update_bits(hw->map[DOS_BUS], IQIDCT_CONTROL, (1 << 16), 0); + regmap_write(hw->map[DOS_BUS], DCAC_DDR_BYTE64_CTL, + (read_dos_reg(hw, DCAC_DDR_BYTE64_CTL) & (~0xf))); + } + + ref_cfg =3D 0; + j =3D 0; + + if (h264_ctx->list_size[0] > 0) { + for (i =3D 0; i < h264_ctx->list_size[0]; i++) { + canvas_pos =3D h264_ctx->ref_list0[i].canvas_pos; + /* bit 0:3 canvas_pos bit 5:6 frame struct cfg */ + ref_cfg_once =3D (canvas_pos & 0x1f) | (type_cfg << 5); + ref_cfg <<=3D 8; + ref_cfg |=3D ref_cfg_once; + j++; + + if (j =3D=3D 4) { + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + dev_dbg(hw->dev, "H264_BUFFER_INFO_DATA: %x\n", ref_cfg); + h264_buffer_info_data_write_count++; + j =3D 0; + } + } + + if (j !=3D 0) { + while (j !=3D 4) { + ref_cfg <<=3D 8; + ref_cfg |=3D ref_cfg_once; + j++; + } + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + dev_dbg(hw->dev, "H264_BUFFER_INFO_DATA: %x\n", ref_cfg); + h264_buffer_info_data_write_count++; + } + ref_cfg =3D (ref_cfg_once << 24) | (ref_cfg_once << 16) | + (ref_cfg_once << 8) | ref_cfg_once; + for (j =3D h264_buffer_info_data_write_count; j < 8; j++) + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + } + + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_INDEX, 8); + j =3D 0; + ref_cfg =3D 0; + + if (h264_ctx->list_size[1] > 0) { + for (i =3D 0; i < h264_ctx->list_size[1]; i++) { + canvas_pos =3D h264_ctx->ref_list1[i].canvas_pos; + ref_cfg_once =3D (canvas_pos & 0x1f) | (type_cfg << 5); + ref_cfg <<=3D 8; + ref_cfg |=3D ref_cfg_once; + j++; + + if (j =3D=3D 4) { + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + dev_dbg(hw->dev, "H264_BUFFER_INFO_DATA: %x\n", ref_cfg); + j =3D 0; + } + } + } + + if (j !=3D 0) { + while (j !=3D 4) { + ref_cfg <<=3D 8; + ref_cfg |=3D ref_cfg_once; + j++; + } + dev_dbg(hw->dev, "H264_BUFFER_INFO_DATA: %x\n", ref_cfg); + regmap_write(hw->map[DOS_BUS], H264_BUFFER_INFO_DATA, ref_cfg); + } + + if (get_flag(ctrls->sps->flags, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) && + get_flag(ctrls->sps->flags, V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)) + use_mode_8x8_flag =3D 1; + else + use_mode_8x8_flag =3D 0; + + read_poll_timeout(read_dos_reg, reg_val, + !(reg_val & 0x800), + 10, 0, true, + hw, H264_CO_MB_RW_CTL); + + /* col buf for curr frame */ + colocate_adr_offset =3D COL_SIZE_FOR_ONE_MB; + if (use_mode_8x8_flag) + colocate_adr_offset >>=3D 2; + colocate_adr_offset *=3D curr_slice->first_mb_in_slice; + + if (h264_ctx->curr_spec.col_buf_index >=3D 0 && + h264_ctx->curr_spec.col_buf_index < h264_ctx->colocated_buf_num) { + colocate_wr_adr =3D h264_ctx->collated_cma_addr + + ((h264_ctx->one_col_buf_size * h264_ctx->curr_spec.col_buf_index) >> + (use_mode_8x8_flag ? 2 : 0)); + if (colocate_adr_offset > h264_ctx->one_col_buf_size || + colocate_wr_adr + h264_ctx->one_col_buf_size > + h264_ctx->collated_cma_addr_end) { + dev_err + (hw->dev, "Error, colocate buf is not enough, index is %d\n", + h264_ctx->curr_spec.col_buf_index); + return -1; + } + regmap_write(hw->map[DOS_BUS], H264_CO_MB_WR_ADDR, + (colocate_wr_adr + colocate_adr_offset)); + dev_dbg(hw->dev, "col buffer addr =3D 0x%x col_buf_index %d\n", + (colocate_wr_adr + colocate_adr_offset), + h264_ctx->curr_spec.col_buf_index); + } else { + regmap_write(hw->map[DOS_BUS], H264_CO_MB_WR_ADDR, 0xffffffff); + dev_dbg(hw->dev, "col buffer addr =3D 0xffffffff\n"); + } + + if (h264_ctx->list_size[1] > 0) { + struct h264_decode_buf_spec *colocate_pic =3D &h264_ctx->ref_list1[0]; + struct h264_decode_buf_spec *curr_pic =3D &h264_ctx->curr_spec; + int l10_structure =3D 2; /* for pic struct =3D=3D FRAME, default to 2 */ + int cur_colocate_ref_type; + unsigned int colocate_rd_adr; + unsigned int colocate_rd_adr_offset =3D 0; + unsigned int val; + + cur_colocate_ref_type =3D + (abs(curr_pic->poc - colocate_pic->dpb->top_field_order_cnt) < + abs(curr_pic->poc - colocate_pic->dpb->bottom_field_order_cnt)) ? + 0 : 1; + colocate_rd_adr_offset =3D COL_SIZE_FOR_ONE_MB; + if (use_mode_8x8_flag) + colocate_rd_adr_offset >>=3D 2; + + colocate_rd_adr_offset *=3D curr_slice->first_mb_in_slice; + if (colocate_pic->col_buf_index >=3D 0 && + colocate_pic->col_buf_index < h264_ctx->colocated_buf_num) { + colocate_rd_adr =3D h264_ctx->collated_cma_addr + + ((h264_ctx->one_col_buf_size * colocate_pic->col_buf_index) >> + (use_mode_8x8_flag ? 2 : 0)); + if (colocate_rd_adr + h264_ctx->one_col_buf_size > + h264_ctx->collated_cma_addr_end) { + dev_err + (hw->dev, "Error, colocate rd buf is not enough, index is %d\n", + colocate_pic->col_buf_index); + return -1; + } + val =3D ((colocate_rd_adr_offset + colocate_rd_adr) >> 3) | + (cur_colocate_ref_type << 29) | + (l10_structure << 30); + regmap_write(hw->map[DOS_BUS], H264_CO_MB_RD_ADDR, val); + } else { + dev_err + (hw->dev, "Error, reference pic has no colocated buf poc %d\n", + curr_pic->poc); + return -1; + } + } + + return 0; +} + +static void release_canvas_index(struct aml_vdec_hw *hw, + struct h264_decode_buf_spec *buf) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)hw->curr_ctx; + + if (h264_ctx->canvas_pos_poc[buf->canvas_pos] =3D=3D INVALID_POC) + return; + + if (buf->y_canvas_index >=3D 0) { + meson_canvas_free(hw->canvas, buf->y_canvas_index); + buf->y_canvas_index =3D -1; + } + + if (buf->u_canvas_index >=3D 0) { + meson_canvas_free(hw->canvas, buf->u_canvas_index); + buf->u_canvas_index =3D -1; + buf->v_canvas_index =3D -1; + } + h264_ctx->canvas_pos_poc[buf->canvas_pos] =3D INVALID_POC; +} + +static void h264_release_decode_spec(struct aml_vdec_hw *hw, struct aml_vd= ec_ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)hw->curr_ctx; + int i; + struct h264_decode_buf_spec *buf; + + release_canvas_index(hw, &h264_ctx->curr_spec); + + if (h264_ctx->list_size[0] > 0) { + for (i =3D 0; i < h264_ctx->list_size[0]; i++) { + buf =3D &h264_ctx->ref_list0[i]; + if (buf->used) { + buf->dpb =3D NULL; + release_canvas_index(hw, buf); + buf->used =3D 0; + } + } + h264_ctx->list_size[0] =3D 0; + } + + if (h264_ctx->list_size[1] > 0) { + for (i =3D 0; i < h264_ctx->list_size[1]; i++) { + buf =3D &h264_ctx->ref_list1[i]; + if (buf->used) { + buf->dpb =3D NULL; + release_canvas_index(hw, buf); + buf->used =3D 0; + } + } + h264_ctx->list_size[1] =3D 0; + } +} + +static void save_reg_status(struct aml_h264_ctx *h264_ctx) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + struct aml_vdec_hw *hw =3D vdec_get_hw(ctx->dev); + + regmap_read(hw->map[DOS_BUS], IQIDCT_CONTROL, &h264_ctx->reg_iqidct_contr= ol); + h264_ctx->reg_iqidct_control_init_flag =3D 1; + regmap_read(hw->map[DOS_BUS], VCOP_CTRL_REG, &h264_ctx->reg_vcop_ctrl_reg= ); + regmap_read(hw->map[DOS_BUS], RV_AI_MB_COUNT, &h264_ctx->reg_rv_ai_mb_cou= nt); + regmap_read(hw->map[DOS_BUS], VLD_DECODE_CONTROL, &h264_ctx->vld_dec_cont= rol); +} + +static void h264_get_slice_params(struct aml_h264_ctx *h264_ctx) +{ + struct slice *curr_slice =3D &h264_ctx->mslice; + + memset(curr_slice, 0, sizeof(struct slice)); + /* parsed by ucode */ + curr_slice->slice_type =3D h264_ctx->dpb_param.l.data[SLICE_TYPE]; + curr_slice->first_mb_in_slice =3D h264_ctx->dpb_param.l.data[FIRST_MB_IN_= SLICE]; + curr_slice->num_ref_idx_l0 =3D h264_ctx->dpb_param.dpb.num_ref_idx_l0_act= ive_minus1 + 1; + curr_slice->num_ref_idx_l1 =3D h264_ctx->dpb_param.dpb.num_ref_idx_l1_act= ive_minus1 + 1; + curr_slice->frame_num =3D h264_ctx->ctrl_ref.decode->frame_num; +} + +static irqreturn_t h264_isr(int irq, void *priv) +{ + struct aml_vdec_dev *dev =3D (struct aml_vdec_dev *)priv; + + regmap_write(dev->dec_hw->map[DOS_BUS], VDEC_ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t h264_threaded_isr_func(int irq, void *priv) +{ + u32 dec_status; + struct aml_vdec_dev *dev =3D (struct aml_vdec_dev *)priv; + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)dev->dec_hw->cur= r_ctx; + struct aml_vdec_ctx *ctx =3D (struct aml_vdec_ctx *)dev->dec_ctx; + struct aml_vdec_hw *hw =3D vdec_get_hw(ctx->dev); + unsigned short *p =3D (unsigned short *)h264_ctx->lmem_addr; + int i, ii; + + regmap_read(hw->map[DOS_BUS], DPB_STATUS_REG, &dec_status); + h264_ctx->dec_status =3D dec_status; + dev_dbg + (&dev->plat_dev->dev, + "%s, dec_status 0x%x VIFF_BIT_CNT 0x%x MBY_MBX 0x%x VLD_SHIFT_STATUS 0x%= x\n", + __func__, dec_status, read_dos_reg(hw, VIFF_BIT_CNT), read_dos_reg(hw= , MBY_MBX), + read_dos_reg(hw, VLD_SHIFT_STATUS)); + + regmap_read(hw->map[DOS_BUS], DPB_STATUS_REG, &h264_ctx->save_avscratch_f= ); + + switch (dec_status) { + case H264_SLICE_HEADER_DONE: + for (i =3D 0; i < 0x400; i +=3D 4) + for (ii =3D 0; ii < 4; ii++) + h264_ctx->dpb_param.l.data[i + ii] =3D p[i + 3 - ii]; + save_reg_status(h264_ctx); + h264_get_slice_params(h264_ctx); + if (h264_ctx->new_pic_flag =3D=3D 1) + h264_config_decode_spec(hw, ctx); + + if (h264_config_decode_buf(hw, ctx) < 0) { + h264_release_decode_spec(hw, ctx); + ctx->int_cond =3D 1; + wake_up_interruptible(&ctx->queue); + goto irq_handled; + } + if (h264_ctx->new_pic_flag =3D=3D 1) { + regmap_write(hw->map[DOS_BUS], DPB_STATUS_REG, H264_ACTION_DECODE_NEWPI= C); + dev_dbg(&dev->plat_dev->dev, "action decode new pic\n"); + h264_ctx->new_pic_flag =3D 0; + } else { + regmap_write(hw->map[DOS_BUS], DPB_STATUS_REG, H264_ACTION_DECODE_SLICE= ); + dev_dbg(&dev->plat_dev->dev, "action decode new slice\n"); + } + break; + case H264_SLICE_DATA_DONE: + h264_release_decode_spec(hw, ctx); + h264_ctx->decode_pic_count++; + ctx->int_cond =3D 1; + wake_up_interruptible(&ctx->queue); + break; + default: + h264_release_decode_spec(hw, ctx); + ctx->int_cond =3D 1; + wake_up_interruptible(&ctx->queue); + break; + } +irq_handled: + return IRQ_HANDLED; +} + +static int h264_restore_hw_ctx(struct aml_vdec_ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx =3D + (struct aml_h264_ctx *)ctx->codec_priv; + struct aml_vdec_hw *hw =3D vdec_get_hw(ctx->dev); + int i; + + regmap_write(hw->map[DOS_BUS], POWER_CTL_VLD, + (read_dos_reg(hw, POWER_CTL_VLD) + | (0 << 10) | (1 << 9) | (1 << 6))); + + regmap_write(hw->map[DOS_BUS], PSCALE_CTRL, 0); + + /* clear mailbox interrupt */ + regmap_write(hw->map[DOS_BUS], VDEC_ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + regmap_write(hw->map[DOS_BUS], VDEC_ASSIST_MBOX1_MASK, 1); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 17), (1 << 1= 7)); + if (ctx->q_data[AML_Q_DATA_DST].fmt->fourcc =3D=3D V4L2_PIX_FMT_NV21 || + ctx->q_data[AML_Q_DATA_DST].fmt->fourcc =3D=3D V4L2_PIX_FMT_NV21M) + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, + (1 << 16), (1 << 16)); + else + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 16), 0); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, + (0xbf << 24), (0xbf << 24)); + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (0xbf << 24), 0); + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_CTRL, (1 << 31), 0); + + regmap_update_bits(hw->map[DOS_BUS], MDEC_PIC_DC_MUX_CTRL, (1 << 31), 0); + regmap_write(hw->map[DOS_BUS], MDEC_EXTIF_CFG1, 0); + regmap_write(hw->map[DOS_BUS], MDEC_PIC_DC_THRESH, 0x404038aa); + + regmap_write(hw->map[DOS_BUS], DPB_STATUS_REG, 0); + + regmap_write(hw->map[DOS_BUS], LMEM_DUMP_ADR, h264_ctx->lmem_phy_addr); + regmap_write(hw->map[DOS_BUS], FRAME_COUNTER_REG, h264_ctx->decode_pic_co= unt); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_8, h264_ctx->workspace_offset); + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_G, h264_ctx->mc_cpu_paddr); + + regmap_write(hw->map[DOS_BUS], AV_SCRATCH_F, + ((h264_ctx->save_avscratch_f & 0xffffffc3) | (1 << 4))); + regmap_update_bits(hw->map[DOS_BUS], AV_SCRATCH_F, (1 << 6), 0); + + regmap_write(hw->map[DOS_BUS], MDEC_PIC_DC_THRESH, 0x404038aa); + + if (h264_ctx->reg_iqidct_control_init_flag =3D=3D 0) + regmap_write(hw->map[DOS_BUS], IQIDCT_CONTROL, 0x200); + + if (h264_ctx->reg_iqidct_control) + regmap_write(hw->map[DOS_BUS], IQIDCT_CONTROL, h264_ctx->reg_iqidct_cont= rol); + + if (h264_ctx->reg_vcop_ctrl_reg) + regmap_write(hw->map[DOS_BUS], VCOP_CTRL_REG, h264_ctx->reg_vcop_ctrl_re= g); + + if (h264_ctx->vld_dec_control) + regmap_write(hw->map[DOS_BUS], VLD_DECODE_CONTROL, h264_ctx->vld_dec_con= trol); + + dev_dbg + (hw->dev, + "IQIDCT_CONTROL =3D 0x%x, VCOP_CTRL_REG 0x%x VLD_DECODE_CONTROL 0x%x= \n", + read_dos_reg(hw, IQIDCT_CONTROL), read_dos_reg(hw, VCOP_CTRL_REG), + read_dos_reg(hw, VLD_DECODE_CONTROL)); + + for (i =3D 0; i < H264_MAX_CANVAS_POS; i++) + h264_ctx->canvas_pos_poc[i] =3D INVALID_POC; + + return 0; +} + +static void *aml_h264_get_ctrl(struct v4l2_ctrl_handler *hdl, u32 id) +{ + struct v4l2_ctrl *ctrl; + + ctrl =3D v4l2_ctrl_find(hdl, id); + return ctrl ? ctrl->p_cur.p : NULL; +} + +static int aml_h264_get_stateless_ctrl_ref(struct aml_h264_ctx *h264_ctx) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + struct vdec_h264_stateless_ctrl_ref *ctrls =3D &h264_ctx->ctrl_ref; + + ctrls->sps =3D + (struct v4l2_ctrl_h264_sps *)aml_h264_get_ctrl(&ctx->ctrl_handler, + V4L2_CID_STATELESS_H264_SPS); + if (WARN_ON(!ctrls->sps)) + return -EINVAL; + + ctrls->pps =3D + (struct v4l2_ctrl_h264_pps *)aml_h264_get_ctrl(&ctx->ctrl_handler, + V4L2_CID_STATELESS_H264_PPS); + if (WARN_ON(!ctrls->pps)) + return -EINVAL; + + ctrls->decode =3D + (struct v4l2_ctrl_h264_decode_params *)aml_h264_get_ctrl(&ctx->ctrl_hand= ler, + V4L2_CID_STATELESS_H264_DECODE_PARAMS); + if (WARN_ON(!ctrls->decode)) + return -EINVAL; + + ctrls->scaling =3D + (struct v4l2_ctrl_h264_scaling_matrix *)aml_h264_get_ctrl(&ctx->ctrl_han= dler, + V4L2_CID_STATELESS_H264_SCALING_MATRIX); + if (WARN_ON(!ctrls->scaling)) + return -EINVAL; + + return 0; +} + +static void copy_mc_cpu_fw(void *mc_cpu_addr, const u8 *data) +{ + /*header */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_HEADER, + data + 0x4000, MC_SWAP_SIZE); + /*data */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_DATA, + data + 0x2000, MC_SWAP_SIZE); + /*mmco */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MMCO, + data + 0x6000, MC_SWAP_SIZE); + /*list */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_LIST, + data + 0x3000, MC_SWAP_SIZE); + /*slice */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_SLICE, + data + 0x5000, MC_SWAP_SIZE); + /*main */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MAIN, data, 0x2000); + /*data */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MAIN + 0x2000, + data + 0x2000, 0x1000); + /*slice */ + memcpy((u8 *)mc_cpu_addr + MC_OFFSET_MAIN + 0x3000, + data + 0x5000, 0x1000); +} + +static int aml_h264_load_fw_ext(void *priv, const u8 *data, u32 len) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)priv; + struct aml_vdec_ctx *ctx =3D (struct aml_vdec_ctx *)h264_ctx->v4l2_ctx; + struct aml_vdec_hw *dec_hw; + + if (h264_ctx->mc_cpu_loaded) + return 0; + + dec_hw =3D vdec_get_hw(ctx->dev); + if (!dec_hw) + return -1; + + if (len > MC_TOTAL_SIZE) { + dev_info(dec_hw->dev, "size of mc_cpu_fw id invalid\n"); + return -1; + } + + h264_ctx->mc_cpu_vaddr =3D dma_alloc_coherent(dec_hw->dev, MC_TOTAL_SIZE, + &h264_ctx->mc_cpu_paddr, + GFP_KERNEL); + if (!h264_ctx->mc_cpu_vaddr) + return -ENOMEM; + + copy_mc_cpu_fw(h264_ctx->mc_cpu_vaddr, data); + + h264_ctx->mc_cpu_loaded =3D true; + + dev_dbg(dec_hw->dev, "h264 mccpu fw loaded\n"); + + return 0; +} + +int aml_h264_init(void *priv) +{ + struct aml_vdec_ctx *ctx =3D (struct aml_vdec_ctx *)priv; + struct aml_vdec_hw *dec_hw; + struct aml_h264_ctx *h264_ctx; + int ret =3D 0; + + h264_ctx =3D kzalloc(sizeof(*h264_ctx), GFP_KERNEL); + if (!h264_ctx) + return -ENOMEM; + + h264_ctx->v4l2_ctx =3D ctx; + dec_hw =3D vdec_get_hw(ctx->dev); + if (!dec_hw) + return -1; + + h264_ctx->mc_cpu_loaded =3D false; + dec_hw->hw_ops.irq_handler =3D h264_isr; + dec_hw->hw_ops.irq_threaded_func =3D h264_threaded_isr_func; + dec_hw->hw_ops.load_firmware_ex =3D aml_h264_load_fw_ext; + + h264_ctx->lmem_addr =3D dma_alloc_coherent(dec_hw->dev, LMEM_DUMP_SIZE, + &h264_ctx->lmem_phy_addr, + GFP_KERNEL); + if (!h264_ctx->lmem_addr) { + ret =3D -ENOMEM; + goto err_alloc_lmem; + } + + h264_ctx->cma_alloc_vaddr =3D + dma_alloc_coherent(dec_hw->dev, V_BUF_ADDR_OFFSET, + &h264_ctx->cma_alloc_addr, GFP_KERNEL); + if (!h264_ctx->cma_alloc_vaddr) { + ret =3D -ENOMEM; + goto err_alloc_workspace; + } + + h264_ctx->workspace_offset =3D h264_ctx->cma_alloc_addr + DCAC_READ_MARGI= N; + h264_ctx->workspace_vaddr =3D h264_ctx->cma_alloc_vaddr + DCAC_READ_MARGI= N; + + ctx->codec_priv =3D h264_ctx; + dec_hw->curr_ctx =3D h264_ctx; + h264_ctx->col_buf_alloc_size =3D 0; + h264_ctx->init_flag =3D 0; + h264_ctx->new_pic_flag =3D 0; + h264_ctx->param_set =3D 0; + h264_ctx->reg_iqidct_control_init_flag =3D 0; + h264_ctx->decode_pic_count =3D 0; + + return 0; + +err_alloc_workspace: + dma_free_coherent(dec_hw->dev, LMEM_DUMP_SIZE, + h264_ctx->lmem_addr, h264_ctx->lmem_phy_addr); +err_alloc_lmem: + kfree(h264_ctx); + + return ret; +} + +void aml_h264_exit(void *priv) +{ + struct aml_vdec_ctx *ctx =3D (struct aml_vdec_ctx *)priv; + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)ctx->codec_priv; + struct aml_vdec_hw *dec_hw; + + if (!h264_ctx) { + dev_info(&ctx->dev->plat_dev->dev, "h264 decoder is already destroyed or= not created!\n"); + return; + } + dec_hw =3D vdec_get_hw(ctx->dev); + h264_ctx->param_set =3D 0; + + if (ctx->dos_clk_en) + aml_stop_vdec_hw(dec_hw); + + if (h264_ctx->collated_cma_vaddr) { + dma_free_coherent(dec_hw->dev, h264_ctx->col_buf_alloc_size, + h264_ctx->collated_cma_vaddr, + h264_ctx->collated_cma_addr); + h264_ctx->col_buf_alloc_size =3D 0; + } + + if (h264_ctx->mc_cpu_vaddr) { + dma_free_coherent(dec_hw->dev, MC_TOTAL_SIZE, + h264_ctx->mc_cpu_vaddr, + h264_ctx->mc_cpu_paddr); + h264_ctx->mc_cpu_loaded =3D false; + } + + if (h264_ctx->lmem_addr) + dma_free_coherent(dec_hw->dev, LMEM_DUMP_SIZE, + h264_ctx->lmem_addr, h264_ctx->lmem_phy_addr); + + if (h264_ctx->cma_alloc_vaddr) + dma_free_coherent(dec_hw->dev, V_BUF_ADDR_OFFSET, + h264_ctx->cma_alloc_vaddr, + h264_ctx->cma_alloc_addr); + + kfree(ctx->codec_priv); + dec_hw->curr_ctx =3D NULL; + ctx->codec_priv =3D NULL; +} + +static void config_decode_mode(struct aml_vdec_ctx *ctx) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)ctx->codec_priv; + struct aml_vdec_hw *hw =3D vdec_get_hw(ctx->dev); + + regmap_write(hw->map[DOS_BUS], H264_DECODE_MODE, 0x1); /*decode mode fram= ebase*/ + regmap_write(hw->map[DOS_BUS], HEAD_PADDING_REG, 0); + regmap_write(hw->map[DOS_BUS], H264_DECODE_SEQINFO, h264_ctx->seq_info); + regmap_write(hw->map[DOS_BUS], INIT_FLAG_REG, 1); +} + +int aml_h264_dec_run(void *priv) +{ + struct aml_vdec_ctx *ctx =3D (struct aml_vdec_ctx *)priv; + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)ctx->codec_priv; + struct aml_vdec_hw *dec_hw =3D vdec_get_hw(ctx->dev); + int ret =3D -1; + int i; + + ret =3D aml_h264_get_stateless_ctrl_ref(h264_ctx); + if (ret < 0) { + dev_err(&ctx->dev->plat_dev->dev, "not ctrl ref for h264 decoder\n"); + return ret; + } + + h264_config_params(ctx); + + if (h264_prepare_input(ctx) < 0) + return ret; + + if (alloc_colocate_cma(h264_ctx, ctx) < 0) + return ret; + + h264_restore_hw_ctx(ctx); + + config_decode_mode(ctx); + /* enable stream input hardware */ + regmap_update_bits(dec_hw->map[DOS_BUS], VLD_MEM_VIFIFO_CONTROL, 0x6, 0x6= ); + /* enable hardware timer */ + regmap_write(dec_hw->map[DOS_BUS], NAL_SEARCH_CTL, + read_dos_reg(dec_hw, NAL_SEARCH_CTL) | (1 << 16)); + regmap_write(dec_hw->map[DOS_BUS], MDEC_EXTIF_CFG2, + read_dos_reg(dec_hw, MDEC_EXTIF_CFG2) | 0x20); + regmap_write(dec_hw->map[DOS_BUS], NAL_SEARCH_CTL, + read_dos_reg(dec_hw, NAL_SEARCH_CTL) & (~0x2)); + regmap_update_bits(dec_hw->map[DOS_BUS], VDEC_ASSIST_MMC_CTRL1, + (1 << 3), 0); + + aml_start_vdec_hw(dec_hw); + h264_ctx->init_flag =3D 1; + h264_ctx->new_pic_flag =3D 1; + + regmap_write(dec_hw->map[DOS_BUS], DPB_STATUS_REG, H264_ACTION_SEARCH_HEA= D); + + ret =3D wait_event_interruptible_timeout(ctx->queue, ctx->int_cond, + msecs_to_jiffies(DECODER_TIMEOUT_MS)); + ctx->int_cond =3D 0; + if (!ret) { + ret =3D -1; + dev_err(&ctx->dev->plat_dev->dev, "dec timeout=3D%u\n", DECODER_TIMEOUT_= MS); + for (i =3D 0; i < 16; i++) { /* 16 : show ucode PC 16 times when timeout= */ + dev_info(&ctx->dev->plat_dev->dev, "decoder timeout, pc 0x%x\n", + read_dos_reg(dec_hw, MPC_E)); + usleep_range(10, 20); + } + } else if (-ERESTARTSYS =3D=3D ret) { + ret =3D -1; + dev_err(&ctx->dev->plat_dev->dev, "dec inter fail\n"); + } + + aml_stop_vdec_hw(dec_hw); + h264_ctx->init_flag =3D 0; + + return ret; +} diff --git a/drivers/media/platform/amlogic/vdec/h264.h b/drivers/media/pla= tform/amlogic/vdec/h264.h new file mode 100644 index 000000000000..3d3a35a641c9 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/h264.h @@ -0,0 +1,300 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ +#ifndef _H264_H_ +#define _H264_H_ + +#define RPM_BEGIN 0x0 +#define FRAME_IN_DPB 24 +#define RPM_END 0x400 +#define DPB_OFFSET 0x100 +#define MMCO_OFFSET 0x200 +#define SPS_OFFSET 0x100 +#define PPS_OFFSET 0x300 +#define PARAM_BASE_VAL 0x414d +#define MEM_MMCO_BASE 0x01c3000 +#define MEM_SPS_BASE 0x01c3c00 +#define MEM_PPS_BASE 0x01cbc00 +#define MC_TOTAL_SIZE ((20 + 16) * SZ_1K) +#define MC_SWAP_SIZE (4 * SZ_1K) +#define LMEM_DUMP_SIZE 4096 +#define V_BUF_ADDR_OFFSET (0x200000 + 0x8000 + 0x20000 + 0x1000) +#define DCAC_READ_MARGIN (64 * 1024) +#define MC_OFFSET_HEADER 0x0000 +#define MC_OFFSET_DATA 0x1000 +#define MC_OFFSET_MMCO 0x2000 +#define MC_OFFSET_LIST 0x3000 +#define MC_OFFSET_SLICE 0x4000 +#define MC_OFFSET_MAIN 0x5000 + +/* Rename the dos regs */ +#define H264_DECODE_INFO M4_CONTROL_REG +#define INIT_FLAG_REG AV_SCRATCH_2 +#define HEAD_PADDING_REG AV_SCRATCH_3 +#define UCODE_WATCHDOG_REG AV_SCRATCH_7 +#define LMEM_DUMP_ADR AV_SCRATCH_L +#define DEBUG_REG1 AV_SCRATCH_M +#define DEBUG_REG2 AV_SCRATCH_N +#define FRAME_COUNTER_REG AV_SCRATCH_I +#define RPM_CMD_REG AV_SCRATCH_A +#define H264_DECODE_SIZE AV_SCRATCH_E +#define H264_DECODE_MODE AV_SCRATCH_4 +#define H264_DECODE_SEQINFO AV_SCRATCH_5 +/** + * NAL_SEARCH_CTL: bit 0, enable itu_t35 + * NAL_SEARCH_CTL: bit 1, enable mmu + * NAL_SEARCH_CTL: bit 2, detect frame_mbs_only_flag whether switch resolu= tion + * NAL_SEARCH_CTL: bit 3, recover the correct sps pps + * NAL_SEARCH_CTL: bit 7-14,level_idc + * NAL_SEARCH_CTL: bit 15,bitstream_restriction_flag + */ +#define NAL_SEARCH_CTL AV_SCRATCH_9 +#define DPB_STATUS_REG AV_SCRATCH_J +#define ERROR_STATUS_REG AV_SCRATCH_9 + +#define H264_BUFFER_INFO_INDEX PMV3_X /* 0xc24 */ +#define H264_BUFFER_INFO_DATA PMV2_X /* 0xc22 */ +#define H264_CURRENT_POC_IDX_RESET LAST_SLICE_MV_ADDR /* 0xc30 */ +#define H264_CURRENT_POC LAST_MVY /* 0xc32 shared with conceal = MV */ +#define H264_CO_MB_WR_ADDR VLD_C38 +#define H264_CO_MB_RD_ADDR VLD_C39 +#define H264_CO_MB_RW_CTL VLD_C3D +#define MBY_MBX MB_MOTION_MODE + +#define H264_ACTION_SEARCH_HEAD 0xf0 +#define H264_ACTION_DECODE_SLICE 0xf1 +#define H264_ACTION_CONFIG_DONE 0xf2 +#define H264_ACTION_DECODE_NEWPIC 0xf3 +#define H264_ACTION_DECODE_START 0xff + +/* RPM memory definition */ +#define FIXED_FRAME_RATE_FLAG 0X21 +#define OFFSET_DELIMITER_LO 0x2f +#define OFFSET_DELIMITER_HI 0x30 +#define REF_IDC_OVERRIDE_FLAG 0x35 +#define SLICE_IPONLY_BREAK 0X5C +#define PREV_MAX_REFERENCE_FRAME_NUM 0X5D +#define EOS 0X5E +#define FRAME_PACKING_TYPE 0X5F +#define OLD_POC_PAR_1 0X60 +#define OLD_POC_PAR_2 0X61 +#define PREV_MBX 0X62 +#define PREV_MBY 0X63 +#define ERROR_SKIP_MB_NUM 0X64 +#define ERROR_MB_STATUS 0X65 +#define L0_PIC0_STATUS 0X66 +#define TIMEOUT_COUNTER 0X67 +#define BUFFER_SIZE 0X68 +#define BUFFER_SIZE_HI 0X69 +#define CROPPING_LEFT_RIGHT 0X6A +#define CROPPING_TOP_BOTTOM 0X6B +/** + * sps_flags2: + * bit 3, bitstream_restriction_flag + * bit 2, pic_struct_present_flag + * bit 1, vcl_hrd_parameters_present_flag + * bit 0, nal_hrd_parameters_present_flag + */ +#define SPS_FLAGS2 0x6C +#define NUM_REORDER_FRAMES 0x6D +#define MAX_BUFFER_FRAME 0X6E + +#define NON_CONFORMING_STREAM 0X70 +#define RECOVERY_POINT 0X71 +#define POST_CANVAS 0X72 +#define POST_CANVAS_H 0X73 +#define SKIP_PIC_COUNT 0X74 +#define TARGET_NUM_SCALING_LIST 0X75 +#define FF_POST_ONE_FRAME 0X76 +#define PREVIOUS_BIT_CNT 0X77 +#define MB_NOT_SHIFT_COUNT 0X78 +#define PIC_STATUS 0X79 +#define FRAME_COUNTER 0X7A +#define NEW_SLICE_TYPE 0X7B +#define NEW_PICTURE_STRUCTURE 0X7C +#define NEW_FRAME_NUM 0X7D +#define NEW_IDR_PIC_ID 0X7E +#define IDR_PIC_ID 0X7F + +/* h264 LOCAL */ +#define NAL_UNIT_TYPE 0X80 +#define NAL_REF_IDC 0X81 +#define SLICE_TYPE 0X82 +#define LOG2_MAX_FRAME_NUM 0X83 +#define FRAME_MBS_ONLY_FLAG 0X84 +#define PIC_ORDER_CNT_TYPE 0X85 +#define LOG2_MAX_PIC_ORDER_CNT_LSB 0X86 +#define PIC_ORDER_PRESENT_FLAG 0X87 +#define REDUNDANT_PIC_CNT_PRESENT_FLAG 0X88 +#define PIC_INIT_QP_MINUS26 0X89 +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG 0X8A +#define NUM_SLICE_GROUPS_MINUS1 0X8B +#define MODE_8X8_FLAGS 0X8C +#define ENTROPY_CODING_MODE_FLAG 0X8D +#define SLICE_QUANT 0X8E +#define TOTAL_MB_HEIGHT 0X8F +#define PICTURE_STRUCTURE 0X90 +#define TOP_INTRA_TYPE 0X91 +#define RV_AI_STATUS 0X92 +#define AI_READ_START 0X93 +#define AI_WRITE_START 0X94 +#define AI_CUR_BUFFER 0X95 +#define AI_DMA_BUFFER 0X96 +#define AI_READ_OFFSET 0X97 +#define AI_WRITE_OFFSET 0X98 +#define AI_WRITE_OFFSET_SAVE 0X99 +#define RV_AI_BUFF_START 0X9A +#define I_PIC_MB_COUNT 0X9B +#define AI_WR_DCAC_DMA_CTRL 0X9C +#define SLICE_MB_COUNT 0X9D +#define PICTYPE 0X9E +#define SLICE_GROUP_MAP_TYPE 0X9F +#define MB_TYPE 0XA0 +#define MB_AFF_ADDED_DMA 0XA1 +#define PREVIOUS_MB_TYPE 0XA2 +#define WEIGHTED_PRED_FLAG 0XA3 +#define WEIGHTED_BIPRED_IDC 0XA4 +/* bit 3:2 - PICTURE_STRUCTURE + * bit 1 - MB_ADAPTIVE_FRAME_FIELD_FLAG + * bit 0 - FRAME_MBS_ONLY_FLAG + */ +#define MBFF_INFO 0XA5 +#define TOP_INTRA_TYPE_TOP 0XA6 +#define RV_AI_BUFF_INC 0xA7 +#define DEFAULT_MB_INFO_LO 0xA8 +/* 0 -- no need to read + * 1 -- need to wait Left + * 2 -- need to read Intra + * 3 -- need to read back MV + */ +#define NEED_READ_TOP_INFO 0xA9 +/* 0 -- idle + * 1 -- wait Left + * 2 -- reading top Intra + * 3 -- reading back MV + */ +#define READ_TOP_INFO_STATE 0xAA +#define DCAC_MBX 0xAB +#define TOP_MB_INFO_OFFSET 0xAC +#define TOP_MB_INFO_RD_IDX 0xAD +#define TOP_MB_INFO_WR_IDX 0xAE + +#define VLD_NO_WAIT 0 +#define VLD_WAIT_BUFFER 1 +#define VLD_WAIT_HOST 2 +#define VLD_WAIT_GAP 3 + +#define VLD_WAITING 0xAF + +#define MB_X_NUM 0xB0 +#define MB_HEIGHT 0xB2 +#define MBX 0xB3 +#define TOTAL_MBY 0xB4 +#define INTR_MSK_SAVE 0xB5 +#define NEED_DISABLE_PPE 0xB6 +#define IS_NEW_PICTURE 0XB7 +#define PREV_NAL_REF_IDC 0XB8 +#define PREV_NAL_UNIT_TYPE 0XB9 +#define FRAME_MB_COUNT 0XBA +#define SLICE_GROUP_UCODE 0XBB +#define SLICE_GROUP_CHANGE_RATE 0XBC +#define SLICE_GROUP_CHANGE_CYCLE_LEN 0XBD +#define DELAY_LENGTH 0XBE +#define PICTURE_STRUCT 0XBF +#define DCAC_PREVIOUS_MB_TYPE 0xC1 + +#define TIME_STAMP 0XC2 +#define H_TIME_STAMP 0XC3 +#define VPTS_MAP_ADDR 0XC4 +#define H_VPTS_MAP_ADDR 0XC5 +#define PIC_INSERT_FLAG 0XC7 +#define TIME_STAMP_START 0XC8 +#define TIME_STAMP_END 0XDF +#define OFFSET_FOR_NON_REF_PIC 0XE0 +#define OFFSET_FOR_TOP_TO_BOTTOM_FIELD 0XE2 +#define MAX_REFERENCE_FRAME_NUM 0XE4 +#define FRAME_NUM_GAP_ALLOWED 0XE5 +#define NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE 0XE6 +#define PROFILE_IDC_MMCO 0XE7 +#define LEVEL_IDC_MMCO 0XE8 +#define FRAME_SIZE_IN_MB 0XE9 +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG 0XEA +#define PPS_NUM_REF_IDX_L0_ACTIVE_MINUS1 0XEB +#define PPS_NUM_REF_IDX_L1_ACTIVE_MINUS1 0XEC +#define CURRENT_SPS_ID 0XED +#define CURRENT_PPS_ID 0XEE +/* bit 0 - sequence parameter set may change + * bit 1 - picture parameter set may change + * bit 2 - new dpb just inited + * bit 3 - IDR picture not decoded yet + * bit 5:4 - 0: mb level code loaded 1: picture + * level code loaded 2: slice level code loaded + */ +#define DECODE_STATUS 0XEF +#define FIRST_MB_IN_SLICE 0XF0 +#define PREV_MB_WIDTH 0XF1 +#define PREV_FRAME_SIZE_IN_MB 0XF2 +/* bit 0 - aspect_ratio_info_present_flag + * bit 1 - timing_info_present_flag + * bit 2 - nal_hrd_parameters_present_flag + * bit 3 - vcl_hrd_parameters_present_flag + * bit 4 - pic_struct_present_flag + * bit 5 - bitstream_restriction_flag + */ +#define VUI_STATUS 0XF4 +#define ASPECT_RATIO_IDC 0XF5 +#define ASPECT_RATIO_SAR_WIDTH 0XF6 +#define ASPECT_RATIO_SAR_HEIGHT 0XF7 +#define NUM_UNITS_IN_TICK 0XF8 +#define TIME_SCALE 0XFA +#define CURRENT_PIC_INFO 0XFC +#define DPB_BUFFER_INFO 0XFD +#define REFERENCE_POOL_INFO 0XFE +#define REFERENCE_LIST_INFO 0XFF + +#define REORDER_CMD_MAX 66 + +/* config parameters to DDR lmem */ +#define GET_SPS_PROFILE_IDC(x) (((x) & 0xff) << 8) +#define GET_SPS_LEVEL_IDC(x) ((x) & 0xff) +#define GET_SPS_SEQ_PARAM_SET_ID(x) (((x) & 0x1f) << 8) +#define GET_SPS_CHROMA_FORMAT_IDC(x) ((x) << 8) +#define GET_SPS_NUM_REF_FRAMES(x) ((x) & 0xff) +#define GET_SPS_GAPS_ALLOWED_FLAG(x) ((x) << 8) +#define GET_SPS_LOG2_MAX_FRAME_NUM(x) ((x) + 4) +#define GET_SPS_PIC_ORDER_CNT_LSB(x) ((x) + 4) +#define GET_SPS_PIC_ORDER_TYPE(x) (x) +#define GET_SPS_OFFSET_FOR_NONREF_PIC_HIGH(x) (((x) & 0xffff0000) >> = 16) +#define GET_SPS_OFFSET_FOR_NONREF_PIC_LOW(x) ((x) & 0xffff) +#define GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_HIGH(x) (((x) & 0xffff0000) = >> 16) +#define GET_SPS_OFFSET_FOR_TOP_BOT_FIELD_LOW(x) ((x) & 0xffff) +#define GET_SPS_PIC_WIDTH_IN_MBS(x) ((x) + 1) +#define GET_SPS_PIC_HEIGHT_IN_MBS(x) ((x) + 1) +#define GET_SPS_DIRECT_8X8_FLAGS(x) (((x) & 0x1) << 2) +#define GET_SPS_MB_ADAPTIVE_FRAME_FIELD_FLAGS(x) (((x) & 0x1) << 1) +#define GET_SPS_FRAME_MBS_ONLY_FLAGS(x) ((x) & 0x1) + +#define GET_PPS_PIC_PARAM_SET_ID(x) ((x) & 0xff) +#define GET_PPS_SEQ_PARAM_SET_ID(x) (((x) & 0x1f) << 8) +#define GET_PPS_ENTROPY_CODING_MODE_FLAG(x) (((x) & 0x1) << 13) +#define GET_PPS_PIC_ORDER_PRESENT_FLAG(x) (((x) & 0x1) << 14) +#define GET_PPS_NUM_IDX_REF_L0_MINUS1(x) ((x) & 0x1f) +#define GET_PPS_NUM_IDX_REF_L1_MINUS1(x) (((x) & 0x1f) << 5) +#define GET_PPS_WEIGHTED_PRED_FLAG(x) (((x) & 0x1) << 10) +#define GET_PPS_WEIGHTED_BIPRED_IDC(x) (((x) & 0x3) << 11) +#define GET_PPS_INIT_QS_MINUS26(x) (((x) & 0xff) << 8) +#define GET_PPS_INIT_QP_MINUS26(x) ((x) & 0xff) +#define GET_PPS_CHROMA_QP_INDEX_OFFSET(x) ((x) & 0xff) +#define GET_PPS_DEBLOCK_FILTER_CTRL_PRESENT_FLAG(x) (((x) & 0x1) << 8) +#define GET_PPS_CONSTRAIN_INTRA_PRED_FLAG(x) (((x) & 0x1) << 9) +#define GET_PPS_REDUNDANT_PIC_CNT_PRESENT_FLAG(x) (((x) & 0x1) << 10) +#define GET_PPS_SCALING_MATRIX_PRESENT_FLAG(x) (((x) & 0x1) << 1) +#define GET_PPS_TRANSFORM_8X8_FLAG(x) ((x) & 0x1) +#define GET_PPS_GET_SECOND_CHROMA_QP_OFFSET(x) (x) + +int aml_h264_init(void *priv); +void aml_h264_exit(void *priv); +int aml_h264_dec_run(void *priv); + +#endif diff --git a/drivers/media/platform/amlogic/vdec/reg_defines.h b/drivers/me= dia/platform/amlogic/vdec/reg_defines.h new file mode 100644 index 000000000000..ea50018a078d --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/reg_defines.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */ +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + */ + +#ifndef _REG_DEFINES_H_ +#define _REG_DEFINES_H_ + +#define REG_ALIGN(x) ((x) << 2) + +#define VDEC_ASSIST_MMC_CTRL0 REG_ALIGN(0x0001) +#define VDEC_ASSIST_MMC_CTRL1 REG_ALIGN(0x0002) + +#define VDEC_ASSIST_CANVAS_BLK32 REG_ALIGN(0x0005) + +#define VDEC_ASSIST_MBOX1_CLR_REG REG_ALIGN(0x0075) +#define VDEC_ASSIST_MBOX1_MASK REG_ALIGN(0x0076) + +#define MPSR REG_ALIGN(0x0301) +#define MPC_P REG_ALIGN(0x0306) +#define MPC_D REG_ALIGN(0x0307) +#define MPC_E REG_ALIGN(0x0308) +#define MPC_W REG_ALIGN(0x0309) +#define CPSR REG_ALIGN(0x0321) +#define IMEM_DMA_CTRL REG_ALIGN(0x0340) +#define IMEM_DMA_ADR REG_ALIGN(0x0341) +#define IMEM_DMA_COUNT REG_ALIGN(0x0342) +#define WRRSP_IMEM REG_ALIGN(0x0343) +#define LMEM_DMA_CTRL REG_ALIGN(0x0350) +#define WRRSP_LMEM REG_ALIGN(0x0353) + +#define PSCALE_CTRL REG_ALIGN(0x0911) +#define GCLK_EN REG_ALIGN(0x0983) +#define MDEC_PIC_DC_CTRL REG_ALIGN(0x098e) +#define MDEC_PIC_DC_MUX_CTRL REG_ALIGN(0x098d) +#define ANC0_CANVAS_ADDR REG_ALIGN(0x0990) +#define ANC1_CANVAS_ADDR REG_ALIGN(0x0991) +#define ANC2_CANVAS_ADDR REG_ALIGN(0x0992) +#define ANC3_CANVAS_ADDR REG_ALIGN(0x0993) +#define ANC4_CANVAS_ADDR REG_ALIGN(0x0994) +#define ANC5_CANVAS_ADDR REG_ALIGN(0x0995) +#define ANC6_CANVAS_ADDR REG_ALIGN(0x0996) +#define ANC7_CANVAS_ADDR REG_ALIGN(0x0997) +#define ANC8_CANVAS_ADDR REG_ALIGN(0x0998) +#define ANC9_CANVAS_ADDR REG_ALIGN(0x0999) +#define ANC10_CANVAS_ADDR REG_ALIGN(0x099a) +#define ANC11_CANVAS_ADDR REG_ALIGN(0x099b) +#define ANC12_CANVAS_ADDR REG_ALIGN(0x099c) +#define ANC13_CANVAS_ADDR REG_ALIGN(0x099d) +#define ANC14_CANVAS_ADDR REG_ALIGN(0x099e) +#define ANC15_CANVAS_ADDR REG_ALIGN(0x099f) +#define ANC16_CANVAS_ADDR REG_ALIGN(0x09a0) +#define ANC17_CANVAS_ADDR REG_ALIGN(0x09a1) +#define ANC18_CANVAS_ADDR REG_ALIGN(0x09a2) +#define ANC19_CANVAS_ADDR REG_ALIGN(0x09a3) +#define ANC20_CANVAS_ADDR REG_ALIGN(0x09a4) +#define ANC21_CANVAS_ADDR REG_ALIGN(0x09a5) +#define ANC22_CANVAS_ADDR REG_ALIGN(0x09a6) +#define ANC23_CANVAS_ADDR REG_ALIGN(0x09a7) +#define ANC24_CANVAS_ADDR REG_ALIGN(0x09a8) +#define ANC25_CANVAS_ADDR REG_ALIGN(0x09a9) +#define ANC26_CANVAS_ADDR REG_ALIGN(0x09aa) +#define ANC27_CANVAS_ADDR REG_ALIGN(0x09ab) +#define ANC28_CANVAS_ADDR REG_ALIGN(0x09ac) +#define ANC29_CANVAS_ADDR REG_ALIGN(0x09ad) +#define ANC30_CANVAS_ADDR REG_ALIGN(0x09ae) +#define ANC31_CANVAS_ADDR REG_ALIGN(0x09af) +#define DBKR_CANVAS_ADDR REG_ALIGN(0x09b0) +#define DBKW_CANVAS_ADDR REG_ALIGN(0x09b1) +#define REC_CANVAS_ADDR REG_ALIGN(0x09b2) +#define CURR_CANVAS_CTRL REG_ALIGN(0x09b3) +#define MDEC_PIC_DC_THRESH REG_ALIGN(0x09b8) +#define AV_SCRATCH_0 REG_ALIGN(0x09c0) +#define AV_SCRATCH_1 REG_ALIGN(0x09c1) +#define AV_SCRATCH_2 REG_ALIGN(0x09c2) +#define AV_SCRATCH_3 REG_ALIGN(0x09c3) +#define AV_SCRATCH_4 REG_ALIGN(0x09c4) +#define AV_SCRATCH_5 REG_ALIGN(0x09c5) +#define AV_SCRATCH_6 REG_ALIGN(0x09c6) +#define AV_SCRATCH_7 REG_ALIGN(0x09c7) +#define AV_SCRATCH_8 REG_ALIGN(0x09c8) +#define AV_SCRATCH_9 REG_ALIGN(0x09c9) +#define AV_SCRATCH_A REG_ALIGN(0x09ca) +#define AV_SCRATCH_B REG_ALIGN(0x09cb) +#define AV_SCRATCH_C REG_ALIGN(0x09cc) +#define AV_SCRATCH_D REG_ALIGN(0x09cd) +#define AV_SCRATCH_E REG_ALIGN(0x09ce) +#define AV_SCRATCH_F REG_ALIGN(0x09cf) +#define AV_SCRATCH_G REG_ALIGN(0x09d0) +#define AV_SCRATCH_H REG_ALIGN(0x09d1) +#define AV_SCRATCH_I REG_ALIGN(0x09d2) +#define AV_SCRATCH_J REG_ALIGN(0x09d3) +#define AV_SCRATCH_K REG_ALIGN(0x09d4) +#define AV_SCRATCH_L REG_ALIGN(0x09d5) +#define AV_SCRATCH_M REG_ALIGN(0x09d6) +#define AV_SCRATCH_N REG_ALIGN(0x09d7) +#define WRRSP_VLD REG_ALIGN(0x09da) +#define MDEC_DOUBLEW_CFG0 REG_ALIGN(0x09db) +#define MDEC_DOUBLEW_CFG1 REG_ALIGN(0x09dc) +#define MDEC_DOUBLEW_CFG2 REG_ALIGN(0x09dd) +#define MDEC_DOUBLEW_CFG3 REG_ALIGN(0x09de) +#define MDEC_DOUBLEW_CFG4 REG_ALIGN(0x09df) +#define MDEC_DOUBLEW_CFG5 REG_ALIGN(0x09e0) +#define MDEC_DOUBLEW_CFG6 REG_ALIGN(0x09e1) +#define MDEC_DOUBLEW_CFG7 REG_ALIGN(0x09e2) +#define MDEC_DOUBLEW_STATUS REG_ALIGN(0x09e3) +#define MDEC_EXTIF_CFG0 REG_ALIGN(0x09e4) + +#define MDEC_EXTIF_CFG1 REG_ALIGN(0x09e5) +#define MDEC_EXTIF_CFG2 REG_ALIGN(0x09e6) + +#define POWER_CTL_VLD REG_ALIGN(0x0c08) +#define VLD_DECODE_CONTROL REG_ALIGN(0x0c18) + +#define PMV1_X REG_ALIGN(0x0c20) +#define PMV1_Y REG_ALIGN(0x0c21) +#define PMV2_X REG_ALIGN(0x0c22) +#define PMV2_Y REG_ALIGN(0x0c23) +#define PMV3_X REG_ALIGN(0x0c24) +#define PMV3_Y REG_ALIGN(0x0c25) +#define PMV4_X REG_ALIGN(0x0c26) +#define PMV4_Y REG_ALIGN(0x0c27) +#define M4_TABLE_SELECT REG_ALIGN(0x0c28) +#define M4_CONTROL_REG REG_ALIGN(0x0c29) +#define BLOCK_NUM REG_ALIGN(0x0c2a) +#define PATTERN_CODE REG_ALIGN(0x0c2b) +#define MB_INFO REG_ALIGN(0x0c2c) +#define VLD_DC_PRED REG_ALIGN(0x0c2d) +#define VLD_ERROR_MASK REG_ALIGN(0x0c2e) +#define VLD_DC_PRED_C REG_ALIGN(0x0c2f) +#define LAST_SLICE_MV_ADDR REG_ALIGN(0x0c30) +#define LAST_MVX REG_ALIGN(0x0c31) +#define LAST_MVY REG_ALIGN(0x0c32) + +#define MB_MOTION_MODE REG_ALIGN(0x0c07) +#define VIFF_BIT_CNT REG_ALIGN(0x0c1a) +#define M4_CONTROL_REG REG_ALIGN(0x0c29) +#define VLD_C38 REG_ALIGN(0x0c38) +#define VLD_C39 REG_ALIGN(0x0c39) +#define VLD_SHIFT_STATUS REG_ALIGN(0x0c3b) +#define VLD_C3D REG_ALIGN(0x0c3d) +#define VLD_MEM_VIFIFO_START_PTR REG_ALIGN(0x0c40) +#define VLD_MEM_VIFIFO_CURR_PTR REG_ALIGN(0x0c41) +#define VLD_MEM_VIFIFO_END_PTR REG_ALIGN(0x0c42) +#define VLD_MEM_VIFIFO_BYTES_AVAIL REG_ALIGN(0x0c43) +#define VLD_MEM_VIFIFO_CONTROL REG_ALIGN(0x0c44) +#define VLD_MEM_VIFIFO_WP REG_ALIGN(0x0c45) +#define VLD_MEM_VIFIFO_RP REG_ALIGN(0x0c46) +#define VLD_MEM_VIFIFO_LEVEL REG_ALIGN(0x0c47) +#define VLD_MEM_VIFIFO_BUF_CNTL REG_ALIGN(0x0c48) + +#define VCOP_CTRL_REG REG_ALIGN(0x0e00) +#define RV_AI_MB_COUNT REG_ALIGN(0x0e0c) +#define IQIDCT_CONTROL REG_ALIGN(0x0e0e) +#define DCAC_DDR_BYTE64_CTL REG_ALIGN(0x0e1d) + +#define VDEC2_IMEM_DMA_CTRL REG_ALIGN(0x2340) +#define VDEC2_IMEM_DMA_ADR REG_ALIGN(0x2341) +#define VDEC2_IMEM_DMA_COUNT REG_ALIGN(0x2342) + +#define DOS_SW_RESET0 REG_ALIGN(0x3f00) +#define DOS_GCLK_EN0 REG_ALIGN(0x3f01) +#define DOS_GCLK_EN1 REG_ALIGN(0x3f09) +#define DOS_GCLK_EN3 REG_ALIGN(0x3f35) + +#define DOS_MEM_PD_VDEC REG_ALIGN(0x3f30) +#define DOS_MEM_PD_VDEC2 REG_ALIGN(0x3f31) +#define DOS_MEM_PD_HCODEC REG_ALIGN(0x3f32) +/*add from M8M2*/ +#define DOS_MEM_PD_HEVC REG_ALIGN(0x3f33) + +#define DOS_SW_RESET3 REG_ALIGN(0x3f34) +#define DOS_GCLK_EN3 REG_ALIGN(0x3f35) +#define DOS_HEVC_INT_EN REG_ALIGN(0x3f36) + +#endif + --=20 2.42.0 From nobody Tue Dec 2 01:04:48 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 DA5B12853F7; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763955155; cv=none; b=OMlcFKOqP6KLCzDDfhmqmAMLtx7DDjr3qaO8gMol3iJLeiZ530ZrZczyLzGIwczBP4jlEEz5PqluwSPbqqK5NAD48OBWrfbpd4DxYEodXF3WUsk4zWskB0b+/QyxtG5040PvDa43GINVCutchoSsX8x/545vtTPffqVbnS+yCHc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763955155; c=relaxed/simple; bh=nQMpas0P68zqe+jsuAUQuc78OljFGZeSHfJ158LMKlg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bB76r+S2ypRz3M712p7iKKJMFE3MA6Yu7DhILUDz42Nb6NCCoJlXZ9SkrcSbJU03uCll7S1VsdVcuD8FYZGqbUROVn4ppszHtDQfbe8jvpRGzxFNj+QqRhoGNWdo1kEjYhvn8YsHqYkKCKWQbTTdbdITVhYmvck6g+ZaN3moqcc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LIvqdsee; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LIvqdsee" Received: by smtp.kernel.org (Postfix) with ESMTPS id 94261C2BC9E; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763955154; bh=nQMpas0P68zqe+jsuAUQuc78OljFGZeSHfJ158LMKlg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=LIvqdsee1woYL1Q8LlyGqPI8UWC+R3TubGMryLxV0Lbrjw1PUqH1+0rJGHSYq1tzW eDifwaEwSBYALohqMjo5p22/686Mjw832VBupIP5TTaoZtBobhGXBLaAftNEjtFIco dX5gzqMTJxLrX2RrCe1r+154pMsLSTq0Is4iJPJ9K7vm1I1fDlSw3Iu567FfLMI9N2 OoNfNVbVdk2YMATkWV2uyfnW7+vyqnaGpvhI0K2dTG39slf9Mw9tFBCKXOEZ1yRDwk 05P/M7Mfirm+JfS8T2VyNAcKtQVFyZEVfqAS+qgUJnEMyhdg352PJA+QpsmmO8+lqT BT3og9flY3swA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8685BCFD31F; Mon, 24 Nov 2025 03:32:34 +0000 (UTC) From: Zhentao Guo via B4 Relay Date: Mon, 24 Nov 2025 11:32:19 +0800 Subject: [PATCH RFC v2 3/3] arm64: dts: amlogic: Add video decoder driver support for S4 SOCs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251124-b4-s4-vdec-upstream-v2-3-bdbbce3f11a6@amlogic.com> References: <20251124-b4-s4-vdec-upstream-v2-0-bdbbce3f11a6@amlogic.com> In-Reply-To: <20251124-b4-s4-vdec-upstream-v2-0-bdbbce3f11a6@amlogic.com> To: Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Neil Armstrong , Kevin Hilman , Jerome Brunet , Martin Blumenstingl Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-amlogic@lists.infradead.org, Zhentao Guo X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763955152; l=1441; i=zhentao.guo@amlogic.com; s=20251024; h=from:subject:message-id; bh=ipvSyoCuAjlj5iGd/O8ZvsuGOB2B3dXxp1S6BN3SOc4=; b=+1wPmnRCbaMUs0SfXj44FPENcjNu8W4k/F7Tgy5s0bjQiMcfnJkYcBKCv5+xdb+cSPZT7jend eHRRpV6VIQSA8ZOxSqEmAJO5YdnW3ZVx3xXwZdX4csth3oNHl+54ezg X-Developer-Key: i=zhentao.guo@amlogic.com; a=ed25519; pk=5yfDKrjreXwcAoEUsdtWafy6YN500upXp/CgtnXjLVU= X-Endpoint-Received: by B4 Relay for zhentao.guo@amlogic.com/20251024 with auth_id=555 X-Original-From: Zhentao Guo Reply-To: zhentao.guo@amlogic.com From: Zhentao Guo Add vcodec node to enable Amlogic V4L2 stateless video decoder support. Signed-off-by: Zhentao Guo --- arch/arm64/boot/dts/amlogic/meson-s4.dtsi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-s4.dtsi b/arch/arm64/boot/dt= s/amlogic/meson-s4.dtsi index 9d99ed2994df..80f1b92492a5 100644 --- a/arch/arm64/boot/dts/amlogic/meson-s4.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-s4.dtsi @@ -853,5 +853,32 @@ emmc: mmc@fe08c000 { no-sd; status =3D "disabled"; }; + + canvas: video-lut@fe036048 { + compatible =3D "amlogic,canvas"; + reg =3D <0x0 0xfe036048 0x0 0x14>; + }; + + video-codec@fe320000 { + compatible =3D "amlogic,s4-vcodec-dec"; + reg =3D <0x0 0xfe320000 0x0 0x10000>, + <0x0 0xfe036000 0x0 0x20>; + amlogic,canvas =3D <&canvas>; + reg-names =3D "dos", + "dmc"; + interrupts =3D , + , + ; + clocks =3D <&clkc_periphs CLKID_DOS>, + <&clkc_periphs CLKID_VDEC_SEL>, + <&clkc_periphs CLKID_HEVCF_SEL>; + clock-names =3D "vdec", + "clk_vdec_mux", + "clk_hevcf_mux"; + power-domains =3D <&pwrc PWRC_S4_DOS_VDEC_ID>, + <&pwrc PWRC_S4_DOS_HEVC_ID>; + power-domain-names =3D "vdec", + "hevc"; + }; }; }; --=20 2.42.0