From nobody Mon Feb 9 01:51:43 2026 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 764403451D6; Wed, 21 Jan 2026 10:31:19 +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=1768991479; cv=none; b=evtwJhsdCWglfWfhP20K2QKECZetN+tfUdEPU+wrngp42r7Rt6h12phGkcRreUlmgeiw+5uVVo6YqE9uVQm9ycAr2iYpCL/5z0n9t0W1Fnaj5+yG83oR36NXKV+zw5xOC1PWiAYQ4iFy8UOAD0gI7OjZ4k8SaoLoPu5MUbU6T7k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768991479; c=relaxed/simple; bh=PaaWQXmLM1E6NYRT94l5gK80A8Ye3/T/39BeYDzC0gY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SE3I/BaGkZCG+TU4+5AoCGaoulVFVa6jhyVN0pqKYnjYZoYQLMsZXN9As1dEZoE74XphUSTC3yBuwESd5cRQz2lvtQOQ2yQnt1a1nZwkiThGrFXBFdxMWk2f5zltJOnyQqxIykNZW3atEJNvAjT48EsuBKvBKn+LBCVsWpyDFp0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ihuwjQfg; 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="ihuwjQfg" Received: by smtp.kernel.org (Postfix) with ESMTPS id AE957C19422; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768991478; bh=PaaWQXmLM1E6NYRT94l5gK80A8Ye3/T/39BeYDzC0gY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=ihuwjQfgxgZS36bwv4zJq/POGtYg/ppj3GgAa16ZQNE13CSu/Ph9ogzTMyiLOruCX 6+aDNMgXBkKjSHUYVCFBBKOSi2Vkynb9mKXZVwUkGaDjUWxn+5ZwYGcIJvIaMqOiEJ X4XDH9IDlDLjUMOvj5P8U+uNn2+anr1cSqdXT0JXCpHcnhajAxOFdZVjWp6bi1TfZS bPGLPt9ILzbMy3Xkd5kOvFJjUNYyTXWJFKhZyAsuJ39dw08hEHhfI0oeV4tysZDjoE msgTheZTEyiH95BqhBuz3u5soTSPaiOvGX9zI//5QWQXXDHQcbWG2LGx3tQElMFs5p gNk6tSnouh9eA== 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 98FD2C44502; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) From: Zhentao Guo via B4 Relay Date: Wed, 21 Jan 2026 18:30:39 +0800 Subject: [PATCH RFC v3 1/4] 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: <20260121-b4-s4-vdec-upstream-v3-1-4496aec3d79e@amlogic.com> References: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@amlogic.com> In-Reply-To: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@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=1768991475; l=3141; i=zhentao.guo@amlogic.com; s=20251024; h=from:subject:message-id; bh=+ALt9bgLT6yjuZPZxCh4y6nzKx5uZews8JNjwnFmwig=; b=eWVMe/Lg1HvciLUweUC9ZzVRriH1aor5JXrry2U3NCFMCjoAd97FgcXUj0fejSMt/NL5lf7io /lao5ud3NHKA7y+W1krJe7djQUlcxM5c7JuN/jn4/jYc7HDrgp3fey0 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 | 96 ++++++++++++++++++= ++++ 1 file changed, 96 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..88780514d06c --- /dev/null +++ b/Documentation/devicetree/bindings/media/amlogic,s4-vcodec-dec.yaml @@ -0,0 +1,96 @@ +# 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,s4-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 + + resets: + maxItems: 1 + + amlogic,canvas: + description: should point to a canvas provider node + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - reg + - reg-names + - interrupts + - clocks + - clock-names + - power-domains + - power-domain-names + +additionalProperties: false + +examples: + - | + #include + #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"; + resets =3D <&reset RESET_DOS>; + }; --=20 2.42.0 From nobody Mon Feb 9 01:51:43 2026 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 D75F835CB6F; Wed, 21 Jan 2026 10:31:19 +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=1768991480; cv=none; b=nwL/kSYjScDAQ40jrv8jfNezU9uO6TEWJ4ZrqQQxZPsPqFMJMKe+A32BytImp0tA7R7deMy2500e/gDP0MD2V/ohxk9GnvMOFUAMoOLJmY8nCGD8wF1xEmMO+AqbZbfJHnLWr6EuFp3h3ITKpiL8wyDkrR4uW4Pw9hdRAy7EEl4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768991480; c=relaxed/simple; bh=h4ujdW79NlhyUZ7OwKZE/EpPa2mH2FWSxcGhIlqSRxY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nbK+4N6+S4krOcqVm9I3kzE4NYCrM690FvY+u92gqbS2XJ8HpmKLoRRGUFHXw3kj1jIrzjJsXSx/WbCSd02QjQqIVHJyt4T6cEB80+n+eub8odzvs+KqxIsbePUNidElNEi76wCXh7U+vYjMQjK75E4q9j2gZ/MI713ci9FajyA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PSI32ITj; 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="PSI32ITj" Received: by smtp.kernel.org (Postfix) with ESMTPS id C52FDC19424; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768991478; bh=h4ujdW79NlhyUZ7OwKZE/EpPa2mH2FWSxcGhIlqSRxY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=PSI32ITjo+5E2krA+1RNRLyh/SyANdbF++Na/KEfOYtr9Xv8EzUVfr6wcP1JLwRqA JEjuy6U7hagLUiCvR7m84Rn6dOWk8k3NbYnvF60IIPuUqspukluF2ir7CAMuelPfgs Ntgs8RWmOQSOAKdHvTEGfg65pCDcGWMSl2woiURQLjB4M1edPER4kN37bhDsY2jJc9 Hr3vhCX2KHmdaRoYCGnbKoj1XYiwV93cTNFWE7Uwd+dH0kRYSrPWJ8G0hONyFQBUNh ne1hfuTxNFtBh0cYL8RAxVU+KQqHiVMPm+FnA/V/J/lSHCAC3TkPKgAs7Nh1DWTFDf do/S7a/rp2SdA== 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 B9E08C44506; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) From: Zhentao Guo via B4 Relay Date: Wed, 21 Jan 2026 18:30:40 +0800 Subject: [PATCH RFC v3 2/4] 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: <20260121-b4-s4-vdec-upstream-v3-2-4496aec3d79e@amlogic.com> References: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@amlogic.com> In-Reply-To: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@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=1768991475; l=156817; i=zhentao.guo@amlogic.com; s=20251024; h=from:subject:message-id; bh=4KC+AY2qIWlM5/DUNdXK3Cd7xVzZ5COP/qZ9AoMBUQw=; b=n9pReaQf+R7tg2Ubo/nYAePajyC58DVqRmmQWdNPRWtbcnN9CZlSFVoyKq9i0waL+xwzIOevf NcIWas+JMqSBuMBeJm7nz6vxM8WyaCBJrdaw0pctwilQneHBh4g1K0C 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 8bit H.264 bitstreams decoding. Currently only progressive streams are supported. 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 | 734 +++++++ drivers/media/platform/amlogic/vdec/aml_vdec.h | 33 + drivers/media/platform/amlogic/vdec/aml_vdec_drv.c | 239 +++ drivers/media/platform/amlogic/vdec/aml_vdec_drv.h | 172 ++ 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 | 85 + .../platform/amlogic/vdec/aml_vdec_platform.h | 50 + drivers/media/platform/amlogic/vdec/h264.c | 2129 ++++++++++++++++= ++++ drivers/media/platform/amlogic/vdec/h264.h | 299 +++ drivers/media/platform/amlogic/vdec/reg_defines.h | 177 ++ 17 files changed, 4708 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 14a06f856b81..077c628a46e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1369,6 +1369,13 @@ S: Maintained F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml F: drivers/spi/spi-amlogic-spisg.c =20 +AMLOGIC VDEC 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..158fd31ac6a2 --- /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_VDEC + tristate "Amlogic Video Decoder 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 decoder 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..9a4e3102a9b8 --- /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_VDEC) +=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..bbcb3978dd6b --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec.c @@ -0,0 +1,734 @@ +// 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 const struct aml_dec_type dec_type_name[] =3D { + { + .codec_type =3D CODEC_TYPE_H264, + .name =3D "H264", + }, +}; + +static const char *dec_type_to_name(unsigned int type) +{ + int i; + int size =3D ARRAY_SIZE(dec_type_name); + + for (i =3D 0; i < size; i++) { + if (dec_type_name[i].codec_type =3D=3D type) + return dec_type_name[i].name; + } + + return "ERR"; +} + +int aml_vdec_ctrls_setup(struct aml_vdec_ctx *ctx) +{ + int i; + int ctrls_size =3D ARRAY_SIZE(controls); + + 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 m2m_priv; + struct aml_vdec_dev *dev =3D ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct media_request *src_req; + int ret =3D 0; + const char *fw_path =3D dev->pvdec_data->fw_path[ctx->curr_dec_type]; + + src_buf =3D v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf =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_buf->vb2_buf.index, dst_buf->vb2_buf.index); + if (WARN_ON_ONCE(!ctx->codec_ops->run)) + goto err_cancel_job; + + src_req =3D src_buf->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); + + if (load_firmware(dev->dec_hw, fw_path)) + goto err_cancel_job; + + ret =3D ctx->codec_ops->run(ctx); + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf); + if (src_req) + v4l2_ctrl_request_complete(src_req, &ctx->ctrl_handler); + if (ret < 0) + goto err_cancel_job; + + return; + +err_cancel_job: + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, + VB2_BUF_STATE_ERROR); +} + +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 aml_vdec_ctx *ctx, + struct v4l2_fmtdesc *f, bool is_output) +{ + struct aml_vdec_dev *dev =3D ctx->dev; + const struct aml_video_fmt *fmt; + int i =3D 0, j =3D 0; + + for (; i < dev->pvdec_data->num_fmts; i++) { + fmt =3D &dev->pvdec_data->dec_fmt[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)); + return 0; + } + ++j; + } + return -EINVAL; +} + +static const struct aml_video_fmt *aml_vdec_get_video_fmt(struct aml_vdec_= dev + *dev, u32 format) +{ + const struct aml_video_fmt *fmt; + unsigned int k; + + for (k =3D 0; k < dev->pvdec_data->num_fmts; k++) { + fmt =3D &dev->pvdec_data->dec_fmt[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_vdec_dev *dev =3D ctx->dev; + struct aml_video_fmt *fmt_out =3D &ctx->dec_fmt[AML_FMT_SRC]; + int ret =3D -1; + + 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 %s\n", + __func__, dec_type_to_name(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) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) { + 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; + } +} + +static int vidioc_vdec_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + const struct aml_video_fmt *fmt; + struct aml_vdec_dev *dev =3D video_drvdata(file); + + if (fsize->index !=3D 0) + return -EINVAL; + + fmt =3D aml_vdec_get_video_fmt(dev, fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type =3D V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise =3D fmt->stepwise; + + return 0; +} + +static int vdec_try_fmt_mp(struct aml_vdec_ctx *ctx, enum v4l2_buf_type ty= pe, + struct v4l2_pix_format_mplane *pix_mp) +{ + struct aml_video_fmt *dec_fmt; + int i, align; + + if (V4L2_TYPE_IS_OUTPUT(type)) + dec_fmt =3D &ctx->dec_fmt[AML_FMT_SRC]; + else + dec_fmt =3D &ctx->dec_fmt[AML_FMT_DST]; + + pix_mp->field =3D V4L2_FIELD_NONE; + + if (V4L2_TYPE_IS_OUTPUT(type)) { + pix_mp->num_planes =3D dec_fmt->num_planes; + pix_mp->pixelformat =3D dec_fmt->fourcc; + if (!pix_mp->plane_fmt[0].sizeimage) + pix_mp->plane_fmt[0].sizeimage =3D + (pix_mp->height * pix_mp->width * 3) / 2; + } + + align =3D ctx->dev->pvdec_data->dec_fmt->align; + pix_mp->height =3D ALIGN(pix_mp->height, align); + pix_mp->width =3D ALIGN(pix_mp->width, align); + + v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, + &dec_fmt->stepwise); + dev_dbg(&ctx->dev->plat_dev->dev, + "%s type %d four_cc %d pix_mp->width %d pix_mp->height %d\n", + __func__, type, dec_fmt->fourcc, pix_mp->width, pix_mp->height); + + v4l2_fill_pixfmt_mp(pix_mp, dec_fmt->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; + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s type %d fmt %d num_plane %d sizeimage0 %d sizeimage1 %d\n", + __func__, type, pix_mp->pixelformat, pix_mp->num_planes, + pix_mp->plane_fmt[0].sizeimage, pix_mp->plane_fmt[1].sizeimage); + + return 0; +} + +static int vdec_s_fmt_output(struct aml_vdec_ctx *ctx, struct v4l2_format = *f) +{ + struct v4l2_pix_format_mplane *pix_mp =3D &f->fmt.pix_mp; + const struct aml_video_fmt *out_fmt; + struct vb2_queue *vq; + int ret; + + vq =3D v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (vb2_is_busy(vq) && + pix_mp->pixelformat !=3D ctx->pix_fmt[AML_FMT_SRC].pixelformat) + return -EBUSY; + + out_fmt =3D aml_vdec_get_video_fmt(ctx->dev, pix_mp->pixelformat); + if (out_fmt) + ctx->dec_fmt[AML_FMT_SRC] =3D *out_fmt; + else + dev_dbg(&ctx->dev->plat_dev->dev, + "%s fmt %d not supported, use default\n", __func__, + pix_mp->pixelformat); + + ret =3D vdec_try_fmt_mp(ctx, f->type, pix_mp); + set_pic_info(ctx, pix_mp, f->type); + + ctx->pix_fmt[AML_FMT_SRC] =3D *pix_mp; + ctx->pix_fmt[AML_FMT_DST] =3D *pix_mp; + + return ret; +} + +static int vdec_s_fmt_capture(struct aml_vdec_ctx *ctx, struct v4l2_format= *f) +{ + struct v4l2_pix_format_mplane *pix_mp =3D &f->fmt.pix_mp; + const struct aml_video_fmt *cap_fmt; + struct vb2_queue *vq; + int ret; + + vq =3D v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + cap_fmt =3D aml_vdec_get_video_fmt(ctx->dev, pix_mp->pixelformat); + if (cap_fmt) + ctx->dec_fmt[AML_FMT_DST] =3D *cap_fmt; + else + dev_dbg(&ctx->dev->plat_dev->dev, + "%s fmt %d not supported, use default\n", __func__, + pix_mp->pixelformat); + + ret =3D vdec_try_fmt_mp(ctx, f->type, pix_mp); + + ctx->pix_fmt[AML_FMT_DST] =3D *pix_mp; + + return ret; +} + +static void reset_output_fmts(struct aml_vdec_ctx *ctx) +{ + struct aml_vdec_dev *dev =3D ctx->dev; + const struct aml_video_fmt *out_fmt; + struct v4l2_pix_format_mplane fmt; + + /* reset default output fmt to V4L2_PIX_FMT_H264_SLICE */ + out_fmt =3D aml_vdec_get_video_fmt(dev, V4L2_PIX_FMT_H264_SLICE); + if (!out_fmt) + return; + + ctx->dec_fmt[AML_FMT_SRC] =3D *out_fmt; + + memset(&fmt, 0, sizeof(struct v4l2_pix_format_mplane)); + + fmt.height =3D out_fmt->stepwise.min_height; + fmt.width =3D out_fmt->stepwise.min_width; + /* reset bytesperline to 0 for output fmts */ + fmt.plane_fmt[0].bytesperline =3D 0; + fmt.colorspace =3D V4L2_COLORSPACE_DEFAULT; + fmt.ycbcr_enc =3D V4L2_YCBCR_ENC_DEFAULT; + fmt.quantization =3D V4L2_QUANTIZATION_DEFAULT; + fmt.xfer_func =3D V4L2_XFER_FUNC_DEFAULT; + vdec_try_fmt_mp(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, &fmt); + + ctx->pix_fmt[AML_FMT_SRC] =3D fmt; +} + +static void reset_capture_fmts(struct aml_vdec_ctx *ctx) +{ + struct aml_vdec_dev *dev =3D ctx->dev; + const struct aml_video_fmt *cap_fmt; + struct v4l2_pix_format_mplane fmt; + + /* reset default output fmt to V4L2_PIX_FMT_NV12 */ + cap_fmt =3D aml_vdec_get_video_fmt(dev, V4L2_PIX_FMT_NV12); + if (!cap_fmt) + return; + + ctx->dec_fmt[AML_FMT_DST] =3D *cap_fmt; + + memset(&fmt, 0, sizeof(struct v4l2_pix_format_mplane)); + + fmt.height =3D cap_fmt->stepwise.min_height; + fmt.width =3D cap_fmt->stepwise.min_width; + fmt.colorspace =3D V4L2_COLORSPACE_DEFAULT; + fmt.ycbcr_enc =3D V4L2_YCBCR_ENC_DEFAULT; + fmt.quantization =3D V4L2_QUANTIZATION_DEFAULT; + fmt.xfer_func =3D V4L2_XFER_FUNC_DEFAULT; + vdec_try_fmt_mp(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, &fmt); + + ctx->pix_fmt[AML_FMT_DST] =3D fmt; +} + +void aml_vdec_reset_fmts(struct aml_vdec_ctx *ctx) +{ + ctx->m2m_ctx->q_lock =3D &ctx->v4l2_intf_lock; + reset_output_fmts(ctx); + reset_capture_fmts(ctx); +} + +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; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) + *pix_mp =3D ctx->pix_fmt[AML_FMT_SRC]; + else + *pix_mp =3D ctx->pix_fmt[AML_FMT_DST]; + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s fmt %d num planes %d\n", __func__, pix_mp->pixelformat, + pix_mp->num_planes); + + return 0; +} + +static int vidioc_try_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vdec_try_fmt_mp(fh_to_dec_ctx(file), f->type, &f->fmt.pix_mp); +} + +static int vidioc_try_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vdec_try_fmt_mp(fh_to_dec_ctx(file), f->type, &f->fmt.pix_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_output(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_capture(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) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + + return vidioc_vdec_enum_fmt(ctx, f, 1); +} + +static int vidioc_vdec_enum_fmt_cap_mplane(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + struct aml_vdec_ctx *ctx =3D fh_to_dec_ctx(file); + + return vidioc_vdec_enum_fmt(ctx, 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 v4l2_pix_format_mplane *pix_fmt; + unsigned int i; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt =3D &ctx->pix_fmt[AML_FMT_SRC]; + else + pix_fmt =3D &ctx->pix_fmt[AML_FMT_DST]; + + if (*nplanes) { + if (*nplanes !=3D pix_fmt->num_planes) + return -EINVAL; + + for (i =3D 0; i < *nplanes; i++) { + if (sizes[i] < pix_fmt->plane_fmt[i].sizeimage) { + 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 pix_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 pix_fmt->plane_fmt[i].sizeimage; + } + } + + if (*nplanes) { + dev_dbg(&ctx->dev->plat_dev->dev, + "type: %d, plane: %d, buf cnt: %d, size: [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 v4l2_pix_format_mplane *pix_fmt; + unsigned int sizeimage =3D 0; + int i; + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + pix_fmt =3D &ctx->pix_fmt[AML_FMT_SRC]; + else + pix_fmt =3D &ctx->pix_fmt[AML_FMT_DST]; + + for (i =3D 0; i < pix_fmt->num_planes; i++) { + sizeimage =3D pix_fmt->plane_fmt[i].sizeimage; + if (vb2_plane_size(vb, i) < sizeimage) + return -EINVAL; + + if (V4L2_TYPE_IS_CAPTURE(vb->type)) { + vb2_set_plane_payload(vb, i, + pix_fmt->plane_fmt[i].sizeimage); + dev_dbg(&ctx->dev->plat_dev->dev, + "%s type: %d set plane: %d, sizeimage: %d\n", + __func__, vb->vb2_queue->type, i, + pix_fmt->plane_fmt[i].sizeimage); + } + } + + 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); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + ctx->is_output_streamon =3D 1; + if (aml_vdec_init_dec_inst(ctx) < 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, + .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..32f7fa245f7e --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec.h @@ -0,0 +1,33 @@ +/* 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" + +/** + * 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; +}; + +struct aml_dec_type { + unsigned int codec_type; + const char *name; +}; + +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_reset_fmts(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..55668d01b526 --- /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_obj(*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_reset_fmts(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..fa86233ea5b3 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_drv.h @@ -0,0 +1,172 @@ +/* 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_queue_type - Type of queue : cap or output + */ +enum aml_queue_type { + AML_FMT_SRC =3D 0, + AML_FMT_DST =3D 1, +}; + +/** + * struct aml_video_fmt - aml video decoder fmt information + * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. + * @align: Describe the align width/height required by hardware. + * @is_10_bit_support: If the curr platform support p010 output. + * @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; + int align; + int is_10_bit_support; + enum aml_fmt_type type; + enum aml_codec_type codec_type; + u32 num_planes; + const u8 *name; + struct v4l2_frmsize_stepwise stepwise; +}; + +/** + * 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 + * @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 { + 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. + * @pix_fmt: To store the V4L2 pixel format. + * @dec_fmt: To describe the decoding format supported by hardware platfor= m. + * @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 v4l2_pix_format_mplane pix_fmt[2]; + struct aml_video_fmt dec_fmt[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..5b43c72375e9 --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.c @@ -0,0 +1,85 @@ +// 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_hw.h" +#include "h264.h" + +#define VIDEO_DEC_H264 "s4_h264_multi.bin" + +static struct aml_video_fmt aml_s4_video_formats[] =3D { + { + .name =3D "H.264", + .fourcc =3D V4L2_PIX_FMT_H264_SLICE, + .type =3D AML_FMT_DEC, + .align =3D 64, + .is_10_bit_support =3D 0, + .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, + .align =3D 64, + .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, + .align =3D 64, + .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, + .align =3D 64, + .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, + .align =3D 64, + .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}, + }, +}; + +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, + }, +}; + +const struct aml_dev_platform_data aml_vdec_s4_pdata =3D { + .codec_ops =3D aml_S4_dec_ops, + .dec_fmt =3D aml_s4_video_formats, + .num_fmts =3D ARRAY_SIZE(aml_s4_video_formats), + .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..6bde55891f4b --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/aml_vdec_platform.h @@ -0,0 +1,50 @@ +/* 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 +#include "aml_vdec_drv.h" + +#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_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_codec_ops *codec_ops; + const struct aml_video_fmt *dec_fmt; + int num_fmts; + 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..a2bb56f2609d --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/h264.c @@ -0,0 +1,2129 @@ +// 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 +}; + +#define I_Slice 2 +#define P_Slice 5 +#define B_Slice 6 +#define P_Slice_0 0 +#define B_Slice_1 1 +#define I_Slice_7 7 + +/* 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_canvas { + u32 canvas_pos; + int poc; +}; + +struct h264_decode_buf_spec { + struct v4l2_h264_dpb_entry *dpb; + u32 canvas_pos; + u32 dpb_index; + int 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]; + u32 canvas_pos_map; + struct h264_canvas ref_canvas[V4L2_H264_NUM_DPB_ENTRIES + 1]; + 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]; + + 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); + + dev_dbg(&ctx->dev->plat_dev->dev, "%s sps id %d, pps id %d\n", + __func__, sps->seq_parameter_set_id, pps->pic_parameter_set_id); + + 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 list_size; +} + +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; + u8 index; + int i, j; + + h264_ctx->list_size[0] =3D list_size; + for (i =3D 0; i < list_size; 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 list_size; + for (j =3D 0; j < list_size; 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_h264_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_h264_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_reordering_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->sps; + 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]; + struct slice *curr_slice =3D &h264_ctx->mslice; + + 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__, + builder.num_valid); + + if (curr_slice->slice_type =3D=3D P_SLICE && + (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 (curr_slice->slice_type =3D=3D B_SLICE && + (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 int allocate_canvas_pos(struct aml_h264_ctx *h264_ctx, int poc) +{ + int i; + int ret =3D -1; + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + + for (i =3D 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + if (((h264_ctx->canvas_pos_map >> i) & 0x1) =3D=3D 0) { + h264_ctx->canvas_pos_map |=3D (1 << i); + h264_ctx->ref_canvas[i].poc =3D poc; + h264_ctx->ref_canvas[i].canvas_pos =3D i; + ret =3D i; + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s i %d pos_poc %d\n", __func__, i, + h264_ctx->ref_canvas[i].poc); + break; + } + } + + return ret; +} + +static void release_canvas_pos(struct aml_h264_ctx *h264_ctx, int index) +{ + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + + if (index >=3D 0) { + if (index > V4L2_H264_NUM_DPB_ENTRIES) { + 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->ref_canvas[index].poc !=3D INVALID_POC && + ((h264_ctx->canvas_pos_map >> index) & 0x1) =3D=3D + 0x1) { + h264_ctx->canvas_pos_map &=3D (~(1 << index)); + dev_dbg(&ctx->dev->plat_dev->dev, + "%s canvas_pos index %d released poc %d, canvas_pos_map 0x%x\n", + __func__, index, h264_ctx->ref_canvas[index].poc, + h264_ctx->canvas_pos_map); + h264_ctx->ref_canvas[index].poc =3D INVALID_POC; + h264_ctx->ref_canvas[index].canvas_pos =3D -1; + } + } + } +} + +static int get_canvas_pos_by_poc(struct aml_h264_ctx *h264_ctx, int poc) +{ + int i; + struct aml_vdec_ctx *ctx =3D h264_ctx->v4l2_ctx; + int ret_pos =3D -1; + + for (i =3D 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + if (h264_ctx->ref_canvas[i].poc =3D=3D poc) { + ret_pos =3D h264_ctx->ref_canvas[i].canvas_pos; + dev_dbg(&ctx->dev->plat_dev->dev, "%s canvas_pos %d\n", + __func__, ret_pos); + return ret_pos; + } + } + + dev_dbg(&ctx->dev->plat_dev->dev, + "%s error, no find canvas pos %d, poc %d\n", __func__, ret_pos, poc); + + return ret_pos; +} + +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); + for (i =3D 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) + release_canvas_pos(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); + } + + for (i =3D 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + col_poc =3D h264_ctx->ref_canvas[i].poc; + if (col_poc !=3D INVALID_POC && + (poc_is_in_dpb(col_poc, decode->dpb) !=3D 1)) + release_canvas_pos(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 + allocate_canvas_pos(h264_ctx, decode->top_field_order_cnt); + if (h264_ctx->curr_spec.canvas_pos < 0) + dev_err(&ctx->dev->plat_dev->dev, "curr_spec.canvas error\n"); + + 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; + + 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 + get_canvas_pos_by_poc(h264_ctx, + dpb->top_field_order_cnt); + if (buf_spec_l0->canvas_pos < 0) { + dev_err(&ctx->dev->plat_dev->dev, + "l0 canvas_pos %d error\n", + buf_spec_l0->canvas_pos); + continue; + } + 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]; + dev_dbg(&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx c_dma_addr 0x%ll= x\n", + buf_spec_l0->dpb->top_field_order_cnt, + buf_spec_l0->canvas_pos, + buf_spec_l0->y_dma_addr, + buf_spec_l0->c_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 + get_canvas_pos_by_poc(h264_ctx, + dpb->top_field_order_cnt); + if (buf_spec_l1->canvas_pos < 0) { + dev_err(&ctx->dev->plat_dev->dev, + "l1 canvas_pos %d error\n", + buf_spec_l1->canvas_pos); + continue; + } + 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]; + dev_dbg(&ctx->dev->plat_dev->dev, + "config canvas for poc %d canvas %d y_dma_addr 0x%llx c_dma_addr 0x%ll= x\n", + buf_spec_l1->dpb->top_field_order_cnt, + buf_spec_l1->canvas_pos, + buf_spec_l1->y_dma_addr, + buf_spec_l1->c_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 c_dma_addr 0x%ll= x\n", + buf_spec_l1->dpb->top_field_order_cnt, + buf_spec_l1->canvas_pos, + buf_spec_l1->y_dma_addr, + buf_spec_l1->c_dma_addr); + } + } +} + +static int get_poc_by_canvas_pos(struct aml_h264_ctx *h264_ctx, int canvas= _pos) +{ + int i; + + for (i =3D 0; i < (V4L2_H264_NUM_DPB_ENTRIES + 1); i++) { + if (h264_ctx->ref_canvas[i].canvas_pos =3D=3D canvas_pos) + return h264_ctx->ref_canvas[i].poc; + } + return -1; +} + +static struct v4l2_h264_dpb_entry *get_dpb_by_poc(struct v4l2_ctrl_h264_de= code_params *decode, + int poc) +{ + int i; + + for (i =3D 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { + if (decode->dpb[i].top_field_order_cnt =3D=3D poc) + return &decode->dpb[i]; + } + return NULL; +} + +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); + + for (j =3D 0; j < (V4L2_H264_NUM_DPB_ENTRIES + 1); j++) { + int poc; + struct v4l2_h264_dpb_entry *dpb =3D NULL; + + info0 =3D 0; + info1 =3D 0; + info2 =3D 0; + + poc =3D get_poc_by_canvas_pos(h264_ctx, j); + if (poc =3D=3D decode->top_field_order_cnt) { + info0 =3D 0xf480 | 0xf; + 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; + } else { + dpb =3D get_dpb_by_poc(decode, poc); + if (dpb && (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; + + 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++) { + dev_dbg(hw->dev, "H264_BUFFER_INFO_DATA: %x\n", ref_cfg); + 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; + + 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 get_canvas_index(struct aml_vdec_hw *hw, struct aml_vdec_ctx *= ctx) +{ + struct aml_h264_ctx *h264_ctx =3D (struct aml_h264_ctx *)hw->curr_ctx; + int i; + struct h264_decode_buf_spec *buf; + + config_decode_canvas(hw, &h264_ctx->curr_spec, + h264_ctx->mb_width, h264_ctx->mb_height); + 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]; + config_decode_canvas(hw, buf, h264_ctx->mb_width, + h264_ctx->mb_height); + } + } + + 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]; + config_decode_canvas(hw, buf, h264_ctx->mb_width, + h264_ctx->mb_height); + } + } +} + +static void release_canvas_index(struct aml_vdec_hw *hw, + struct h264_decode_buf_spec *buf) +{ + if (buf->y_canvas_index >=3D 0) { + dev_dbg(hw->dev, "free y_canvas %d\n", buf->y_canvas_index); + meson_canvas_free(hw->canvas, buf->y_canvas_index); + buf->y_canvas_index =3D -1; + } + + if (buf->u_canvas_index >=3D 0) { + dev_dbg(hw->dev, "free uv_canvas_index %d\n", + buf->u_canvas_index); + meson_canvas_free(hw->canvas, buf->u_canvas_index); + buf->u_canvas_index =3D -1; + buf->v_canvas_index =3D -1; + } +} + +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 */ + switch (h264_ctx->dpb_param.l.data[SLICE_TYPE]) { + case I_Slice: + curr_slice->slice_type =3D I_SLICE; + break; + case P_Slice: + curr_slice->slice_type =3D P_SLICE; + break; + case B_Slice: + curr_slice->slice_type =3D B_SLICE; + break; + default: + curr_slice->slice_type =3D MAX_SLICE_TYPES; + break; + } + + 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_active_minus1 + 1; + curr_slice->num_ref_idx_l1 =3D + h264_ctx->dpb_param.dpb.num_ref_idx_l1_active_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], AV_SCRATCH_F, &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->mslice.first_mb_in_slice !=3D 0) + h264_release_decode_spec(hw, ctx); + + h264_config_decode_spec(hw, ctx); + h264_reorder_reflists(h264_ctx); + get_canvas_index(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; + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, + VB2_BUF_STATE_DONE); + wake_up_interruptible(&ctx->queue); + break; + default: + h264_release_decode_spec(hw, ctx); + ctx->int_cond =3D 1; + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, + VB2_BUF_STATE_ERROR); + 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); + + 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->dec_fmt[AML_FMT_DST].fourcc =3D=3D V4L2_PIX_FMT_NV21 || + ctx->dec_fmt[AML_FMT_DST].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)); + + 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_obj(*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_ctx->new_pic_flag =3D 1; + 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; + + 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); + } + h264_release_decode_spec(dec_hw, ctx); + } else if (-ERESTARTSYS =3D=3D ret) { + ret =3D -1; + h264_release_decode_spec(dec_hw, ctx); + 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..830ab3241a1e --- /dev/null +++ b/drivers/media/platform/amlogic/vdec/h264.h @@ -0,0 +1,299 @@ +/* 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 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 REF_IDC_OVERRIDE_FLAG 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 Mon Feb 9 01:51:43 2026 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 201703B8D56; Wed, 21 Jan 2026 10:31:19 +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=1768991480; cv=none; b=nYWmpUKBDfctpBS7CdFAjks30N/pJxmIGwWLYJKDIG+ot+BjQ8gPlUMtYDLZzidN6dM5X/kg95PXb91hylccZxdMxKJYpGt21/ck+FAWkdQPXYMqn+jZDy2IvJGy4nfjbaEv/BToL4xbZz6DUqN/k/ntd/4dBz66ImIzbym40BU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768991480; c=relaxed/simple; bh=Zy41UwtVc78Y7hlHNkNvuMf7hnbnIw6ZTXc0eFogMko=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=mQow2z2JEAe8EpMkT5G0nNKQmm64xtgl7HKlJmUdbpeHL+y3oaIBl9/561wiMJc0m869yJfzHZYL29uNJSM5xfV2VUCcX4KV2Fd1oENHD7e7Q9tQEZpKQB/w9nisqmRtyhnqB8QyA9ZI1N8DwhtQNYYJnUSMhP1QJSXD53Q+tPQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FCMwyh1o; 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="FCMwyh1o" Received: by smtp.kernel.org (Postfix) with ESMTPS id D1E3BC19425; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768991478; bh=Zy41UwtVc78Y7hlHNkNvuMf7hnbnIw6ZTXc0eFogMko=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=FCMwyh1o7SWaoHVIGii+6tVQpFDHAxDN8dx2u4A6PXKYNbzewqIj6RxYq8LYMsbbf 1wD5u37qIFYvLKXmWEoeoSpo+CsjuColYqF0b9clu/24KJg5JlBkchVGfo1i7KNz8M SCophZVXnXCfPn20P2mKu8t3NCCxdxOQFSWfhW1TLCkvCIdioJriHwFwOHiKDLyyFG x1kPvbeg3tkgrPMDF1EL/ple/BTPc474tnkmh4RDNMKl3V5E2XKsMbN9rKQK39CJQJ NWke5z/MYK7QH4qOy5p1wsXD1D9w9HX4C63tAae8LyR2Ot3xRzPZiJVxiCHC8An/zV NGt2FIWpF5i1w== 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 CA5F3C44501; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) From: Zhentao Guo via B4 Relay Date: Wed, 21 Jan 2026 18:30:41 +0800 Subject: [PATCH RFC v3 3/4] 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: <20260121-b4-s4-vdec-upstream-v3-3-4496aec3d79e@amlogic.com> References: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@amlogic.com> In-Reply-To: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@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=1768991475; l=1534; i=zhentao.guo@amlogic.com; s=20251024; h=from:subject:message-id; bh=IzNXt2+VyFRVs1Y87t2pUqlUvtHK8Yc5ii/YgU+Xf3g=; b=xx4IFR50dTvR+IhoaSdyB9ewHO/ihu8C/le7zGIqU9cauI5USp2V62XmZWrQG6Xyn6pDqCTaP dN6f7CPzI+OATv8nm6k8GloH5TIl4kfEPREWeVr7nZvAcdz0mqLPX2i 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 | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/amlogic/meson-s4.dtsi b/arch/arm64/boot/dt= s/amlogic/meson-s4.dtsi index dfc0a30a6e61..b8355e41d550 100644 --- a/arch/arm64/boot/dts/amlogic/meson-s4.dtsi +++ b/arch/arm64/boot/dts/amlogic/meson-s4.dtsi @@ -862,5 +862,33 @@ emmc: mmc@fe08c000 { assigned-clocks =3D <&clkc_periphs CLKID_SD_EMMC_C>; assigned-clock-rates =3D <24000000>; }; + + 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"; + resets =3D <&reset RESET_DOS>; + }; }; }; --=20 2.42.0 From nobody Mon Feb 9 01:51:43 2026 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 EBB873D3307; Wed, 21 Jan 2026 10:31:19 +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=1768991480; cv=none; b=ez2j36LyuzeXOkm3EqGdD/yWZ4fUHBtM9AQtey6xdlO8SomGi/VwAi3YTH1m8SBnFlNlZZgcln7av6I0q/nH8tujP9o0zFfW6w9JUnGorOXGNgyvHimXUFhDOSNIWVu27lDkRLW/LyPlIE4IKdGTfapBdzm1Yc7P1fsFIAoq5SY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768991480; c=relaxed/simple; bh=ktJjYiJZb1HIGWFRxHzP1S+rxzcyHIkrThbcKjDlUng=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fnKHfn8HnBy1kewmIhfnKP7huJI0lbJx7+4B8efdxEIOdG1J/8E8wZzrI9mx+l7uIjBRaRUxO4kJ4GsottFiY9vJ0fJI0tgL71qp2xa2OJSvg5MRNyWQ8zAOFBLppUt4U6p/nO/eVK0YiQXnvCQReVEjIG5nyrqQj0kxD6H9jms= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eivTrjmC; 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="eivTrjmC" Received: by smtp.kernel.org (Postfix) with ESMTPS id E2F1FC16AAE; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768991478; bh=ktJjYiJZb1HIGWFRxHzP1S+rxzcyHIkrThbcKjDlUng=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=eivTrjmCt4PeQYw+N7FblAX/K+3zovf2BhYFTJ4DWHC4bY8x3msJ57a05Dw2OCW8q 2iPgCO8ikASaKdqcvzih+9Vie1TFC+e4NWVmdgtwXSzQujOYPkr/B2sYE2LsB7HO5U JPKDSOeACSvSGuCCuPHC7OmK85ptXz1eDJlEMCiU62qtiyUKHe97nTyi1WT8KRozi8 00hEbiqm7JJlF4YbnH72XcBV5GP4DiZfvZwuTUEmLFaKAKm5oMivCQPuLHh1G4eRNM whnP5uuPYZbi2KNFMatAiXe45YeFhjju/Jeyw6H7i/K6IIizs4evLgcRKl0u/06UWp wW+7LUXI/vNnA== 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 D9A1DC44503; Wed, 21 Jan 2026 10:31:18 +0000 (UTC) From: Zhentao Guo via B4 Relay Date: Wed, 21 Jan 2026 18:30:42 +0800 Subject: [PATCH RFC v3 4/4] arm64: defconfig: Enable VDEC driver for Amlogic 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: <20260121-b4-s4-vdec-upstream-v3-4-4496aec3d79e@amlogic.com> References: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@amlogic.com> In-Reply-To: <20260121-b4-s4-vdec-upstream-v3-0-4496aec3d79e@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=1768991475; l=650; i=zhentao.guo@amlogic.com; s=20251024; h=from:subject:message-id; bh=/Xlp3fNnkZZdZMhr2a18FNe8nGeN4JqqPDgMB8hAANM=; b=K+4paB1rPUspGyUu/1OETF9afMaVejfVTVDj8RBXrZzlVhhpdos/BnD7H6gTG9mQYVbeVAUou SEKbdncolWcDpRDiiYcIHHY8Fv2KzXuhTneysBb6jF+axM1ygDcCjBK 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 Enable the driver for Amlogic's stateless decoder. Signed-off-by: Zhentao Guo --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index c43dcadabec4..7c89837f683c 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -903,6 +903,7 @@ CONFIG_USB_VIDEO_CLASS=3Dm CONFIG_V4L_PLATFORM_DRIVERS=3Dy CONFIG_SDR_PLATFORM_DRIVERS=3Dy CONFIG_V4L_MEM2MEM_DRIVERS=3Dy +CONFIG_VIDEO_AMLOGIC_VDEC=3Dm CONFIG_VIDEO_AMPHION_VPU=3Dm CONFIG_VIDEO_CADENCE_CSI2RX=3Dm CONFIG_VIDEO_MEDIATEK_JPEG=3Dm --=20 2.42.0