From fc23107e1c85aa2ff700a593afa04b063f2be9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Pe=C4=8Dovnik?= Date: Sun, 19 Jan 2020 00:03:54 +0100 Subject: [PATCH] [Allwinner] Add analogue audio to H6, enable Cedrus (#1750) * [Allwinner] Add analogue audio to H6, enable Cedrus, remove deprecated patches, adjust config * Cleanup --- config/kernel/linux-sunxi-current.config | 19 +- config/kernel/linux-sunxi-dev.config | 9 +- config/kernel/linux-sunxi64-current.config | 15 +- ...-sun8i-support-RGMII-modes-with-PHY-.patch | 35 - ...-v4l2-add_CID_UNIT_CELL_SIZE-control.patch | 57 + ...v4l2-add_CTRL_TYPE_AREA-control-type.patch | 165 ++ ...initions-for-HEVC-stateless-decoding.patch | 1099 +++++++++++++ ...mem2mem-support-held-capture-buffers.patch | 265 ++++ ...-add-stateless_try_decoder_cmd-ioctl.patch | 101 ++ ...-add-mem2mem-add-new_frame-detection.patch | 74 + ...d-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch | 199 +++ ...2-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch | 66 + ...add-mem2mem-Fix-hold-buf-flag-checks.patch | 39 + ...-Add-DPB-entry-field-reference-flags.patch | 47 + ...TPUT-queued-buffer-after-CMD_STOP-v2.patch | 145 ++ ...m-sun4i-Add-support-for-Allwinner-H6.patch | 366 +++++ ...-allwinner-h6-add-AC200-EPHY-support.patch | 569 +++++++ ...-arm64-dts-sun50i-h6-add-AC200-nodes.patch | 79 + ...hoose-default-pixelformat-in-try_fmt.patch | 127 ++ ...ous-format-related-compliance-issues.patch | 135 ++ ...64-Support-multiple-slices-per-frame.patch | 139 ++ ...ssary-parenthesis-aroundDIV_ROUND_UP.patch | 34 + ...dia-cedrus-Add-HEVC-decoding-support.patch | 1176 ++++++++++++++ ...us-Fix-decoding-for-some-H264-videos.patch | 110 ++ ...us-Use-correct-H264-8x8-scaling-list.patch | 34 + ...Properly-signa-size-in-mode-register.patch | 118 ++ ...620-media-cedrus-Fix-H264-4k-support.patch | 211 +++ ...drus-Increase-maximum-supported-size.patch | 36 + .../0622-media-cedrus-improvements.patch | 1395 +++++++++++++++++ .../0623-media-cedrus-10bit-HEVC-hack.patch | 89 ++ .../0004-sun4i-i2s-improvements.patch | 12 +- ...-allwinner-h6-add-AC200-EPHY-support.patch | 569 +++++++ ...-arm64-dts-sun50i-h6-add-AC200-nodes.patch | 79 + ...m-sun4i-Add-support-for-Allwinner-H6.patch | 363 +++++ 34 files changed, 7927 insertions(+), 49 deletions(-) delete mode 100644 patch/kernel/sunxi-current/0003-net-stmmac-dwmac-sun8i-support-RGMII-modes-with-PHY-.patch create mode 100644 patch/kernel/sunxi-current/0010-media-v4l2-add_CID_UNIT_CELL_SIZE-control.patch create mode 100644 patch/kernel/sunxi-current/0011-media-v4l2-add_CTRL_TYPE_AREA-control-type.patch create mode 100644 patch/kernel/sunxi-current/0012-media-v4l2-add-definitions-for-HEVC-stateless-decoding.patch create mode 100644 patch/kernel/sunxi-current/0013-media-v4l2-add-mem2mem-support-held-capture-buffers.patch create mode 100644 patch/kernel/sunxi-current/0014-media-v4l2-add-mem2mem-add-stateless_try_decoder_cmd-ioctl.patch create mode 100644 patch/kernel/sunxi-current/0015-media-v4l2-add-mem2mem-add-new_frame-detection.patch create mode 100644 patch/kernel/sunxi-current/0016-media-v4l2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch create mode 100644 patch/kernel/sunxi-current/0017-media-v4l2-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch create mode 100644 patch/kernel/sunxi-current/0018-media-v4l2-add-mem2mem-Fix-hold-buf-flag-checks.patch create mode 100644 patch/kernel/sunxi-current/0019-media-uapi-h264-Add-DPB-entry-field-reference-flags.patch create mode 100644 patch/kernel/sunxi-current/0020-media-v4l2-mem2mem-mark-DONE-any-OUTPUT-queued-buffer-after-CMD_STOP-v2.patch create mode 100644 patch/kernel/sunxi-current/0526-pwm-sun4i-Add-support-for-Allwinner-H6.patch create mode 100644 patch/kernel/sunxi-current/0533-allwinner-h6-add-AC200-EPHY-support.patch create mode 100644 patch/kernel/sunxi-current/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch create mode 100644 patch/kernel/sunxi-current/0607-media-cedrus-choose-default-pixelformat-in-try_fmt.patch create mode 100644 patch/kernel/sunxi-current/0608-media-cedrus-fix-various-format-related-compliance-issues.patch create mode 100644 patch/kernel/sunxi-current/0609-media-cedrus-h264-Support-multiple-slices-per-frame.patch create mode 100644 patch/kernel/sunxi-current/0611-media-cedrus-Remove-unnecessary-parenthesis-aroundDIV_ROUND_UP.patch create mode 100644 patch/kernel/sunxi-current/0612-media-cedrus-Add-HEVC-decoding-support.patch create mode 100644 patch/kernel/sunxi-current/0615-media-cedrus-Fix-decoding-for-some-H264-videos.patch create mode 100644 patch/kernel/sunxi-current/0617-media-cedrus-Use-correct-H264-8x8-scaling-list.patch create mode 100644 patch/kernel/sunxi-current/0619-media-cedrus-Properly-signa-size-in-mode-register.patch create mode 100644 patch/kernel/sunxi-current/0620-media-cedrus-Fix-H264-4k-support.patch create mode 100644 patch/kernel/sunxi-current/0621-media-cedrus-Increase-maximum-supported-size.patch create mode 100644 patch/kernel/sunxi-current/0622-media-cedrus-improvements.patch create mode 100644 patch/kernel/sunxi-current/0623-media-cedrus-10bit-HEVC-hack.patch create mode 100644 patch/kernel/sunxi-dev/0533-allwinner-h6-add-AC200-EPHY-support.patch create mode 100644 patch/kernel/sunxi-dev/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch create mode 100644 patch/kernel/sunxi-dev/pwm-sun4i-Add-support-for-Allwinner-H6.patch diff --git a/config/kernel/linux-sunxi-current.config b/config/kernel/linux-sunxi-current.config index ff44cf79d..a39660e24 100644 --- a/config/kernel/linux-sunxi-current.config +++ b/config/kernel/linux-sunxi-current.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 5.4.10 Kernel Configuration +# Linux/arm 5.4.12 Kernel Configuration # # @@ -656,6 +656,7 @@ CONFIG_HAVE_ARCH_MMAP_RND_BITS=y CONFIG_HAVE_EXIT_THREAD=y CONFIG_ARCH_MMAP_RND_BITS=8 CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y +CONFIG_HAVE_COPY_THREAD_TLS=y CONFIG_CLONE_BACKWARDS=y CONFIG_OLD_SIGSUSPEND3=y CONFIG_OLD_SIGACTION=y @@ -2338,6 +2339,7 @@ CONFIG_LED_TRIGGER_PHY=y # # CONFIG_SFP is not set CONFIG_ADIN_PHY=m +CONFIG_AC200_PHY=m CONFIG_AMD_PHY=m CONFIG_AQUANTIA_PHY=m CONFIG_AX88796B_PHY=m @@ -3435,6 +3437,7 @@ CONFIG_MFD_SUN4I_GPADC=m # CONFIG_MFD_BCM590XX is not set # CONFIG_MFD_BD9571MWV is not set # CONFIG_MFD_AC100 is not set +CONFIG_MFD_AC200=m CONFIG_MFD_AXP20X=y CONFIG_MFD_AXP20X_I2C=y CONFIG_MFD_AXP20X_RSB=y @@ -3639,12 +3642,12 @@ CONFIG_MEDIA_ANALOG_TV_SUPPORT=y CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y CONFIG_MEDIA_RADIO_SUPPORT=y CONFIG_MEDIA_SDR_SUPPORT=y -# CONFIG_MEDIA_CEC_SUPPORT is not set -# CONFIG_MEDIA_CEC_RC is not set +CONFIG_MEDIA_CEC_SUPPORT=y +CONFIG_MEDIA_CEC_RC=y # CONFIG_CEC_PIN_ERROR_INJ is not set CONFIG_MEDIA_CONTROLLER=y CONFIG_MEDIA_CONTROLLER_DVB=y -# CONFIG_MEDIA_CONTROLLER_REQUEST_API is not set +CONFIG_MEDIA_CONTROLLER_REQUEST_API=y CONFIG_VIDEO_DEV=y CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_V4L2=y @@ -3827,6 +3830,12 @@ CONFIG_VIDEO_EM28XX_RC=m CONFIG_USB_AIRSPY=m CONFIG_USB_HACKRF=m CONFIG_USB_MSI2500=m + +# +# USB HDMI CEC adapters +# +CONFIG_USB_PULSE8_CEC=m +CONFIG_USB_RAINSHADOW_CEC=m CONFIG_V4L_PLATFORM_DRIVERS=y # CONFIG_VIDEO_CADENCE is not set CONFIG_VIDEO_ASPEED=m @@ -3846,6 +3855,7 @@ CONFIG_VIDEO_VIM2M=m CONFIG_VIDEO_VICODEC=m CONFIG_DVB_PLATFORM_DRIVERS=y CONFIG_DVB_C8SECTPFE=m +CONFIG_CEC_PLATFORM_DRIVERS=y # CONFIG_SDR_PLATFORM_DRIVERS is not set # @@ -5586,6 +5596,7 @@ CONFIG_AD9834=m CONFIG_STAGING_MEDIA=y CONFIG_VIDEO_SUNXI=y +CONFIG_VIDEO_SUNXI_CEDRUS=m # # soc_camera sensor drivers diff --git a/config/kernel/linux-sunxi-dev.config b/config/kernel/linux-sunxi-dev.config index 45e5fd360..d418824e1 100644 --- a/config/kernel/linux-sunxi-dev.config +++ b/config/kernel/linux-sunxi-dev.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 5.5.0-rc5 Kernel Configuration +# Linux/arm 5.5.0-rc6 Kernel Configuration # # @@ -655,6 +655,7 @@ CONFIG_HAVE_ARCH_MMAP_RND_BITS=y CONFIG_HAVE_EXIT_THREAD=y CONFIG_ARCH_MMAP_RND_BITS=8 CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y +CONFIG_HAVE_COPY_THREAD_TLS=y CONFIG_CLONE_BACKWARDS=y CONFIG_OLD_SIGSUSPEND3=y CONFIG_OLD_SIGACTION=y @@ -2305,6 +2306,7 @@ CONFIG_LED_TRIGGER_PHY=y # # CONFIG_SFP is not set CONFIG_ADIN_PHY=m +CONFIG_AC200_PHY=m CONFIG_AMD_PHY=m CONFIG_AQUANTIA_PHY=m CONFIG_AX88796B_PHY=m @@ -3410,6 +3412,7 @@ CONFIG_MFD_SUN4I_GPADC=m # CONFIG_MFD_BCM590XX is not set # CONFIG_MFD_BD9571MWV is not set # CONFIG_MFD_AC100 is not set +CONFIG_MFD_AC200=m CONFIG_MFD_AXP20X=y CONFIG_MFD_AXP20X_I2C=y CONFIG_MFD_AXP20X_RSB=y @@ -3619,7 +3622,7 @@ CONFIG_MEDIA_SDR_SUPPORT=y # CONFIG_CEC_PIN_ERROR_INJ is not set CONFIG_MEDIA_CONTROLLER=y CONFIG_MEDIA_CONTROLLER_DVB=y -# CONFIG_MEDIA_CONTROLLER_REQUEST_API is not set +CONFIG_MEDIA_CONTROLLER_REQUEST_API=y CONFIG_VIDEO_DEV=y CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_V4L2=y @@ -4689,6 +4692,7 @@ CONFIG_SND_SOC_ES8328=m CONFIG_SND_SOC_ES8328_I2C=m CONFIG_SND_SOC_ES8328_SPI=m # CONFIG_SND_SOC_GTM601 is not set +CONFIG_SND_SOC_EC25=m # CONFIG_SND_SOC_INNO_RK3036 is not set CONFIG_SND_SOC_MAX98088=m # CONFIG_SND_SOC_MAX98357A is not set @@ -5574,6 +5578,7 @@ CONFIG_AD9834=m CONFIG_STAGING_MEDIA=y CONFIG_VIDEO_SUNXI=y +CONFIG_VIDEO_SUNXI_CEDRUS=m # # soc_camera sensor drivers diff --git a/config/kernel/linux-sunxi64-current.config b/config/kernel/linux-sunxi64-current.config index 8409ac0c4..a63407bfd 100644 --- a/config/kernel/linux-sunxi64-current.config +++ b/config/kernel/linux-sunxi64-current.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 5.4.10 Kernel Configuration +# Linux/arm64 5.4.12 Kernel Configuration # # @@ -600,6 +600,7 @@ CONFIG_ARCH_MMAP_RND_BITS=18 CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y +CONFIG_HAVE_COPY_THREAD_TLS=y CONFIG_CLONE_BACKWARDS=y CONFIG_OLD_SIGSUSPEND3=y CONFIG_COMPAT_OLD_SIGACTION=y @@ -2235,6 +2236,7 @@ CONFIG_LED_TRIGGER_PHY=y # # CONFIG_SFP is not set CONFIG_ADIN_PHY=m +CONFIG_AC200_PHY=m CONFIG_AMD_PHY=m CONFIG_AQUANTIA_PHY=m CONFIG_AX88796B_PHY=m @@ -3356,6 +3358,7 @@ CONFIG_MFD_SUN4I_GPADC=y # CONFIG_MFD_BCM590XX is not set CONFIG_MFD_BD9571MWV=m # CONFIG_MFD_AC100 is not set +CONFIG_MFD_AC200=m CONFIG_MFD_AXP20X=y CONFIG_MFD_AXP20X_I2C=y CONFIG_MFD_AXP20X_RSB=y @@ -3561,6 +3564,7 @@ CONFIG_MEDIA_SDR_SUPPORT=y # CONFIG_CEC_PIN_ERROR_INJ is not set CONFIG_MEDIA_CONTROLLER=y CONFIG_MEDIA_CONTROLLER_DVB=y +CONFIG_MEDIA_CONTROLLER_REQUEST_API=y CONFIG_VIDEO_DEV=m CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_V4L2=m @@ -3568,6 +3572,7 @@ CONFIG_VIDEO_V4L2_I2C=y # CONFIG_VIDEO_ADV_DEBUG is not set CONFIG_VIDEO_FIXED_MINOR_RANGES=y CONFIG_VIDEO_TUNER=m +CONFIG_V4L2_MEM2MEM_DEV=m CONFIG_V4L2_FWNODE=m CONFIG_VIDEOBUF_GEN=m CONFIG_VIDEOBUF_VMALLOC=m @@ -5377,7 +5382,13 @@ CONFIG_AD9834=m # CONFIG_SPEAKUP is not set # end of Speakup console speech -# CONFIG_STAGING_MEDIA is not set +CONFIG_STAGING_MEDIA=y +CONFIG_VIDEO_SUNXI=y +CONFIG_VIDEO_SUNXI_CEDRUS=m + +# +# soc_camera sensor drivers +# # # Android diff --git a/patch/kernel/sunxi-current/0003-net-stmmac-dwmac-sun8i-support-RGMII-modes-with-PHY-.patch b/patch/kernel/sunxi-current/0003-net-stmmac-dwmac-sun8i-support-RGMII-modes-with-PHY-.patch deleted file mode 100644 index c417b7244..000000000 --- a/patch/kernel/sunxi-current/0003-net-stmmac-dwmac-sun8i-support-RGMII-modes-with-PHY-.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 98800eeb2244387e821f4af8d21ccf2deaf18da0 Mon Sep 17 00:00:00 2001 -From: Icenowy Zheng -Date: Mon, 21 Aug 2017 23:02:32 +0800 -Subject: [PATCH 003/146] net: stmmac: dwmac-sun8i: support RGMII modes with - PHY internal delay - -Some boards uses a PHY with internal delay with an Allwinner SoC. - -Support these PHY modes in the driver. - -As the driver has no configuration registers for these modes, just treat -them as ordinary RGMII. - -Signed-off-by: Icenowy Zheng ---- - drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c -index f9a61f90cfbc..3c18f4a9dd6c 100644 ---- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c -+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c -@@ -933,6 +933,9 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) - /* default */ - break; - case PHY_INTERFACE_MODE_RGMII: -+ case PHY_INTERFACE_MODE_RGMII_ID: -+ case PHY_INTERFACE_MODE_RGMII_RXID: -+ case PHY_INTERFACE_MODE_RGMII_TXID: - reg |= SYSCON_EPIT | SYSCON_ETCS_INT_GMII; - break; - case PHY_INTERFACE_MODE_RMII: --- -2.17.1 - diff --git a/patch/kernel/sunxi-current/0010-media-v4l2-add_CID_UNIT_CELL_SIZE-control.patch b/patch/kernel/sunxi-current/0010-media-v4l2-add_CID_UNIT_CELL_SIZE-control.patch new file mode 100644 index 000000000..3a5bb0ddc --- /dev/null +++ b/patch/kernel/sunxi-current/0010-media-v4l2-add_CID_UNIT_CELL_SIZE-control.patch @@ -0,0 +1,57 @@ + +From 61fd036d01111679b01e4b92e6bd0cdd33809aea Mon Sep 17 00:00:00 2001 +From: Ricardo Ribalda Delgado +Date: Mon, 7 Oct 2019 12:06:33 -0300 +Subject: [PATCH] media: add V4L2_CID_UNIT_CELL_SIZE control + +This control returns the unit cell size in nanometres. The struct provides +the width and the height in separated fields to take into consideration +asymmetric pixels and/or hardware binning. +This control is required for automatic calibration of sensors/cameras. + +Reviewed-by: Philipp Zabel +Signed-off-by: Ricardo Ribalda Delgado +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/v4l2-core/v4l2-ctrls.c | 5 +++++ + include/uapi/linux/v4l2-controls.h | 1 + + 2 files changed, 6 insertions(+) + +diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c +index 96cab2e173d3..bf50d37ef6c1 100644 +--- a/drivers/media/v4l2-core/v4l2-ctrls.c ++++ b/drivers/media/v4l2-core/v4l2-ctrls.c +@@ -996,6 +996,7 @@ const char *v4l2_ctrl_get_name(u32 id) + case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; + case V4L2_CID_PAN_SPEED: return "Pan, Speed"; + case V4L2_CID_TILT_SPEED: return "Tilt, Speed"; ++ case V4L2_CID_UNIT_CELL_SIZE: return "Unit Cell Size"; + + /* FM Radio Modulator controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ +@@ -1377,6 +1378,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER: + *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER; + break; ++ case V4L2_CID_UNIT_CELL_SIZE: ++ *type = V4L2_CTRL_TYPE_AREA; ++ *flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ break; + default: + *type = V4L2_CTRL_TYPE_INTEGER; + break; +diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h +index a2669b79b294..5a7bedee2b0e 100644 +--- a/include/uapi/linux/v4l2-controls.h ++++ b/include/uapi/linux/v4l2-controls.h +@@ -1034,6 +1034,7 @@ enum v4l2_jpeg_chroma_subsampling { + #define V4L2_CID_TEST_PATTERN_GREENR (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 5) + #define V4L2_CID_TEST_PATTERN_BLUE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 6) + #define V4L2_CID_TEST_PATTERN_GREENB (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 7) ++#define V4L2_CID_UNIT_CELL_SIZE (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 8) + + + /* Image processing controls */ +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0011-media-v4l2-add_CTRL_TYPE_AREA-control-type.patch b/patch/kernel/sunxi-current/0011-media-v4l2-add_CTRL_TYPE_AREA-control-type.patch new file mode 100644 index 000000000..d2af13eb0 --- /dev/null +++ b/patch/kernel/sunxi-current/0011-media-v4l2-add_CTRL_TYPE_AREA-control-type.patch @@ -0,0 +1,165 @@ + +From d1dc49370f8371b00e682ac409aa1987ce641e93 Mon Sep 17 00:00:00 2001 +From: Ricardo Ribalda Delgado +Date: Mon, 7 Oct 2019 12:06:31 -0300 +Subject: [PATCH] media: add V4L2_CTRL_TYPE_AREA control type + +This type contains the width and the height of a rectangular area. + +Reviewed-by: Jacopo Mondi +Signed-off-by: Ricardo Ribalda Delgado +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/v4l2-core/v4l2-ctrls.c | 21 ++++++++++++++ + include/media/v4l2-ctrls.h | 42 ++++++++++++++++++++++++++++ + include/uapi/linux/videodev2.h | 6 ++++ + 3 files changed, 69 insertions(+) + +diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c +index 219d8aeefa20..96cab2e173d3 100644 +--- a/drivers/media/v4l2-core/v4l2-ctrls.c ++++ b/drivers/media/v4l2-core/v4l2-ctrls.c +@@ -1677,6 +1677,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, + { + struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; + struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; ++ struct v4l2_area *area; + void *p = ptr.p + idx * ctrl->elem_size; + + switch ((u32)ctrl->type) { +@@ -1753,6 +1754,11 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, + zero_padding(p_vp8_frame_header->entropy_header); + zero_padding(p_vp8_frame_header->coder_state); + break; ++ case V4L2_CTRL_TYPE_AREA: ++ area = p; ++ if (!area->width || !area->height) ++ return -EINVAL; ++ break; + default: + return -EINVAL; + } +@@ -2427,6 +2433,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, + case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: + elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header); + break; ++ case V4L2_CTRL_TYPE_AREA: ++ elem_size = sizeof(struct v4l2_area); ++ break; + default: + if (type < V4L2_CTRL_COMPOUND_TYPES) + elem_size = sizeof(s32); +@@ -4116,6 +4125,18 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) + } + EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); + ++int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl, ++ const struct v4l2_area *area) ++{ ++ lockdep_assert_held(ctrl->handler->lock); ++ ++ /* It's a driver bug if this happens. */ ++ WARN_ON(ctrl->type != V4L2_CTRL_TYPE_AREA); ++ *ctrl->p_new.p_area = *area; ++ return set_ctrl(NULL, ctrl, 0); ++} ++EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_area); ++ + void v4l2_ctrl_request_complete(struct media_request *req, + struct v4l2_ctrl_handler *main_hdl) + { +diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h +index fb0883836548..c9ca867ef32b 100644 +--- a/include/media/v4l2-ctrls.h ++++ b/include/media/v4l2-ctrls.h +@@ -50,6 +50,7 @@ struct poll_table_struct; + * @p_h264_slice_params: Pointer to a struct v4l2_ctrl_h264_slice_params. + * @p_h264_decode_params: Pointer to a struct v4l2_ctrl_h264_decode_params. + * @p_vp8_frame_header: Pointer to a VP8 frame header structure. ++ * @p_area: Pointer to an area. + * @p: Pointer to a compound value. + */ + union v4l2_ctrl_ptr { +@@ -68,6 +69,7 @@ union v4l2_ctrl_ptr { + struct v4l2_ctrl_h264_slice_params *p_h264_slice_params; + struct v4l2_ctrl_h264_decode_params *p_h264_decode_params; + struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; ++ struct v4l2_area *p_area; + void *p; + }; + +@@ -1087,6 +1089,46 @@ static inline int v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) + return rval; + } + ++/** ++ * __v4l2_ctrl_s_ctrl_area() - Unlocked variant of v4l2_ctrl_s_ctrl_area(). ++ * ++ * @ctrl: The control. ++ * @area: The new area. ++ * ++ * This sets the control's new area safely by going through the control ++ * framework. This function assumes the control's handler is already locked, ++ * allowing it to be used from within the &v4l2_ctrl_ops functions. ++ * ++ * This function is for area type controls only. ++ */ ++int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl, ++ const struct v4l2_area *area); ++ ++/** ++ * v4l2_ctrl_s_ctrl_area() - Helper function to set a control's area value ++ * from within a driver. ++ * ++ * @ctrl: The control. ++ * @area: The new area. ++ * ++ * This sets the control's new area safely by going through the control ++ * framework. This function will lock the control's handler, so it cannot be ++ * used from within the &v4l2_ctrl_ops functions. ++ * ++ * This function is for area type controls only. ++ */ ++static inline int v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl, ++ const struct v4l2_area *area) ++{ ++ int rval; ++ ++ v4l2_ctrl_lock(ctrl); ++ rval = __v4l2_ctrl_s_ctrl_area(ctrl, area); ++ v4l2_ctrl_unlock(ctrl); ++ ++ return rval; ++} ++ + /* Internal helper functions that deal with control events. */ + extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; + +diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h +index 530638dffd93..b3c0961b62a0 100644 +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -422,6 +422,11 @@ struct v4l2_fract { + __u32 denominator; + }; + ++struct v4l2_area { ++ __u32 width; ++ __u32 height; ++}; ++ + /** + * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP + * +@@ -1720,6 +1725,7 @@ enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_U8 = 0x0100, + V4L2_CTRL_TYPE_U16 = 0x0101, + V4L2_CTRL_TYPE_U32 = 0x0102, ++ V4L2_CTRL_TYPE_AREA = 0x0106, + }; + + /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ +-- +2.23.0 + diff --git a/patch/kernel/sunxi-current/0012-media-v4l2-add-definitions-for-HEVC-stateless-decoding.patch b/patch/kernel/sunxi-current/0012-media-v4l2-add-definitions-for-HEVC-stateless-decoding.patch new file mode 100644 index 000000000..187a9fe47 --- /dev/null +++ b/patch/kernel/sunxi-current/0012-media-v4l2-add-definitions-for-HEVC-stateless-decoding.patch @@ -0,0 +1,1099 @@ + +From 256fa3920874b0f1f4cb79ad6766493a22187153 Mon Sep 17 00:00:00 2001 +From: Paul Kocialkowski +Date: Tue, 22 Oct 2019 12:26:52 -0300 +Subject: [PATCH] media: v4l: Add definitions for HEVC stateless decoding + +This introduces the required definitions for HEVC decoding support with +stateless VPUs. The controls associated to the HEVC slice format provide +the required meta-data for decoding slices extracted from the bitstream. + +They are not exported to the public V4L2 API since reworking this API +will likely be needed for covering various use-cases and new hardware. + +Multi-slice decoding is exposed as a valid decoding mode to match current +H.264 support but it is not yet implemented. + +The interface comes with the following limitations: +* No custom quantization matrices (scaling lists); +* Support for a single temporal layer only; +* No slice entry point offsets support; +* No conformance window support; +* No VUI parameters support; +* No support for SPS extensions: range, multilayer, 3d, scc, 4 bits; +* No support for PPS extensions: range, multilayer, 3d, scc, 4 bits. + +Signed-off-by: Paul Kocialkowski +[hverkuil-cisco@xs4all.nl: use 1ULL in flags defines in hevc-ctrls.h] +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + Documentation/media/uapi/v4l/biblio.rst | 9 + + .../media/uapi/v4l/ext-ctrls-codec.rst | 553 +++++++++++++++++- + .../media/uapi/v4l/vidioc-queryctrl.rst | 18 + + .../media/videodev2.h.rst.exceptions | 3 + + drivers/media/v4l2-core/v4l2-ctrls.c | 109 +++- + drivers/media/v4l2-core/v4l2-ioctl.c | 1 + + include/media/hevc-ctrls.h | 212 +++++++ + include/media/v4l2-ctrls.h | 7 + + 8 files changed, 908 insertions(+), 4 deletions(-) + create mode 100644 include/media/hevc-ctrls.h + +diff --git a/Documentation/media/uapi/v4l/biblio.rst b/Documentation/media/uapi/v4l/biblio.rst +index ad2ff258afa8..8095f57d3d75 100644 +--- a/Documentation/media/uapi/v4l/biblio.rst ++++ b/Documentation/media/uapi/v4l/biblio.rst +@@ -131,6 +131,15 @@ ITU-T Rec. H.264 Specification (04/2017 Edition) + + :author: International Telecommunication Union (http://www.itu.ch) + ++.. _hevc: ++ ++ITU H.265/HEVC ++============== ++ ++:title: ITU-T Rec. H.265 | ISO/IEC 23008-2 "High Efficiency Video Coding" ++ ++:author: International Telecommunication Union (http://www.itu.ch), International Organisation for Standardisation (http://www.iso.ch) ++ + .. _jfif: + + JFIF +diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +index 6bb901de0939..a1209f68c5e8 100644 +--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst ++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +@@ -1985,9 +1985,9 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - + - ``reference_ts`` + - Timestamp of the V4L2 capture buffer to use as reference, used + with B-coded and P-coded frames. The timestamp refers to the +- ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the +- :c:func:`v4l2_timeval_to_ns()` function to convert the struct +- :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. ++ ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the ++ :c:func:`v4l2_timeval_to_ns()` function to convert the struct ++ :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u16 + - ``frame_num`` + - +@@ -3695,3 +3695,550 @@ enum v4l2_mpeg_video_hevc_size_of_length_field - + Indicates whether to generate SPS and PPS at every IDR. Setting it to 0 + disables generating SPS and PPS at every IDR. Setting it to one enables + generating SPS and PPS at every IDR. ++ ++.. _v4l2-mpeg-hevc: ++ ++``V4L2_CID_MPEG_VIDEO_HEVC_SPS (struct)`` ++ Specifies the Sequence Parameter Set fields (as extracted from the ++ bitstream) for the associated HEVC slice data. ++ These bitstream parameters are defined according to :ref:`hevc`. ++ They are described in section 7.4.3.2 "Sequence parameter set RBSP ++ semantics" of the specification. ++ ++.. c:type:: v4l2_ctrl_hevc_sps ++ ++.. cssclass:: longtable ++ ++.. flat-table:: struct v4l2_ctrl_hevc_sps ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - __u16 ++ - ``pic_width_in_luma_samples`` ++ - ++ * - __u16 ++ - ``pic_height_in_luma_samples`` ++ - ++ * - __u8 ++ - ``bit_depth_luma_minus8`` ++ - ++ * - __u8 ++ - ``bit_depth_chroma_minus8`` ++ - ++ * - __u8 ++ - ``log2_max_pic_order_cnt_lsb_minus4`` ++ - ++ * - __u8 ++ - ``sps_max_dec_pic_buffering_minus1`` ++ - ++ * - __u8 ++ - ``sps_max_num_reorder_pics`` ++ - ++ * - __u8 ++ - ``sps_max_latency_increase_plus1`` ++ - ++ * - __u8 ++ - ``log2_min_luma_coding_block_size_minus3`` ++ - ++ * - __u8 ++ - ``log2_diff_max_min_luma_coding_block_size`` ++ - ++ * - __u8 ++ - ``log2_min_luma_transform_block_size_minus2`` ++ - ++ * - __u8 ++ - ``log2_diff_max_min_luma_transform_block_size`` ++ - ++ * - __u8 ++ - ``max_transform_hierarchy_depth_inter`` ++ - ++ * - __u8 ++ - ``max_transform_hierarchy_depth_intra`` ++ - ++ * - __u8 ++ - ``pcm_sample_bit_depth_luma_minus1`` ++ - ++ * - __u8 ++ - ``pcm_sample_bit_depth_chroma_minus1`` ++ - ++ * - __u8 ++ - ``log2_min_pcm_luma_coding_block_size_minus3`` ++ - ++ * - __u8 ++ - ``log2_diff_max_min_pcm_luma_coding_block_size`` ++ - ++ * - __u8 ++ - ``num_short_term_ref_pic_sets`` ++ - ++ * - __u8 ++ - ``num_long_term_ref_pics_sps`` ++ - ++ * - __u8 ++ - ``chroma_format_idc`` ++ - ++ * - __u64 ++ - ``flags`` ++ - See :ref:`Sequence Parameter Set Flags ` ++ ++.. _hevc_sps_flags: ++ ++``Sequence Parameter Set Flags`` ++ ++.. cssclass:: longtable ++ ++.. flat-table:: ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - ``V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE`` ++ - 0x00000001 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED`` ++ - 0x00000002 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_AMP_ENABLED`` ++ - 0x00000004 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET`` ++ - 0x00000008 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_PCM_ENABLED`` ++ - 0x00000010 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED`` ++ - 0x00000020 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT`` ++ - 0x00000040 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED`` ++ - 0x00000080 ++ - ++ * - ``V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED`` ++ - 0x00000100 ++ - ++ ++``V4L2_CID_MPEG_VIDEO_HEVC_PPS (struct)`` ++ Specifies the Picture Parameter Set fields (as extracted from the ++ bitstream) for the associated HEVC slice data. ++ These bitstream parameters are defined according to :ref:`hevc`. ++ They are described in section 7.4.3.3 "Picture parameter set RBSP ++ semantics" of the specification. ++ ++.. c:type:: v4l2_ctrl_hevc_pps ++ ++.. cssclass:: longtable ++ ++.. flat-table:: struct v4l2_ctrl_hevc_pps ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - __u8 ++ - ``num_extra_slice_header_bits`` ++ - ++ * - __s8 ++ - ``init_qp_minus26`` ++ - ++ * - __u8 ++ - ``diff_cu_qp_delta_depth`` ++ - ++ * - __s8 ++ - ``pps_cb_qp_offset`` ++ - ++ * - __s8 ++ - ``pps_cr_qp_offset`` ++ - ++ * - __u8 ++ - ``num_tile_columns_minus1`` ++ - ++ * - __u8 ++ - ``num_tile_rows_minus1`` ++ - ++ * - __u8 ++ - ``column_width_minus1[20]`` ++ - ++ * - __u8 ++ - ``row_height_minus1[22]`` ++ - ++ * - __s8 ++ - ``pps_beta_offset_div2`` ++ - ++ * - __s8 ++ - ``pps_tc_offset_div2`` ++ - ++ * - __u8 ++ - ``log2_parallel_merge_level_minus2`` ++ - ++ * - __u8 ++ - ``padding[4]`` ++ - Applications and drivers must set this to zero. ++ * - __u64 ++ - ``flags`` ++ - See :ref:`Picture Parameter Set Flags ` ++ ++.. _hevc_pps_flags: ++ ++``Picture Parameter Set Flags`` ++ ++.. cssclass:: longtable ++ ++.. flat-table:: ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - ``V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT`` ++ - 0x00000001 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT`` ++ - 0x00000002 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED`` ++ - 0x00000004 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT`` ++ - 0x00000008 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED`` ++ - 0x00000010 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED`` ++ - 0x00000020 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED`` ++ - 0x00000040 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT`` ++ - 0x00000080 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED`` ++ - 0x00000100 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED`` ++ - 0x00000200 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED`` ++ - 0x00000400 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_TILES_ENABLED`` ++ - 0x00000800 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED`` ++ - 0x00001000 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED`` ++ - 0x00002000 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED`` ++ - 0x00004000 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED`` ++ - 0x00008000 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER`` ++ - 0x00010000 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT`` ++ - 0x00020000 ++ - ++ * - ``V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT`` ++ - 0x00040000 ++ - ++ ++``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (struct)`` ++ Specifies various slice-specific parameters, especially from the NAL unit ++ header, general slice segment header and weighted prediction parameter ++ parts of the bitstream. ++ These bitstream parameters are defined according to :ref:`hevc`. ++ They are described in section 7.4.7 "General slice segment header ++ semantics" of the specification. ++ ++.. c:type:: v4l2_ctrl_hevc_slice_params ++ ++.. cssclass:: longtable ++ ++.. flat-table:: struct v4l2_ctrl_hevc_slice_params ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - __u32 ++ - ``bit_size`` ++ - Size (in bits) of the current slice data. ++ * - __u32 ++ - ``data_bit_offset`` ++ - Offset (in bits) to the video data in the current slice data. ++ * - __u8 ++ - ``nal_unit_type`` ++ - ++ * - __u8 ++ - ``nuh_temporal_id_plus1`` ++ - ++ * - __u8 ++ - ``slice_type`` ++ - ++ (V4L2_HEVC_SLICE_TYPE_I, V4L2_HEVC_SLICE_TYPE_P or ++ V4L2_HEVC_SLICE_TYPE_B). ++ * - __u8 ++ - ``colour_plane_id`` ++ - ++ * - __u16 ++ - ``slice_pic_order_cnt`` ++ - ++ * - __u8 ++ - ``num_ref_idx_l0_active_minus1`` ++ - ++ * - __u8 ++ - ``num_ref_idx_l1_active_minus1`` ++ - ++ * - __u8 ++ - ``collocated_ref_idx`` ++ - ++ * - __u8 ++ - ``five_minus_max_num_merge_cand`` ++ - ++ * - __s8 ++ - ``slice_qp_delta`` ++ - ++ * - __s8 ++ - ``slice_cb_qp_offset`` ++ - ++ * - __s8 ++ - ``slice_cr_qp_offset`` ++ - ++ * - __s8 ++ - ``slice_act_y_qp_offset`` ++ - ++ * - __s8 ++ - ``slice_act_cb_qp_offset`` ++ - ++ * - __s8 ++ - ``slice_act_cr_qp_offset`` ++ - ++ * - __s8 ++ - ``slice_beta_offset_div2`` ++ - ++ * - __s8 ++ - ``slice_tc_offset_div2`` ++ - ++ * - __u8 ++ - ``pic_struct`` ++ - ++ * - __u8 ++ - ``num_active_dpb_entries`` ++ - The number of entries in ``dpb``. ++ * - __u8 ++ - ``ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` ++ - The list of L0 reference elements as indices in the DPB. ++ * - __u8 ++ - ``ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` ++ - The list of L1 reference elements as indices in the DPB. ++ * - __u8 ++ - ``num_rps_poc_st_curr_before`` ++ - The number of reference pictures in the short-term set that come before ++ the current frame. ++ * - __u8 ++ - ``num_rps_poc_st_curr_after`` ++ - The number of reference pictures in the short-term set that come after ++ the current frame. ++ * - __u8 ++ - ``num_rps_poc_lt_curr`` ++ - The number of reference pictures in the long-term set. ++ * - __u8 ++ - ``padding[7]`` ++ - Applications and drivers must set this to zero. ++ * - struct :c:type:`v4l2_hevc_dpb_entry` ++ - ``dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` ++ - The decoded picture buffer, for meta-data about reference frames. ++ * - struct :c:type:`v4l2_hevc_pred_weight_table` ++ - ``pred_weight_table`` ++ - The prediction weight coefficients for inter-picture prediction. ++ * - __u64 ++ - ``flags`` ++ - See :ref:`Slice Parameters Flags ` ++ ++.. _hevc_slice_params_flags: ++ ++``Slice Parameters Flags`` ++ ++.. cssclass:: longtable ++ ++.. flat-table:: ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA`` ++ - 0x00000001 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA`` ++ - 0x00000002 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED`` ++ - 0x00000004 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO`` ++ - 0x00000008 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT`` ++ - 0x00000010 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0`` ++ - 0x00000020 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV`` ++ - 0x00000040 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED`` ++ - 0x00000080 ++ - ++ * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED`` ++ - 0x00000100 ++ - ++ ++.. c:type:: v4l2_hevc_dpb_entry ++ ++.. cssclass:: longtable ++ ++.. flat-table:: struct v4l2_hevc_dpb_entry ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - __u64 ++ - ``timestamp`` ++ - Timestamp of the V4L2 capture buffer to use as reference, used ++ with B-coded and P-coded frames. The timestamp refers to the ++ ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the ++ :c:func:`v4l2_timeval_to_ns()` function to convert the struct ++ :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. ++ * - __u8 ++ - ``rps`` ++ - The reference set for the reference frame ++ (V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE, ++ V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER or ++ V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR) ++ * - __u8 ++ - ``field_pic`` ++ - Whether the reference is a field picture or a frame. ++ * - __u16 ++ - ``pic_order_cnt[2]`` ++ - The picture order count of the reference. Only the first element of the ++ array is used for frame pictures, while the first element identifies the ++ top field and the second the bottom field in field-coded pictures. ++ * - __u8 ++ - ``padding[2]`` ++ - Applications and drivers must set this to zero. ++ ++.. c:type:: v4l2_hevc_pred_weight_table ++ ++.. cssclass:: longtable ++ ++.. flat-table:: struct v4l2_hevc_pred_weight_table ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - __u8 ++ - ``luma_log2_weight_denom`` ++ - ++ * - __s8 ++ - ``delta_chroma_log2_weight_denom`` ++ - ++ * - __s8 ++ - ``delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` ++ - ++ * - __s8 ++ - ``luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` ++ - ++ * - __s8 ++ - ``delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]`` ++ - ++ * - __s8 ++ - ``chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]`` ++ - ++ * - __s8 ++ - ``delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` ++ - ++ * - __s8 ++ - ``luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` ++ - ++ * - __s8 ++ - ``delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]`` ++ - ++ * - __s8 ++ - ``chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]`` ++ - ++ * - __u8 ++ - ``padding[6]`` ++ - Applications and drivers must set this to zero. ++ ++``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (enum)`` ++ Specifies the decoding mode to use. Currently exposes slice-based and ++ frame-based decoding but new modes might be added later on. ++ This control is used as a modifier for V4L2_PIX_FMT_HEVC_SLICE ++ pixel format. Applications that support V4L2_PIX_FMT_HEVC_SLICE ++ are required to set this control in order to specify the decoding mode ++ that is expected for the buffer. ++ Drivers may expose a single or multiple decoding modes, depending ++ on what they can support. ++ ++ .. note:: ++ ++ This menu control is not yet part of the public kernel API and ++ it is expected to change. ++ ++.. c:type:: v4l2_mpeg_video_hevc_decode_mode ++ ++.. cssclass:: longtable ++ ++.. flat-table:: ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - ``V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED`` ++ - 0 ++ - Decoding is done at the slice granularity. ++ The OUTPUT buffer must contain a single slice. ++ * - ``V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED`` ++ - 1 ++ - Decoding is done at the frame granularity. ++ The OUTPUT buffer must contain all slices needed to decode the ++ frame. The OUTPUT buffer must also contain both fields. ++ ++``V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (enum)`` ++ Specifies the HEVC slice start code expected for each slice. ++ This control is used as a modifier for V4L2_PIX_FMT_HEVC_SLICE ++ pixel format. Applications that support V4L2_PIX_FMT_HEVC_SLICE ++ are required to set this control in order to specify the start code ++ that is expected for the buffer. ++ Drivers may expose a single or multiple start codes, depending ++ on what they can support. ++ ++ .. note:: ++ ++ This menu control is not yet part of the public kernel API and ++ it is expected to change. ++ ++.. c:type:: v4l2_mpeg_video_hevc_start_code ++ ++.. cssclass:: longtable ++ ++.. flat-table:: ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - ``V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE`` ++ - 0 ++ - Selecting this value specifies that HEVC slices are passed ++ to the driver without any start code. ++ * - ``V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B`` ++ - 1 ++ - Selecting this value specifies that HEVC slices are expected ++ to be prefixed by Annex B start codes. According to :ref:`hevc` ++ valid start codes can be 3-bytes 0x000001 or 4-bytes 0x00000001. +diff --git a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst +index 33aff21b7d11..6690928e657b 100644 +--- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst ++++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst +@@ -479,6 +479,24 @@ See also the examples in :ref:`control`. + - n/a + - A struct :c:type:`v4l2_ctrl_h264_decode_params`, containing H264 + decode parameters for stateless video decoders. ++ * - ``V4L2_CTRL_TYPE_HEVC_SPS`` ++ - n/a ++ - n/a ++ - n/a ++ - A struct :c:type:`v4l2_ctrl_hevc_sps`, containing HEVC Sequence ++ Parameter Set for stateless video decoders. ++ * - ``V4L2_CTRL_TYPE_HEVC_PPS`` ++ - n/a ++ - n/a ++ - n/a ++ - A struct :c:type:`v4l2_ctrl_hevc_pps`, containing HEVC Picture ++ Parameter Set for stateless video decoders. ++ * - ``V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS`` ++ - n/a ++ - n/a ++ - n/a ++ - A struct :c:type:`v4l2_ctrl_hevc_slice_params`, containing HEVC ++ slice parameters for stateless video decoders. + + .. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}| + + +diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c +index bf50d37ef6c1..b4caf2d4d076 100644 +--- a/drivers/media/v4l2-core/v4l2-ctrls.c ++++ b/drivers/media/v4l2-core/v4l2-ctrls.c +@@ -568,6 +568,16 @@ const char * const *v4l2_ctrl_get_menu(u32 id) + "Disabled at slice boundary", + "NULL", + }; ++ static const char * const hevc_decode_mode[] = { ++ "Slice-Based", ++ "Frame-Based", ++ NULL, ++ }; ++ static const char * const hevc_start_code[] = { ++ "No Start Code", ++ "Annex B Start Code", ++ NULL, ++ }; + + switch (id) { + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: +@@ -689,7 +699,10 @@ const char * const *v4l2_ctrl_get_menu(u32 id) + return hevc_tier; + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: + return hevc_loop_filter_mode; +- ++ case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: ++ return hevc_decode_mode; ++ case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: ++ return hevc_start_code; + default: + return NULL; + } +@@ -959,6 +972,11 @@ const char *v4l2_ctrl_get_name(u32 id) + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: return "HEVC Size of Length Field"; + case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES: return "Reference Frames for a P-Frame"; + case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: return "Prepend SPS and PPS to IDR"; ++ case V4L2_CID_MPEG_VIDEO_HEVC_SPS: return "HEVC Sequence Parameter Set"; ++ case V4L2_CID_MPEG_VIDEO_HEVC_PPS: return "HEVC Picture Parameter Set"; ++ case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters"; ++ case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: return "HEVC Decode Mode"; ++ case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return "HEVC Start Code"; + + /* CAMERA controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ +@@ -1268,6 +1286,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD: + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: ++ case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: ++ case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: + *type = V4L2_CTRL_TYPE_MENU; + break; + case V4L2_CID_LINK_FREQ: +@@ -1378,6 +1398,15 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER: + *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER; + break; ++ case V4L2_CID_MPEG_VIDEO_HEVC_SPS: ++ *type = V4L2_CTRL_TYPE_HEVC_SPS; ++ break; ++ case V4L2_CID_MPEG_VIDEO_HEVC_PPS: ++ *type = V4L2_CTRL_TYPE_HEVC_PPS; ++ break; ++ case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: ++ *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS; ++ break; + case V4L2_CID_UNIT_CELL_SIZE: + *type = V4L2_CTRL_TYPE_AREA; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; +@@ -1682,8 +1711,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, + { + struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; + struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; ++ struct v4l2_ctrl_hevc_sps *p_hevc_sps; ++ struct v4l2_ctrl_hevc_pps *p_hevc_pps; ++ struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; + struct v4l2_area *area; + void *p = ptr.p + idx * ctrl->elem_size; ++ unsigned int i; + + switch ((u32)ctrl->type) { + case V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS: +@@ -1759,11 +1792,76 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, + zero_padding(p_vp8_frame_header->entropy_header); + zero_padding(p_vp8_frame_header->coder_state); + break; ++ ++ case V4L2_CTRL_TYPE_HEVC_SPS: ++ p_hevc_sps = p; ++ ++ if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) { ++ p_hevc_sps->pcm_sample_bit_depth_luma_minus1 = 0; ++ p_hevc_sps->pcm_sample_bit_depth_chroma_minus1 = 0; ++ p_hevc_sps->log2_min_pcm_luma_coding_block_size_minus3 = 0; ++ p_hevc_sps->log2_diff_max_min_pcm_luma_coding_block_size = 0; ++ } ++ ++ if (!(p_hevc_sps->flags & ++ V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT)) ++ p_hevc_sps->num_long_term_ref_pics_sps = 0; ++ break; ++ ++ case V4L2_CTRL_TYPE_HEVC_PPS: ++ p_hevc_pps = p; ++ ++ if (!(p_hevc_pps->flags & ++ V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED)) ++ p_hevc_pps->diff_cu_qp_delta_depth = 0; ++ ++ if (!(p_hevc_pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)) { ++ p_hevc_pps->num_tile_columns_minus1 = 0; ++ p_hevc_pps->num_tile_rows_minus1 = 0; ++ memset(&p_hevc_pps->column_width_minus1, 0, ++ sizeof(p_hevc_pps->column_width_minus1)); ++ memset(&p_hevc_pps->row_height_minus1, 0, ++ sizeof(p_hevc_pps->row_height_minus1)); ++ ++ p_hevc_pps->flags &= ++ ~V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED; ++ } ++ ++ if (p_hevc_pps->flags & ++ V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER) { ++ p_hevc_pps->pps_beta_offset_div2 = 0; ++ p_hevc_pps->pps_tc_offset_div2 = 0; ++ } ++ ++ zero_padding(*p_hevc_pps); ++ break; ++ ++ case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: ++ p_hevc_slice_params = p; ++ ++ if (p_hevc_slice_params->num_active_dpb_entries > ++ V4L2_HEVC_DPB_ENTRIES_NUM_MAX) ++ return -EINVAL; ++ ++ zero_padding(p_hevc_slice_params->pred_weight_table); ++ ++ for (i = 0; i < p_hevc_slice_params->num_active_dpb_entries; ++ i++) { ++ struct v4l2_hevc_dpb_entry *dpb_entry = ++ &p_hevc_slice_params->dpb[i]; ++ ++ zero_padding(*dpb_entry); ++ } ++ ++ zero_padding(*p_hevc_slice_params); ++ break; ++ + case V4L2_CTRL_TYPE_AREA: + area = p; + if (!area->width || !area->height) + return -EINVAL; + break; ++ + default: + return -EINVAL; + } +@@ -2438,6 +2536,15 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, + case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: + elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header); + break; ++ case V4L2_CTRL_TYPE_HEVC_SPS: ++ elem_size = sizeof(struct v4l2_ctrl_hevc_sps); ++ break; ++ case V4L2_CTRL_TYPE_HEVC_PPS: ++ elem_size = sizeof(struct v4l2_ctrl_hevc_pps); ++ break; ++ case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: ++ elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params); ++ break; + case V4L2_CTRL_TYPE_AREA: + elem_size = sizeof(struct v4l2_area); + break; +diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c +index 2753073cf340..d26c83d4c255 100644 +--- a/drivers/media/v4l2-core/v4l2-ioctl.c ++++ b/drivers/media/v4l2-core/v4l2-ioctl.c +@@ -1367,6 +1367,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) + case V4L2_PIX_FMT_VP8_FRAME: descr = "VP8 Frame"; break; + case V4L2_PIX_FMT_VP9: descr = "VP9"; break; + case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */ ++ case V4L2_PIX_FMT_HEVC_SLICE: descr = "HEVC Parsed Slice Data"; break; + case V4L2_PIX_FMT_FWHT: descr = "FWHT"; break; /* used in vicodec */ + case V4L2_PIX_FMT_FWHT_STATELESS: descr = "FWHT Stateless"; break; /* used in vicodec */ + case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break; +diff --git a/include/media/hevc-ctrls.h b/include/media/hevc-ctrls.h +new file mode 100644 +index 000000000000..1009cf0891cc +--- /dev/null ++++ b/include/media/hevc-ctrls.h +@@ -0,0 +1,212 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * These are the HEVC state controls for use with stateless HEVC ++ * codec drivers. ++ * ++ * It turns out that these structs are not stable yet and will undergo ++ * more changes. So keep them private until they are stable and ready to ++ * become part of the official public API. ++ */ ++ ++#ifndef _HEVC_CTRLS_H_ ++#define _HEVC_CTRLS_H_ ++ ++#include ++ ++/* The pixel format isn't stable at the moment and will likely be renamed. */ ++#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */ ++ ++#define V4L2_CID_MPEG_VIDEO_HEVC_SPS (V4L2_CID_MPEG_BASE + 1008) ++#define V4L2_CID_MPEG_VIDEO_HEVC_PPS (V4L2_CID_MPEG_BASE + 1009) ++#define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_MPEG_BASE + 1010) ++#define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (V4L2_CID_MPEG_BASE + 1015) ++#define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (V4L2_CID_MPEG_BASE + 1016) ++ ++/* enum v4l2_ctrl_type type values */ ++#define V4L2_CTRL_TYPE_HEVC_SPS 0x0120 ++#define V4L2_CTRL_TYPE_HEVC_PPS 0x0121 ++#define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122 ++ ++enum v4l2_mpeg_video_hevc_decode_mode { ++ V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED, ++ V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED, ++}; ++ ++enum v4l2_mpeg_video_hevc_start_code { ++ V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE, ++ V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B, ++}; ++ ++#define V4L2_HEVC_SLICE_TYPE_B 0 ++#define V4L2_HEVC_SLICE_TYPE_P 1 ++#define V4L2_HEVC_SLICE_TYPE_I 2 ++ ++#define V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE (1ULL << 0) ++#define V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED (1ULL << 1) ++#define V4L2_HEVC_SPS_FLAG_AMP_ENABLED (1ULL << 2) ++#define V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET (1ULL << 3) ++#define V4L2_HEVC_SPS_FLAG_PCM_ENABLED (1ULL << 4) ++#define V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED (1ULL << 5) ++#define V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT (1ULL << 6) ++#define V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED (1ULL << 7) ++#define V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED (1ULL << 8) ++ ++/* The controls are not stable at the moment and will likely be reworked. */ ++struct v4l2_ctrl_hevc_sps { ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Sequence parameter set */ ++ __u16 pic_width_in_luma_samples; ++ __u16 pic_height_in_luma_samples; ++ __u8 bit_depth_luma_minus8; ++ __u8 bit_depth_chroma_minus8; ++ __u8 log2_max_pic_order_cnt_lsb_minus4; ++ __u8 sps_max_dec_pic_buffering_minus1; ++ __u8 sps_max_num_reorder_pics; ++ __u8 sps_max_latency_increase_plus1; ++ __u8 log2_min_luma_coding_block_size_minus3; ++ __u8 log2_diff_max_min_luma_coding_block_size; ++ __u8 log2_min_luma_transform_block_size_minus2; ++ __u8 log2_diff_max_min_luma_transform_block_size; ++ __u8 max_transform_hierarchy_depth_inter; ++ __u8 max_transform_hierarchy_depth_intra; ++ __u8 pcm_sample_bit_depth_luma_minus1; ++ __u8 pcm_sample_bit_depth_chroma_minus1; ++ __u8 log2_min_pcm_luma_coding_block_size_minus3; ++ __u8 log2_diff_max_min_pcm_luma_coding_block_size; ++ __u8 num_short_term_ref_pic_sets; ++ __u8 num_long_term_ref_pics_sps; ++ __u8 chroma_format_idc; ++ ++ __u8 padding; ++ ++ __u64 flags; ++}; ++ ++#define V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT (1ULL << 0) ++#define V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT (1ULL << 1) ++#define V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED (1ULL << 2) ++#define V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT (1ULL << 3) ++#define V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED (1ULL << 4) ++#define V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED (1ULL << 5) ++#define V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED (1ULL << 6) ++#define V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT (1ULL << 7) ++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED (1ULL << 8) ++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED (1ULL << 9) ++#define V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED (1ULL << 10) ++#define V4L2_HEVC_PPS_FLAG_TILES_ENABLED (1ULL << 11) ++#define V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED (1ULL << 12) ++#define V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED (1ULL << 13) ++#define V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 14) ++#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED (1ULL << 15) ++#define V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER (1ULL << 16) ++#define V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT (1ULL << 17) ++#define V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT (1ULL << 18) ++ ++struct v4l2_ctrl_hevc_pps { ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture parameter set */ ++ __u8 num_extra_slice_header_bits; ++ __s8 init_qp_minus26; ++ __u8 diff_cu_qp_delta_depth; ++ __s8 pps_cb_qp_offset; ++ __s8 pps_cr_qp_offset; ++ __u8 num_tile_columns_minus1; ++ __u8 num_tile_rows_minus1; ++ __u8 column_width_minus1[20]; ++ __u8 row_height_minus1[22]; ++ __s8 pps_beta_offset_div2; ++ __s8 pps_tc_offset_div2; ++ __u8 log2_parallel_merge_level_minus2; ++ ++ __u8 padding[4]; ++ __u64 flags; ++}; ++ ++#define V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE 0x01 ++#define V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER 0x02 ++#define V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR 0x03 ++ ++#define V4L2_HEVC_DPB_ENTRIES_NUM_MAX 16 ++ ++struct v4l2_hevc_dpb_entry { ++ __u64 timestamp; ++ __u8 rps; ++ __u8 field_pic; ++ __u16 pic_order_cnt[2]; ++ __u8 padding[2]; ++}; ++ ++struct v4l2_hevc_pred_weight_table { ++ __s8 delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ __s8 luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ __s8 delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]; ++ __s8 chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]; ++ ++ __s8 delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ __s8 luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ __s8 delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]; ++ __s8 chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]; ++ ++ __u8 padding[6]; ++ ++ __u8 luma_log2_weight_denom; ++ __s8 delta_chroma_log2_weight_denom; ++}; ++ ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA (1ULL << 0) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA (1ULL << 1) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED (1ULL << 2) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO (1ULL << 3) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT (1ULL << 4) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0 (1ULL << 5) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV (1ULL << 6) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7) ++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8) ++ ++struct v4l2_ctrl_hevc_slice_params { ++ __u32 bit_size; ++ __u32 data_bit_offset; ++ ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */ ++ __u8 nal_unit_type; ++ __u8 nuh_temporal_id_plus1; ++ ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ ++ __u8 slice_type; ++ __u8 colour_plane_id; ++ __u16 slice_pic_order_cnt; ++ __u8 num_ref_idx_l0_active_minus1; ++ __u8 num_ref_idx_l1_active_minus1; ++ __u8 collocated_ref_idx; ++ __u8 five_minus_max_num_merge_cand; ++ __s8 slice_qp_delta; ++ __s8 slice_cb_qp_offset; ++ __s8 slice_cr_qp_offset; ++ __s8 slice_act_y_qp_offset; ++ __s8 slice_act_cb_qp_offset; ++ __s8 slice_act_cr_qp_offset; ++ __s8 slice_beta_offset_div2; ++ __s8 slice_tc_offset_div2; ++ ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */ ++ __u8 pic_struct; ++ ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ ++ __u8 num_active_dpb_entries; ++ __u8 ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ __u8 ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ ++ __u8 num_rps_poc_st_curr_before; ++ __u8 num_rps_poc_st_curr_after; ++ __u8 num_rps_poc_lt_curr; ++ ++ __u8 padding; ++ ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ ++ struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; ++ ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */ ++ struct v4l2_hevc_pred_weight_table pred_weight_table; ++ ++ __u64 flags; ++}; ++ ++#endif +diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h +index 26205ba3a0a0..e719d56fc024 100644 +--- a/include/media/v4l2-ctrls.h ++++ b/include/media/v4l2-ctrls.h +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + /* forward references */ + struct file; +@@ -50,6 +51,9 @@ struct poll_table_struct; + * @p_h264_slice_params: Pointer to a struct v4l2_ctrl_h264_slice_params. + * @p_h264_decode_params: Pointer to a struct v4l2_ctrl_h264_decode_params. + * @p_vp8_frame_header: Pointer to a VP8 frame header structure. ++ * @p_hevc_sps: Pointer to an HEVC sequence parameter set structure. ++ * @p_hevc_pps: Pointer to an HEVC picture parameter set structure. ++ * @p_hevc_slice_params: Pointer to an HEVC slice parameters structure. + * @p_area: Pointer to an area. + * @p: Pointer to a compound value. + */ +@@ -69,6 +73,9 @@ union v4l2_ctrl_ptr { + struct v4l2_ctrl_h264_slice_params *p_h264_slice_params; + struct v4l2_ctrl_h264_decode_params *p_h264_decode_params; + struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; ++ struct v4l2_ctrl_hevc_sps *p_hevc_sps; ++ struct v4l2_ctrl_hevc_pps *p_hevc_pps; ++ struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; + struct v4l2_area *p_area; + void *p; + }; +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0013-media-v4l2-add-mem2mem-support-held-capture-buffers.patch b/patch/kernel/sunxi-current/0013-media-v4l2-add-mem2mem-support-held-capture-buffers.patch new file mode 100644 index 000000000..2ce318b7d --- /dev/null +++ b/patch/kernel/sunxi-current/0013-media-v4l2-add-mem2mem-support-held-capture-buffers.patch @@ -0,0 +1,265 @@ + +From f8cca8c97a63d77f48334cde81d15014f43530ef Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Fri, 11 Oct 2019 06:32:41 -0300 +Subject: [PATCH] media: v4l2-mem2mem: support held capture buffers + +Check for held buffers that are ready to be returned to vb2 in +__v4l2_m2m_try_queue(). This avoids drivers having to handle this +case. + +Add v4l2_m2m_buf_done_and_job_finish() to correctly return source +and destination buffers and mark the job as finished while taking +a held destination buffer into account (i.e. that buffer won't be +returned). This has to be done while job_spinlock is held to avoid +race conditions. + +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/v4l2-core/v4l2-mem2mem.c | 130 ++++++++++++++++++------- + include/media/v4l2-mem2mem.h | 33 ++++++- + 2 files changed, 128 insertions(+), 35 deletions(-) + +diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c +index 19937dd3c6f6..79c3656f24f7 100644 +--- a/drivers/media/v4l2-core/v4l2-mem2mem.c ++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c +@@ -284,7 +284,8 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) + static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) + { +- unsigned long flags_job, flags_out, flags_cap; ++ unsigned long flags_job; ++ struct vb2_v4l2_buffer *dst, *src; + + dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); + +@@ -307,20 +308,30 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + goto job_unlock; + } + +- spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); +- if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue) +- && !m2m_ctx->out_q_ctx.buffered) { ++ src = v4l2_m2m_next_src_buf(m2m_ctx); ++ dst = v4l2_m2m_next_dst_buf(m2m_ctx); ++ if (!src && !m2m_ctx->out_q_ctx.buffered) { + dprintk("No input buffers available\n"); +- goto out_unlock; ++ goto job_unlock; + } +- spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); +- if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue) +- && !m2m_ctx->cap_q_ctx.buffered) { ++ if (!dst && !m2m_ctx->cap_q_ctx.buffered) { + dprintk("No output buffers available\n"); +- goto cap_unlock; ++ goto job_unlock; ++ } ++ ++ if (src && dst && ++ dst->is_held && dst->vb2_buf.copied_timestamp && ++ dst->vb2_buf.timestamp != src->vb2_buf.timestamp) { ++ dst->is_held = false; ++ v4l2_m2m_dst_buf_remove(m2m_ctx); ++ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); ++ dst = v4l2_m2m_next_dst_buf(m2m_ctx); ++ ++ if (!dst && !m2m_ctx->cap_q_ctx.buffered) { ++ dprintk("No output buffers available after returning held buffer\n"); ++ goto job_unlock; ++ } + } +- spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); +- spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); + + if (m2m_dev->m2m_ops->job_ready + && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { +@@ -331,13 +342,6 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue); + m2m_ctx->job_flags |= TRANS_QUEUED; + +- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); +- return; +- +-cap_unlock: +- spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); +-out_unlock: +- spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); + job_unlock: + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); + } +@@ -412,37 +416,97 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) + } + } + +-void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, +- struct v4l2_m2m_ctx *m2m_ctx) ++/* ++ * Schedule the next job, called from v4l2_m2m_job_finish() or ++ * v4l2_m2m_buf_done_and_job_finish(). ++ */ ++static void v4l2_m2m_schedule_next_job(struct v4l2_m2m_dev *m2m_dev, ++ struct v4l2_m2m_ctx *m2m_ctx) + { +- unsigned long flags; ++ /* ++ * This instance might have more buffers ready, but since we do not ++ * allow more than one job on the job_queue per instance, each has ++ * to be scheduled separately after the previous one finishes. ++ */ ++ __v4l2_m2m_try_queue(m2m_dev, m2m_ctx); + +- spin_lock_irqsave(&m2m_dev->job_spinlock, flags); ++ /* ++ * We might be running in atomic context, ++ * but the job must be run in non-atomic context. ++ */ ++ schedule_work(&m2m_dev->job_work); ++} ++ ++/* ++ * Assumes job_spinlock is held, called from v4l2_m2m_job_finish() or ++ * v4l2_m2m_buf_done_and_job_finish(). ++ */ ++static bool _v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, ++ struct v4l2_m2m_ctx *m2m_ctx) ++{ + if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) { +- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Called by an instance not currently running\n"); +- return; ++ return false; + } + + list_del(&m2m_dev->curr_ctx->queue); + m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); + wake_up(&m2m_dev->curr_ctx->finished); + m2m_dev->curr_ctx = NULL; ++ return true; ++} + +- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); +- +- /* This instance might have more buffers ready, but since we do not +- * allow more than one job on the job_queue per instance, each has +- * to be scheduled separately after the previous one finishes. */ +- __v4l2_m2m_try_queue(m2m_dev, m2m_ctx); ++void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, ++ struct v4l2_m2m_ctx *m2m_ctx) ++{ ++ unsigned long flags; ++ bool schedule_next; + +- /* We might be running in atomic context, +- * but the job must be run in non-atomic context. ++ /* ++ * This function should not be used for drivers that support ++ * holding capture buffers. Those should use ++ * v4l2_m2m_buf_done_and_job_finish() instead. + */ +- schedule_work(&m2m_dev->job_work); ++ WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags & ++ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF); ++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags); ++ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx); ++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); ++ ++ if (schedule_next) ++ v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx); + } + EXPORT_SYMBOL(v4l2_m2m_job_finish); + ++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev, ++ struct v4l2_m2m_ctx *m2m_ctx, ++ enum vb2_buffer_state state) ++{ ++ struct vb2_v4l2_buffer *src_buf, *dst_buf; ++ bool schedule_next = false; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags); ++ src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); ++ dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx); ++ ++ if (WARN_ON(!src_buf || !dst_buf)) ++ goto unlock; ++ v4l2_m2m_buf_done(src_buf, state); ++ dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; ++ if (!dst_buf->is_held) { ++ v4l2_m2m_dst_buf_remove(m2m_ctx); ++ v4l2_m2m_buf_done(dst_buf, state); ++ } ++ schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx); ++unlock: ++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); ++ ++ if (schedule_next) ++ v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx); ++} ++EXPORT_SYMBOL(v4l2_m2m_buf_done_and_job_finish); ++ + int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_requestbuffers *reqbufs) + { +diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h +index 0b9c3a287061..229d9f5d4370 100644 +--- a/include/media/v4l2-mem2mem.h ++++ b/include/media/v4l2-mem2mem.h +@@ -21,7 +21,8 @@ + * callback. + * The job does NOT have to end before this callback returns + * (and it will be the usual case). When the job finishes, +- * v4l2_m2m_job_finish() has to be called. ++ * v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish() ++ * has to be called. + * @job_ready: optional. Should return 0 if the driver does not have a job + * fully prepared to run yet (i.e. it will not be able to finish a + * transaction without sleeping). If not provided, it will be +@@ -33,7 +34,8 @@ + * stop the device safely; e.g. in the next interrupt handler), + * even if the transaction would not have been finished by then. + * After the driver performs the necessary steps, it has to call +- * v4l2_m2m_job_finish() (as if the transaction ended normally). ++ * v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish() as ++ * if the transaction ended normally. + * This function does not have to (and will usually not) wait + * until the device enters a state when it can be stopped. + */ +@@ -173,6 +175,33 @@ void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx); + void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx); + ++/** ++ * v4l2_m2m_buf_done_and_job_finish() - return source/destination buffers with ++ * state and inform the framework that a job has been finished and have it ++ * clean up ++ * ++ * @m2m_dev: opaque pointer to the internal data to handle M2M context ++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx ++ * @state: vb2 buffer state passed to v4l2_m2m_buf_done(). ++ * ++ * Drivers that set V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF must use this ++ * function instead of job_finish() to take held buffers into account. It is ++ * optional for other drivers. ++ * ++ * This function removes the source buffer from the ready list and returns ++ * it with the given state. The same is done for the destination buffer, unless ++ * it is marked 'held'. In that case the buffer is kept on the ready list. ++ * ++ * After that the job is finished (see job_finish()). ++ * ++ * This allows for multiple output buffers to be used to fill in a single ++ * capture buffer. This is typically used by stateless decoders where ++ * multiple e.g. H.264 slices contribute to a single decoded frame. ++ */ ++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev, ++ struct v4l2_m2m_ctx *m2m_ctx, ++ enum vb2_buffer_state state); ++ + static inline void + v4l2_m2m_buf_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state) + { +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0014-media-v4l2-add-mem2mem-add-stateless_try_decoder_cmd-ioctl.patch b/patch/kernel/sunxi-current/0014-media-v4l2-add-mem2mem-add-stateless_try_decoder_cmd-ioctl.patch new file mode 100644 index 000000000..9b97eb0b8 --- /dev/null +++ b/patch/kernel/sunxi-current/0014-media-v4l2-add-mem2mem-add-stateless_try_decoder_cmd-ioctl.patch @@ -0,0 +1,101 @@ + +From bef41d93aac64b54c3008ca6170bec54f85784f5 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 11 Oct 2019 06:32:43 -0300 +Subject: [PATCH] media: v4l2-mem2mem: add stateless_(try_)decoder_cmd ioctl + helpers + +These helpers are used by stateless codecs when they support multiple +slices per frame and hold capture buffer flag is set. It's expected that +all such codecs will use this code. + +Signed-off-by: Jernej Skrabec +Co-developed-by: Hans Verkuil +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/v4l2-core/v4l2-mem2mem.c | 53 ++++++++++++++++++++++++++ + include/media/v4l2-mem2mem.h | 4 ++ + 2 files changed, 57 insertions(+) + +diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c +index 79c3656f24f7..b46d2c388349 100644 +--- a/drivers/media/v4l2-core/v4l2-mem2mem.c ++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c +@@ -1218,6 +1218,59 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, + } + EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd); + ++int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh, ++ struct v4l2_decoder_cmd *dc) ++{ ++ if (dc->cmd != V4L2_DEC_CMD_FLUSH) ++ return -EINVAL; ++ ++ dc->flags = 0; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_try_decoder_cmd); ++ ++int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv, ++ struct v4l2_decoder_cmd *dc) ++{ ++ struct v4l2_fh *fh = file->private_data; ++ struct vb2_v4l2_buffer *out_vb, *cap_vb; ++ struct v4l2_m2m_dev *m2m_dev = fh->m2m_ctx->m2m_dev; ++ unsigned long flags; ++ int ret; ++ ++ ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, dc); ++ if (ret < 0) ++ return ret; ++ ++ spin_lock_irqsave(&m2m_dev->job_spinlock, flags); ++ out_vb = v4l2_m2m_last_src_buf(fh->m2m_ctx); ++ cap_vb = v4l2_m2m_last_dst_buf(fh->m2m_ctx); ++ ++ /* ++ * If there is an out buffer pending, then clear any HOLD flag. ++ * ++ * By clearing this flag we ensure that when this output ++ * buffer is processed any held capture buffer will be released. ++ */ ++ if (out_vb) { ++ out_vb->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; ++ } else if (cap_vb && cap_vb->is_held) { ++ /* ++ * If there were no output buffers, but there is a ++ * capture buffer that is held, then release that ++ * buffer. ++ */ ++ cap_vb->is_held = false; ++ v4l2_m2m_dst_buf_remove(fh->m2m_ctx); ++ v4l2_m2m_buf_done(cap_vb, VB2_BUF_STATE_DONE); ++ } ++ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_decoder_cmd); ++ + /* + * v4l2_file_operations helpers. It is assumed here same lock is used + * for the output and the capture buffer queue. +diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h +index 229d9f5d4370..3d9e48ed8817 100644 +--- a/include/media/v4l2-mem2mem.h ++++ b/include/media/v4l2-mem2mem.h +@@ -701,6 +701,10 @@ int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec); + int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc); ++int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh, ++ struct v4l2_decoder_cmd *dc); ++int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv, ++ struct v4l2_decoder_cmd *dc); + int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma); + __poll_t v4l2_m2m_fop_poll(struct file *file, poll_table *wait); + +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0015-media-v4l2-add-mem2mem-add-new_frame-detection.patch b/patch/kernel/sunxi-current/0015-media-v4l2-add-mem2mem-add-new_frame-detection.patch new file mode 100644 index 000000000..b6522f625 --- /dev/null +++ b/patch/kernel/sunxi-current/0015-media-v4l2-add-mem2mem-add-new_frame-detection.patch @@ -0,0 +1,74 @@ + +From f07602ac388723233e9e3c5a05b54baf34e0a3e9 Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Fri, 11 Oct 2019 06:32:44 -0300 +Subject: [PATCH] media: v4l2-mem2mem: add new_frame detection + +Drivers that support VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF +typically want to know if a new frame is started (i.e. the first +slice is about to be processed). Add a new_frame bool to v4l2_m2m_ctx +and set it accordingly. + +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/v4l2-core/v4l2-mem2mem.c | 11 +++++++++-- + include/media/v4l2-mem2mem.h | 7 +++++++ + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c +index b46d2c388349..db07ef3bf3d0 100644 +--- a/drivers/media/v4l2-core/v4l2-mem2mem.c ++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c +@@ -319,8 +319,10 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + goto job_unlock; + } + +- if (src && dst && +- dst->is_held && dst->vb2_buf.copied_timestamp && ++ m2m_ctx->new_frame = true; ++ ++ if (src && dst && dst->is_held && ++ dst->vb2_buf.copied_timestamp && + dst->vb2_buf.timestamp != src->vb2_buf.timestamp) { + dst->is_held = false; + v4l2_m2m_dst_buf_remove(m2m_ctx); +@@ -333,6 +335,11 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + } + } + ++ if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags & ++ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)) ++ m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp || ++ dst->vb2_buf.timestamp != src->vb2_buf.timestamp; ++ + if (m2m_dev->m2m_ops->job_ready + && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { + dprintk("Driver not ready\n"); +diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h +index 3d9e48ed8817..1d85e24791e4 100644 +--- a/include/media/v4l2-mem2mem.h ++++ b/include/media/v4l2-mem2mem.h +@@ -75,6 +75,11 @@ struct v4l2_m2m_queue_ctx { + * struct v4l2_m2m_ctx - Memory to memory context structure + * + * @q_lock: struct &mutex lock ++ * @new_frame: valid in the device_run callback: if true, then this ++ * starts a new frame; if false, then this is a new slice ++ * for an existing frame. This is always true unless ++ * V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF is set, which ++ * indicates slicing support. + * @m2m_dev: opaque pointer to the internal data to handle M2M context + * @cap_q_ctx: Capture (output to memory) queue context + * @out_q_ctx: Output (input from memory) queue context +@@ -91,6 +96,8 @@ struct v4l2_m2m_ctx { + /* optional cap/out vb2 queues lock */ + struct mutex *q_lock; + ++ bool new_frame; ++ + /* internal use only */ + struct v4l2_m2m_dev *m2m_dev; + +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0016-media-v4l2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch b/patch/kernel/sunxi-current/0016-media-v4l2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch new file mode 100644 index 000000000..b39940427 --- /dev/null +++ b/patch/kernel/sunxi-current/0016-media-v4l2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch @@ -0,0 +1,199 @@ + +From 137272cdf7cc5be835f44216e6003769d1638480 Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Fri, 11 Oct 2019 06:32:40 -0300 +Subject: [PATCH] media: vb2: add V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF + +This patch adds support for the V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF +flag. + +It also adds a new V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF +capability. + +Drivers should set vb2_queue->subsystem_flags to +VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF to indicate support +for this flag. + +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + Documentation/media/uapi/v4l/buffer.rst | 13 +++++++++++++ + Documentation/media/uapi/v4l/vidioc-reqbufs.rst | 6 ++++++ + drivers/media/common/videobuf2/videobuf2-v4l2.c | 12 ++++++++++-- + include/media/videobuf2-core.h | 3 +++ + include/media/videobuf2-v4l2.h | 5 +++++ + include/uapi/linux/videodev2.h | 13 ++++++++----- + 6 files changed, 45 insertions(+), 7 deletions(-) + +diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst +index 1cbd9cde57f3..9149b57728e5 100644 +--- a/Documentation/media/uapi/v4l/buffer.rst ++++ b/Documentation/media/uapi/v4l/buffer.rst +@@ -607,6 +607,19 @@ Buffer Flags + applications shall use this flag for output buffers if the data in + this buffer has not been created by the CPU but by some + DMA-capable unit, in which case caches have not been used. ++ * .. _`V4L2-BUF-FLAG-M2M-HOLD-CAPTURE-BUF`: ++ ++ - ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` ++ - 0x00000200 ++ - Only valid if ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF`` is ++ set. It is typically used with stateless decoders where multiple ++ output buffers each decode to a slice of the decoded frame. ++ Applications can set this flag when queueing the output buffer ++ to prevent the driver from dequeueing the capture buffer after ++ the output buffer has been decoded (i.e. the capture buffer is ++ 'held'). If the timestamp of this output buffer differs from that ++ of the previous output buffer, then that indicates the start of a ++ new frame and the previously held capture buffer is dequeued. + * .. _`V4L2-BUF-FLAG-LAST`: + + - ``V4L2_BUF_FLAG_LAST`` +diff --git a/Documentation/media/uapi/v4l/vidioc-reqbufs.rst b/Documentation/media/uapi/v4l/vidioc-reqbufs.rst +index d7faef10e39b..d0c643db477a 100644 +--- a/Documentation/media/uapi/v4l/vidioc-reqbufs.rst ++++ b/Documentation/media/uapi/v4l/vidioc-reqbufs.rst +@@ -125,6 +125,7 @@ aborting or finishing any DMA in progress, an implicit + .. _V4L2-BUF-CAP-SUPPORTS-DMABUF: + .. _V4L2-BUF-CAP-SUPPORTS-REQUESTS: + .. _V4L2-BUF-CAP-SUPPORTS-ORPHANED-BUFS: ++.. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF: + + .. cssclass:: longtable + +@@ -150,6 +151,11 @@ aborting or finishing any DMA in progress, an implicit + - The kernel allows calling :ref:`VIDIOC_REQBUFS` while buffers are still + mapped or exported via DMABUF. These orphaned buffers will be freed + when they are unmapped or when the exported DMABUF fds are closed. ++ * - ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF`` ++ - 0x00000020 ++ - Only valid for stateless decoders. If set, then userspace can set the ++ ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag to hold off on returning the ++ capture buffer until the OUTPUT timestamp changes. + + Return Value + ============ +diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c +index 5a9ba3846f0a..e652f4318284 100644 +--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c ++++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c +@@ -49,8 +49,11 @@ module_param(debug, int, 0644); + V4L2_BUF_FLAG_REQUEST_FD | \ + V4L2_BUF_FLAG_TIMESTAMP_MASK) + /* Output buffer flags that should be passed on to the driver */ +-#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \ +- V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE) ++#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | \ ++ V4L2_BUF_FLAG_BFRAME | \ ++ V4L2_BUF_FLAG_KEYFRAME | \ ++ V4L2_BUF_FLAG_TIMECODE | \ ++ V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) + + /* + * __verify_planes_array() - verify that the planes array passed in struct +@@ -194,6 +197,7 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b + } + vbuf->sequence = 0; + vbuf->request_fd = -1; ++ vbuf->is_held = false; + + if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { + switch (b->memory) { +@@ -321,6 +325,8 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b + */ + vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE; + vbuf->field = b->field; ++ if (!(q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)) ++ vbuf->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; + } else { + /* Zero any output buffer flags as this is a capture buffer */ + vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS; +@@ -654,6 +660,8 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) + *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR; + if (q->io_modes & VB2_DMABUF) + *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; ++ if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) ++ *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API + if (q->supports_requests) + *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; +diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h +index 640aabe69450..a2b2208b02da 100644 +--- a/include/media/videobuf2-core.h ++++ b/include/media/videobuf2-core.h +@@ -505,6 +505,8 @@ struct vb2_buf_ops { + * @buf_ops: callbacks to deliver buffer information. + * between user-space and kernel-space. + * @drv_priv: driver private data. ++ * @subsystem_flags: Flags specific to the subsystem (V4L2/DVB/etc.). Not used ++ * by the vb2 core. + * @buf_struct_size: size of the driver-specific buffer structure; + * "0" indicates the driver doesn't want to use a custom buffer + * structure type. for example, ``sizeof(struct vb2_v4l2_buffer)`` +@@ -571,6 +573,7 @@ struct vb2_queue { + const struct vb2_buf_ops *buf_ops; + + void *drv_priv; ++ u32 subsystem_flags; + unsigned int buf_struct_size; + u32 timestamp_flags; + gfp_t gfp_flags; +diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h +index 8a10889dc2fd..59bf33a12648 100644 +--- a/include/media/videobuf2-v4l2.h ++++ b/include/media/videobuf2-v4l2.h +@@ -33,6 +33,7 @@ + * @timecode: frame timecode. + * @sequence: sequence count of this frame. + * @request_fd: the request_fd associated with this buffer ++ * @is_held: if true, then this capture buffer was held + * @planes: plane information (userptr/fd, length, bytesused, data_offset). + * + * Should contain enough information to be able to cover all the fields +@@ -46,9 +47,13 @@ struct vb2_v4l2_buffer { + struct v4l2_timecode timecode; + __u32 sequence; + __s32 request_fd; ++ bool is_held; + struct vb2_plane planes[VB2_MAX_PLANES]; + }; + ++/* VB2 V4L2 flags as set in vb2_queue.subsystem_flags */ ++#define VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 0) ++ + /* + * to_vb2_v4l2_buffer() - cast struct vb2_buffer * to struct vb2_v4l2_buffer * + */ +diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h +index b3c0961b62a0..9f4e66affac4 100644 +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -920,11 +920,12 @@ struct v4l2_requestbuffers { + }; + + /* capabilities for struct v4l2_requestbuffers and v4l2_create_buffers */ +-#define V4L2_BUF_CAP_SUPPORTS_MMAP (1 << 0) +-#define V4L2_BUF_CAP_SUPPORTS_USERPTR (1 << 1) +-#define V4L2_BUF_CAP_SUPPORTS_DMABUF (1 << 2) +-#define V4L2_BUF_CAP_SUPPORTS_REQUESTS (1 << 3) +-#define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4) ++#define V4L2_BUF_CAP_SUPPORTS_MMAP (1 << 0) ++#define V4L2_BUF_CAP_SUPPORTS_USERPTR (1 << 1) ++#define V4L2_BUF_CAP_SUPPORTS_DMABUF (1 << 2) ++#define V4L2_BUF_CAP_SUPPORTS_REQUESTS (1 << 3) ++#define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4) ++#define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 5) + + /** + * struct v4l2_plane - plane info for multi-planar buffers +@@ -1046,6 +1047,8 @@ static inline __u64 v4l2_timeval_to_ns(const struct timeval *tv) + #define V4L2_BUF_FLAG_IN_REQUEST 0x00000080 + /* timecode field is valid */ + #define V4L2_BUF_FLAG_TIMECODE 0x00000100 ++/* Don't return the capture buffer until OUTPUT timestamp changes */ ++#define V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF 0x00000200 + /* Buffer is prepared for queuing */ + #define V4L2_BUF_FLAG_PREPARED 0x00000400 + /* Cache handling flags */ +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0017-media-v4l2-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch b/patch/kernel/sunxi-current/0017-media-v4l2-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch new file mode 100644 index 000000000..9b4607b72 --- /dev/null +++ b/patch/kernel/sunxi-current/0017-media-v4l2-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch @@ -0,0 +1,66 @@ + +From bac06ec36ea2012ff0daa9767d0f77bf9c6064ec Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Fri, 11 Oct 2019 06:32:42 -0300 +Subject: [PATCH] media: videodev2.h: add V4L2_DEC_CMD_FLUSH + +Add this new V4L2_DEC_CMD_FLUSH decoder command and document it. + +Reviewed-by: Boris Brezillon +Reviewed-by: Alexandre Courbot +Signed-off-by: Hans Verkuil +Signed-off-by: Jernej Skrabec +Signed-off-by: Mauro Carvalho Chehab +--- + Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst | 10 +++++++++- + Documentation/media/videodev2.h.rst.exceptions | 1 + + include/uapi/linux/videodev2.h | 1 + + 3 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst +index 57f0066f4cff..f1a504836f31 100644 +--- a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst ++++ b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst +@@ -208,7 +208,15 @@ introduced in Linux 3.3. They are, however, mandatory for stateful mem2mem decod + been started yet, the driver will return an ``EPERM`` error code. When + the decoder is already running, this command does nothing. No + flags are defined for this command. +- ++ * - ``V4L2_DEC_CMD_FLUSH`` ++ - 4 ++ - Flush any held capture buffers. Only valid for stateless decoders. ++ This command is typically used when the application reached the ++ end of the stream and the last output buffer had the ++ ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag set. This would prevent ++ dequeueing the capture buffer containing the last decoded frame. ++ So this command can be used to explicitly flush that final decoded ++ frame. This command does nothing if there are no held capture buffers. + + Return Value + ============ +diff --git a/Documentation/media/videodev2.h.rst.exceptions b/Documentation/media/videodev2.h.rst.exceptions +index b58e381bdf7b..c23e5ef30c78 100644 +--- a/Documentation/media/videodev2.h.rst.exceptions ++++ b/Documentation/media/videodev2.h.rst.exceptions +@@ -435,6 +435,7 @@ replace define V4L2_DEC_CMD_START decoder-cmds + replace define V4L2_DEC_CMD_STOP decoder-cmds + replace define V4L2_DEC_CMD_PAUSE decoder-cmds + replace define V4L2_DEC_CMD_RESUME decoder-cmds ++replace define V4L2_DEC_CMD_FLUSH decoder-cmds + + replace define V4L2_DEC_CMD_START_MUTE_AUDIO decoder-cmds + replace define V4L2_DEC_CMD_PAUSE_TO_BLACK decoder-cmds +diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h +index 9f4e66affac4..d969842bbfe2 100644 +--- a/include/uapi/linux/videodev2.h ++++ b/include/uapi/linux/videodev2.h +@@ -1984,6 +1984,7 @@ struct v4l2_encoder_cmd { + #define V4L2_DEC_CMD_STOP (1) + #define V4L2_DEC_CMD_PAUSE (2) + #define V4L2_DEC_CMD_RESUME (3) ++#define V4L2_DEC_CMD_FLUSH (4) + + /* Flags for V4L2_DEC_CMD_START */ + #define V4L2_DEC_CMD_START_MUTE_AUDIO (1 << 0) +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0018-media-v4l2-add-mem2mem-Fix-hold-buf-flag-checks.patch b/patch/kernel/sunxi-current/0018-media-v4l2-add-mem2mem-Fix-hold-buf-flag-checks.patch new file mode 100644 index 000000000..a6629338d --- /dev/null +++ b/patch/kernel/sunxi-current/0018-media-v4l2-add-mem2mem-Fix-hold-buf-flag-checks.patch @@ -0,0 +1,39 @@ + +From 7119ecef4e5ec51e45e6fbe1b5da4385fcae8ded Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 6 Nov 2019 08:02:53 +0100 +Subject: [PATCH] media: v4l2-mem2mem: Fix hold buf flag checks + +Hold buf flag is set on output queue, not capture. Fix that. + +Fixes: f07602ac3887 ("media: v4l2-mem2mem: add new_frame detection") +Signed-off-by: Jernej Skrabec +Signed-off-by: Hans Verkuil +--- + drivers/media/v4l2-core/v4l2-mem2mem.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c +index db07ef3bf3d0..1afd9c6ad908 100644 +--- a/drivers/media/v4l2-core/v4l2-mem2mem.c ++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c +@@ -335,7 +335,7 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + } + } + +- if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags & ++ if (src && dst && (m2m_ctx->out_q_ctx.q.subsystem_flags & + VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)) + m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp || + dst->vb2_buf.timestamp != src->vb2_buf.timestamp; +@@ -474,7 +474,7 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, + * holding capture buffers. Those should use + * v4l2_m2m_buf_done_and_job_finish() instead. + */ +- WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags & ++ WARN_ON(m2m_ctx->out_q_ctx.q.subsystem_flags & + VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF); + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx); +-- +2.24.0 diff --git a/patch/kernel/sunxi-current/0019-media-uapi-h264-Add-DPB-entry-field-reference-flags.patch b/patch/kernel/sunxi-current/0019-media-uapi-h264-Add-DPB-entry-field-reference-flags.patch new file mode 100644 index 000000000..6c1a7dc9e --- /dev/null +++ b/patch/kernel/sunxi-current/0019-media-uapi-h264-Add-DPB-entry-field-reference-flags.patch @@ -0,0 +1,47 @@ +Add DPB entry flags to help indicate when a reference frame is a field picture +and how the DPB entry is referenced, top or bottom field or full frame. + +Signed-off-by: Jonas Karlman +--- + Documentation/media/uapi/v4l/ext-ctrls-codec.rst | 12 ++++++++++++ + include/media/h264-ctrls.h | 4 ++++ + 2 files changed, 16 insertions(+) + +diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +index bc5dd8e76567..eb6c32668ad7 100644 +--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst ++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +@@ -2022,6 +2022,18 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - + * - ``V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM`` + - 0x00000004 + - The DPB entry is a long term reference frame ++ * - ``V4L2_H264_DPB_ENTRY_FLAG_FIELD_PICTURE`` ++ - 0x00000008 ++ - The DPB entry is a field picture ++ * - ``V4L2_H264_DPB_ENTRY_FLAG_REF_TOP`` ++ - 0x00000010 ++ - The DPB entry is a top field reference ++ * - ``V4L2_H264_DPB_ENTRY_FLAG_REF_BOTTOM`` ++ - 0x00000020 ++ - The DPB entry is a bottom field reference ++ * - ``V4L2_H264_DPB_ENTRY_FLAG_REF_FRAME`` ++ - 0x00000030 ++ - The DPB entry is a reference frame + + ``V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE (enum)`` + Specifies the decoding mode to use. Currently exposes slice-based and +diff --git a/include/media/h264-ctrls.h b/include/media/h264-ctrls.h +index e877bf1d537c..76020ebd1e6c 100644 +--- a/include/media/h264-ctrls.h ++++ b/include/media/h264-ctrls.h +@@ -185,6 +185,10 @@ struct v4l2_ctrl_h264_slice_params { + #define V4L2_H264_DPB_ENTRY_FLAG_VALID 0x01 + #define V4L2_H264_DPB_ENTRY_FLAG_ACTIVE 0x02 + #define V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM 0x04 ++#define V4L2_H264_DPB_ENTRY_FLAG_FIELD_PICTURE 0x08 ++#define V4L2_H264_DPB_ENTRY_FLAG_REF_TOP 0x10 ++#define V4L2_H264_DPB_ENTRY_FLAG_REF_BOTTOM 0x20 ++#define V4L2_H264_DPB_ENTRY_FLAG_REF_FRAME 0x30 + + struct v4l2_h264_dpb_entry { + __u64 reference_ts; diff --git a/patch/kernel/sunxi-current/0020-media-v4l2-mem2mem-mark-DONE-any-OUTPUT-queued-buffer-after-CMD_STOP-v2.patch b/patch/kernel/sunxi-current/0020-media-v4l2-mem2mem-mark-DONE-any-OUTPUT-queued-buffer-after-CMD_STOP-v2.patch new file mode 100644 index 000000000..f8cc4ae01 --- /dev/null +++ b/patch/kernel/sunxi-current/0020-media-v4l2-mem2mem-mark-DONE-any-OUTPUT-queued-buffer-after-CMD_STOP-v2.patch @@ -0,0 +1,145 @@ + +If a decoder needs a minimal buffer count to be queued on it's CAPTURE +queue, if a CMD_STOP is sent after a STREAMON but before all the required +buffers are queued, it should comply to the drain sequence and mark the +last queued buffer with V4L2_BUF_FLAG_LAST and mark it done to be dequeued. + +This introduces a v4l2-mem2mem ioctl decoder command to track the command +sent to the decoder, and should be called by the affected drivers. + +Suggested-by: Hans Verkuil +Suggested-by: Maxime Jourdan +Signed-off-by: Neil Armstrong +--- + drivers/media/v4l2-core/v4l2-mem2mem.c | 61 +++++++++++++++++++++++++- + include/media/v4l2-mem2mem.h | 14 ++++++ + 2 files changed, 73 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c +index 1afd9c6ad908..b09616f9f102 100644 +--- a/drivers/media/v4l2-core/v4l2-mem2mem.c ++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c +@@ -556,6 +556,28 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + } + EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); + ++static void v4l2_m2m_flag_last_buf_done(struct vb2_queue *q) ++{ ++ struct vb2_buffer *vb; ++ struct vb2_v4l2_buffer *vbuf; ++ unsigned int i; ++ ++ if (WARN_ON(q->is_output)) ++ return; ++ if (list_empty(&q->queued_list)) ++ return; ++ ++ vb = list_first_entry(&q->queued_list, struct vb2_buffer, queued_entry); ++ for (i = 0; i < vb->num_planes; i++) ++ vb2_set_plane_payload(vb, i, 0); ++ ++ vb->state = VB2_BUF_STATE_ACTIVE; ++ atomic_inc(&q->owned_by_drv_count); ++ vbuf = to_vb2_v4l2_buffer(vb); ++ vbuf->flags |= V4L2_BUF_FLAG_LAST; ++ vb2_buffer_done(vb, VB2_BUF_STATE_DONE); ++} ++ + int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_buffer *buf) + { +@@ -570,11 +592,22 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + __func__); + return -EPERM; + } ++ + ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf); +- if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST)) ++ if (ret) ++ return ret; ++ ++ /* ++ * If the capture queue isn't streaming and we were asked to ++ * stop, DONE the buffer instantly and flag as LAST ++ */ ++ if (!V4L2_TYPE_IS_OUTPUT(vq->type) && m2m_ctx->stopped && ++ vb2_is_streaming(vq) && !vb2_start_streaming_called(vq)) ++ v4l2_m2m_flag_last_buf_done(vq); ++ else if ((buf->flags & V4L2_BUF_FLAG_IN_REQUEST)) + v4l2_m2m_try_schedule(m2m_ctx); + +- return ret; ++ return 0; + } + EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); + +@@ -1225,6 +1258,30 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, + } + EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd); + ++int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, ++ struct v4l2_decoder_cmd *dc) ++{ ++ if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START) ++ return -EINVAL; ++ ++ if (dc->cmd == V4L2_DEC_CMD_STOP) ++ m2m_ctx->stopped = true; ++ else ++ m2m_ctx->stopped = false; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(v4l2_m2m_decoder_cmd); ++ ++int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *priv, ++ struct v4l2_decoder_cmd *dc) ++{ ++ struct v4l2_fh *fh = file->private_data; ++ ++ return v4l2_m2m_decoder_cmd(file, fh->m2m_ctx, dc); ++} ++EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_decoder_cmd); ++ + /* + * v4l2_file_operations helpers. It is assumed here same lock is used + * for the output and the capture buffer queue. +diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h +index 1d85e24791e4..4c083cffdd86 100644 +--- a/include/media/v4l2-mem2mem.h ++++ b/include/media/v4l2-mem2mem.h +@@ -98,6 +98,8 @@ struct v4l2_m2m_ctx { + + bool new_frame; + ++ bool stopped; ++ + /* internal use only */ + struct v4l2_m2m_dev *m2m_dev; + +@@ -312,6 +314,16 @@ int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + enum v4l2_buf_type type); + ++/** ++ * v4l2_m2m_decoder_cmd() - execute a decoder command ++ * ++ * @file: pointer to struct &file ++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx ++ * @dc: pointer to the decoder command ++ */ ++int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, ++ struct v4l2_decoder_cmd *dc); ++ + /** + * v4l2_m2m_poll() - poll replacement, for destination buffers only + * +@@ -704,6 +716,8 @@ int v4l2_m2m_ioctl_streamon(struct file *file, void *fh, + enum v4l2_buf_type type); + int v4l2_m2m_ioctl_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type); ++int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *fh, ++ struct v4l2_decoder_cmd *dc); + int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec); + int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, diff --git a/patch/kernel/sunxi-current/0526-pwm-sun4i-Add-support-for-Allwinner-H6.patch b/patch/kernel/sunxi-current/0526-pwm-sun4i-Add-support-for-Allwinner-H6.patch new file mode 100644 index 000000000..1c4598ff3 --- /dev/null +++ b/patch/kernel/sunxi-current/0526-pwm-sun4i-Add-support-for-Allwinner-H6.patch @@ -0,0 +1,366 @@ +H6 PWM block is basically the same as A20 PWM, except that it also has +bus clock and reset line which needs to be handled accordingly. + +Expand Allwinner PWM binding with H6 PWM specifics. + +Signed-off-by: Jernej Skrabec +--- + .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 36 ++++++++++++++++++- + 1 file changed, 35 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +index 0ac52f83a58c..deca5d81802f 100644 +--- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml ++++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +@@ -30,13 +30,47 @@ properties: + - items: + - const: allwinner,sun50i-h5-pwm + - const: allwinner,sun5i-a13-pwm ++ - const: allwinner,sun50i-h6-pwm + + reg: + maxItems: 1 + +- clocks: ++ # Even though it only applies to subschemas under the conditionals, ++ # not listing them here will trigger a warning because of the ++ # additionalsProperties set to false. ++ clocks: true ++ clock-names: true ++ resets: + maxItems: 1 + ++allOf: ++ - if: ++ properties: ++ compatible: ++ contains: ++ const: allwinner,sun50i-h6-pwm ++ ++ then: ++ properties: ++ clocks: ++ items: ++ - description: Module Clock ++ - description: Bus Clock ++ ++ clock-names: ++ items: ++ - const: pwm ++ - const: bus ++ ++ required: ++ - clock-names ++ - resets ++ ++ else: ++ properties: ++ clocks: ++ maxItems: 1 ++ + required: + - "#pwm-cells" + - compatible + +H6 PWM core needs deasserted reset line in order to work. + +Add a quirk for it. + +Signed-off-by: Jernej Skrabec +Acked-by: Maxime Ripard +--- + drivers/pwm/pwm-sun4i.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index de78c824bbfd..1b7be8fbde86 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -72,12 +73,14 @@ static const u32 prescaler_table[] = { + + struct sun4i_pwm_data { + bool has_prescaler_bypass; ++ bool has_reset; + unsigned int npwm; + }; + + struct sun4i_pwm_chip { + struct pwm_chip chip; + struct clk *clk; ++ struct reset_control *rst; + void __iomem *base; + spinlock_t ctrl_lock; + const struct sun4i_pwm_data *data; +@@ -371,6 +374,14 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + if (IS_ERR(pwm->clk)) + return PTR_ERR(pwm->clk); + ++ if (pwm->data->has_reset) { ++ pwm->rst = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(pwm->rst)) ++ return PTR_ERR(pwm->rst); ++ ++ reset_control_deassert(pwm->rst); ++ } ++ + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &sun4i_pwm_ops; + pwm->chip.base = -1; +@@ -383,19 +394,31 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); +- return ret; ++ goto err_pwm_add; + } + + platform_set_drvdata(pdev, pwm); + + return 0; ++ ++err_pwm_add: ++ reset_control_assert(pwm->rst); ++ ++ return ret; + } + + static int sun4i_pwm_remove(struct platform_device *pdev) + { + struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = pwmchip_remove(&pwm->chip); ++ if (ret) ++ return ret; + +- return pwmchip_remove(&pwm->chip); ++ reset_control_assert(pwm->rst); ++ ++ return 0; + } + + static struct platform_driver sun4i_pwm_driver = { + +H6 PWM core needs bus clock to be enabled in order to work. + +Add a quirk for it. + +Signed-off-by: Jernej Skrabec +--- + drivers/pwm/pwm-sun4i.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index 1b7be8fbde86..7d3ac3f2dc3f 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -72,6 +72,7 @@ static const u32 prescaler_table[] = { + }; + + struct sun4i_pwm_data { ++ bool has_bus_clock; + bool has_prescaler_bypass; + bool has_reset; + unsigned int npwm; +@@ -79,6 +80,7 @@ struct sun4i_pwm_data { + + struct sun4i_pwm_chip { + struct pwm_chip chip; ++ struct clk *bus_clk; + struct clk *clk; + struct reset_control *rst; + void __iomem *base; +@@ -382,6 +384,16 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + reset_control_deassert(pwm->rst); + } + ++ if (pwm->data->has_bus_clock) { ++ pwm->bus_clk = devm_clk_get(&pdev->dev, "bus"); ++ if (IS_ERR(pwm->bus_clk)) { ++ ret = PTR_ERR(pwm->bus_clk); ++ goto err_bus; ++ } ++ ++ clk_prepare_enable(pwm->bus_clk); ++ } ++ + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &sun4i_pwm_ops; + pwm->chip.base = -1; +@@ -402,6 +414,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + return 0; + + err_pwm_add: ++ clk_disable_unprepare(pwm->bus_clk); ++err_bus: + reset_control_assert(pwm->rst); + + return ret; +@@ -416,6 +430,7 @@ static int sun4i_pwm_remove(struct platform_device *pdev) + if (ret) + return ret; + ++ clk_disable_unprepare(pwm->bus_clk); + reset_control_assert(pwm->rst); + + return 0; + +Note that while H6 PWM has two channels, only first one is wired to +output pin. Second channel is used as a clock source to companion AC200 +chip which is bundled into same package. + +Signed-off-by: Jernej Skrabec +--- + drivers/pwm/pwm-sun4i.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index 7d3ac3f2dc3f..9e0eca79ff88 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -331,6 +331,13 @@ static const struct sun4i_pwm_data sun4i_pwm_single_bypass = { + .npwm = 1, + }; + ++static const struct sun4i_pwm_data sun50i_pwm_dual_bypass_clk_rst = { ++ .has_bus_clock = true, ++ .has_prescaler_bypass = true, ++ .has_reset = true, ++ .npwm = 2, ++}; ++ + static const struct of_device_id sun4i_pwm_dt_ids[] = { + { + .compatible = "allwinner,sun4i-a10-pwm", +@@ -347,6 +354,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = { + }, { + .compatible = "allwinner,sun8i-h3-pwm", + .data = &sun4i_pwm_single_bypass, ++ }, { ++ .compatible = "allwinner,sun50i-h6-pwm", ++ .data = &sun50i_pwm_dual_bypass_clk_rst, + }, { + /* sentinel */ + }, + +PWM core has an option to bypass whole logic and output unchanged source +clock as PWM output. This is achieved by enabling bypass bit. + +Note that when bypass is enabled, no other setting has any meaning, not +even enable bit. + +This mode of operation is needed to achieve high enough frequency to +serve as clock source for AC200 chip, which is integrated into same +package as H6 SoC. + +Signed-off-by: Jernej Skrabec +--- + drivers/pwm/pwm-sun4i.c | 31 ++++++++++++++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index 9e0eca79ff88..848cff26f385 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -120,6 +120,19 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, + + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + ++ /* ++ * PWM chapter in H6 manual has a diagram which explains that if bypass ++ * bit is set, no other setting has any meaning. Even more, experiment ++ * proved that also enable bit is ignored in this case. ++ */ ++ if (val & BIT_CH(PWM_BYPASS, pwm->hwpwm)) { ++ state->period = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, clk_rate); ++ state->duty_cycle = state->period / 2; ++ state->polarity = PWM_POLARITY_NORMAL; ++ state->enabled = true; ++ return; ++ } ++ + if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) && + sun4i_pwm->data->has_prescaler_bypass) + prescaler = 1; +@@ -211,7 +224,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + { + struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + struct pwm_state cstate; +- u32 ctrl; ++ u32 ctrl, clk_rate; ++ bool bypass; + int ret; + unsigned int delay_us; + unsigned long now; +@@ -226,6 +240,16 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + } + } + ++ /* ++ * Although it would make much more sense to check for bypass in ++ * sun4i_pwm_calculate(), value of bypass bit also depends on "enabled". ++ * Period is allowed to be rounded up or down. ++ */ ++ clk_rate = clk_get_rate(sun4i_pwm->clk); ++ bypass = (state->period == NSEC_PER_SEC / clk_rate || ++ state->period == DIV_ROUND_UP(NSEC_PER_SEC, clk_rate)) && ++ state->enabled; ++ + spin_lock(&sun4i_pwm->ctrl_lock); + ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + +@@ -273,6 +297,11 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + } + ++ if (bypass) ++ ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm); ++ else ++ ctrl &= ~BIT_CH(PWM_BYPASS, pwm->hwpwm); ++ + sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); + + spin_unlock(&sun4i_pwm->ctrl_lock); + +Allwinner H6 PWM is similar to that in A20 except that it has additional +bus clock and reset line. + +Note that first PWM channel is connected to output pin and second +channel is used internally, as a clock source to AC200 co-packaged chip. +This means that any combination of these two channels can be used and +thus it doesn't make sense to add pinctrl nodes at this point. + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index e8bed58e7246..c1abd805cfdc 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -229,6 +229,16 @@ + status = "disabled"; + }; + ++ pwm: pwm@300a000 { ++ compatible = "allwinner,sun50i-h6-pwm"; ++ reg = <0x0300a000 0x400>; ++ clocks = <&osc24M>, <&ccu CLK_BUS_PWM>; ++ clock-names = "pwm", "bus"; ++ resets = <&ccu RST_BUS_PWM>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + pio: pinctrl@300b000 { + compatible = "allwinner,sun50i-h6-pinctrl"; + reg = <0x0300b000 0x400>; diff --git a/patch/kernel/sunxi-current/0533-allwinner-h6-add-AC200-EPHY-support.patch b/patch/kernel/sunxi-current/0533-allwinner-h6-add-AC200-EPHY-support.patch new file mode 100644 index 000000000..a916a1b3d --- /dev/null +++ b/patch/kernel/sunxi-current/0533-allwinner-h6-add-AC200-EPHY-support.patch @@ -0,0 +1,569 @@ +diff -Naur linux-5.3-rc2-old/drivers/mfd/ac200.c linux-5.3-rc2-old-new/drivers/mfd/ac200.c +--- linux-5.3-rc2-old/drivers/mfd/ac200.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-5.3-rc2-old-new/drivers/mfd/ac200.c 2019-08-03 15:24:25.929999997 +0200 +@@ -0,0 +1,223 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * MFD core driver for X-Powers' AC200 IC ++ * ++ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and ++ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse ++ * and RTC. ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ * ++ * Based on AC100 driver with following copyrights: ++ * Copyright (2016) Chen-Yu Tsai ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct ac200_dev { ++ struct clk *clk; ++ /* ++ * Lock is needed for serializing concurrent access to ++ * AC200 registers in order not to mess with register page. ++ */ ++ struct mutex lock; ++ struct regmap *regmap; ++}; ++ ++/* ++ * Register values can't be cached because registers are divided ++ * into multiple pages. ++ */ ++static const struct regmap_config ac200_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 16, ++}; ++ ++int ac200_reg_read(struct ac200_dev *ac200, u16 reg, u16 *value) ++{ ++ unsigned int val; ++ int ret; ++ ++ mutex_lock(&ac200->lock); ++ ret = regmap_write(ac200->regmap, AC200_TWI_REG_ADDR_H, reg >> 8); ++ if (ret) ++ goto read_reg_out; ++ ++ ret = regmap_read(ac200->regmap, reg & 0xff, &val); ++ *value = val; ++ ++read_reg_out: ++ mutex_unlock(&ac200->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ac200_reg_read); ++ ++int ac200_reg_write(struct ac200_dev *ac200, u16 reg, u16 value) ++{ ++ int ret; ++ ++ mutex_lock(&ac200->lock); ++ ret = regmap_write(ac200->regmap, AC200_TWI_REG_ADDR_H, reg >> 8); ++ if (ret) ++ goto write_reg_out; ++ ++ ret = regmap_write(ac200->regmap, reg & 0xff, value); ++ ++write_reg_out: ++ mutex_unlock(&ac200->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ac200_reg_write); ++ ++int ac200_reg_mod(struct ac200_dev *ac200, u16 reg, u16 mask, u16 value) ++{ ++ unsigned int val; ++ int ret; ++ ++ mutex_lock(&ac200->lock); ++ ret = regmap_write(ac200->regmap, AC200_TWI_REG_ADDR_H, reg >> 8); ++ if (ret) ++ goto mod_reg_out; ++ ++ ret = regmap_read(ac200->regmap, reg & 0xff, &val); ++ if (ret) ++ goto mod_reg_out; ++ ++ val &= ~mask; ++ val |= value; ++ ++ ret = regmap_write(ac200->regmap, reg & 0xff, val); ++ ++mod_reg_out: ++ mutex_unlock(&ac200->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ac200_reg_mod); ++ ++static struct mfd_cell ac200_cells[] = { ++ { ++ .name = "ac200-codec", ++ .of_compatible = "x-powers,ac200-codec", ++ }, { ++ .name = "ac200-efuse", ++ .of_compatible = "x-powers,ac200-efuse", ++ }, { ++ .name = "ac200-ephy", ++ .of_compatible = "x-powers,ac200-ephy", ++ }, { ++ .name = "ac200-rtc", ++ .of_compatible = "x-powers,ac200-rtc", ++ }, { ++ .name = "ac200-tve", ++ .of_compatible = "x-powers,ac200-tve", ++ }, ++}; ++ ++static int ac200_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &i2c->dev; ++ struct ac200_dev *ac200; ++ int ret; ++ ++ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL); ++ if (!ac200) ++ return -ENOMEM; ++ ++ mutex_init(&ac200->lock); ++ i2c_set_clientdata(i2c, ac200); ++ ++ ac200->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(ac200->clk)) { ++ ret = PTR_ERR(ac200->clk); ++ dev_err(dev, "Can't obtain the clock: %d\n", ret); ++ return ret; ++ } ++ ++ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config); ++ if (IS_ERR(ac200->regmap)) { ++ ret = PTR_ERR(ac200->regmap); ++ dev_err(dev, "Regmap init failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(ac200->clk); ++ if (ret) { ++ dev_err(dev, "Can't enable the clock: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_CONTROL, 0); ++ if (ret) { ++ dev_err(dev, "Can't put AC200 in reset: %d\n", ret); ++ goto err; ++ } ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_CONTROL, 1); ++ if (ret) { ++ dev_err(dev, "Can't put AC200 out of reset: %d\n", ret); ++ goto err; ++ } ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells, ++ ARRAY_SIZE(ac200_cells), NULL, 0, NULL); ++ if (ret) { ++ dev_err(dev, "Failed to add MFD devices: %d\n", ret); ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(ac200->clk); ++ return ret; ++} ++ ++static int ac200_i2c_remove(struct i2c_client *i2c) ++{ ++ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); ++ ++ ac200_reg_write(ac200, AC200_SYS_CONTROL, 0); ++ ++ clk_disable_unprepare(ac200->clk); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id ac200_ids[] = { ++ { "ac200", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(i2c, ac200_ids); ++ ++static const struct of_device_id ac200_of_match[] = { ++ { .compatible = "x-powers,ac200" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ac200_of_match); ++ ++static struct i2c_driver ac200_i2c_driver = { ++ .driver = { ++ .name = "ac200", ++ .of_match_table = of_match_ptr(ac200_of_match), ++ }, ++ .probe = ac200_i2c_probe, ++ .remove = ac200_i2c_remove, ++ .id_table = ac200_ids, ++}; ++module_i2c_driver(ac200_i2c_driver); ++ ++MODULE_DESCRIPTION("MFD core driver for AC200"); ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_LICENSE("GPL v2"); +diff -Naur linux-5.3-rc2-old/drivers/mfd/Kconfig linux-5.3-rc2-old-new/drivers/mfd/Kconfig +--- linux-5.3-rc2-old/drivers/mfd/Kconfig 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/mfd/Kconfig 2019-08-03 15:24:25.929999997 +0200 +@@ -178,6 +178,15 @@ + This driver include only the core APIs. You have to select individual + components like codecs or RTC under the corresponding menus. + ++config MFD_AC200 ++ tristate "X-Powers AC200" ++ select MFD_CORE ++ depends on I2C ++ help ++ If you say Y here you get support for the X-Powers AC200 IC. ++ This driver include only the core APIs. You have to select individual ++ components like Ethernet PHY or RTC under the corresponding menus. ++ + config MFD_AXP20X + tristate + select MFD_CORE +diff -Naur linux-5.3-rc2-old/drivers/mfd/Makefile linux-5.3-rc2-old-new/drivers/mfd/Makefile +--- linux-5.3-rc2-old/drivers/mfd/Makefile 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/mfd/Makefile 2019-08-03 15:24:25.929999997 +0200 +@@ -143,6 +143,7 @@ + obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o + + obj-$(CONFIG_MFD_AC100) += ac100.o ++obj-$(CONFIG_MFD_AC200) += ac200.o + obj-$(CONFIG_MFD_AXP20X) += axp20x.o + obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o + obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o +diff -Naur linux-5.3-rc2-old/drivers/net/phy/ac200.c linux-5.3-rc2-old-new/drivers/net/phy/ac200.c +--- linux-5.3-rc2-old/drivers/net/phy/ac200.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-5.3-rc2-old-new/drivers/net/phy/ac200.c 2019-08-03 15:24:25.929999997 +0200 +@@ -0,0 +1,231 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/** ++ * Driver for AC200 Ethernet PHY ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AC200_EPHY_ID 0x00441400 ++#define AC200_EPHY_ID_MASK 0x0ffffff0 ++ ++/* macros for system ephy control 0 register */ ++#define AC200_EPHY_RESET_INVALID BIT(0) ++#define AC200_EPHY_SYSCLK_GATING BIT(1) ++ ++/* macros for system ephy control 1 register */ ++#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0) ++#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1) ++#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2) ++#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3) ++ ++/* macros for ephy control register */ ++#define AC200_EPHY_SHUTDOWN BIT(0) ++#define AC200_EPHY_LED_POL BIT(1) ++#define AC200_EPHY_CLK_SEL BIT(2) ++#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4) ++#define AC200_EPHY_XMII_SEL BIT(11) ++#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12) ++ ++struct ac200_ephy_dev { ++ struct phy_driver *ephy; ++ struct ac200_dev *ac200; ++}; ++ ++static char *ac200_phy_name = "AC200 EPHY"; ++ ++static void disable_intelligent_ieee(struct phy_device *phydev) ++{ ++ unsigned int value; ++ ++ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ ++ value = phy_read(phydev, 0x17); ++ value &= ~BIT(3); /* disable IEEE */ ++ phy_write(phydev, 0x17, value); ++ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ ++} ++ ++static void disable_802_3az_ieee(struct phy_device *phydev) ++{ ++ unsigned int value; ++ ++ phy_write(phydev, 0xd, 0x7); ++ phy_write(phydev, 0xe, 0x3c); ++ phy_write(phydev, 0xd, BIT(14) | 0x7); ++ value = phy_read(phydev, 0xe); ++ value &= ~BIT(1); ++ phy_write(phydev, 0xd, 0x7); ++ phy_write(phydev, 0xe, 0x3c); ++ phy_write(phydev, 0xd, BIT(14) | 0x7); ++ phy_write(phydev, 0xe, value); ++ ++ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ ++ phy_write(phydev, 0x18, 0x0000); ++} ++ ++static int ac200_ephy_config_init(struct phy_device *phydev) ++{ ++ const struct ac200_ephy_dev *priv = phydev->drv->driver_data; ++ u16 value; ++ ++ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ ++ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ ++ ++ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ ++ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ ++ ++ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ ++ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */ ++ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ ++ phy_write(phydev, 0x15, 0x1530); ++ ++ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 6 */ ++ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ ++ ++ disable_intelligent_ieee(phydev); /* Disable Intelligent IEEE */ ++ disable_802_3az_ieee(phydev); /* Disable 802.3az IEEE */ ++ phy_write(phydev, 0x1f, 0x0000); /* Switch to Page 0 */ ++ ++ value = (phydev->interface == PHY_INTERFACE_MODE_RMII) ? ++ AC200_EPHY_XMII_SEL : 0; ++ ac200_reg_mod(priv->ac200, AC200_EPHY_CTL, AC200_EPHY_XMII_SEL, value); ++ ++ /* FIXME: This is probably H6 specific */ ++ value = phy_read(phydev, 0x13); ++ value |= BIT(12); ++ phy_write(phydev, 0x13, value); ++ ++ return 0; ++ ++} ++ ++static const struct mdio_device_id __maybe_unused ac200_ephy_phy_tbl[] = { ++ { AC200_EPHY_ID, AC200_EPHY_ID_MASK }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(mdio, ac200_ephy_phy_tbl); ++ ++static int ac200_ephy_probe(struct platform_device *pdev) ++{ ++ struct ac200_dev *ac200 = dev_get_drvdata(pdev->dev.parent); ++ struct device *dev = &pdev->dev; ++ struct ac200_ephy_dev *priv; ++ struct nvmem_cell *calcell; ++ struct phy_driver *ephy; ++ u16 *caldata, calib; ++ size_t callen; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ ephy = devm_kzalloc(dev, sizeof(*ephy), GFP_KERNEL); ++ if (!ephy) ++ return -ENOMEM; ++ ++ calcell = devm_nvmem_cell_get(dev, "ephy_calib"); ++ if (IS_ERR(calcell)) { ++ dev_err(dev, "Unable to find calibration data!\n"); ++ return PTR_ERR(calcell); ++ } ++ ++ caldata = nvmem_cell_read(calcell, &callen); ++ if (IS_ERR(caldata)) { ++ dev_err(dev, "Unable to read calibration data!\n"); ++ return PTR_ERR(caldata); ++ } ++ ++ if (callen != 2) { ++ dev_err(dev, "Calibration data has wrong length: 2 != %lu\n", ++ callen); ++ return -EINVAL; ++ } ++ ++ calib = *caldata + 3; ++ kfree(caldata); ++ ++ ephy->phy_id = AC200_EPHY_ID; ++ ephy->phy_id_mask = AC200_EPHY_ID_MASK; ++ ephy->name = ac200_phy_name; ++ ephy->driver_data = priv; ++ ephy->soft_reset = genphy_soft_reset; ++ ephy->config_init = ac200_ephy_config_init; ++ ephy->suspend = genphy_suspend; ++ ephy->resume = genphy_resume; ++ ++ priv->ac200 = ac200; ++ priv->ephy = ephy; ++ platform_set_drvdata(pdev, priv); ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID | ++ AC200_EPHY_SYSCLK_GATING); ++ if (ret) ++ return ret; ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_EPHY_CTL1, ++ AC200_EPHY_E_EPHY_MII_IO_EN | ++ AC200_EPHY_E_LNK_LED_IO_EN | ++ AC200_EPHY_E_SPD_LED_IO_EN | ++ AC200_EPHY_E_DPX_LED_IO_EN); ++ if (ret) ++ return ret; ++ ++ ret = ac200_reg_write(ac200, AC200_EPHY_CTL, ++ AC200_EPHY_LED_POL | ++ AC200_EPHY_CLK_SEL | ++ AC200_EPHY_ADDR(1) | ++ AC200_EPHY_CALIB(calib)); ++ if (ret) ++ return ret; ++ ++ ret = phy_driver_register(priv->ephy, THIS_MODULE); ++ if (ret) { ++ dev_err(dev, "Unable to register phy\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ac200_ephy_remove(struct platform_device *pdev) ++{ ++ struct ac200_ephy_dev *priv = platform_get_drvdata(pdev); ++ ++ phy_driver_unregister(priv->ephy); ++ ++ ac200_reg_write(priv->ac200, AC200_EPHY_CTL, ++ AC200_EPHY_SHUTDOWN); ++ ac200_reg_write(priv->ac200, AC200_SYS_EPHY_CTL1, 0); ++ ac200_reg_write(priv->ac200, AC200_SYS_EPHY_CTL0, 0); ++ ++ return 0; ++} ++ ++static const struct of_device_id ac200_ephy_match[] = { ++ { .compatible = "x-powers,ac200-ephy" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ac200_ephy_match); ++ ++static struct platform_driver ac200_ephy_driver = { ++ .probe = ac200_ephy_probe, ++ .remove = ac200_ephy_remove, ++ .driver = { ++ .name = "ac200-ephy", ++ .of_match_table = ac200_ephy_match, ++ }, ++}; ++module_platform_driver(ac200_ephy_driver); ++ ++MODULE_AUTHOR("Jernej Skrabec >"); ++MODULE_DESCRIPTION("AC200 Ethernet PHY driver"); ++MODULE_LICENSE("GPL"); +diff -Naur linux-5.3-rc2-old/drivers/net/phy/Kconfig linux-5.3-rc2-old-new/drivers/net/phy/Kconfig +--- linux-5.3-rc2-old/drivers/net/phy/Kconfig 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/net/phy/Kconfig 2019-08-03 15:24:25.929999997 +0200 +@@ -244,6 +244,13 @@ + depends on HWMON || HWMON=n + select MDIO_I2C + ++config AC200_PHY ++ tristate "AC200 EPHY" ++ depends on NVMEM ++ depends on OF ++ help ++ Fast ethernet PHY as found in X-Powers AC200 multi-function device. ++ + config AMD_PHY + tristate "AMD PHYs" + ---help--- +diff -Naur linux-5.3-rc2-old/drivers/net/phy/Makefile linux-5.3-rc2-old-new/drivers/net/phy/Makefile +--- linux-5.3-rc2-old/drivers/net/phy/Makefile 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/net/phy/Makefile 2019-08-03 15:24:25.929999997 +0200 +@@ -46,6 +46,7 @@ + sfp-obj-$(CONFIG_SFP) += sfp-bus.o + obj-y += $(sfp-obj-y) $(sfp-obj-m) + ++obj-$(CONFIG_AC200_PHY) += ac200.o + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_AMD_PHY) += amd.o + aquantia-objs += aquantia_main.o + +diff -Naur linux-5.3-rc2-old/include/linux/mfd/ac200.h linux-5.3-rc2-old-new/include/linux/mfd/ac200.h +--- linux-5.3-rc2-old/include/linux/mfd/ac200.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-5.3-rc2-old-new/include/linux/mfd/ac200.h 2019-08-03 15:24:25.929999997 +0200 +@@ -0,0 +1,44 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Functions and registers to access AC200 IC. ++ * ++ * Copyright (C) 2019 Jernej Skrabec ++ */ ++ ++#ifndef __LINUX_MFD_AC200_H ++#define __LINUX_MFD_AC200_H ++ ++#include ++ ++struct ac200_dev; ++ ++/* interface registers (can be accessed from any page) */ ++#define AC200_TWI_CHANGE_TO_RSB 0x3E ++#define AC200_TWI_PAD_DELAY 0xC4 ++#define AC200_TWI_REG_ADDR_H 0xFE ++ ++/* General registers */ ++#define AC200_SYS_VERSION 0x0000 ++#define AC200_SYS_CONTROL 0x0002 ++#define AC200_SYS_IRQ_ENABLE 0x0004 ++#define AC200_SYS_IRQ_STATUS 0x0006 ++#define AC200_SYS_CLK_CTL 0x0008 ++#define AC200_SYS_DLDO_OSC_CTL 0x000A ++#define AC200_SYS_PLL_CTL0 0x000C ++#define AC200_SYS_PLL_CTL1 0x000E ++#define AC200_SYS_AUDIO_CTL0 0x0010 ++#define AC200_SYS_AUDIO_CTL1 0x0012 ++#define AC200_SYS_EPHY_CTL0 0x0014 ++#define AC200_SYS_EPHY_CTL1 0x0016 ++#define AC200_SYS_TVE_CTL0 0x0018 ++#define AC200_SYS_TVE_CTL1 0x001A ++ ++/* EPHY registers */ ++#define AC200_EPHY_CTL 0x6000 ++#define AC200_EPHY_BIST 0x6002 ++ ++int ac200_reg_read(struct ac200_dev *ac200, u16 reg, u16 *value); ++int ac200_reg_write(struct ac200_dev *ac200, u16 reg, u16 value); ++int ac200_reg_mod(struct ac200_dev *ac200, u16 reg, u16 mask, u16 value); ++ ++#endif /* __LINUX_MFD_AC200_H */ diff --git a/patch/kernel/sunxi-current/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch b/patch/kernel/sunxi-current/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch new file mode 100644 index 000000000..17a7edb2e --- /dev/null +++ b/patch/kernel/sunxi-current/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch @@ -0,0 +1,79 @@ +diff -Naur linux-5.3-rc6-old/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi linux-5.3-rc6-new/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +--- linux-5.3-rc6-old/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 2019-09-02 12:17:46.116666674 +0200 ++++ linux-5.3-rc6-new/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 2019-09-02 12:31:05.253333344 +0200 +@@ -17,6 +17,16 @@ + #address-cells = <1>; + #size-cells = <1>; + ++ ac200_pwm_clk: ac200_clk { ++ compatible = "pwm-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm1_pin>; ++ pwms = <&pwm 1 42 0>; ++ status = "disabled"; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +@@ -241,7 +250,10 @@ + reg = <0x03006000 0x400>; + #address-cells = <1>; + #size-cells = <1>; + ++ ephy_calib: ephy_calib@2c { ++ reg = <0x2c 0x2>; ++ }; + speedbin_efuse: speed@1c { + reg = <0x1c 0x4>; + }; +@@ -326,6 +341,16 @@ + pins = "PH0", "PH1"; + function = "uart0"; + }; ++ ++ i2c3_pins: i2c3-pins { ++ pins = "PB17", "PB18"; ++ function = "i2c3"; ++ }; ++ ++ pwm1_pin: pwm1-pin { ++ pins = "PB19"; ++ function = "pwm1"; ++ }; + }; + + gic: interrupt-controller@3021000 { +@@ -431,6 +456,30 @@ + status = "disabled"; + }; + ++ i2c3: i2c@5002c00 { ++ compatible = "allwinner,sun6i-a31-i2c"; ++ reg = <0x05002c00 0x400>; ++ interrupts = ; ++ clocks = <&ccu CLK_BUS_I2C3>; ++ resets = <&ccu RST_BUS_I2C3>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c3_pins>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ac200: mfd@10 { ++ compatible = "x-powers,ac200"; ++ reg = <0x10>; ++ clocks = <&ac200_pwm_clk>; ++ ++ ac200_ephy: phy { ++ compatible = "x-powers,ac200-ephy"; ++ nvmem-cells = <&ephy_calib>; ++ nvmem-cell-names = "ephy_calib"; ++ }; ++ }; ++ }; ++ + emac: ethernet@5020000 { + compatible = "allwinner,sun50i-h6-emac", + "allwinner,sun50i-a64-emac"; diff --git a/patch/kernel/sunxi-current/0607-media-cedrus-choose-default-pixelformat-in-try_fmt.patch b/patch/kernel/sunxi-current/0607-media-cedrus-choose-default-pixelformat-in-try_fmt.patch new file mode 100644 index 000000000..7a2202402 --- /dev/null +++ b/patch/kernel/sunxi-current/0607-media-cedrus-choose-default-pixelformat-in-try_fmt.patch @@ -0,0 +1,127 @@ + +From dec555256f2cb61ee94975727ec2d4a8d592ac92 Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Fri, 30 Aug 2019 06:26:23 -0300 +Subject: [PATCH] media: cedrus: choose default pixelformat in try_fmt + +If an unsupported pixelformat is passed to try_fmt, then pick +the first valid pixelformat instead. This is more standard V4L2 +behavior. + +Signed-off-by: Hans Verkuil +Reviewed-by: Jernej Skrabec +Signed-off-by: Mauro Carvalho Chehab +--- + .../staging/media/sunxi/cedrus/cedrus_video.c | 46 ++++++++----------- + 1 file changed, 20 insertions(+), 26 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index eeee3efd247b..d69c9bcdb8e2 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -62,33 +62,30 @@ static inline struct cedrus_ctx *cedrus_file2ctx(struct file *file) + static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions, + unsigned int capabilities) + { ++ struct cedrus_format *first_valid_fmt = NULL; + struct cedrus_format *fmt; + unsigned int i; + + for (i = 0; i < CEDRUS_FORMATS_COUNT; i++) { + fmt = &cedrus_formats[i]; + +- if (fmt->capabilities && (fmt->capabilities & capabilities) != +- fmt->capabilities) ++ if ((fmt->capabilities & capabilities) != fmt->capabilities || ++ !(fmt->directions & directions)) + continue; + +- if (fmt->pixelformat == pixelformat && +- (fmt->directions & directions) != 0) ++ if (fmt->pixelformat == pixelformat) + break; ++ ++ if (!first_valid_fmt) ++ first_valid_fmt = fmt; + } + + if (i == CEDRUS_FORMATS_COUNT) +- return NULL; ++ return first_valid_fmt; + + return &cedrus_formats[i]; + } + +-static bool cedrus_check_format(u32 pixelformat, u32 directions, +- unsigned int capabilities) +-{ +- return cedrus_find_format(pixelformat, directions, capabilities); +-} +- + static void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) + { + unsigned int width = pix_fmt->width; +@@ -252,11 +249,14 @@ static int cedrus_try_fmt_vid_cap(struct file *file, void *priv, + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct cedrus_dev *dev = ctx->dev; + struct v4l2_pix_format *pix_fmt = &f->fmt.pix; ++ struct cedrus_format *fmt = ++ cedrus_find_format(pix_fmt->pixelformat, CEDRUS_DECODE_DST, ++ dev->capabilities); + +- if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_DST, +- dev->capabilities)) ++ if (!fmt) + return -EINVAL; + ++ pix_fmt->pixelformat = fmt->pixelformat; + cedrus_prepare_format(pix_fmt); + + return 0; +@@ -268,15 +268,18 @@ static int cedrus_try_fmt_vid_out(struct file *file, void *priv, + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + struct cedrus_dev *dev = ctx->dev; + struct v4l2_pix_format *pix_fmt = &f->fmt.pix; ++ struct cedrus_format *fmt = ++ cedrus_find_format(pix_fmt->pixelformat, CEDRUS_DECODE_SRC, ++ dev->capabilities); + +- if (!cedrus_check_format(pix_fmt->pixelformat, CEDRUS_DECODE_SRC, +- dev->capabilities)) ++ if (!fmt) + return -EINVAL; + + /* Source image size has to be provided by userspace. */ + if (pix_fmt->sizeimage == 0) + return -EINVAL; + ++ pix_fmt->pixelformat = fmt->pixelformat; + cedrus_prepare_format(pix_fmt); + + return 0; +@@ -364,21 +367,12 @@ static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, + struct device *alloc_devs[]) + { + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); +- struct cedrus_dev *dev = ctx->dev; + struct v4l2_pix_format *pix_fmt; +- u32 directions; + +- if (V4L2_TYPE_IS_OUTPUT(vq->type)) { +- directions = CEDRUS_DECODE_SRC; ++ if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; +- } else { +- directions = CEDRUS_DECODE_DST; ++ else + pix_fmt = &ctx->dst_fmt; +- } +- +- if (!cedrus_check_format(pix_fmt->pixelformat, directions, +- dev->capabilities)) +- return -EINVAL; + + if (*nplanes) { + if (sizes[0] < pix_fmt->sizeimage) +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0608-media-cedrus-fix-various-format-related-compliance-issues.patch b/patch/kernel/sunxi-current/0608-media-cedrus-fix-various-format-related-compliance-issues.patch new file mode 100644 index 000000000..3c73342ac --- /dev/null +++ b/patch/kernel/sunxi-current/0608-media-cedrus-fix-various-format-related-compliance-issues.patch @@ -0,0 +1,135 @@ + +From 965c71e8adcff315e16b58c00cd312598fc0222c Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Fri, 30 Aug 2019 06:26:24 -0300 +Subject: [PATCH] media: cedrus: fix various format-related compliance issues + +Initialize the context on open() with valid capture and output +formats. It is good practice to always have valid formats internally. + +This solves one vb2 warning in the kernel log where the sizeimage +value of the output format was 0 when requesting buffers, which is +not allowed. + +It also simplifies the g_fmt ioctl implementations since they no longer +have to check if a valid format was ever set. + +cedrus_prepare_format() now also validates sizeimage for the output +formats, ensuring userspace can't set it to 0 since that would cause +the same vb2 warning. + +Finally remove the sizeimage == 0 check in cedrus_try_fmt_vid_out() +since cedrus_prepare_format() will now adjust this value. + +Signed-off-by: Hans Verkuil +Reviewed-by: Jernej Skrabec +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/staging/media/sunxi/cedrus/cedrus.c | 10 +++++++ + .../staging/media/sunxi/cedrus/cedrus_video.c | 28 ++----------------- + .../staging/media/sunxi/cedrus/cedrus_video.h | 1 + + 3 files changed, 14 insertions(+), 25 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c +index 3439f6ad6338..0cf637c8a1e3 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.c +@@ -241,6 +241,16 @@ static int cedrus_open(struct file *file) + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_ctrls; + } ++ ctx->dst_fmt.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12; ++ cedrus_prepare_format(&ctx->dst_fmt); ++ ctx->src_fmt.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE; ++ /* ++ * TILED_NV12 has more strict requirements, so copy the width and ++ * height to src_fmt to ensure that is matches the dst_fmt resolution. ++ */ ++ ctx->src_fmt.width = ctx->dst_fmt.width; ++ ctx->src_fmt.height = ctx->dst_fmt.height; ++ cedrus_prepare_format(&ctx->src_fmt); + + v4l2_fh_add(&ctx->fh); + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index d69c9bcdb8e2..3ec3a2db790c 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -86,7 +86,7 @@ static struct cedrus_format *cedrus_find_format(u32 pixelformat, u32 directions, + return &cedrus_formats[i]; + } + +-static void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) ++void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) + { + unsigned int width = pix_fmt->width; + unsigned int height = pix_fmt->height; +@@ -104,7 +104,8 @@ static void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) + case V4L2_PIX_FMT_H264_SLICE: + /* Zero bytes per line for encoded source. */ + bytesperline = 0; +- ++ /* Choose some minimum size since this can't be 0 */ ++ sizeimage = max_t(u32, SZ_1K, sizeimage); + break; + + case V4L2_PIX_FMT_SUNXI_TILED_NV12: +@@ -211,16 +212,7 @@ static int cedrus_g_fmt_vid_cap(struct file *file, void *priv, + { + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + +- /* Fall back to dummy default by lack of hardware configuration. */ +- if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) { +- f->fmt.pix.pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12; +- cedrus_prepare_format(&f->fmt.pix); +- +- return 0; +- } +- + f->fmt.pix = ctx->dst_fmt; +- + return 0; + } + +@@ -229,17 +221,7 @@ static int cedrus_g_fmt_vid_out(struct file *file, void *priv, + { + struct cedrus_ctx *ctx = cedrus_file2ctx(file); + +- /* Fall back to dummy default by lack of hardware configuration. */ +- if (!ctx->dst_fmt.width || !ctx->dst_fmt.height) { +- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG2_SLICE; +- f->fmt.pix.sizeimage = SZ_1K; +- cedrus_prepare_format(&f->fmt.pix); +- +- return 0; +- } +- + f->fmt.pix = ctx->src_fmt; +- + return 0; + } + +@@ -275,10 +257,6 @@ static int cedrus_try_fmt_vid_out(struct file *file, void *priv, + if (!fmt) + return -EINVAL; + +- /* Source image size has to be provided by userspace. */ +- if (pix_fmt->sizeimage == 0) +- return -EINVAL; +- + pix_fmt->pixelformat = fmt->pixelformat; + cedrus_prepare_format(pix_fmt); + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.h b/drivers/staging/media/sunxi/cedrus/cedrus_video.h +index 0e4f7a8cccf2..05050c0a0921 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.h +@@ -26,5 +26,6 @@ extern const struct v4l2_ioctl_ops cedrus_ioctl_ops; + + int cedrus_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); ++void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt); + + #endif +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0609-media-cedrus-h264-Support-multiple-slices-per-frame.patch b/patch/kernel/sunxi-current/0609-media-cedrus-h264-Support-multiple-slices-per-frame.patch new file mode 100644 index 000000000..a6cf1d3a9 --- /dev/null +++ b/patch/kernel/sunxi-current/0609-media-cedrus-h264-Support-multiple-slices-per-frame.patch @@ -0,0 +1,139 @@ + +From eabf10e5e3009e0c7e9a9b98a7f8299e690bcc55 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 11 Oct 2019 06:32:45 -0300 +Subject: [PATCH] media: cedrus: h264: Support multiple slices per frame + +With recent changes, support for decoding multi-slice frames can be +easily added now. + +Signal VPU if current slice is first in frame or not and add information +about first macroblock coordinates. + +When frame contains multiple slices and driver works in slice mode, it's +more efficient to hold capture buffer in queue until all slices of a +same frame are decoded. + +Add support for that to Cedrus driver by exposing and implementing +V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF capability. + +Signed-off-by: Jernej Skrabec +[hverkuil-cisco@xs4all.nl: rewritten to use v4l2_m2m_buf_done_and_job_finish] +[hverkuil-cisco@xs4all.nl: removed unnecessary (u32) cast] +[hverkuil-cisco@xs4all.nl: use new_frame v4l2_m2m_ctx bool] +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 12 +++++++++++- + drivers/staging/media/sunxi/cedrus/cedrus_hw.c | 16 ++-------------- + .../staging/media/sunxi/cedrus/cedrus_video.c | 14 ++++++++++++++ + 3 files changed, 27 insertions(+), 15 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +index d6a782703c9b..cd85668f9c80 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +@@ -301,6 +301,8 @@ static void cedrus_set_params(struct cedrus_ctx *ctx, + dma_addr_t src_buf_addr; + u32 offset = slice->header_bit_size; + u32 len = (slice->size * 8) - offset; ++ unsigned int pic_width_in_mbs; ++ bool mbaff_pic; + u32 reg; + + cedrus_write(dev, VE_H264_VLD_LEN, len); +@@ -370,12 +372,20 @@ static void cedrus_set_params(struct cedrus_ctx *ctx, + reg |= VE_H264_SPS_DIRECT_8X8_INFERENCE; + cedrus_write(dev, VE_H264_SPS, reg); + ++ mbaff_pic = !(slice->flags & V4L2_H264_SLICE_FLAG_FIELD_PIC) && ++ (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); ++ pic_width_in_mbs = sps->pic_width_in_mbs_minus1 + 1; ++ + // slice parameters + reg = 0; ++ reg |= ((slice->first_mb_in_slice % pic_width_in_mbs) & 0xff) << 24; ++ reg |= (((slice->first_mb_in_slice / pic_width_in_mbs) * ++ (mbaff_pic + 1)) & 0xff) << 16; + reg |= decode->nal_ref_idc ? BIT(12) : 0; + reg |= (slice->slice_type & 0xf) << 8; + reg |= slice->cabac_init_idc & 0x3; +- reg |= VE_H264_SHS_FIRST_SLICE_IN_PIC; ++ if (ctx->fh.m2m_ctx->new_frame) ++ reg |= VE_H264_SHS_FIRST_SLICE_IN_PIC; + if (slice->flags & V4L2_H264_SLICE_FLAG_FIELD_PIC) + reg |= VE_H264_SHS_FIELD_PIC; + if (slice->flags & V4L2_H264_SLICE_FLAG_BOTTOM_FIELD) +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +index a942cd9bed57..e7e18424bab1 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +@@ -103,7 +103,6 @@ static irqreturn_t cedrus_irq(int irq, void *data) + { + struct cedrus_dev *dev = data; + struct cedrus_ctx *ctx; +- struct vb2_v4l2_buffer *src_buf, *dst_buf; + enum vb2_buffer_state state; + enum cedrus_irq_status status; + +@@ -121,24 +120,13 @@ static irqreturn_t cedrus_irq(int irq, void *data) + dev->dec_ops[ctx->current_codec]->irq_disable(ctx); + dev->dec_ops[ctx->current_codec]->irq_clear(ctx); + +- src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); +- dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); +- +- if (!src_buf || !dst_buf) { +- v4l2_err(&dev->v4l2_dev, +- "Missing source and/or destination buffers\n"); +- return IRQ_HANDLED; +- } +- + if (status == CEDRUS_IRQ_ERROR) + state = VB2_BUF_STATE_ERROR; + else + state = VB2_BUF_STATE_DONE; + +- v4l2_m2m_buf_done(src_buf, state); +- v4l2_m2m_buf_done(dst_buf, state); +- +- v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); ++ v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, ++ state); + + return IRQ_HANDLED; + } +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index 3ec3a2db790c..f745f66c4440 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -303,6 +303,17 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv, + + ctx->src_fmt = f->fmt.pix; + ++ switch (ctx->src_fmt.pixelformat) { ++ case V4L2_PIX_FMT_H264_SLICE: ++ vq->subsystem_flags |= ++ VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; ++ break; ++ default: ++ vq->subsystem_flags &= ++ ~VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; ++ break; ++ } ++ + /* Propagate colorspace information to capture. */ + ctx->dst_fmt.colorspace = f->fmt.pix.colorspace; + ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func; +@@ -336,6 +347,9 @@ const struct v4l2_ioctl_ops cedrus_ioctl_ops = { + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + ++ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd, ++ .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd, ++ + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + }; +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0611-media-cedrus-Remove-unnecessary-parenthesis-aroundDIV_ROUND_UP.patch b/patch/kernel/sunxi-current/0611-media-cedrus-Remove-unnecessary-parenthesis-aroundDIV_ROUND_UP.patch new file mode 100644 index 000000000..35f0689a6 --- /dev/null +++ b/patch/kernel/sunxi-current/0611-media-cedrus-Remove-unnecessary-parenthesis-aroundDIV_ROUND_UP.patch @@ -0,0 +1,34 @@ + +From c3b32900fbf5178473c6b39260e891e19067edc2 Mon Sep 17 00:00:00 2001 +From: Paul Kocialkowski +Date: Tue, 22 Oct 2019 12:26:51 -0300 +Subject: [PATCH] media: cedrus: Remove unnecessary parenthesis aroundDIV_ROUND_UP + +DIV_ROUND_UP's first argument doesn't need to be wrapped in parenthesis +since that is already being taken care of in the macro's definition. + +Signed-off-by: Paul Kocialkowski +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/staging/media/sunxi/cedrus/cedrus_regs.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index f9dd8cbf3458..21676a1797f1 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -101,9 +101,9 @@ + #define VE_DEC_MPEG_PICCODEDSIZE (VE_ENGINE_DEC_MPEG + 0x08) + + #define VE_DEC_MPEG_PICCODEDSIZE_WIDTH(w) \ +- SHIFT_AND_MASK_BITS(DIV_ROUND_UP((w), 16), 15, 8) ++ SHIFT_AND_MASK_BITS(DIV_ROUND_UP(w, 16), 15, 8) + #define VE_DEC_MPEG_PICCODEDSIZE_HEIGHT(h) \ +- SHIFT_AND_MASK_BITS(DIV_ROUND_UP((h), 16), 7, 0) ++ SHIFT_AND_MASK_BITS(DIV_ROUND_UP(h, 16), 7, 0) + + #define VE_DEC_MPEG_PICBOUNDSIZE (VE_ENGINE_DEC_MPEG + 0x0c) + +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0612-media-cedrus-Add-HEVC-decoding-support.patch b/patch/kernel/sunxi-current/0612-media-cedrus-Add-HEVC-decoding-support.patch new file mode 100644 index 000000000..fa20b7e8a --- /dev/null +++ b/patch/kernel/sunxi-current/0612-media-cedrus-Add-HEVC-decoding-support.patch @@ -0,0 +1,1176 @@ + +From 86caab29da78961d73e489554c8b2573fae523d5 Mon Sep 17 00:00:00 2001 +From: Paul Kocialkowski +Date: Tue, 22 Oct 2019 12:26:54 -0300 +Subject: [PATCH] media: cedrus: Add HEVC/H.265 decoding support + +This introduces support for HEVC/H.265 to the Cedrus VPU driver, with +both uni-directional and bi-directional prediction modes supported. + +Field-coded (interlaced) pictures, custom quantization matrices and +10-bit output are not supported at this point. + +Signed-off-by: Paul Kocialkowski +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/staging/media/sunxi/cedrus/Makefile | 2 +- + drivers/staging/media/sunxi/cedrus/cedrus.c | 52 +- + drivers/staging/media/sunxi/cedrus/cedrus.h | 18 + + .../staging/media/sunxi/cedrus/cedrus_dec.c | 9 + + .../staging/media/sunxi/cedrus/cedrus_h265.c | 616 ++++++++++++++++++ + .../staging/media/sunxi/cedrus/cedrus_hw.c | 4 + + .../staging/media/sunxi/cedrus/cedrus_regs.h | 271 ++++++++ + .../staging/media/sunxi/cedrus/cedrus_video.c | 10 + + 8 files changed, 977 insertions(+), 5 deletions(-) + create mode 100644 drivers/staging/media/sunxi/cedrus/cedrus_h265.c + +diff --git a/drivers/staging/media/sunxi/cedrus/Makefile b/drivers/staging/media/sunxi/cedrus/Makefile +index c85ac6db0302..1bce49d3e7e2 100644 +--- a/drivers/staging/media/sunxi/cedrus/Makefile ++++ b/drivers/staging/media/sunxi/cedrus/Makefile +@@ -2,4 +2,4 @@ + obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi-cedrus.o + + sunxi-cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o \ +- cedrus_mpeg2.o cedrus_h264.o ++ cedrus_mpeg2.o cedrus_h264.o cedrus_h265.o +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c +index 0cf637c8a1e3..c6ddd46eff82 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.c +@@ -95,6 +95,45 @@ static const struct cedrus_control cedrus_controls[] = { + .codec = CEDRUS_CODEC_H264, + .required = false, + }, ++ { ++ .cfg = { ++ .id = V4L2_CID_MPEG_VIDEO_HEVC_SPS, ++ }, ++ .codec = CEDRUS_CODEC_H265, ++ .required = true, ++ }, ++ { ++ .cfg = { ++ .id = V4L2_CID_MPEG_VIDEO_HEVC_PPS, ++ }, ++ .codec = CEDRUS_CODEC_H265, ++ .required = true, ++ }, ++ { ++ .cfg = { ++ .id = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS, ++ }, ++ .codec = CEDRUS_CODEC_H265, ++ .required = true, ++ }, ++ { ++ .cfg = { ++ .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE, ++ .max = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED, ++ .def = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED, ++ }, ++ .codec = CEDRUS_CODEC_H265, ++ .required = false, ++ }, ++ { ++ .cfg = { ++ .id = V4L2_CID_MPEG_VIDEO_HEVC_START_CODE, ++ .max = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE, ++ .def = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE, ++ }, ++ .codec = CEDRUS_CODEC_H265, ++ .required = false, ++ }, + }; + + #define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls) +@@ -340,6 +379,7 @@ static int cedrus_probe(struct platform_device *pdev) + + dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; + dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264; ++ dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265; + + mutex_init(&dev->dev_mutex); + +@@ -450,22 +490,26 @@ static const struct cedrus_variant sun8i_a33_cedrus_variant = { + }; + + static const struct cedrus_variant sun8i_h3_cedrus_variant = { +- .capabilities = CEDRUS_CAPABILITY_UNTILED, ++ .capabilities = CEDRUS_CAPABILITY_UNTILED | ++ CEDRUS_CAPABILITY_H265_DEC, + .mod_rate = 402000000, + }; + + static const struct cedrus_variant sun50i_a64_cedrus_variant = { +- .capabilities = CEDRUS_CAPABILITY_UNTILED, ++ .capabilities = CEDRUS_CAPABILITY_UNTILED | ++ CEDRUS_CAPABILITY_H265_DEC, + .mod_rate = 402000000, + }; + + static const struct cedrus_variant sun50i_h5_cedrus_variant = { +- .capabilities = CEDRUS_CAPABILITY_UNTILED, ++ .capabilities = CEDRUS_CAPABILITY_UNTILED | ++ CEDRUS_CAPABILITY_H265_DEC, + .mod_rate = 402000000, + }; + + static const struct cedrus_variant sun50i_h6_cedrus_variant = { +- .capabilities = CEDRUS_CAPABILITY_UNTILED, ++ .capabilities = CEDRUS_CAPABILITY_UNTILED | ++ CEDRUS_CAPABILITY_H265_DEC, + .quirks = CEDRUS_QUIRK_NO_DMA_OFFSET, + .mod_rate = 600000000, + }; +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index 2f017a651848..986e059e3202 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -27,12 +27,14 @@ + #define CEDRUS_NAME "cedrus" + + #define CEDRUS_CAPABILITY_UNTILED BIT(0) ++#define CEDRUS_CAPABILITY_H265_DEC BIT(1) + + #define CEDRUS_QUIRK_NO_DMA_OFFSET BIT(0) + + enum cedrus_codec { + CEDRUS_CODEC_MPEG2, + CEDRUS_CODEC_H264, ++ CEDRUS_CODEC_H265, + CEDRUS_CODEC_LAST, + }; + +@@ -67,6 +69,12 @@ struct cedrus_mpeg2_run { + const struct v4l2_ctrl_mpeg2_quantization *quantization; + }; + ++struct cedrus_h265_run { ++ const struct v4l2_ctrl_hevc_sps *sps; ++ const struct v4l2_ctrl_hevc_pps *pps; ++ const struct v4l2_ctrl_hevc_slice_params *slice_params; ++}; ++ + struct cedrus_run { + struct vb2_v4l2_buffer *src; + struct vb2_v4l2_buffer *dst; +@@ -74,6 +82,7 @@ struct cedrus_run { + union { + struct cedrus_h264_run h264; + struct cedrus_mpeg2_run mpeg2; ++ struct cedrus_h265_run h265; + }; + }; + +@@ -110,6 +119,14 @@ struct cedrus_ctx { + void *neighbor_info_buf; + dma_addr_t neighbor_info_buf_dma; + } h264; ++ struct { ++ void *mv_col_buf; ++ dma_addr_t mv_col_buf_addr; ++ ssize_t mv_col_buf_size; ++ ssize_t mv_col_buf_unit_size; ++ void *neighbor_info_buf; ++ dma_addr_t neighbor_info_buf_addr; ++ } h265; + } codec; + }; + +@@ -155,6 +172,7 @@ struct cedrus_dev { + + extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2; + extern struct cedrus_dec_ops cedrus_dec_ops_h264; ++extern struct cedrus_dec_ops cedrus_dec_ops_h265; + + static inline void cedrus_write(struct cedrus_dev *dev, u32 reg, u32 val) + { +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +index 56ca4c9ad01c..4a2fc33a1d79 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +@@ -59,6 +59,15 @@ void cedrus_device_run(void *priv) + V4L2_CID_MPEG_VIDEO_H264_SPS); + break; + ++ case V4L2_PIX_FMT_HEVC_SLICE: ++ run.h265.sps = cedrus_find_control_data(ctx, ++ V4L2_CID_MPEG_VIDEO_HEVC_SPS); ++ run.h265.pps = cedrus_find_control_data(ctx, ++ V4L2_CID_MPEG_VIDEO_HEVC_PPS); ++ run.h265.slice_params = cedrus_find_control_data(ctx, ++ V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS); ++ break; ++ + default: + break; + } +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +new file mode 100644 +index 000000000000..9bc921866f70 +--- /dev/null ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -0,0 +1,616 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Cedrus VPU driver ++ * ++ * Copyright (C) 2013 Jens Kuske ++ * Copyright (C) 2018 Paul Kocialkowski ++ * Copyright (C) 2018 Bootlin ++ */ ++ ++#include ++ ++#include ++ ++#include "cedrus.h" ++#include "cedrus_hw.h" ++#include "cedrus_regs.h" ++ ++/* ++ * These are the sizes for side buffers required by the hardware for storing ++ * internal decoding metadata. They match the values used by the early BSP ++ * implementations, that were initially exposed in libvdpau-sunxi. ++ * Subsequent BSP implementations seem to double the neighbor info buffer size ++ * for the H6 SoC, which may be related to 10 bit H265 support. ++ */ ++#define CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE (397 * SZ_1K) ++#define CEDRUS_H265_ENTRY_POINTS_BUF_SIZE (4 * SZ_1K) ++#define CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE 160 ++ ++struct cedrus_h265_sram_frame_info { ++ __le32 top_pic_order_cnt; ++ __le32 bottom_pic_order_cnt; ++ __le32 top_mv_col_buf_addr; ++ __le32 bottom_mv_col_buf_addr; ++ __le32 luma_addr; ++ __le32 chroma_addr; ++} __packed; ++ ++struct cedrus_h265_sram_pred_weight { ++ __s8 delta_weight; ++ __s8 offset; ++} __packed; ++ ++static enum cedrus_irq_status cedrus_h265_irq_status(struct cedrus_ctx *ctx) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ u32 reg; ++ ++ reg = cedrus_read(dev, VE_DEC_H265_STATUS); ++ reg &= VE_DEC_H265_STATUS_CHECK_MASK; ++ ++ if (reg & VE_DEC_H265_STATUS_CHECK_ERROR || ++ !(reg & VE_DEC_H265_STATUS_SUCCESS)) ++ return CEDRUS_IRQ_ERROR; ++ ++ return CEDRUS_IRQ_OK; ++} ++ ++static void cedrus_h265_irq_clear(struct cedrus_ctx *ctx) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ ++ cedrus_write(dev, VE_DEC_H265_STATUS, VE_DEC_H265_STATUS_CHECK_MASK); ++} ++ ++static void cedrus_h265_irq_disable(struct cedrus_ctx *ctx) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ u32 reg = cedrus_read(dev, VE_DEC_H265_CTRL); ++ ++ reg &= ~VE_DEC_H265_CTRL_IRQ_MASK; ++ ++ cedrus_write(dev, VE_DEC_H265_CTRL, reg); ++} ++ ++static void cedrus_h265_sram_write_offset(struct cedrus_dev *dev, u32 offset) ++{ ++ cedrus_write(dev, VE_DEC_H265_SRAM_OFFSET, offset); ++} ++ ++static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, void *data, ++ unsigned int size) ++{ ++ u32 *word = data; ++ ++ while (size >= sizeof(u32)) { ++ cedrus_write(dev, VE_DEC_H265_SRAM_DATA, *word++); ++ size -= sizeof(u32); ++ } ++} ++ ++static inline dma_addr_t ++cedrus_h265_frame_info_mv_col_buf_addr(struct cedrus_ctx *ctx, ++ unsigned int index, unsigned int field) ++{ ++ return ctx->codec.h265.mv_col_buf_addr + index * ++ ctx->codec.h265.mv_col_buf_unit_size + ++ field * ctx->codec.h265.mv_col_buf_unit_size / 2; ++} ++ ++static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, ++ unsigned int index, ++ bool field_pic, ++ u32 pic_order_cnt[], ++ int buffer_index) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 0); ++ dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 1); ++ dma_addr_t mv_col_buf_addr[2] = { ++ cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, 0), ++ cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, ++ field_pic ? 1 : 0) ++ }; ++ u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO + ++ VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT * index; ++ struct cedrus_h265_sram_frame_info frame_info = { ++ .top_pic_order_cnt = cpu_to_le32(pic_order_cnt[0]), ++ .bottom_pic_order_cnt = cpu_to_le32(field_pic ? ++ pic_order_cnt[1] : ++ pic_order_cnt[0]), ++ .top_mv_col_buf_addr = ++ cpu_to_le32(VE_DEC_H265_SRAM_DATA_ADDR_BASE(mv_col_buf_addr[0])), ++ .bottom_mv_col_buf_addr = cpu_to_le32(field_pic ? ++ VE_DEC_H265_SRAM_DATA_ADDR_BASE(mv_col_buf_addr[1]) : ++ VE_DEC_H265_SRAM_DATA_ADDR_BASE(mv_col_buf_addr[0])), ++ .luma_addr = cpu_to_le32(VE_DEC_H265_SRAM_DATA_ADDR_BASE(dst_luma_addr)), ++ .chroma_addr = cpu_to_le32(VE_DEC_H265_SRAM_DATA_ADDR_BASE(dst_chroma_addr)), ++ }; ++ ++ cedrus_h265_sram_write_offset(dev, offset); ++ cedrus_h265_sram_write_data(dev, &frame_info, sizeof(frame_info)); ++} ++ ++static void cedrus_h265_frame_info_write_dpb(struct cedrus_ctx *ctx, ++ const struct v4l2_hevc_dpb_entry *dpb, ++ u8 num_active_dpb_entries) ++{ ++ struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ unsigned int i; ++ ++ for (i = 0; i < num_active_dpb_entries; i++) { ++ int buffer_index = vb2_find_timestamp(vq, dpb[i].timestamp, 0); ++ u32 pic_order_cnt[2] = { ++ dpb[i].pic_order_cnt[0], ++ dpb[i].pic_order_cnt[1] ++ }; ++ ++ cedrus_h265_frame_info_write_single(ctx, i, dpb[i].field_pic, ++ pic_order_cnt, ++ buffer_index); ++ } ++} ++ ++static void cedrus_h265_ref_pic_list_write(struct cedrus_dev *dev, ++ const struct v4l2_hevc_dpb_entry *dpb, ++ const u8 list[], ++ u8 num_ref_idx_active, ++ u32 sram_offset) ++{ ++ unsigned int i; ++ u32 word = 0; ++ ++ cedrus_h265_sram_write_offset(dev, sram_offset); ++ ++ for (i = 0; i < num_ref_idx_active; i++) { ++ unsigned int shift = (i % 4) * 8; ++ unsigned int index = list[i]; ++ u8 value = list[i]; ++ ++ if (dpb[index].rps == V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR) ++ value |= VE_DEC_H265_SRAM_REF_PIC_LIST_LT_REF; ++ ++ /* Each SRAM word gathers up to 4 references. */ ++ word |= value << shift; ++ ++ /* Write the word to SRAM and clear it for the next batch. */ ++ if ((i % 4) == 3 || i == (num_ref_idx_active - 1)) { ++ cedrus_h265_sram_write_data(dev, &word, sizeof(word)); ++ word = 0; ++ } ++ } ++} ++ ++static void cedrus_h265_pred_weight_write(struct cedrus_dev *dev, ++ const s8 delta_luma_weight[], ++ const s8 luma_offset[], ++ const s8 delta_chroma_weight[][2], ++ const s8 chroma_offset[][2], ++ u8 num_ref_idx_active, ++ u32 sram_luma_offset, ++ u32 sram_chroma_offset) ++{ ++ struct cedrus_h265_sram_pred_weight pred_weight[2] = { { 0 } }; ++ unsigned int i, j; ++ ++ cedrus_h265_sram_write_offset(dev, sram_luma_offset); ++ ++ for (i = 0; i < num_ref_idx_active; i++) { ++ unsigned int index = i % 2; ++ ++ pred_weight[index].delta_weight = delta_luma_weight[i]; ++ pred_weight[index].offset = luma_offset[i]; ++ ++ if (index == 1 || i == (num_ref_idx_active - 1)) ++ cedrus_h265_sram_write_data(dev, (u32 *)&pred_weight, ++ sizeof(pred_weight)); ++ } ++ ++ cedrus_h265_sram_write_offset(dev, sram_chroma_offset); ++ ++ for (i = 0; i < num_ref_idx_active; i++) { ++ for (j = 0; j < 2; j++) { ++ pred_weight[j].delta_weight = delta_chroma_weight[i][j]; ++ pred_weight[j].offset = chroma_offset[i][j]; ++ } ++ ++ cedrus_h265_sram_write_data(dev, &pred_weight, ++ sizeof(pred_weight)); ++ } ++} ++ ++static void cedrus_h265_setup(struct cedrus_ctx *ctx, ++ struct cedrus_run *run) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ const struct v4l2_ctrl_hevc_sps *sps; ++ const struct v4l2_ctrl_hevc_pps *pps; ++ const struct v4l2_ctrl_hevc_slice_params *slice_params; ++ const struct v4l2_hevc_pred_weight_table *pred_weight_table; ++ dma_addr_t src_buf_addr; ++ dma_addr_t src_buf_end_addr; ++ u32 chroma_log2_weight_denom; ++ u32 output_pic_list_index; ++ u32 pic_order_cnt[2]; ++ u32 reg; ++ ++ sps = run->h265.sps; ++ pps = run->h265.pps; ++ slice_params = run->h265.slice_params; ++ pred_weight_table = &slice_params->pred_weight_table; ++ ++ /* MV column buffer size and allocation. */ ++ if (!ctx->codec.h265.mv_col_buf_size) { ++ unsigned int num_buffers = ++ run->dst->vb2_buf.vb2_queue->num_buffers; ++ unsigned int log2_max_luma_coding_block_size = ++ sps->log2_min_luma_coding_block_size_minus3 + 3 + ++ sps->log2_diff_max_min_luma_coding_block_size; ++ unsigned int ctb_size_luma = ++ 1UL << log2_max_luma_coding_block_size; ++ ++ /* ++ * Each CTB requires a MV col buffer with a specific unit size. ++ * Since the address is given with missing lsb bits, 1 KiB is ++ * added to each buffer to ensure proper alignment. ++ */ ++ ctx->codec.h265.mv_col_buf_unit_size = ++ DIV_ROUND_UP(ctx->src_fmt.width, ctb_size_luma) * ++ DIV_ROUND_UP(ctx->src_fmt.height, ctb_size_luma) * ++ CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE + SZ_1K; ++ ++ ctx->codec.h265.mv_col_buf_size = num_buffers * ++ ctx->codec.h265.mv_col_buf_unit_size; ++ ++ ctx->codec.h265.mv_col_buf = ++ dma_alloc_coherent(dev->dev, ++ ctx->codec.h265.mv_col_buf_size, ++ &ctx->codec.h265.mv_col_buf_addr, ++ GFP_KERNEL); ++ if (!ctx->codec.h265.mv_col_buf) { ++ ctx->codec.h265.mv_col_buf_size = 0; ++ // TODO: Abort the process here. ++ return; ++ } ++ } ++ ++ /* Activate H265 engine. */ ++ cedrus_engine_enable(dev, CEDRUS_CODEC_H265); ++ ++ /* Source offset and length in bits. */ ++ ++ reg = slice_params->data_bit_offset; ++ cedrus_write(dev, VE_DEC_H265_BITS_OFFSET, reg); ++ ++ reg = slice_params->bit_size - slice_params->data_bit_offset; ++ cedrus_write(dev, VE_DEC_H265_BITS_LEN, reg); ++ ++ /* Source beginning and end addresses. */ ++ ++ src_buf_addr = vb2_dma_contig_plane_dma_addr(&run->src->vb2_buf, 0); ++ ++ reg = VE_DEC_H265_BITS_ADDR_BASE(src_buf_addr); ++ reg |= VE_DEC_H265_BITS_ADDR_VALID_SLICE_DATA; ++ reg |= VE_DEC_H265_BITS_ADDR_LAST_SLICE_DATA; ++ reg |= VE_DEC_H265_BITS_ADDR_FIRST_SLICE_DATA; ++ ++ cedrus_write(dev, VE_DEC_H265_BITS_ADDR, reg); ++ ++ src_buf_end_addr = src_buf_addr + ++ DIV_ROUND_UP(slice_params->bit_size, 8); ++ ++ reg = VE_DEC_H265_BITS_END_ADDR_BASE(src_buf_end_addr); ++ cedrus_write(dev, VE_DEC_H265_BITS_END_ADDR, reg); ++ ++ /* Coding tree block address: start at the beginning. */ ++ reg = VE_DEC_H265_DEC_CTB_ADDR_X(0) | VE_DEC_H265_DEC_CTB_ADDR_Y(0); ++ cedrus_write(dev, VE_DEC_H265_DEC_CTB_ADDR, reg); ++ ++ cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, 0); ++ cedrus_write(dev, VE_DEC_H265_TILE_END_CTB, 0); ++ ++ /* Clear the number of correctly-decoded coding tree blocks. */ ++ cedrus_write(dev, VE_DEC_H265_DEC_CTB_NUM, 0); ++ ++ /* Initialize bitstream access. */ ++ cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_INIT_SWDEC); ++ ++ /* Bitstream parameters. */ ++ ++ reg = VE_DEC_H265_DEC_NAL_HDR_NAL_UNIT_TYPE(slice_params->nal_unit_type) | ++ VE_DEC_H265_DEC_NAL_HDR_NUH_TEMPORAL_ID_PLUS1(slice_params->nuh_temporal_id_plus1); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_NAL_HDR, reg); ++ ++ /* SPS. */ ++ ++ reg = VE_DEC_H265_DEC_SPS_HDR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA(sps->max_transform_hierarchy_depth_intra) | ++ VE_DEC_H265_DEC_SPS_HDR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTER(sps->max_transform_hierarchy_depth_inter) | ++ VE_DEC_H265_DEC_SPS_HDR_LOG2_DIFF_MAX_MIN_TRANSFORM_BLOCK_SIZE(sps->log2_diff_max_min_luma_transform_block_size) | ++ VE_DEC_H265_DEC_SPS_HDR_LOG2_MIN_TRANSFORM_BLOCK_SIZE_MINUS2(sps->log2_min_luma_transform_block_size_minus2) | ++ VE_DEC_H265_DEC_SPS_HDR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE(sps->log2_diff_max_min_luma_coding_block_size) | ++ VE_DEC_H265_DEC_SPS_HDR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3(sps->log2_min_luma_coding_block_size_minus3) | ++ VE_DEC_H265_DEC_SPS_HDR_BIT_DEPTH_CHROMA_MINUS8(sps->bit_depth_chroma_minus8) | ++ VE_DEC_H265_DEC_SPS_HDR_CHROMA_FORMAT_IDC(sps->chroma_format_idc); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SPS_HDR_FLAG_STRONG_INTRA_SMOOTHING_ENABLE, ++ V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED, ++ sps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SPS_HDR_FLAG_SPS_TEMPORAL_MVP_ENABLED, ++ V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED, ++ sps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SPS_HDR_FLAG_SAMPLE_ADAPTIVE_OFFSET_ENABLED, ++ V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET, ++ sps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SPS_HDR_FLAG_AMP_ENABLED, ++ V4L2_HEVC_SPS_FLAG_AMP_ENABLED, sps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SPS_HDR_FLAG_SEPARATE_COLOUR_PLANE, ++ V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE, ++ sps->flags); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_SPS_HDR, reg); ++ ++ reg = VE_DEC_H265_DEC_PCM_CTRL_LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE(sps->log2_diff_max_min_pcm_luma_coding_block_size) | ++ VE_DEC_H265_DEC_PCM_CTRL_LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE_MINUS3(sps->log2_min_pcm_luma_coding_block_size_minus3) | ++ VE_DEC_H265_DEC_PCM_CTRL_PCM_SAMPLE_BIT_DEPTH_CHROMA_MINUS1(sps->pcm_sample_bit_depth_chroma_minus1) | ++ VE_DEC_H265_DEC_PCM_CTRL_PCM_SAMPLE_BIT_DEPTH_LUMA_MINUS1(sps->pcm_sample_bit_depth_luma_minus1); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PCM_CTRL_FLAG_PCM_ENABLED, ++ V4L2_HEVC_SPS_FLAG_PCM_ENABLED, sps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PCM_CTRL_FLAG_PCM_LOOP_FILTER_DISABLED, ++ V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED, ++ sps->flags); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_PCM_CTRL, reg); ++ ++ /* PPS. */ ++ ++ reg = VE_DEC_H265_DEC_PPS_CTRL0_PPS_CR_QP_OFFSET(pps->pps_cr_qp_offset) | ++ VE_DEC_H265_DEC_PPS_CTRL0_PPS_CB_QP_OFFSET(pps->pps_cb_qp_offset) | ++ VE_DEC_H265_DEC_PPS_CTRL0_INIT_QP_MINUS26(pps->init_qp_minus26) | ++ VE_DEC_H265_DEC_PPS_CTRL0_DIFF_CU_QP_DELTA_DEPTH(pps->diff_cu_qp_delta_depth); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL0_FLAG_CU_QP_DELTA_ENABLED, ++ V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED, ++ pps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL0_FLAG_TRANSFORM_SKIP_ENABLED, ++ V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED, ++ pps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL0_FLAG_CONSTRAINED_INTRA_PRED, ++ V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED, ++ pps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL0_FLAG_SIGN_DATA_HIDING_ENABLED, ++ V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED, ++ pps->flags); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_PPS_CTRL0, reg); ++ ++ reg = VE_DEC_H265_DEC_PPS_CTRL1_LOG2_PARALLEL_MERGE_LEVEL_MINUS2(pps->log2_parallel_merge_level_minus2); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED, ++ V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED, ++ pps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED, ++ V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED, ++ pps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_ENTROPY_CODING_SYNC_ENABLED, ++ V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED, ++ pps->flags); ++ ++ /* TODO: VE_DEC_H265_DEC_PPS_CTRL1_FLAG_TILES_ENABLED */ ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_TRANSQUANT_BYPASS_ENABLED, ++ V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED, ++ pps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_WEIGHTED_BIPRED, ++ V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED, pps->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_WEIGHTED_PRED, ++ V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED, pps->flags); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_PPS_CTRL1, reg); ++ ++ /* Slice Parameters. */ ++ ++ reg = VE_DEC_H265_DEC_SLICE_HDR_INFO0_PICTURE_TYPE(slice_params->pic_struct) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO0_FIVE_MINUS_MAX_NUM_MERGE_CAND(slice_params->five_minus_max_num_merge_cand) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO0_NUM_REF_IDX_L1_ACTIVE_MINUS1(slice_params->num_ref_idx_l1_active_minus1) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO0_NUM_REF_IDX_L0_ACTIVE_MINUS1(slice_params->num_ref_idx_l0_active_minus1) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO0_COLLOCATED_REF_IDX(slice_params->collocated_ref_idx) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO0_COLOUR_PLANE_ID(slice_params->colour_plane_id) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO0_SLICE_TYPE(slice_params->slice_type); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_COLLOCATED_FROM_L0, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0, ++ slice_params->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_CABAC_INIT, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT, ++ slice_params->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_MVD_L1_ZERO, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO, ++ slice_params->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_SLICE_SAO_CHROMA, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA, ++ slice_params->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_SLICE_SAO_LUMA, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA, ++ slice_params->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_SLICE_TEMPORAL_MVP_ENABLE, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED, ++ slice_params->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_DEPENDENT_SLICE_SEGMENT, ++ V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT, ++ pps->flags); ++ ++ /* FIXME: For multi-slice support. */ ++ reg |= VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_FIRST_SLICE_SEGMENT_IN_PIC; ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO0, reg); ++ ++ reg = VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_TC_OFFSET_DIV2(slice_params->slice_tc_offset_div2) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_BETA_OFFSET_DIV2(slice_params->slice_beta_offset_div2) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_POC_BIGEST_IN_RPS_ST(slice_params->num_rps_poc_st_curr_after == 0) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_CR_QP_OFFSET(slice_params->slice_cr_qp_offset) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_CB_QP_OFFSET(slice_params->slice_cb_qp_offset) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_QP_DELTA(slice_params->slice_qp_delta); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO1_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED, ++ slice_params->flags); ++ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SLICE_HDR_INFO1_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED, ++ V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED, ++ slice_params->flags); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO1, reg); ++ ++ chroma_log2_weight_denom = pred_weight_table->luma_log2_weight_denom + ++ pred_weight_table->delta_chroma_log2_weight_denom; ++ reg = VE_DEC_H265_DEC_SLICE_HDR_INFO2_NUM_ENTRY_POINT_OFFSETS(0) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO2_CHROMA_LOG2_WEIGHT_DENOM(chroma_log2_weight_denom) | ++ VE_DEC_H265_DEC_SLICE_HDR_INFO2_LUMA_LOG2_WEIGHT_DENOM(pred_weight_table->luma_log2_weight_denom); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO2, reg); ++ ++ /* Decoded picture size. */ ++ ++ reg = VE_DEC_H265_DEC_PIC_SIZE_WIDTH(ctx->src_fmt.width) | ++ VE_DEC_H265_DEC_PIC_SIZE_HEIGHT(ctx->src_fmt.height); ++ ++ cedrus_write(dev, VE_DEC_H265_DEC_PIC_SIZE, reg); ++ ++ /* Scaling list. */ ++ ++ reg = VE_DEC_H265_SCALING_LIST_CTRL0_DEFAULT; ++ cedrus_write(dev, VE_DEC_H265_SCALING_LIST_CTRL0, reg); ++ ++ /* Neightbor information address. */ ++ reg = VE_DEC_H265_NEIGHBOR_INFO_ADDR_BASE(ctx->codec.h265.neighbor_info_buf_addr); ++ cedrus_write(dev, VE_DEC_H265_NEIGHBOR_INFO_ADDR, reg); ++ ++ /* Write decoded picture buffer in pic list. */ ++ cedrus_h265_frame_info_write_dpb(ctx, slice_params->dpb, ++ slice_params->num_active_dpb_entries); ++ ++ /* Output frame. */ ++ ++ output_pic_list_index = V4L2_HEVC_DPB_ENTRIES_NUM_MAX; ++ pic_order_cnt[0] = slice_params->slice_pic_order_cnt; ++ pic_order_cnt[1] = slice_params->slice_pic_order_cnt; ++ ++ cedrus_h265_frame_info_write_single(ctx, output_pic_list_index, ++ slice_params->pic_struct != 0, ++ pic_order_cnt, ++ run->dst->vb2_buf.index); ++ ++ cedrus_write(dev, VE_DEC_H265_OUTPUT_FRAME_IDX, output_pic_list_index); ++ ++ /* Reference picture list 0 (for P/B frames). */ ++ if (slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_I) { ++ cedrus_h265_ref_pic_list_write(dev, slice_params->dpb, ++ slice_params->ref_idx_l0, ++ slice_params->num_ref_idx_l0_active_minus1 + 1, ++ VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST0); ++ ++ if ((pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED) || ++ (pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED)) ++ cedrus_h265_pred_weight_write(dev, ++ pred_weight_table->delta_luma_weight_l0, ++ pred_weight_table->luma_offset_l0, ++ pred_weight_table->delta_chroma_weight_l0, ++ pred_weight_table->chroma_offset_l0, ++ slice_params->num_ref_idx_l0_active_minus1 + 1, ++ VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L0, ++ VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L0); ++ } ++ ++ /* Reference picture list 1 (for B frames). */ ++ if (slice_params->slice_type == V4L2_HEVC_SLICE_TYPE_B) { ++ cedrus_h265_ref_pic_list_write(dev, slice_params->dpb, ++ slice_params->ref_idx_l1, ++ slice_params->num_ref_idx_l1_active_minus1 + 1, ++ VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST1); ++ ++ if (pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED) ++ cedrus_h265_pred_weight_write(dev, ++ pred_weight_table->delta_luma_weight_l1, ++ pred_weight_table->luma_offset_l1, ++ pred_weight_table->delta_chroma_weight_l1, ++ pred_weight_table->chroma_offset_l1, ++ slice_params->num_ref_idx_l1_active_minus1 + 1, ++ VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L1, ++ VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L1); ++ } ++ ++ /* Enable appropriate interruptions. */ ++ cedrus_write(dev, VE_DEC_H265_CTRL, VE_DEC_H265_CTRL_IRQ_MASK); ++} ++ ++static int cedrus_h265_start(struct cedrus_ctx *ctx) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ ++ /* The buffer size is calculated at setup time. */ ++ ctx->codec.h265.mv_col_buf_size = 0; ++ ++ ctx->codec.h265.neighbor_info_buf = ++ dma_alloc_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, ++ &ctx->codec.h265.neighbor_info_buf_addr, ++ GFP_KERNEL); ++ if (!ctx->codec.h265.neighbor_info_buf) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void cedrus_h265_stop(struct cedrus_ctx *ctx) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ ++ if (ctx->codec.h265.mv_col_buf_size > 0) { ++ dma_free_coherent(dev->dev, ctx->codec.h265.mv_col_buf_size, ++ ctx->codec.h265.mv_col_buf, ++ ctx->codec.h265.mv_col_buf_addr); ++ ++ ctx->codec.h265.mv_col_buf_size = 0; ++ } ++ ++ dma_free_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, ++ ctx->codec.h265.neighbor_info_buf, ++ ctx->codec.h265.neighbor_info_buf_addr); ++} ++ ++static void cedrus_h265_trigger(struct cedrus_ctx *ctx) ++{ ++ struct cedrus_dev *dev = ctx->dev; ++ ++ cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_DEC_SLICE); ++} ++ ++struct cedrus_dec_ops cedrus_dec_ops_h265 = { ++ .irq_clear = cedrus_h265_irq_clear, ++ .irq_disable = cedrus_h265_irq_disable, ++ .irq_status = cedrus_h265_irq_status, ++ .setup = cedrus_h265_setup, ++ .start = cedrus_h265_start, ++ .stop = cedrus_h265_stop, ++ .trigger = cedrus_h265_trigger, ++}; +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +index e7e18424bab1..570a9165dd5d 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +@@ -50,6 +50,10 @@ int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec) + reg |= VE_MODE_DEC_H264; + break; + ++ case CEDRUS_CODEC_H265: ++ reg |= VE_MODE_DEC_H265; ++ break; ++ + default: + return -EINVAL; + } +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index 21676a1797f1..6fc28d21a6c7 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -21,10 +21,17 @@ + * * MC: Motion Compensation + * * STCD: Start Code Detect + * * SDRT: Scale Down and Rotate ++ * * WB: Writeback ++ * * BITS/BS: Bitstream ++ * * MB: Macroblock ++ * * CTU: Coding Tree Unit ++ * * CTB: Coding Tree Block ++ * * IDX: Index + */ + + #define VE_ENGINE_DEC_MPEG 0x100 + #define VE_ENGINE_DEC_H264 0x200 ++#define VE_ENGINE_DEC_H265 0x500 + + #define VE_MODE 0x00 + +@@ -235,6 +242,270 @@ + #define VE_DEC_MPEG_ROT_LUMA (VE_ENGINE_DEC_MPEG + 0xcc) + #define VE_DEC_MPEG_ROT_CHROMA (VE_ENGINE_DEC_MPEG + 0xd0) + ++#define VE_DEC_H265_DEC_NAL_HDR (VE_ENGINE_DEC_H265 + 0x00) ++ ++#define VE_DEC_H265_DEC_NAL_HDR_NUH_TEMPORAL_ID_PLUS1(v) \ ++ SHIFT_AND_MASK_BITS(v, 8, 6) ++#define VE_DEC_H265_DEC_NAL_HDR_NAL_UNIT_TYPE(v) \ ++ SHIFT_AND_MASK_BITS(v, 5, 0) ++ ++#define VE_DEC_H265_FLAG(reg_flag, ctrl_flag, flags) \ ++ (((flags) & (ctrl_flag)) ? reg_flag : 0) ++ ++#define VE_DEC_H265_DEC_SPS_HDR (VE_ENGINE_DEC_H265 + 0x04) ++ ++#define VE_DEC_H265_DEC_SPS_HDR_FLAG_STRONG_INTRA_SMOOTHING_ENABLE BIT(26) ++#define VE_DEC_H265_DEC_SPS_HDR_FLAG_SPS_TEMPORAL_MVP_ENABLED BIT(25) ++#define VE_DEC_H265_DEC_SPS_HDR_FLAG_SAMPLE_ADAPTIVE_OFFSET_ENABLED BIT(24) ++#define VE_DEC_H265_DEC_SPS_HDR_FLAG_AMP_ENABLED BIT(23) ++#define VE_DEC_H265_DEC_SPS_HDR_FLAG_SEPARATE_COLOUR_PLANE BIT(2) ++ ++#define VE_DEC_H265_DEC_SPS_HDR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA(v) \ ++ SHIFT_AND_MASK_BITS(v, 22, 20) ++#define VE_DEC_H265_DEC_SPS_HDR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTER(v) \ ++ SHIFT_AND_MASK_BITS(v, 19, 17) ++#define VE_DEC_H265_DEC_SPS_HDR_LOG2_DIFF_MAX_MIN_TRANSFORM_BLOCK_SIZE(v) \ ++ SHIFT_AND_MASK_BITS(v, 16, 15) ++#define VE_DEC_H265_DEC_SPS_HDR_LOG2_MIN_TRANSFORM_BLOCK_SIZE_MINUS2(v) \ ++ SHIFT_AND_MASK_BITS(v, 14, 13) ++#define VE_DEC_H265_DEC_SPS_HDR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE(v) \ ++ SHIFT_AND_MASK_BITS(v, 12, 11) ++#define VE_DEC_H265_DEC_SPS_HDR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3(v) \ ++ SHIFT_AND_MASK_BITS(v, 10, 9) ++#define VE_DEC_H265_DEC_SPS_HDR_BIT_DEPTH_CHROMA_MINUS8(v) \ ++ SHIFT_AND_MASK_BITS(v, 8, 6) ++#define VE_DEC_H265_DEC_SPS_HDR_BIT_DEPTH_LUMA_MINUS8(v) \ ++ SHIFT_AND_MASK_BITS(v, 5, 3) ++#define VE_DEC_H265_DEC_SPS_HDR_CHROMA_FORMAT_IDC(v) \ ++ SHIFT_AND_MASK_BITS(v, 1, 0) ++ ++#define VE_DEC_H265_DEC_PIC_SIZE (VE_ENGINE_DEC_H265 + 0x08) ++ ++#define VE_DEC_H265_DEC_PIC_SIZE_WIDTH(w) (((w) << 0) & GENMASK(13, 0)) ++#define VE_DEC_H265_DEC_PIC_SIZE_HEIGHT(h) (((h) << 16) & GENMASK(29, 16)) ++ ++#define VE_DEC_H265_DEC_PCM_CTRL (VE_ENGINE_DEC_H265 + 0x0c) ++ ++#define VE_DEC_H265_DEC_PCM_CTRL_FLAG_PCM_ENABLED BIT(15) ++#define VE_DEC_H265_DEC_PCM_CTRL_FLAG_PCM_LOOP_FILTER_DISABLED BIT(14) ++ ++#define VE_DEC_H265_DEC_PCM_CTRL_LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE(v) \ ++ SHIFT_AND_MASK_BITS(v, 11, 10) ++#define VE_DEC_H265_DEC_PCM_CTRL_LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE_MINUS3(v) \ ++ SHIFT_AND_MASK_BITS(v, 9, 8) ++#define VE_DEC_H265_DEC_PCM_CTRL_PCM_SAMPLE_BIT_DEPTH_CHROMA_MINUS1(v) \ ++ SHIFT_AND_MASK_BITS(v, 7, 4) ++#define VE_DEC_H265_DEC_PCM_CTRL_PCM_SAMPLE_BIT_DEPTH_LUMA_MINUS1(v) \ ++ SHIFT_AND_MASK_BITS(v, 3, 0) ++ ++#define VE_DEC_H265_DEC_PPS_CTRL0 (VE_ENGINE_DEC_H265 + 0x10) ++ ++#define VE_DEC_H265_DEC_PPS_CTRL0_FLAG_CU_QP_DELTA_ENABLED BIT(3) ++#define VE_DEC_H265_DEC_PPS_CTRL0_FLAG_TRANSFORM_SKIP_ENABLED BIT(2) ++#define VE_DEC_H265_DEC_PPS_CTRL0_FLAG_CONSTRAINED_INTRA_PRED BIT(1) ++#define VE_DEC_H265_DEC_PPS_CTRL0_FLAG_SIGN_DATA_HIDING_ENABLED BIT(0) ++ ++#define VE_DEC_H265_DEC_PPS_CTRL0_PPS_CR_QP_OFFSET(v) \ ++ SHIFT_AND_MASK_BITS(v, 29, 24) ++#define VE_DEC_H265_DEC_PPS_CTRL0_PPS_CB_QP_OFFSET(v) \ ++ SHIFT_AND_MASK_BITS(v, 21, 16) ++#define VE_DEC_H265_DEC_PPS_CTRL0_INIT_QP_MINUS26(v) \ ++ SHIFT_AND_MASK_BITS(v, 14, 8) ++#define VE_DEC_H265_DEC_PPS_CTRL0_DIFF_CU_QP_DELTA_DEPTH(v) \ ++ SHIFT_AND_MASK_BITS(v, 5, 4) ++ ++#define VE_DEC_H265_DEC_PPS_CTRL1 (VE_ENGINE_DEC_H265 + 0x14) ++ ++#define VE_DEC_H265_DEC_PPS_CTRL1_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED BIT(6) ++#define VE_DEC_H265_DEC_PPS_CTRL1_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED BIT(5) ++#define VE_DEC_H265_DEC_PPS_CTRL1_FLAG_ENTROPY_CODING_SYNC_ENABLED BIT(4) ++#define VE_DEC_H265_DEC_PPS_CTRL1_FLAG_TILES_ENABLED BIT(3) ++#define VE_DEC_H265_DEC_PPS_CTRL1_FLAG_TRANSQUANT_BYPASS_ENABLED BIT(2) ++#define VE_DEC_H265_DEC_PPS_CTRL1_FLAG_WEIGHTED_BIPRED BIT(1) ++#define VE_DEC_H265_DEC_PPS_CTRL1_FLAG_WEIGHTED_PRED BIT(0) ++ ++#define VE_DEC_H265_DEC_PPS_CTRL1_LOG2_PARALLEL_MERGE_LEVEL_MINUS2(v) \ ++ SHIFT_AND_MASK_BITS(v, 10, 8) ++ ++#define VE_DEC_H265_SCALING_LIST_CTRL0 (VE_ENGINE_DEC_H265 + 0x18) ++ ++#define VE_DEC_H265_SCALING_LIST_CTRL0_FLAG_ENABLED BIT(31) ++ ++#define VE_DEC_H265_SCALING_LIST_CTRL0_SRAM (0 << 30) ++#define VE_DEC_H265_SCALING_LIST_CTRL0_DEFAULT (1 << 30) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0 (VE_ENGINE_DEC_H265 + 0x20) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_COLLOCATED_FROM_L0 BIT(11) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_CABAC_INIT BIT(10) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_MVD_L1_ZERO BIT(9) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_SLICE_SAO_CHROMA BIT(8) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_SLICE_SAO_LUMA BIT(7) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_SLICE_TEMPORAL_MVP_ENABLE BIT(6) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_DEPENDENT_SLICE_SEGMENT BIT(1) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_FIRST_SLICE_SEGMENT_IN_PIC BIT(0) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_PICTURE_TYPE(v) \ ++ SHIFT_AND_MASK_BITS(v, 29, 28) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_FIVE_MINUS_MAX_NUM_MERGE_CAND(v) \ ++ SHIFT_AND_MASK_BITS(v, 26, 24) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_NUM_REF_IDX_L1_ACTIVE_MINUS1(v) \ ++ SHIFT_AND_MASK_BITS(v, 23, 20) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_NUM_REF_IDX_L0_ACTIVE_MINUS1(v) \ ++ SHIFT_AND_MASK_BITS(v, 19, 16) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_COLLOCATED_REF_IDX(v) \ ++ SHIFT_AND_MASK_BITS(v, 15, 12) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_COLOUR_PLANE_ID(v) \ ++ SHIFT_AND_MASK_BITS(v, 5, 4) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO0_SLICE_TYPE(v) \ ++ SHIFT_AND_MASK_BITS(v, 3, 2) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1 (VE_ENGINE_DEC_H265 + 0x24) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED BIT(23) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED BIT(22) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_TC_OFFSET_DIV2(v) \ ++ SHIFT_AND_MASK_BITS(v, 31, 28) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_BETA_OFFSET_DIV2(v) \ ++ SHIFT_AND_MASK_BITS(v, 27, 24) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_POC_BIGEST_IN_RPS_ST(v) \ ++ ((v) ? BIT(21) : 0) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_CR_QP_OFFSET(v) \ ++ SHIFT_AND_MASK_BITS(v, 20, 16) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_CB_QP_OFFSET(v) \ ++ SHIFT_AND_MASK_BITS(v, 12, 8) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO1_SLICE_QP_DELTA(v) \ ++ SHIFT_AND_MASK_BITS(v, 6, 0) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO2 (VE_ENGINE_DEC_H265 + 0x28) ++ ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO2_NUM_ENTRY_POINT_OFFSETS(v) \ ++ SHIFT_AND_MASK_BITS(v, 21, 8) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO2_CHROMA_LOG2_WEIGHT_DENOM(v) \ ++ SHIFT_AND_MASK_BITS(v, 6, 4) ++#define VE_DEC_H265_DEC_SLICE_HDR_INFO2_LUMA_LOG2_WEIGHT_DENOM(v) \ ++ SHIFT_AND_MASK_BITS(v, 2, 0) ++ ++#define VE_DEC_H265_DEC_CTB_ADDR (VE_ENGINE_DEC_H265 + 0x2c) ++ ++#define VE_DEC_H265_DEC_CTB_ADDR_Y(y) SHIFT_AND_MASK_BITS(y, 25, 16) ++#define VE_DEC_H265_DEC_CTB_ADDR_X(x) SHIFT_AND_MASK_BITS(x, 9, 0) ++ ++#define VE_DEC_H265_CTRL (VE_ENGINE_DEC_H265 + 0x30) ++ ++#define VE_DEC_H265_CTRL_DDR_CONSISTENCY_EN BIT(31) ++#define VE_DEC_H265_CTRL_STCD_EN BIT(25) ++#define VE_DEC_H265_CTRL_EPTB_DEC_BYPASS_EN BIT(24) ++#define VE_DEC_H265_CTRL_TQ_BYPASS_EN BIT(12) ++#define VE_DEC_H265_CTRL_VLD_BYPASS_EN BIT(11) ++#define VE_DEC_H265_CTRL_NCRI_CACHE_DISABLE BIT(10) ++#define VE_DEC_H265_CTRL_ROTATE_SCALE_OUT_EN BIT(9) ++#define VE_DEC_H265_CTRL_MC_NO_WRITEBACK BIT(8) ++#define VE_DEC_H265_CTRL_VLD_DATA_REQ_IRQ_EN BIT(2) ++#define VE_DEC_H265_CTRL_ERROR_IRQ_EN BIT(1) ++#define VE_DEC_H265_CTRL_FINISH_IRQ_EN BIT(0) ++#define VE_DEC_H265_CTRL_IRQ_MASK \ ++ (VE_DEC_H265_CTRL_FINISH_IRQ_EN | VE_DEC_H265_CTRL_ERROR_IRQ_EN | \ ++ VE_DEC_H265_CTRL_VLD_DATA_REQ_IRQ_EN) ++ ++#define VE_DEC_H265_TRIGGER (VE_ENGINE_DEC_H265 + 0x34) ++ ++#define VE_DEC_H265_TRIGGER_STCD_VC1 (0x02 << 4) ++#define VE_DEC_H265_TRIGGER_STCD_AVS (0x01 << 4) ++#define VE_DEC_H265_TRIGGER_STCD_HEVC (0x00 << 4) ++#define VE_DEC_H265_TRIGGER_DEC_SLICE (0x08 << 0) ++#define VE_DEC_H265_TRIGGER_INIT_SWDEC (0x07 << 0) ++#define VE_DEC_H265_TRIGGER_BYTE_ALIGN (0x06 << 0) ++#define VE_DEC_H265_TRIGGER_GET_VLCUE (0x05 << 0) ++#define VE_DEC_H265_TRIGGER_GET_VLCSE (0x04 << 0) ++#define VE_DEC_H265_TRIGGER_FLUSH_BITS (0x03 << 0) ++#define VE_DEC_H265_TRIGGER_GET_BITS (0x02 << 0) ++#define VE_DEC_H265_TRIGGER_SHOW_BITS (0x01 << 0) ++ ++#define VE_DEC_H265_STATUS (VE_ENGINE_DEC_H265 + 0x38) ++ ++#define VE_DEC_H265_STATUS_STCD BIT(24) ++#define VE_DEC_H265_STATUS_STCD_BUSY BIT(21) ++#define VE_DEC_H265_STATUS_WB_BUSY BIT(20) ++#define VE_DEC_H265_STATUS_BS_DMA_BUSY BIT(19) ++#define VE_DEC_H265_STATUS_IQIT_BUSY BIT(18) ++#define VE_DEC_H265_STATUS_INTER_BUSY BIT(17) ++#define VE_DEC_H265_STATUS_MORE_DATA BIT(16) ++#define VE_DEC_H265_STATUS_VLD_BUSY BIT(14) ++#define VE_DEC_H265_STATUS_DEBLOCKING_BUSY BIT(13) ++#define VE_DEC_H265_STATUS_DEBLOCKING_DRAM_BUSY BIT(12) ++#define VE_DEC_H265_STATUS_INTRA_BUSY BIT(11) ++#define VE_DEC_H265_STATUS_SAO_BUSY BIT(10) ++#define VE_DEC_H265_STATUS_MVP_BUSY BIT(9) ++#define VE_DEC_H265_STATUS_SWDEC_BUSY BIT(8) ++#define VE_DEC_H265_STATUS_OVER_TIME BIT(3) ++#define VE_DEC_H265_STATUS_VLD_DATA_REQ BIT(2) ++#define VE_DEC_H265_STATUS_ERROR BIT(1) ++#define VE_DEC_H265_STATUS_SUCCESS BIT(0) ++#define VE_DEC_H265_STATUS_STCD_TYPE_MASK GENMASK(23, 22) ++#define VE_DEC_H265_STATUS_CHECK_MASK \ ++ (VE_DEC_H265_STATUS_SUCCESS | VE_DEC_H265_STATUS_ERROR | \ ++ VE_DEC_H265_STATUS_VLD_DATA_REQ) ++#define VE_DEC_H265_STATUS_CHECK_ERROR \ ++ (VE_DEC_H265_STATUS_ERROR | VE_DEC_H265_STATUS_VLD_DATA_REQ) ++ ++#define VE_DEC_H265_DEC_CTB_NUM (VE_ENGINE_DEC_H265 + 0x3c) ++ ++#define VE_DEC_H265_BITS_ADDR (VE_ENGINE_DEC_H265 + 0x40) ++ ++#define VE_DEC_H265_BITS_ADDR_FIRST_SLICE_DATA BIT(30) ++#define VE_DEC_H265_BITS_ADDR_LAST_SLICE_DATA BIT(29) ++#define VE_DEC_H265_BITS_ADDR_VALID_SLICE_DATA BIT(28) ++#define VE_DEC_H265_BITS_ADDR_BASE(a) (((a) >> 8) & GENMASK(27, 0)) ++ ++#define VE_DEC_H265_BITS_OFFSET (VE_ENGINE_DEC_H265 + 0x44) ++#define VE_DEC_H265_BITS_LEN (VE_ENGINE_DEC_H265 + 0x48) ++ ++#define VE_DEC_H265_BITS_END_ADDR (VE_ENGINE_DEC_H265 + 0x4c) ++ ++#define VE_DEC_H265_BITS_END_ADDR_BASE(a) ((a) >> 8) ++ ++#define VE_DEC_H265_SDRT_CTRL (VE_ENGINE_DEC_H265 + 0x50) ++#define VE_DEC_H265_SDRT_LUMA_ADDR (VE_ENGINE_DEC_H265 + 0x54) ++#define VE_DEC_H265_SDRT_CHROMA_ADDR (VE_ENGINE_DEC_H265 + 0x58) ++ ++#define VE_DEC_H265_OUTPUT_FRAME_IDX (VE_ENGINE_DEC_H265 + 0x5c) ++ ++#define VE_DEC_H265_NEIGHBOR_INFO_ADDR (VE_ENGINE_DEC_H265 + 0x60) ++ ++#define VE_DEC_H265_NEIGHBOR_INFO_ADDR_BASE(a) ((a) >> 8) ++ ++#define VE_DEC_H265_ENTRY_POINT_OFFSET_ADDR (VE_ENGINE_DEC_H265 + 0x64) ++#define VE_DEC_H265_TILE_START_CTB (VE_ENGINE_DEC_H265 + 0x68) ++#define VE_DEC_H265_TILE_END_CTB (VE_ENGINE_DEC_H265 + 0x6c) ++ ++#define VE_DEC_H265_LOW_ADDR (VE_ENGINE_DEC_H265 + 0x80) ++ ++#define VE_DEC_H265_LOW_ADDR_PRIMARY_CHROMA(a) \ ++ SHIFT_AND_MASK_BITS(a, 31, 24) ++#define VE_DEC_H265_LOW_ADDR_SECONDARY_CHROMA(a) \ ++ SHIFT_AND_MASK_BITS(a, 23, 16) ++#define VE_DEC_H265_LOW_ADDR_ENTRY_POINTS_BUF(a) \ ++ SHIFT_AND_MASK_BITS(a, 7, 0) ++ ++#define VE_DEC_H265_SRAM_OFFSET (VE_ENGINE_DEC_H265 + 0xe0) ++ ++#define VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L0 0x00 ++#define VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L0 0x20 ++#define VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L1 0x60 ++#define VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_CHROMA_L1 0x80 ++#define VE_DEC_H265_SRAM_OFFSET_FRAME_INFO 0x400 ++#define VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT 0x20 ++#define VE_DEC_H265_SRAM_OFFSET_SCALING_LISTS 0x800 ++#define VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST0 0xc00 ++#define VE_DEC_H265_SRAM_OFFSET_REF_PIC_LIST1 0xc10 ++ ++#define VE_DEC_H265_SRAM_DATA (VE_ENGINE_DEC_H265 + 0xe4) ++ ++#define VE_DEC_H265_SRAM_DATA_ADDR_BASE(a) ((a) >> 8) ++#define VE_DEC_H265_SRAM_REF_PIC_LIST_LT_REF BIT(7) ++ + #define VE_H264_SPS 0x200 + #define VE_H264_SPS_MBS_ONLY BIT(18) + #define VE_H264_SPS_MB_ADAPTIVE_FRAME_FIELD BIT(17) +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index f745f66c4440..cc15a5cf107d 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -41,6 +41,11 @@ static struct cedrus_format cedrus_formats[] = { + .pixelformat = V4L2_PIX_FMT_H264_SLICE, + .directions = CEDRUS_DECODE_SRC, + }, ++ { ++ .pixelformat = V4L2_PIX_FMT_HEVC_SLICE, ++ .directions = CEDRUS_DECODE_SRC, ++ .capabilities = CEDRUS_CAPABILITY_H265_DEC, ++ }, + { + .pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12, + .directions = CEDRUS_DECODE_DST, +@@ -102,6 +107,7 @@ void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_MPEG2_SLICE: + case V4L2_PIX_FMT_H264_SLICE: ++ case V4L2_PIX_FMT_HEVC_SLICE: + /* Zero bytes per line for encoded source. */ + bytesperline = 0; + /* Choose some minimum size since this can't be 0 */ +@@ -439,6 +445,10 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) + ctx->current_codec = CEDRUS_CODEC_H264; + break; + ++ case V4L2_PIX_FMT_HEVC_SLICE: ++ ctx->current_codec = CEDRUS_CODEC_H265; ++ break; ++ + default: + return -EINVAL; + } +-- +2.23.0 diff --git a/patch/kernel/sunxi-current/0615-media-cedrus-Fix-decoding-for-some-H264-videos.patch b/patch/kernel/sunxi-current/0615-media-cedrus-Fix-decoding-for-some-H264-videos.patch new file mode 100644 index 000000000..9cbeb5350 --- /dev/null +++ b/patch/kernel/sunxi-current/0615-media-cedrus-Fix-decoding-for-some-H264-videos.patch @@ -0,0 +1,110 @@ + +From b4e33e09e7938513bfaba034731c7e84e73c6a5b Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 26 Oct 2019 09:27:51 +0200 +Subject: [PATCH] media: cedrus: Fix decoding for some H264 videos + +It seems that for some H264 videos at least one bitstream parsing +trigger must be called in order to be decoded correctly. There is no +explanation why this helps, but it was observed that two sample videos +with this fix are now decoded correctly and there is no regression with +others. + +Acked-by: Paul Kocialkowski +Signed-off-by: Jernej Skrabec +Signed-off-by: Hans Verkuil +--- + .../staging/media/sunxi/cedrus/cedrus_h264.c | 30 +++++++++++++++++-- + .../staging/media/sunxi/cedrus/cedrus_regs.h | 3 ++ + 2 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +index cd85668f9c80..db336449c4f2 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +@@ -6,6 +6,7 @@ + * Copyright (c) 2018 Bootlin + */ + ++#include + #include + + #include +@@ -289,6 +290,28 @@ static void cedrus_write_pred_weight_table(struct cedrus_ctx *ctx, + } + } + ++/* ++ * It turns out that using VE_H264_VLD_OFFSET to skip bits is not reliable. In ++ * rare cases frame is not decoded correctly. However, setting offset to 0 and ++ * skipping appropriate amount of bits with flush bits trigger always works. ++ */ ++static void cedrus_skip_bits(struct cedrus_dev *dev, int num) ++{ ++ int count = 0; ++ ++ while (count < num) { ++ int tmp = min(num - count, 32); ++ ++ cedrus_write(dev, VE_H264_TRIGGER_TYPE, ++ VE_H264_TRIGGER_TYPE_FLUSH_BITS | ++ VE_H264_TRIGGER_TYPE_N_BITS(tmp)); ++ while (cedrus_read(dev, VE_H264_STATUS) & VE_H264_STATUS_VLD_BUSY) ++ udelay(1); ++ ++ count += tmp; ++ } ++} ++ + static void cedrus_set_params(struct cedrus_ctx *ctx, + struct cedrus_run *run) + { +@@ -299,14 +322,13 @@ static void cedrus_set_params(struct cedrus_ctx *ctx, + struct vb2_buffer *src_buf = &run->src->vb2_buf; + struct cedrus_dev *dev = ctx->dev; + dma_addr_t src_buf_addr; +- u32 offset = slice->header_bit_size; +- u32 len = (slice->size * 8) - offset; ++ u32 len = slice->size * 8; + unsigned int pic_width_in_mbs; + bool mbaff_pic; + u32 reg; + + cedrus_write(dev, VE_H264_VLD_LEN, len); +- cedrus_write(dev, VE_H264_VLD_OFFSET, offset); ++ cedrus_write(dev, VE_H264_VLD_OFFSET, 0); + + src_buf_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + cedrus_write(dev, VE_H264_VLD_END, +@@ -325,6 +347,8 @@ static void cedrus_set_params(struct cedrus_ctx *ctx, + cedrus_write(dev, VE_H264_TRIGGER_TYPE, + VE_H264_TRIGGER_TYPE_INIT_SWDEC); + ++ cedrus_skip_bits(dev, slice->header_bit_size); ++ + if (((pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) && + (slice->slice_type == V4L2_H264_SLICE_TYPE_P || + slice->slice_type == V4L2_H264_SLICE_TYPE_SP)) || +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index 6fc28d21a6c7..4275a307d282 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -541,13 +541,16 @@ + VE_H264_CTRL_SLICE_DECODE_INT) + + #define VE_H264_TRIGGER_TYPE 0x224 ++#define VE_H264_TRIGGER_TYPE_N_BITS(x) (((x) & 0x3f) << 8) + #define VE_H264_TRIGGER_TYPE_AVC_SLICE_DECODE (8 << 0) + #define VE_H264_TRIGGER_TYPE_INIT_SWDEC (7 << 0) ++#define VE_H264_TRIGGER_TYPE_FLUSH_BITS (3 << 0) + + #define VE_H264_STATUS 0x228 + #define VE_H264_STATUS_VLD_DATA_REQ_INT VE_H264_CTRL_VLD_DATA_REQ_INT + #define VE_H264_STATUS_DECODE_ERR_INT VE_H264_CTRL_DECODE_ERR_INT + #define VE_H264_STATUS_SLICE_DECODE_INT VE_H264_CTRL_SLICE_DECODE_INT ++#define VE_H264_STATUS_VLD_BUSY BIT(8) + + #define VE_H264_STATUS_INT_MASK VE_H264_CTRL_INT_MASK + +-- +2.24.0 diff --git a/patch/kernel/sunxi-current/0617-media-cedrus-Use-correct-H264-8x8-scaling-list.patch b/patch/kernel/sunxi-current/0617-media-cedrus-Use-correct-H264-8x8-scaling-list.patch new file mode 100644 index 000000000..2bac163d6 --- /dev/null +++ b/patch/kernel/sunxi-current/0617-media-cedrus-Use-correct-H264-8x8-scaling-list.patch @@ -0,0 +1,34 @@ + +From e2c02ba9bd0068b00628d7874d8a8d1eb5168177 Mon Sep 17 00:00:00 2001 +From: Jonas Karlman +Date: Tue, 29 Oct 2019 00:00:52 +0000 +Subject: [PATCH] media: cedrus: Use correct H264 8x8 scaling list + +Documentation now defines the expected order of scaling lists, +change to use correct indices. + +Fixes: 6eb9b758e307 ("media: cedrus: Add H264 decoding support") +Signed-off-by: Jonas Karlman +Reviewed-by: Philipp Zabel +Signed-off-by: Hans Verkuil +--- + drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +index 7487f6ab7576..74e4c5e3894e 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +@@ -245,8 +245,8 @@ static void cedrus_write_scaling_lists(struct cedrus_ctx *ctx, + sizeof(scaling->scaling_list_8x8[0])); + + cedrus_h264_write_sram(dev, CEDRUS_SRAM_H264_SCALING_LIST_8x8_1, +- scaling->scaling_list_8x8[3], +- sizeof(scaling->scaling_list_8x8[3])); ++ scaling->scaling_list_8x8[1], ++ sizeof(scaling->scaling_list_8x8[1])); + + cedrus_h264_write_sram(dev, CEDRUS_SRAM_H264_SCALING_LIST_4x4, + scaling->scaling_list_4x4, +-- +2.24.0 diff --git a/patch/kernel/sunxi-current/0619-media-cedrus-Properly-signa-size-in-mode-register.patch b/patch/kernel/sunxi-current/0619-media-cedrus-Properly-signa-size-in-mode-register.patch new file mode 100644 index 000000000..5d0c0fd2f --- /dev/null +++ b/patch/kernel/sunxi-current/0619-media-cedrus-Properly-signa-size-in-mode-register.patch @@ -0,0 +1,118 @@ + +From 3aef46bd5bf24a845e05d2531ed61f53ee8c7797 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 10 Nov 2019 07:30:01 +0100 +Subject: [PATCH 1/3] media: cedrus: Properly signal size in mode register + +Mode register also holds information if video width is bigger than 2048 +and if it is equal to 4096. + +Rework cedrus_engine_enable() to properly signal this properties. + +Signed-off-by: Jernej Skrabec +Acked-by: Paul Kocialkowski +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 2 +- + drivers/staging/media/sunxi/cedrus/cedrus_h265.c | 2 +- + drivers/staging/media/sunxi/cedrus/cedrus_hw.c | 9 +++++++-- + drivers/staging/media/sunxi/cedrus/cedrus_hw.h | 2 +- + drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c | 2 +- + drivers/staging/media/sunxi/cedrus/cedrus_regs.h | 2 ++ + 6 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +index 74e4c5e3894e..8a09a08b1af2 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +@@ -485,7 +485,7 @@ static void cedrus_h264_setup(struct cedrus_ctx *ctx, + { + struct cedrus_dev *dev = ctx->dev; + +- cedrus_engine_enable(dev, CEDRUS_CODEC_H264); ++ cedrus_engine_enable(ctx, CEDRUS_CODEC_H264); + + cedrus_write(dev, VE_H264_SDROT_CTRL, 0); + cedrus_write(dev, VE_H264_EXTRA_BUFFER1, +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 9bc921866f70..6945dc74e1d7 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -276,7 +276,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + } + + /* Activate H265 engine. */ +- cedrus_engine_enable(dev, CEDRUS_CODEC_H265); ++ cedrus_engine_enable(ctx, CEDRUS_CODEC_H265); + + /* Source offset and length in bits. */ + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +index 93347d3ba360..daf5f244f93b 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +@@ -30,7 +30,7 @@ + #include "cedrus_hw.h" + #include "cedrus_regs.h" + +-int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec) ++int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec) + { + u32 reg = 0; + +@@ -58,7 +58,12 @@ int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec) + return -EINVAL; + } + +- cedrus_write(dev, VE_MODE, reg); ++ if (ctx->src_fmt.width == 4096) ++ reg |= VE_MODE_PIC_WIDTH_IS_4096; ++ if (ctx->src_fmt.width > 2048) ++ reg |= VE_MODE_PIC_WIDTH_MORE_2048; ++ ++ cedrus_write(ctx->dev, VE_MODE, reg); + + return 0; + } +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h +index 27d0882397aa..604ff932fbf5 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h +@@ -16,7 +16,7 @@ + #ifndef _CEDRUS_HW_H_ + #define _CEDRUS_HW_H_ + +-int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec); ++int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec); + void cedrus_engine_disable(struct cedrus_dev *dev); + + void cedrus_dst_format_set(struct cedrus_dev *dev, +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +index 13c34927bad5..8bcd6b8f9e2d 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +@@ -96,7 +96,7 @@ static void cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) + quantization = run->mpeg2.quantization; + + /* Activate MPEG engine. */ +- cedrus_engine_enable(dev, CEDRUS_CODEC_MPEG2); ++ cedrus_engine_enable(ctx, CEDRUS_CODEC_MPEG2); + + /* Set intra quantization matrix. */ + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index 4275a307d282..ace3d49fcd82 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -35,6 +35,8 @@ + + #define VE_MODE 0x00 + ++#define VE_MODE_PIC_WIDTH_IS_4096 BIT(22) ++#define VE_MODE_PIC_WIDTH_MORE_2048 BIT(21) + #define VE_MODE_REC_WR_MODE_2MB (0x01 << 20) + #define VE_MODE_REC_WR_MODE_1MB (0x00 << 20) + #define VE_MODE_DDR_MODE_BW_128 (0x03 << 16) +-- +2.24.0 diff --git a/patch/kernel/sunxi-current/0620-media-cedrus-Fix-H264-4k-support.patch b/patch/kernel/sunxi-current/0620-media-cedrus-Fix-H264-4k-support.patch new file mode 100644 index 000000000..60ff338ec --- /dev/null +++ b/patch/kernel/sunxi-current/0620-media-cedrus-Fix-H264-4k-support.patch @@ -0,0 +1,211 @@ + +From 03e612e701a61aa9cc9fd8e25cd47d8d685ef675 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 6 Nov 2019 22:05:37 +0100 +Subject: [PATCH 2/3] media: cedrus: Fix H264 4k support + +H264 decoder needs additional or bigger buffers in order to decode 4k +videos. + +Signed-off-by: Jernej Skrabec +Acked-by: Paul Kocialkowski +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/staging/media/sunxi/cedrus/cedrus.h | 7 ++ + .../staging/media/sunxi/cedrus/cedrus_h264.c | 91 +++++++++++++++++-- + .../staging/media/sunxi/cedrus/cedrus_regs.h | 11 +++ + 3 files changed, 101 insertions(+), 8 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index c45fb9a7ad07..96765555ab8a 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -116,8 +116,15 @@ struct cedrus_ctx { + ssize_t mv_col_buf_size; + void *pic_info_buf; + dma_addr_t pic_info_buf_dma; ++ ssize_t pic_info_buf_size; + void *neighbor_info_buf; + dma_addr_t neighbor_info_buf_dma; ++ void *deblk_buf; ++ dma_addr_t deblk_buf_dma; ++ ssize_t deblk_buf_size; ++ void *intra_pred_buf; ++ dma_addr_t intra_pred_buf_dma; ++ ssize_t intra_pred_buf_size; + } h264; + struct { + void *mv_col_buf; +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +index 8a09a08b1af2..bfb4a4820a67 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +@@ -39,7 +39,7 @@ struct cedrus_h264_sram_ref_pic { + #define CEDRUS_H264_FRAME_NUM 18 + + #define CEDRUS_NEIGHBOR_INFO_BUF_SIZE (16 * SZ_1K) +-#define CEDRUS_PIC_INFO_BUF_SIZE (128 * SZ_1K) ++#define CEDRUS_MIN_PIC_INFO_BUF_SIZE (130 * SZ_1K) + + static void cedrus_h264_write_sram(struct cedrus_dev *dev, + enum cedrus_h264_sram_off off, +@@ -342,6 +342,20 @@ static void cedrus_set_params(struct cedrus_ctx *ctx, + VE_H264_VLD_ADDR_FIRST | VE_H264_VLD_ADDR_VALID | + VE_H264_VLD_ADDR_LAST); + ++ if (ctx->src_fmt.width > 2048) { ++ cedrus_write(dev, VE_BUF_CTRL, ++ VE_BUF_CTRL_INTRAPRED_MIXED_RAM | ++ VE_BUF_CTRL_DBLK_MIXED_RAM); ++ cedrus_write(dev, VE_DBLK_DRAM_BUF_ADDR, ++ ctx->codec.h264.deblk_buf_dma); ++ cedrus_write(dev, VE_INTRAPRED_DRAM_BUF_ADDR, ++ ctx->codec.h264.intra_pred_buf_dma); ++ } else { ++ cedrus_write(dev, VE_BUF_CTRL, ++ VE_BUF_CTRL_INTRAPRED_INT_SRAM | ++ VE_BUF_CTRL_DBLK_INT_SRAM); ++ } ++ + /* + * FIXME: Since the bitstream parsing is done in software, and + * in userspace, this shouldn't be needed anymore. But it +@@ -502,18 +516,30 @@ static void cedrus_h264_setup(struct cedrus_ctx *ctx, + static int cedrus_h264_start(struct cedrus_ctx *ctx) + { + struct cedrus_dev *dev = ctx->dev; ++ unsigned int pic_info_size; + unsigned int field_size; + unsigned int mv_col_size; + int ret; + ++ /* Formula for picture buffer size is taken from CedarX source. */ ++ ++ if (ctx->src_fmt.width > 2048) ++ pic_info_size = CEDRUS_H264_FRAME_NUM * 0x4000; ++ else ++ pic_info_size = CEDRUS_H264_FRAME_NUM * 0x1000; ++ + /* +- * FIXME: It seems that the H6 cedarX code is using a formula +- * here based on the size of the frame, while all the older +- * code is using a fixed size, so that might need to be +- * changed at some point. ++ * FIXME: If V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is set, ++ * there is no need to multiply by 2. + */ ++ pic_info_size += ctx->src_fmt.height * 2 * 64; ++ ++ if (pic_info_size < CEDRUS_MIN_PIC_INFO_BUF_SIZE) ++ pic_info_size = CEDRUS_MIN_PIC_INFO_BUF_SIZE; ++ ++ ctx->codec.h264.pic_info_buf_size = pic_info_size; + ctx->codec.h264.pic_info_buf = +- dma_alloc_coherent(dev->dev, CEDRUS_PIC_INFO_BUF_SIZE, ++ dma_alloc_coherent(dev->dev, ctx->codec.h264.pic_info_buf_size, + &ctx->codec.h264.pic_info_buf_dma, + GFP_KERNEL); + if (!ctx->codec.h264.pic_info_buf) +@@ -566,15 +592,56 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) + goto err_neighbor_buf; + } + ++ if (ctx->src_fmt.width > 2048) { ++ /* ++ * Formulas for deblock and intra prediction buffer sizes ++ * are taken from CedarX source. ++ */ ++ ++ ctx->codec.h264.deblk_buf_size = ++ ALIGN(ctx->src_fmt.width, 32) * 12; ++ ctx->codec.h264.deblk_buf = ++ dma_alloc_coherent(dev->dev, ++ ctx->codec.h264.deblk_buf_size, ++ &ctx->codec.h264.deblk_buf_dma, ++ GFP_KERNEL); ++ if (!ctx->codec.h264.deblk_buf) { ++ ret = -ENOMEM; ++ goto err_mv_col_buf; ++ } ++ ++ ctx->codec.h264.intra_pred_buf_size = ++ ALIGN(ctx->src_fmt.width, 64) * 5; ++ ctx->codec.h264.intra_pred_buf = ++ dma_alloc_coherent(dev->dev, ++ ctx->codec.h264.intra_pred_buf_size, ++ &ctx->codec.h264.intra_pred_buf_dma, ++ GFP_KERNEL); ++ if (!ctx->codec.h264.intra_pred_buf) { ++ ret = -ENOMEM; ++ goto err_deblk_buf; ++ } ++ } ++ + return 0; + ++err_deblk_buf: ++ dma_free_coherent(dev->dev, ctx->codec.h264.deblk_buf_size, ++ ctx->codec.h264.deblk_buf, ++ ctx->codec.h264.deblk_buf_dma); ++ ++err_mv_col_buf: ++ dma_free_coherent(dev->dev, ctx->codec.h264.mv_col_buf_size, ++ ctx->codec.h264.mv_col_buf, ++ ctx->codec.h264.mv_col_buf_dma); ++ + err_neighbor_buf: + dma_free_coherent(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h264.neighbor_info_buf, + ctx->codec.h264.neighbor_info_buf_dma); + + err_pic_buf: +- dma_free_coherent(dev->dev, CEDRUS_PIC_INFO_BUF_SIZE, ++ dma_free_coherent(dev->dev, ctx->codec.h264.pic_info_buf_size, + ctx->codec.h264.pic_info_buf, + ctx->codec.h264.pic_info_buf_dma); + return ret; +@@ -590,9 +657,17 @@ static void cedrus_h264_stop(struct cedrus_ctx *ctx) + dma_free_coherent(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h264.neighbor_info_buf, + ctx->codec.h264.neighbor_info_buf_dma); +- dma_free_coherent(dev->dev, CEDRUS_PIC_INFO_BUF_SIZE, ++ dma_free_coherent(dev->dev, ctx->codec.h264.pic_info_buf_size, + ctx->codec.h264.pic_info_buf, + ctx->codec.h264.pic_info_buf_dma); ++ if (ctx->codec.h264.deblk_buf_size) ++ dma_free_coherent(dev->dev, ctx->codec.h264.deblk_buf_size, ++ ctx->codec.h264.deblk_buf, ++ ctx->codec.h264.deblk_buf_dma); ++ if (ctx->codec.h264.intra_pred_buf_size) ++ dma_free_coherent(dev->dev, ctx->codec.h264.intra_pred_buf_size, ++ ctx->codec.h264.intra_pred_buf, ++ ctx->codec.h264.intra_pred_buf_dma); + } + + static void cedrus_h264_trigger(struct cedrus_ctx *ctx) +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index ace3d49fcd82..7beb03d3bb39 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -46,6 +46,17 @@ + #define VE_MODE_DEC_H264 (0x01 << 0) + #define VE_MODE_DEC_MPEG (0x00 << 0) + ++#define VE_BUF_CTRL 0x50 ++ ++#define VE_BUF_CTRL_INTRAPRED_EXT_RAM (0x02 << 2) ++#define VE_BUF_CTRL_INTRAPRED_MIXED_RAM (0x01 << 2) ++#define VE_BUF_CTRL_INTRAPRED_INT_SRAM (0x00 << 2) ++#define VE_BUF_CTRL_DBLK_EXT_RAM (0x02 << 0) ++#define VE_BUF_CTRL_DBLK_MIXED_RAM (0x01 << 0) ++#define VE_BUF_CTRL_DBLK_INT_SRAM (0x00 << 0) ++ ++#define VE_DBLK_DRAM_BUF_ADDR 0x54 ++#define VE_INTRAPRED_DRAM_BUF_ADDR 0x58 + #define VE_PRIMARY_CHROMA_BUF_LEN 0xc4 + #define VE_PRIMARY_FB_LINE_STRIDE 0xc8 + +-- +2.24.0 + diff --git a/patch/kernel/sunxi-current/0621-media-cedrus-Increase-maximum-supported-size.patch b/patch/kernel/sunxi-current/0621-media-cedrus-Increase-maximum-supported-size.patch new file mode 100644 index 000000000..d1e9cfef2 --- /dev/null +++ b/patch/kernel/sunxi-current/0621-media-cedrus-Increase-maximum-supported-size.patch @@ -0,0 +1,36 @@ + +From 0b3e5c15f9cb8b56599c50e6bf4f46ee1c1253bc Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 6 Nov 2019 22:05:38 +0100 +Subject: [PATCH 3/3] media: cedrus: Increase maximum supported size + +There are few variations of 4k resolutions. The biggest one is +4096x2304 which is also supported by HW. It has also nice property that +both width and size are divisible by maximum HEVC CTB size, which is 64. + +Signed-off-by: Jernej Skrabec +Acked-by: Paul Kocialkowski +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/staging/media/sunxi/cedrus/cedrus_video.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index cc15a5cf107d..15cf1f10221b 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -29,8 +29,8 @@ + + #define CEDRUS_MIN_WIDTH 16U + #define CEDRUS_MIN_HEIGHT 16U +-#define CEDRUS_MAX_WIDTH 3840U +-#define CEDRUS_MAX_HEIGHT 2160U ++#define CEDRUS_MAX_WIDTH 4096U ++#define CEDRUS_MAX_HEIGHT 2304U + + static struct cedrus_format cedrus_formats[] = { + { +-- +2.24.0 + diff --git a/patch/kernel/sunxi-current/0622-media-cedrus-improvements.patch b/patch/kernel/sunxi-current/0622-media-cedrus-improvements.patch new file mode 100644 index 000000000..d188e4bf4 --- /dev/null +++ b/patch/kernel/sunxi-current/0622-media-cedrus-improvements.patch @@ -0,0 +1,1395 @@ +From 4d25b2ae236bf42f5f9ef1d57cbc2523222a4422 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 26 Oct 2019 13:55:15 +0200 +Subject: [PATCH 04/14] media: uapi: hevc: Add scaling matrix control + +HEVC has a scaling matrix concept. Add support for it. + +Signed-off-by: Jernej Skrabec +--- + .../media/uapi/v4l/ext-ctrls-codec.rst | 41 +++++++++++++++++++ + .../media/uapi/v4l/pixfmt-compressed.rst | 1 + + drivers/media/v4l2-core/v4l2-ctrls.c | 10 +++++ + include/media/hevc-ctrls.h | 11 +++++ + 4 files changed, 63 insertions(+) + +diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +index a1209f68c5e8..382e85e16444 100644 +--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst ++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +@@ -4176,6 +4176,47 @@ enum v4l2_mpeg_video_hevc_size_of_length_field - + - ``padding[6]`` + - Applications and drivers must set this to zero. + ++``V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX (struct)`` ++ Specifies the scaling matrix (as extracted from the bitstream) for ++ the associated HEVC slice data. The bitstream parameters are ++ defined according to :ref:`hevc`, section 7.4.5 "Scaling list ++ data semantics". For further documentation, refer to the above ++ specification, unless there is an explicit comment stating ++ otherwise. ++ ++ .. note:: ++ ++ This compound control is not yet part of the public kernel API and ++ it is expected to change. ++ ++.. c:type:: v4l2_ctrl_hevc_scaling_matrix ++ ++.. cssclass:: longtable ++ ++.. flat-table:: struct v4l2_ctrl_hevc_scaling_matrix ++ :header-rows: 0 ++ :stub-columns: 0 ++ :widths: 1 1 2 ++ ++ * - __u8 ++ - ``scaling_list_4x4[6][16]`` ++ - ++ * - __u8 ++ - ``scaling_list_8x8[6][64]`` ++ - ++ * - __u8 ++ - ``scaling_list_16x16[6][64]`` ++ - ++ * - __u8 ++ - ``scaling_list_32x32[2][64]`` ++ - ++ * - __u8 ++ - ``scaling_list_dc_coef_16x16[6]`` ++ - ++ * - __u8 ++ - ``scaling_list_dc_coef_32x32[2]`` ++ - ++ + ``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (enum)`` + Specifies the decoding mode to use. Currently exposes slice-based and + frame-based decoding but new modes might be added later on. + +diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c +index b4caf2d4d076..2803165cbc6a 100644 +--- a/drivers/media/v4l2-core/v4l2-ctrls.c ++++ b/drivers/media/v4l2-core/v4l2-ctrls.c +@@ -975,6 +975,7 @@ const char *v4l2_ctrl_get_name(u32 id) + case V4L2_CID_MPEG_VIDEO_HEVC_SPS: return "HEVC Sequence Parameter Set"; + case V4L2_CID_MPEG_VIDEO_HEVC_PPS: return "HEVC Picture Parameter Set"; + case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: return "HEVC Slice Parameters"; ++ case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX: return "HEVC Scaling Matrix"; + case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: return "HEVC Decode Mode"; + case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: return "HEVC Start Code"; + +@@ -1407,6 +1408,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, + case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS: + *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS; + break; ++ case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX: ++ *type = V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX; ++ break; + case V4L2_CID_UNIT_CELL_SIZE: + *type = V4L2_CTRL_TYPE_AREA; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; +@@ -1856,6 +1860,9 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, + zero_padding(*p_hevc_slice_params); + break; + ++ case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX: ++ break; ++ + case V4L2_CTRL_TYPE_AREA: + area = p; + if (!area->width || !area->height) +@@ -2545,6 +2552,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, + case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: + elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params); + break; ++ case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX: ++ elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix); ++ break; + case V4L2_CTRL_TYPE_AREA: + elem_size = sizeof(struct v4l2_area); + break; +diff --git a/include/media/hevc-ctrls.h b/include/media/hevc-ctrls.h +index 1009cf0891cc..1592e52c3614 100644 +--- a/include/media/hevc-ctrls.h ++++ b/include/media/hevc-ctrls.h +@@ -19,6 +19,7 @@ + #define V4L2_CID_MPEG_VIDEO_HEVC_SPS (V4L2_CID_MPEG_BASE + 1008) + #define V4L2_CID_MPEG_VIDEO_HEVC_PPS (V4L2_CID_MPEG_BASE + 1009) + #define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_MPEG_BASE + 1010) ++#define V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX (V4L2_CID_MPEG_BASE + 1011) + #define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (V4L2_CID_MPEG_BASE + 1015) + #define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (V4L2_CID_MPEG_BASE + 1016) + +@@ -26,6 +27,7 @@ + #define V4L2_CTRL_TYPE_HEVC_SPS 0x0120 + #define V4L2_CTRL_TYPE_HEVC_PPS 0x0121 + #define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122 ++#define V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX 0x0123 + + enum v4l2_mpeg_video_hevc_decode_mode { + V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED, +@@ -209,4 +211,13 @@ struct v4l2_ctrl_hevc_slice_params { + __u64 flags; + }; + ++struct v4l2_ctrl_hevc_scaling_matrix { ++ __u8 scaling_list_4x4[6][16]; ++ __u8 scaling_list_8x8[6][64]; ++ __u8 scaling_list_16x16[6][64]; ++ __u8 scaling_list_32x32[2][64]; ++ __u8 scaling_list_dc_coef_16x16[6]; ++ __u8 scaling_list_dc_coef_32x32[2]; ++}; ++ + #endif +-- +2.24.0 + + +From 000a33e20cc53fa82e3bc116d411135e93d5ab95 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 26 Oct 2019 13:58:49 +0200 +Subject: [PATCH 05/14] media: cedrus: hevc: Add support for scaling matrix + +HEVC frames may use scaling list feature. Add support for it. + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus.c | 7 ++ + drivers/staging/media/sunxi/cedrus/cedrus.h | 1 + + .../staging/media/sunxi/cedrus/cedrus_dec.c | 2 + + .../staging/media/sunxi/cedrus/cedrus_h265.c | 70 ++++++++++++++++++- + .../staging/media/sunxi/cedrus/cedrus_regs.h | 2 + + 5 files changed, 81 insertions(+), 1 deletion(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c +index c6ddd46eff82..bf68bc6b20c8 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.c +@@ -116,6 +116,13 @@ static const struct cedrus_control cedrus_controls[] = { + .codec = CEDRUS_CODEC_H265, + .required = true, + }, ++ { ++ .cfg = { ++ .id = V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX, ++ }, ++ .codec = CEDRUS_CODEC_H265, ++ .required = true, ++ }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE, +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index 96765555ab8a..d945f4f0ff2d 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -73,6 +73,7 @@ struct cedrus_h265_run { + const struct v4l2_ctrl_hevc_sps *sps; + const struct v4l2_ctrl_hevc_pps *pps; + const struct v4l2_ctrl_hevc_slice_params *slice_params; ++ const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix; + }; + + struct cedrus_run { +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +index 4a2fc33a1d79..327ed6c264dc 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +@@ -66,6 +66,8 @@ void cedrus_device_run(void *priv) + V4L2_CID_MPEG_VIDEO_HEVC_PPS); + run.h265.slice_params = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS); ++ run.h265.scaling_matrix = cedrus_find_control_data(ctx, ++ V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX); + break; + + default: +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 6945dc74e1d7..888bfd5ca224 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -220,6 +220,69 @@ static void cedrus_h265_pred_weight_write(struct cedrus_dev *dev, + } + } + ++static void cedrus_h265_write_scaling_list(struct cedrus_ctx *ctx, ++ struct cedrus_run *run) ++{ ++ const struct v4l2_ctrl_hevc_scaling_matrix *scaling; ++ struct cedrus_dev *dev = ctx->dev; ++ u32 i, j, k, val; ++ ++ scaling = run->h265.scaling_matrix; ++ ++ cedrus_write(dev, VE_DEC_H265_SCALING_LIST_DC_COEF0, ++ (scaling->scaling_list_dc_coef_32x32[1] << 24) | ++ (scaling->scaling_list_dc_coef_32x32[0] << 16) | ++ (scaling->scaling_list_dc_coef_16x16[1] << 8) | ++ (scaling->scaling_list_dc_coef_16x16[0] << 0)); ++ ++ cedrus_write(dev, VE_DEC_H265_SCALING_LIST_DC_COEF1, ++ (scaling->scaling_list_dc_coef_16x16[5] << 24) | ++ (scaling->scaling_list_dc_coef_16x16[4] << 16) | ++ (scaling->scaling_list_dc_coef_16x16[3] << 8) | ++ (scaling->scaling_list_dc_coef_16x16[2] << 0)); ++ ++ cedrus_h265_sram_write_offset(dev, VE_DEC_H265_SRAM_OFFSET_SCALING_LISTS); ++ ++ for (i = 0; i < 6; i++) ++ for (j = 0; j < 8; j++) ++ for (k = 0; k < 8; k += 4) { ++ val = ((u32)scaling->scaling_list_8x8[i][j + (k + 3) * 8] << 24) | ++ ((u32)scaling->scaling_list_8x8[i][j + (k + 2) * 8] << 16) | ++ ((u32)scaling->scaling_list_8x8[i][j + (k + 1) * 8] << 8) | ++ scaling->scaling_list_8x8[i][j + k * 8]; ++ cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val); ++ } ++ ++ for (i = 0; i < 2; i++) ++ for (j = 0; j < 8; j++) ++ for (k = 0; k < 8; k += 4) { ++ val = ((u32)scaling->scaling_list_32x32[i][j + (k + 3) * 8] << 24) | ++ ((u32)scaling->scaling_list_32x32[i][j + (k + 2) * 8] << 16) | ++ ((u32)scaling->scaling_list_32x32[i][j + (k + 1) * 8] << 8) | ++ scaling->scaling_list_32x32[i][j + k * 8]; ++ cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val); ++ } ++ ++ for (i = 0; i < 6; i++) ++ for (j = 0; j < 8; j++) ++ for (k = 0; k < 8; k += 4) { ++ val = ((u32)scaling->scaling_list_16x16[i][j + (k + 3) * 8] << 24) | ++ ((u32)scaling->scaling_list_16x16[i][j + (k + 2) * 8] << 16) | ++ ((u32)scaling->scaling_list_16x16[i][j + (k + 1) * 8] << 8) | ++ scaling->scaling_list_16x16[i][j + k * 8]; ++ cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val); ++ } ++ ++ for (i = 0; i < 6; i++) ++ for (j = 0; j < 4; j++) { ++ val = ((u32)scaling->scaling_list_4x4[i][j + 12] << 24) | ++ ((u32)scaling->scaling_list_4x4[i][j + 8] << 16) | ++ ((u32)scaling->scaling_list_4x4[i][j + 4] << 8) | ++ scaling->scaling_list_4x4[i][j]; ++ cedrus_write(dev, VE_DEC_H265_SRAM_DATA, val); ++ } ++} ++ + static void cedrus_h265_setup(struct cedrus_ctx *ctx, + struct cedrus_run *run) + { +@@ -499,7 +562,12 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + + /* Scaling list. */ + +- reg = VE_DEC_H265_SCALING_LIST_CTRL0_DEFAULT; ++ if (sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED) { ++ cedrus_h265_write_scaling_list(ctx, run); ++ reg = VE_DEC_H265_SCALING_LIST_CTRL0_FLAG_ENABLED; ++ } else { ++ reg = VE_DEC_H265_SCALING_LIST_CTRL0_DEFAULT; ++ } + cedrus_write(dev, VE_DEC_H265_SCALING_LIST_CTRL0, reg); + + /* Neightbor information address. */ +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index 7beb03d3bb39..0d9449fe2b28 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -492,6 +492,8 @@ + #define VE_DEC_H265_ENTRY_POINT_OFFSET_ADDR (VE_ENGINE_DEC_H265 + 0x64) + #define VE_DEC_H265_TILE_START_CTB (VE_ENGINE_DEC_H265 + 0x68) + #define VE_DEC_H265_TILE_END_CTB (VE_ENGINE_DEC_H265 + 0x6c) ++#define VE_DEC_H265_SCALING_LIST_DC_COEF0 (VE_ENGINE_DEC_H265 + 0x78) ++#define VE_DEC_H265_SCALING_LIST_DC_COEF1 (VE_ENGINE_DEC_H265 + 0x7c) + + #define VE_DEC_H265_LOW_ADDR (VE_ENGINE_DEC_H265 + 0x80) + +-- +2.24.0 + + +From 48f70ba58e4df3bd9b9cb9c7969ba93f95e25e75 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 26 Oct 2019 15:42:28 +0200 +Subject: [PATCH 06/14] media: uapi: hevc: Add segment address field + +If HEVC frame consists of multiple slices, segment address has to be +known in order to properly decode it. + +Add segment address field to slice parameters. + +Signed-off-by: Jernej Skrabec +--- + Documentation/media/uapi/v4l/ext-ctrls-codec.rst | 5 ++++- + include/media/hevc-ctrls.h | 5 ++++- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +index 382e85e16444..99e4a7099614 100644 +--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst ++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +@@ -3971,6 +3971,9 @@ enum v4l2_mpeg_video_hevc_size_of_length_field - + * - __u32 + - ``data_bit_offset`` + - Offset (in bits) to the video data in the current slice data. ++ * - __u32 ++ - ``slice_segment_addr`` ++ - + * - __u8 + - ``nal_unit_type`` + - +@@ -4048,7 +4051,7 @@ enum v4l2_mpeg_video_hevc_size_of_length_field - + - ``num_rps_poc_lt_curr`` + - The number of reference pictures in the long-term set. + * - __u8 +- - ``padding[7]`` ++ - ``padding[5]`` + - Applications and drivers must set this to zero. + * - struct :c:type:`v4l2_hevc_dpb_entry` + - ``dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]`` +diff --git a/include/media/hevc-ctrls.h b/include/media/hevc-ctrls.h +index 1592e52c3614..3e2e32098312 100644 +--- a/include/media/hevc-ctrls.h ++++ b/include/media/hevc-ctrls.h +@@ -167,6 +167,9 @@ struct v4l2_ctrl_hevc_slice_params { + __u32 bit_size; + __u32 data_bit_offset; + ++ /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ ++ __u32 slice_segment_addr; ++ + /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */ + __u8 nal_unit_type; + __u8 nuh_temporal_id_plus1; +@@ -200,7 +203,7 @@ struct v4l2_ctrl_hevc_slice_params { + __u8 num_rps_poc_st_curr_after; + __u8 num_rps_poc_lt_curr; + +- __u8 padding; ++ __u8 padding[5]; + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ + struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; +-- +2.24.0 + + +From 468a7019d3475a6dbdf368519c75f9e6625f2a1a Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 26 Oct 2019 15:44:15 +0200 +Subject: [PATCH 07/14] media: cedrus: hevc: Add support for multiple slices + +Now that segment address is available, support for multi-slice frames +can be easily added. + +Signed-off-by: Jernej Skrabec +--- + .../staging/media/sunxi/cedrus/cedrus_h265.c | 26 ++++++++++++------- + .../staging/media/sunxi/cedrus/cedrus_video.c | 1 + + 2 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 888bfd5ca224..109d3289418c 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -291,6 +291,8 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + const struct v4l2_ctrl_hevc_pps *pps; + const struct v4l2_ctrl_hevc_slice_params *slice_params; + const struct v4l2_hevc_pred_weight_table *pred_weight_table; ++ unsigned int width_in_ctb_luma, ctb_size_luma; ++ unsigned int log2_max_luma_coding_block_size; + dma_addr_t src_buf_addr; + dma_addr_t src_buf_end_addr; + u32 chroma_log2_weight_denom; +@@ -303,15 +305,17 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + slice_params = run->h265.slice_params; + pred_weight_table = &slice_params->pred_weight_table; + ++ log2_max_luma_coding_block_size = ++ sps->log2_min_luma_coding_block_size_minus3 + 3 + ++ sps->log2_diff_max_min_luma_coding_block_size; ++ ctb_size_luma = 1UL << log2_max_luma_coding_block_size; ++ width_in_ctb_luma = ++ DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma); ++ + /* MV column buffer size and allocation. */ + if (!ctx->codec.h265.mv_col_buf_size) { + unsigned int num_buffers = + run->dst->vb2_buf.vb2_queue->num_buffers; +- unsigned int log2_max_luma_coding_block_size = +- sps->log2_min_luma_coding_block_size_minus3 + 3 + +- sps->log2_diff_max_min_luma_coding_block_size; +- unsigned int ctb_size_luma = +- 1UL << log2_max_luma_coding_block_size; + + /* + * Each CTB requires a MV col buffer with a specific unit size. +@@ -366,15 +370,17 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + reg = VE_DEC_H265_BITS_END_ADDR_BASE(src_buf_end_addr); + cedrus_write(dev, VE_DEC_H265_BITS_END_ADDR, reg); + +- /* Coding tree block address: start at the beginning. */ +- reg = VE_DEC_H265_DEC_CTB_ADDR_X(0) | VE_DEC_H265_DEC_CTB_ADDR_Y(0); ++ /* Coding tree block address */ ++ reg = VE_DEC_H265_DEC_CTB_ADDR_X(slice_params->slice_segment_addr % width_in_ctb_luma); ++ reg |= VE_DEC_H265_DEC_CTB_ADDR_Y(slice_params->slice_segment_addr / width_in_ctb_luma); + cedrus_write(dev, VE_DEC_H265_DEC_CTB_ADDR, reg); + + cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, 0); + cedrus_write(dev, VE_DEC_H265_TILE_END_CTB, 0); + + /* Clear the number of correctly-decoded coding tree blocks. */ +- cedrus_write(dev, VE_DEC_H265_DEC_CTB_NUM, 0); ++ if (ctx->fh.m2m_ctx->new_frame) ++ cedrus_write(dev, VE_DEC_H265_DEC_CTB_NUM, 0); + + /* Initialize bitstream access. */ + cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_INIT_SWDEC); +@@ -523,8 +529,8 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT, + pps->flags); + +- /* FIXME: For multi-slice support. */ +- reg |= VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_FIRST_SLICE_SEGMENT_IN_PIC; ++ if (ctx->fh.m2m_ctx->new_frame) ++ reg |= VE_DEC_H265_DEC_SLICE_HDR_INFO0_FLAG_FIRST_SLICE_SEGMENT_IN_PIC; + + cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO0, reg); + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index 15cf1f10221b..497b1199d3fe 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -311,6 +311,7 @@ static int cedrus_s_fmt_vid_out(struct file *file, void *priv, + + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_H264_SLICE: ++ case V4L2_PIX_FMT_HEVC_SLICE: + vq->subsystem_flags |= + VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; + break; +-- +2.24.0 + + +From 3be57e9b0d05bbe59aa60eb037d46b523a8a4103 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sun, 3 Nov 2019 12:36:52 +0100 +Subject: [PATCH 08/14] media: cedrus: Fix decoding for some HEVC videos + +It seems that for some HEVC videos at least one bitstream parsing +trigger must be called in order to be decoded correctly. There is no +explanation why this helps, but it was observed that several videos +with this fix are now decoded correctly and there is no regression with +others. + +Without this fix, those same videos totaly crash HEVC decoder (others +are unaffected). After decoding those problematic videos, HEVC decoder +always returns only green image (all zeros). Only complete HW reset +helps. + +This fix is similar to that for H264. + +Signed-off-by: Jernej Skrabec +--- + .../staging/media/sunxi/cedrus/cedrus_h265.c | 25 ++++++++++++++++--- + .../staging/media/sunxi/cedrus/cedrus_regs.h | 1 + + 2 files changed, 23 insertions(+), 3 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 109d3289418c..5a207f1e137c 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -7,6 +7,7 @@ + * Copyright (C) 2018 Bootlin + */ + ++#include + #include + + #include +@@ -283,6 +284,23 @@ static void cedrus_h265_write_scaling_list(struct cedrus_ctx *ctx, + } + } + ++static void cedrus_h265_skip_bits(struct cedrus_dev *dev, int num) ++{ ++ int count = 0; ++ ++ while (count < num) { ++ int tmp = min(num - count, 32); ++ ++ cedrus_write(dev, VE_DEC_H265_TRIGGER, ++ VE_DEC_H265_TRIGGER_FLUSH_BITS | ++ VE_DEC_H265_TRIGGER_TYPE_N_BITS(tmp)); ++ while (cedrus_read(dev, VE_DEC_H265_STATUS) & VE_DEC_H265_STATUS_VLD_BUSY) ++ udelay(1); ++ ++ count += tmp; ++ } ++} ++ + static void cedrus_h265_setup(struct cedrus_ctx *ctx, + struct cedrus_run *run) + { +@@ -347,10 +365,9 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + + /* Source offset and length in bits. */ + +- reg = slice_params->data_bit_offset; +- cedrus_write(dev, VE_DEC_H265_BITS_OFFSET, reg); ++ cedrus_write(dev, VE_DEC_H265_BITS_OFFSET, 0); + +- reg = slice_params->bit_size - slice_params->data_bit_offset; ++ reg = slice_params->bit_size; + cedrus_write(dev, VE_DEC_H265_BITS_LEN, reg); + + /* Source beginning and end addresses. */ +@@ -385,6 +402,8 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + /* Initialize bitstream access. */ + cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_INIT_SWDEC); + ++ cedrus_h265_skip_bits(dev, slice_params->data_bit_offset); ++ + /* Bitstream parameters. */ + + reg = VE_DEC_H265_DEC_NAL_HDR_NAL_UNIT_TYPE(slice_params->nal_unit_type) | +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index 0d9449fe2b28..df1cceef8d93 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -424,6 +424,7 @@ + + #define VE_DEC_H265_TRIGGER (VE_ENGINE_DEC_H265 + 0x34) + ++#define VE_DEC_H265_TRIGGER_TYPE_N_BITS(x) (((x) & 0x3f) << 8) + #define VE_DEC_H265_TRIGGER_STCD_VC1 (0x02 << 4) + #define VE_DEC_H265_TRIGGER_STCD_AVS (0x01 << 4) + #define VE_DEC_H265_TRIGGER_STCD_HEVC (0x00 << 4) +-- +2.24.0 + + +From cf4ceb6095f67569dc22b09a150176d42ded09a5 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 26 Oct 2019 21:23:55 +0200 +Subject: [PATCH 09/14] media: cedrus: hevc: tiles hack + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus.h | 2 + + .../staging/media/sunxi/cedrus/cedrus_h265.c | 93 +++++++++++++++++-- + include/media/hevc-ctrls.h | 5 +- + 3 files changed, 93 insertions(+), 7 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index d945f4f0ff2d..1204e32d83bc 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -134,6 +134,8 @@ struct cedrus_ctx { + ssize_t mv_col_buf_unit_size; + void *neighbor_info_buf; + dma_addr_t neighbor_info_buf_addr; ++ void *entry_points_buf; ++ dma_addr_t entry_points_buf_addr; + } h265; + } codec; + }; +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 5a207f1e137c..97dce6ffbbc5 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -284,6 +284,61 @@ static void cedrus_h265_write_scaling_list(struct cedrus_ctx *ctx, + } + } + ++static void write_entry_point_list(struct cedrus_ctx *ctx, ++ struct cedrus_run *run, ++ unsigned int ctb_addr_x, ++ unsigned int ctb_addr_y) ++{ ++ const struct v4l2_ctrl_hevc_slice_params *slice_params; ++ const struct v4l2_ctrl_hevc_pps *pps; ++ struct cedrus_dev *dev = ctx->dev; ++ int i, x, tx, y, ty; ++ u32 *entry_points; ++ ++ pps = run->h265.pps; ++ slice_params = run->h265.slice_params; ++ ++ for (x = 0, tx = 0; tx < pps->num_tile_columns_minus1 + 1; tx++) { ++ if (x + pps->column_width_minus1[tx] + 1 > ctb_addr_x) ++ break; ++ ++ x += pps->column_width_minus1[tx] + 1; ++ } ++ ++ for (y = 0, ty = 0; ty < pps->num_tile_rows_minus1 + 1; ty++) { ++ if (y + pps->row_height_minus1[ty] + 1 > ctb_addr_y) ++ break; ++ ++ y += pps->row_height_minus1[ty] + 1; ++ } ++ ++ cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, (y << 16) | (x << 0)); ++ cedrus_write(dev, VE_DEC_H265_TILE_END_CTB, ++ ((y + pps->row_height_minus1[ty]) << 16) | ++ ((x + pps->column_width_minus1[tx]) << 0)); ++ ++ entry_points = ctx->codec.h265.entry_points_buf; ++ if (pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED) { ++ for (i = 0; i < slice_params->num_entry_point_offsets; i++) ++ entry_points[i] = slice_params->entry_point_offset_minus1[i] + 1; ++ } else { ++ for (i = 0; i < slice_params->num_entry_point_offsets; i++) { ++ if (tx + 1 >= pps->num_tile_columns_minus1 + 1) { ++ x = 0; ++ tx = 0; ++ y += pps->row_height_minus1[ty++] + 1; ++ } else { ++ x += pps->column_width_minus1[tx++] + 1; ++ } ++ ++ entry_points[i * 4 + 0] = slice_params->entry_point_offset_minus1[i] + 1; ++ entry_points[i * 4 + 1] = 0x0; ++ entry_points[i * 4 + 2] = (y << 16) | (x << 0); ++ entry_points[i * 4 + 3] = ((y + pps->row_height_minus1[ty]) << 16) | ((x + pps->column_width_minus1[tx]) << 0); ++ } ++ } ++} ++ + static void cedrus_h265_skip_bits(struct cedrus_dev *dev, int num) + { + int count = 0; +@@ -311,6 +366,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + const struct v4l2_hevc_pred_weight_table *pred_weight_table; + unsigned int width_in_ctb_luma, ctb_size_luma; + unsigned int log2_max_luma_coding_block_size; ++ unsigned int ctb_addr_x, ctb_addr_y; + dma_addr_t src_buf_addr; + dma_addr_t src_buf_end_addr; + u32 chroma_log2_weight_denom; +@@ -388,12 +444,19 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + cedrus_write(dev, VE_DEC_H265_BITS_END_ADDR, reg); + + /* Coding tree block address */ +- reg = VE_DEC_H265_DEC_CTB_ADDR_X(slice_params->slice_segment_addr % width_in_ctb_luma); +- reg |= VE_DEC_H265_DEC_CTB_ADDR_Y(slice_params->slice_segment_addr / width_in_ctb_luma); ++ ctb_addr_x = slice_params->slice_segment_addr % width_in_ctb_luma; ++ ctb_addr_y = slice_params->slice_segment_addr / width_in_ctb_luma; ++ reg = VE_DEC_H265_DEC_CTB_ADDR_X(ctb_addr_x); ++ reg |= VE_DEC_H265_DEC_CTB_ADDR_Y(ctb_addr_y); + cedrus_write(dev, VE_DEC_H265_DEC_CTB_ADDR, reg); + +- cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, 0); +- cedrus_write(dev, VE_DEC_H265_TILE_END_CTB, 0); ++ if ((pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) || ++ (pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED)) { ++ write_entry_point_list(ctx, run, ctb_addr_x, ctb_addr_y); ++ } else { ++ cedrus_write(dev, VE_DEC_H265_TILE_START_CTB, 0); ++ cedrus_write(dev, VE_DEC_H265_TILE_END_CTB, 0); ++ } + + /* Clear the number of correctly-decoded coding tree blocks. */ + if (ctx->fh.m2m_ctx->new_frame) +@@ -496,7 +559,9 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED, + pps->flags); + +- /* TODO: VE_DEC_H265_DEC_PPS_CTRL1_FLAG_TILES_ENABLED */ ++ reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_TILES_ENABLED, ++ V4L2_HEVC_PPS_FLAG_TILES_ENABLED, ++ pps->flags); + + reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_PPS_CTRL1_FLAG_TRANSQUANT_BYPASS_ENABLED, + V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED, +@@ -572,12 +637,14 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + + chroma_log2_weight_denom = pred_weight_table->luma_log2_weight_denom + + pred_weight_table->delta_chroma_log2_weight_denom; +- reg = VE_DEC_H265_DEC_SLICE_HDR_INFO2_NUM_ENTRY_POINT_OFFSETS(0) | ++ reg = VE_DEC_H265_DEC_SLICE_HDR_INFO2_NUM_ENTRY_POINT_OFFSETS(slice_params->num_entry_point_offsets) | + VE_DEC_H265_DEC_SLICE_HDR_INFO2_CHROMA_LOG2_WEIGHT_DENOM(chroma_log2_weight_denom) | + VE_DEC_H265_DEC_SLICE_HDR_INFO2_LUMA_LOG2_WEIGHT_DENOM(pred_weight_table->luma_log2_weight_denom); + + cedrus_write(dev, VE_DEC_H265_DEC_SLICE_HDR_INFO2, reg); + ++ cedrus_write(dev, VE_DEC_H265_ENTRY_POINT_OFFSET_ADDR, ctx->codec.h265.entry_points_buf_addr >> 8); ++ + /* Decoded picture size. */ + + reg = VE_DEC_H265_DEC_PIC_SIZE_WIDTH(ctx->src_fmt.width) | +@@ -671,6 +738,17 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx) + if (!ctx->codec.h265.neighbor_info_buf) + return -ENOMEM; + ++ ctx->codec.h265.entry_points_buf = ++ dma_alloc_coherent(dev->dev, CEDRUS_H265_ENTRY_POINTS_BUF_SIZE, ++ &ctx->codec.h265.entry_points_buf_addr, ++ GFP_KERNEL); ++ if (!ctx->codec.h265.entry_points_buf) { ++ dma_free_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, ++ ctx->codec.h265.neighbor_info_buf, ++ ctx->codec.h265.neighbor_info_buf_addr); ++ return -ENOMEM; ++ } ++ + return 0; + } + +@@ -689,6 +767,9 @@ static void cedrus_h265_stop(struct cedrus_ctx *ctx) + dma_free_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h265.neighbor_info_buf, + ctx->codec.h265.neighbor_info_buf_addr); ++ dma_free_coherent(dev->dev, CEDRUS_H265_ENTRY_POINTS_BUF_SIZE, ++ ctx->codec.h265.entry_points_buf, ++ ctx->codec.h265.entry_points_buf_addr); + } + + static void cedrus_h265_trigger(struct cedrus_ctx *ctx) +diff --git a/include/media/hevc-ctrls.h b/include/media/hevc-ctrls.h +index 3e2e32098312..d1b094c8aaeb 100644 +--- a/include/media/hevc-ctrls.h ++++ b/include/media/hevc-ctrls.h +@@ -169,6 +169,7 @@ struct v4l2_ctrl_hevc_slice_params { + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ + __u32 slice_segment_addr; ++ __u32 num_entry_point_offsets; + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */ + __u8 nal_unit_type; +@@ -203,7 +204,9 @@ struct v4l2_ctrl_hevc_slice_params { + __u8 num_rps_poc_st_curr_after; + __u8 num_rps_poc_lt_curr; + +- __u8 padding[5]; ++ __u8 padding; ++ ++ __u32 entry_point_offset_minus1[256]; + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ + struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; +-- +2.24.0 + + +From 42b71243ef0c15e1d940583ef3fce99ec79a1975 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Thu, 14 Feb 2019 22:50:12 +0100 +Subject: [PATCH 10/14] media: cedrus: H264 interlace hack + +Signed-off-by: Jernej Skrabec +--- + .../staging/media/sunxi/cedrus/cedrus_h264.c | 24 ++++++++++++------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +index ab83a6f1f921..b0ee4aed79f9 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +@@ -102,7 +102,7 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, + struct cedrus_dev *dev = ctx->dev; + unsigned long used_dpbs = 0; + unsigned int position; +- unsigned int output = 0; ++ int output = -1; + unsigned int i; + + cap_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); +@@ -125,6 +125,11 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, + position = cedrus_buf->codec.h264.position; + used_dpbs |= BIT(position); + ++ if (run->dst->vb2_buf.timestamp == dpb->reference_ts) { ++ output = position; ++ continue; ++ } ++ + if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + continue; + +@@ -132,13 +137,11 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, + dpb->top_field_order_cnt, + dpb->bottom_field_order_cnt, + &pic_list[position]); +- +- output = max(position, output); + } + +- position = find_next_zero_bit(&used_dpbs, CEDRUS_H264_FRAME_NUM, +- output); +- if (position >= CEDRUS_H264_FRAME_NUM) ++ if (output >= 0) ++ position = output; ++ else + position = find_first_zero_bit(&used_dpbs, CEDRUS_H264_FRAME_NUM); + + output_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf); +@@ -164,6 +167,10 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, + + #define CEDRUS_MAX_REF_IDX 32 + ++#define REF_IDX(v) (v & GENMASK(5, 0)) ++#define REF_FIELD(v) (v >> 6) ++#define REF_FIELD_BOTTOM 2 ++ + static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, + struct cedrus_run *run, + const u8 *ref_list, u8 num_ref, +@@ -188,7 +195,7 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, + int buf_idx; + u8 dpb_idx; + +- dpb_idx = ref_list[i]; ++ dpb_idx = REF_IDX(ref_list[i]); + dpb = &decode->dpb[dpb_idx]; + + if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) +@@ -203,7 +210,8 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, + position = cedrus_buf->codec.h264.position; + + sram_array[i] |= position << 1; +- if (ref_buf->field == V4L2_FIELD_BOTTOM) ++ /* set bottom field flag when reference is to bottom field */ ++ if (REF_FIELD(ref_list[i]) == REF_FIELD_BOTTOM) + sram_array[i] |= BIT(0); + } + +-- +2.24.0 + + +From 6c1b98710d93611eec39bbcfcf07b3fc48e11459 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 9 Nov 2019 11:50:40 +0100 +Subject: [PATCH 11/14] media: cedrus: hevc: Add luma bit depth + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus_h265.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 97dce6ffbbc5..89e269cc066d 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -483,6 +483,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + VE_DEC_H265_DEC_SPS_HDR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE(sps->log2_diff_max_min_luma_coding_block_size) | + VE_DEC_H265_DEC_SPS_HDR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3(sps->log2_min_luma_coding_block_size_minus3) | + VE_DEC_H265_DEC_SPS_HDR_BIT_DEPTH_CHROMA_MINUS8(sps->bit_depth_chroma_minus8) | ++ VE_DEC_H265_DEC_SPS_HDR_BIT_DEPTH_LUMA_MINUS8(sps->bit_depth_luma_minus8) | + VE_DEC_H265_DEC_SPS_HDR_CHROMA_FORMAT_IDC(sps->chroma_format_idc); + + reg |= VE_DEC_H265_FLAG(VE_DEC_H265_DEC_SPS_HDR_FLAG_STRONG_INTRA_SMOOTHING_ENABLE, +-- +2.24.0 + + +From 187e20b079317e312a1be425ff4c19f838b9e625 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 9 Nov 2019 13:06:15 +0100 +Subject: [PATCH 12/14] media: cedrus: Add callback for buffer cleanup + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus.h | 1 + + drivers/staging/media/sunxi/cedrus/cedrus_video.c | 13 +++++++++++++ + 2 files changed, 14 insertions(+) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index 1204e32d83bc..9298aa5f229d 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -148,6 +148,7 @@ struct cedrus_dec_ops { + int (*start)(struct cedrus_ctx *ctx); + void (*stop)(struct cedrus_ctx *ctx); + void (*trigger)(struct cedrus_ctx *ctx); ++ void (*buf_cleanup)(struct cedrus_ctx *ctx, struct cedrus_buffer *buf); + }; + + struct cedrus_variant { +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index 497b1199d3fe..7f95216a552e 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -431,6 +431,18 @@ static int cedrus_buf_prepare(struct vb2_buffer *vb) + return 0; + } + ++static void cedrus_buf_cleanup(struct vb2_buffer *vb) ++{ ++ struct vb2_queue *vq = vb->vb2_queue; ++ struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); ++ struct cedrus_dev *dev = ctx->dev; ++ struct cedrus_dec_ops *ops = dev->dec_ops[ctx->current_codec]; ++ ++ if (!V4L2_TYPE_IS_OUTPUT(vq->type) && ops->buf_cleanup) ++ ops->buf_cleanup(ctx, ++ vb2_to_cedrus_buffer(vq->bufs[vb->index])); ++} ++ + static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) + { + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); +@@ -494,6 +506,7 @@ static void cedrus_buf_request_complete(struct vb2_buffer *vb) + static struct vb2_ops cedrus_qops = { + .queue_setup = cedrus_queue_setup, + .buf_prepare = cedrus_buf_prepare, ++ .buf_cleanup = cedrus_buf_cleanup, + .buf_queue = cedrus_buf_queue, + .buf_out_validate = cedrus_buf_out_validate, + .buf_request_complete = cedrus_buf_request_complete, +-- +2.24.0 + + +From 024630941ade1aa57b4c16a5577ee9cf4f62be18 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 9 Nov 2019 13:22:05 +0100 +Subject: [PATCH 13/14] media: cedrus: hevc: Improve buffer management + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus.h | 9 +- + .../staging/media/sunxi/cedrus/cedrus_h265.c | 117 ++++++++++-------- + 2 files changed, 69 insertions(+), 57 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index 9298aa5f229d..d8a4f8e83f94 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -95,6 +95,11 @@ struct cedrus_buffer { + unsigned int position; + enum cedrus_h264_pic_type pic_type; + } h264; ++ struct { ++ void *mv_col_buf; ++ dma_addr_t mv_col_buf_dma; ++ ssize_t mv_col_buf_size; ++ } h265; + } codec; + }; + +@@ -128,10 +133,6 @@ struct cedrus_ctx { + ssize_t intra_pred_buf_size; + } h264; + struct { +- void *mv_col_buf; +- dma_addr_t mv_col_buf_addr; +- ssize_t mv_col_buf_size; +- ssize_t mv_col_buf_unit_size; + void *neighbor_info_buf; + dma_addr_t neighbor_info_buf_addr; + void *entry_points_buf; +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 89e269cc066d..7c806ef6e8ef 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -91,26 +91,66 @@ static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, void *data, + + static inline dma_addr_t + cedrus_h265_frame_info_mv_col_buf_addr(struct cedrus_ctx *ctx, +- unsigned int index, unsigned int field) ++ unsigned int index, ++ const struct v4l2_ctrl_hevc_sps *sps) + { +- return ctx->codec.h265.mv_col_buf_addr + index * +- ctx->codec.h265.mv_col_buf_unit_size + +- field * ctx->codec.h265.mv_col_buf_unit_size / 2; ++ struct cedrus_buffer *cedrus_buf = NULL; ++ struct vb2_buffer *buf = NULL; ++ struct vb2_queue *vq; ++ ++ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ if (vq) ++ buf = vb2_get_buffer(vq, index); ++ ++ if (buf) ++ cedrus_buf = vb2_to_cedrus_buffer(buf); ++ ++ if (!cedrus_buf) ++ return 0; ++ ++ if (!cedrus_buf->codec.h265.mv_col_buf_size) { ++ unsigned int ctb_size_luma, width_in_ctb_luma; ++ unsigned int log2_max_luma_coding_block_size; ++ ++ log2_max_luma_coding_block_size = ++ sps->log2_min_luma_coding_block_size_minus3 + 3 + ++ sps->log2_diff_max_min_luma_coding_block_size; ++ ctb_size_luma = 1 << log2_max_luma_coding_block_size; ++ width_in_ctb_luma = DIV_ROUND_UP(sps->pic_width_in_luma_samples, ++ ctb_size_luma); ++ ++ cedrus_buf->codec.h265.mv_col_buf_size = ALIGN(width_in_ctb_luma * ++ DIV_ROUND_UP(sps->pic_height_in_luma_samples, ctb_size_luma) * ++ CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE, 1024); ++ ++ cedrus_buf->codec.h265.mv_col_buf = ++ dma_alloc_coherent(ctx->dev->dev, ++ cedrus_buf->codec.h265.mv_col_buf_size, ++ &cedrus_buf->codec.h265.mv_col_buf_dma, ++ GFP_KERNEL); ++ ++ if (!cedrus_buf->codec.h265.mv_col_buf) { ++ cedrus_buf->codec.h265.mv_col_buf_size = 0; ++ cedrus_buf->codec.h265.mv_col_buf_dma = 0; ++ } ++ } ++ ++ return cedrus_buf->codec.h265.mv_col_buf_dma; + } + + static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, + unsigned int index, + bool field_pic, + u32 pic_order_cnt[], +- int buffer_index) ++ int buffer_index, ++ const struct v4l2_ctrl_hevc_sps *sps) + { + struct cedrus_dev *dev = ctx->dev; + dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 0); + dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 1); + dma_addr_t mv_col_buf_addr[2] = { +- cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, 0), +- cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, +- field_pic ? 1 : 0) ++ cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, sps), ++ cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, sps) + }; + u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO + + VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT * index; +@@ -134,7 +174,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, + + static void cedrus_h265_frame_info_write_dpb(struct cedrus_ctx *ctx, + const struct v4l2_hevc_dpb_entry *dpb, +- u8 num_active_dpb_entries) ++ u8 num_active_dpb_entries, ++ const struct v4l2_ctrl_hevc_sps *sps) + { + struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); +@@ -149,7 +190,7 @@ static void cedrus_h265_frame_info_write_dpb(struct cedrus_ctx *ctx, + + cedrus_h265_frame_info_write_single(ctx, i, dpb[i].field_pic, + pic_order_cnt, +- buffer_index); ++ buffer_index, sps); + } + } + +@@ -386,36 +427,6 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + width_in_ctb_luma = + DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma); + +- /* MV column buffer size and allocation. */ +- if (!ctx->codec.h265.mv_col_buf_size) { +- unsigned int num_buffers = +- run->dst->vb2_buf.vb2_queue->num_buffers; +- +- /* +- * Each CTB requires a MV col buffer with a specific unit size. +- * Since the address is given with missing lsb bits, 1 KiB is +- * added to each buffer to ensure proper alignment. +- */ +- ctx->codec.h265.mv_col_buf_unit_size = +- DIV_ROUND_UP(ctx->src_fmt.width, ctb_size_luma) * +- DIV_ROUND_UP(ctx->src_fmt.height, ctb_size_luma) * +- CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE + SZ_1K; +- +- ctx->codec.h265.mv_col_buf_size = num_buffers * +- ctx->codec.h265.mv_col_buf_unit_size; +- +- ctx->codec.h265.mv_col_buf = +- dma_alloc_coherent(dev->dev, +- ctx->codec.h265.mv_col_buf_size, +- &ctx->codec.h265.mv_col_buf_addr, +- GFP_KERNEL); +- if (!ctx->codec.h265.mv_col_buf) { +- ctx->codec.h265.mv_col_buf_size = 0; +- // TODO: Abort the process here. +- return; +- } +- } +- + /* Activate H265 engine. */ + cedrus_engine_enable(ctx, CEDRUS_CODEC_H265); + +@@ -669,7 +680,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + + /* Write decoded picture buffer in pic list. */ + cedrus_h265_frame_info_write_dpb(ctx, slice_params->dpb, +- slice_params->num_active_dpb_entries); ++ slice_params->num_active_dpb_entries, sps); + + /* Output frame. */ + +@@ -680,7 +691,7 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + cedrus_h265_frame_info_write_single(ctx, output_pic_list_index, + slice_params->pic_struct != 0, + pic_order_cnt, +- run->dst->vb2_buf.index); ++ run->dst->vb2_buf.index, sps); + + cedrus_write(dev, VE_DEC_H265_OUTPUT_FRAME_IDX, output_pic_list_index); + +@@ -729,9 +740,6 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx) + { + struct cedrus_dev *dev = ctx->dev; + +- /* The buffer size is calculated at setup time. */ +- ctx->codec.h265.mv_col_buf_size = 0; +- + ctx->codec.h265.neighbor_info_buf = + dma_alloc_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, + &ctx->codec.h265.neighbor_info_buf_addr, +@@ -757,14 +765,6 @@ static void cedrus_h265_stop(struct cedrus_ctx *ctx) + { + struct cedrus_dev *dev = ctx->dev; + +- if (ctx->codec.h265.mv_col_buf_size > 0) { +- dma_free_coherent(dev->dev, ctx->codec.h265.mv_col_buf_size, +- ctx->codec.h265.mv_col_buf, +- ctx->codec.h265.mv_col_buf_addr); +- +- ctx->codec.h265.mv_col_buf_size = 0; +- } +- + dma_free_coherent(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h265.neighbor_info_buf, + ctx->codec.h265.neighbor_info_buf_addr); +@@ -780,6 +780,16 @@ static void cedrus_h265_trigger(struct cedrus_ctx *ctx) + cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_DEC_SLICE); + } + ++static void cedrus_h265_buf_cleanup(struct cedrus_ctx *ctx, ++ struct cedrus_buffer *buf) ++{ ++ if (buf->codec.h265.mv_col_buf_size) ++ dma_free_coherent(ctx->dev->dev, ++ buf->codec.h265.mv_col_buf_size, ++ buf->codec.h265.mv_col_buf, ++ buf->codec.h265.mv_col_buf_dma); ++} ++ + struct cedrus_dec_ops cedrus_dec_ops_h265 = { + .irq_clear = cedrus_h265_irq_clear, + .irq_disable = cedrus_h265_irq_disable, +@@ -788,4 +798,5 @@ struct cedrus_dec_ops cedrus_dec_ops_h265 = { + .start = cedrus_h265_start, + .stop = cedrus_h265_stop, + .trigger = cedrus_h265_trigger, ++ .buf_cleanup = cedrus_h265_buf_cleanup, + }; +-- +2.24.0 + + +From b38b517739880462a43a1f4b36cf9384ffdae66c Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 9 Nov 2019 14:12:42 +0100 +Subject: [PATCH 14/14] media: cedrus: h264: Improve buffer management + +Signed-off-by: Jernej Skrabec +--- + drivers/staging/media/sunxi/cedrus/cedrus.h | 3 + + .../staging/media/sunxi/cedrus/cedrus_h264.c | 93 ++++++++----------- + 2 files changed, 44 insertions(+), 52 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h +index d8a4f8e83f94..f8264953dd04 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus.h +@@ -94,6 +94,9 @@ struct cedrus_buffer { + struct { + unsigned int position; + enum cedrus_h264_pic_type pic_type; ++ void *mv_col_buf; ++ dma_addr_t mv_col_buf_dma; ++ ssize_t mv_col_buf_size; + } h264; + struct { + void *mv_col_buf; +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +index b0ee4aed79f9..aa5bd181cdaf 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +@@ -55,16 +55,14 @@ static void cedrus_h264_write_sram(struct cedrus_dev *dev, + } + + static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_ctx *ctx, +- unsigned int position, ++ struct cedrus_buffer *buf, + unsigned int field) + { +- dma_addr_t addr = ctx->codec.h264.mv_col_buf_dma; ++ dma_addr_t addr = buf->codec.h264.mv_col_buf_dma; + +- /* Adjust for the position */ +- addr += position * ctx->codec.h264.mv_col_buf_field_size * 2; +- +- /* Adjust for the field */ +- addr += field * ctx->codec.h264.mv_col_buf_field_size; ++ /* Adjust for the field */ ++ if (field) ++ addr += buf->codec.h264.mv_col_buf_size / 2; + + return addr; + } +@@ -76,7 +74,6 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx, + struct cedrus_h264_sram_ref_pic *pic) + { + struct vb2_buffer *vbuf = &buf->m2m_buf.vb.vb2_buf; +- unsigned int position = buf->codec.h264.position; + + pic->top_field_order_cnt = cpu_to_le32(top_field_order_cnt); + pic->bottom_field_order_cnt = cpu_to_le32(bottom_field_order_cnt); +@@ -85,9 +82,9 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx, + pic->luma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 0)); + pic->chroma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 1)); + pic->mv_col_top_ptr = +- cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 0)); ++ cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, buf, 0)); + pic->mv_col_bot_ptr = +- cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 1)); ++ cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, buf, 1)); + } + + static void cedrus_write_frame_list(struct cedrus_ctx *ctx, +@@ -147,6 +144,28 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, + output_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf); + output_buf->codec.h264.position = position; + ++ if (!output_buf->codec.h264.mv_col_buf_size) { ++ const struct v4l2_ctrl_h264_sps *sps = run->h264.sps; ++ unsigned int field_size; ++ ++ field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) * ++ DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16; ++ if (!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)) ++ field_size = field_size * 2; ++ if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) ++ field_size = field_size * 2; ++ ++ output_buf->codec.h264.mv_col_buf_size = field_size * 2; ++ output_buf->codec.h264.mv_col_buf = ++ dma_alloc_coherent(dev->dev, ++ output_buf->codec.h264.mv_col_buf_size, ++ &output_buf->codec.h264.mv_col_buf_dma, ++ GFP_KERNEL); ++ ++ if (!output_buf->codec.h264.mv_col_buf) ++ output_buf->codec.h264.mv_col_buf_size = 0; ++ } ++ + if (slice->flags & V4L2_H264_SLICE_FLAG_FIELD_PIC) + output_buf->codec.h264.pic_type = CEDRUS_H264_PIC_TYPE_FIELD; + else if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) +@@ -525,8 +544,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) + { + struct cedrus_dev *dev = ctx->dev; + unsigned int pic_info_size; +- unsigned int field_size; +- unsigned int mv_col_size; + int ret; + + /* Formula for picture buffer size is taken from CedarX source. */ +@@ -569,37 +586,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) + goto err_pic_buf; + } + +- field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) * +- DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16; +- +- /* +- * FIXME: This is actually conditional to +- * V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE not being set, we +- * might have to rework this if memory efficiency ever is +- * something we need to work on. +- */ +- field_size = field_size * 2; +- +- /* +- * FIXME: This is actually conditional to +- * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY not being set, we might +- * have to rework this if memory efficiency ever is something +- * we need to work on. +- */ +- field_size = field_size * 2; +- ctx->codec.h264.mv_col_buf_field_size = field_size; +- +- mv_col_size = field_size * 2 * CEDRUS_H264_FRAME_NUM; +- ctx->codec.h264.mv_col_buf_size = mv_col_size; +- ctx->codec.h264.mv_col_buf = dma_alloc_coherent(dev->dev, +- ctx->codec.h264.mv_col_buf_size, +- &ctx->codec.h264.mv_col_buf_dma, +- GFP_KERNEL); +- if (!ctx->codec.h264.mv_col_buf) { +- ret = -ENOMEM; +- goto err_neighbor_buf; +- } +- + if (ctx->src_fmt.width > 2048) { + /* + * Formulas for deblock and intra prediction buffer sizes +@@ -615,7 +601,7 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) + GFP_KERNEL); + if (!ctx->codec.h264.deblk_buf) { + ret = -ENOMEM; +- goto err_mv_col_buf; ++ goto err_neighbor_buf; + } + + ctx->codec.h264.intra_pred_buf_size = +@@ -638,11 +624,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) + ctx->codec.h264.deblk_buf, + ctx->codec.h264.deblk_buf_dma); + +-err_mv_col_buf: +- dma_free_coherent(dev->dev, ctx->codec.h264.mv_col_buf_size, +- ctx->codec.h264.mv_col_buf, +- ctx->codec.h264.mv_col_buf_dma); +- + err_neighbor_buf: + dma_free_coherent(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h264.neighbor_info_buf, +@@ -659,9 +640,6 @@ static void cedrus_h264_stop(struct cedrus_ctx *ctx) + { + struct cedrus_dev *dev = ctx->dev; + +- dma_free_coherent(dev->dev, ctx->codec.h264.mv_col_buf_size, +- ctx->codec.h264.mv_col_buf, +- ctx->codec.h264.mv_col_buf_dma); + dma_free_coherent(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h264.neighbor_info_buf, + ctx->codec.h264.neighbor_info_buf_dma); +@@ -686,6 +664,16 @@ static void cedrus_h264_trigger(struct cedrus_ctx *ctx) + VE_H264_TRIGGER_TYPE_AVC_SLICE_DECODE); + } + ++static void cedrus_h264_buf_cleanup(struct cedrus_ctx *ctx, ++ struct cedrus_buffer *buf) ++{ ++ if (buf->codec.h264.mv_col_buf_size) ++ dma_free_coherent(ctx->dev->dev, ++ buf->codec.h264.mv_col_buf_size, ++ buf->codec.h264.mv_col_buf, ++ buf->codec.h264.mv_col_buf_dma); ++} ++ + struct cedrus_dec_ops cedrus_dec_ops_h264 = { + .irq_clear = cedrus_h264_irq_clear, + .irq_disable = cedrus_h264_irq_disable, +@@ -694,4 +682,5 @@ struct cedrus_dec_ops cedrus_dec_ops_h264 = { + .start = cedrus_h264_start, + .stop = cedrus_h264_stop, + .trigger = cedrus_h264_trigger, ++ .buf_cleanup = cedrus_h264_buf_cleanup, + }; +-- +2.24.0 diff --git a/patch/kernel/sunxi-current/0623-media-cedrus-10bit-HEVC-hack.patch b/patch/kernel/sunxi-current/0623-media-cedrus-10bit-HEVC-hack.patch new file mode 100644 index 000000000..7fcbcf30d --- /dev/null +++ b/patch/kernel/sunxi-current/0623-media-cedrus-10bit-HEVC-hack.patch @@ -0,0 +1,89 @@ +From 60808cc1810d47f91c368de8ffb7db59cabceaf9 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Tue, 28 May 2019 21:05:34 +0200 +Subject: [PATCH] 10-bit HEVC hack + +Signed-off-by: Jernej Skrabec +--- + .../staging/media/sunxi/cedrus/cedrus_h265.c | 12 +++++++++++ + .../staging/media/sunxi/cedrus/cedrus_regs.h | 4 ++++ + .../staging/media/sunxi/cedrus/cedrus_video.c | 20 ++++++++++++++----- + 3 files changed, 31 insertions(+), 5 deletions(-) + +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +index 97dce6ffbbc5..d866662cd5a5 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +@@ -520,6 +520,18 @@ static void cedrus_h265_setup(struct cedrus_ctx *ctx, + + cedrus_write(dev, VE_DEC_H265_DEC_PCM_CTRL, reg); + ++ if (sps->bit_depth_luma_minus8) { ++ unsigned int size; ++ ++ size = ALIGN(ctx->src_fmt.width, 16) * ALIGN(ctx->src_fmt.height, 16); ++ ++ reg = (size * 3) / 2; ++ cedrus_write(dev, VE_DEC_H265_OFFSET_ADDR_FIRST_OUT, reg); ++ ++ reg = DIV_ROUND_UP(ctx->src_fmt.width, 4); ++ cedrus_write(dev, VE_DEC_H265_10BIT_CONFIGURE, ALIGN(reg, 32)); ++ } ++ + /* PPS. */ + + reg = VE_DEC_H265_DEC_PPS_CTRL0_PPS_CR_QP_OFFSET(pps->pps_cr_qp_offset) | +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +index df1cceef8d93..150eae2d92d2 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +@@ -498,6 +498,10 @@ + + #define VE_DEC_H265_LOW_ADDR (VE_ENGINE_DEC_H265 + 0x80) + ++#define VE_DEC_H265_OFFSET_ADDR_FIRST_OUT (VE_ENGINE_DEC_H265 + 0x84) ++#define VE_DEC_H265_OFFSET_ADDR_SECOND_OUT (VE_ENGINE_DEC_H265 + 0x88) ++#define VE_DEC_H265_10BIT_CONFIGURE (VE_ENGINE_DEC_H265 + 0x8c) ++ + #define VE_DEC_H265_LOW_ADDR_PRIMARY_CHROMA(a) \ + SHIFT_AND_MASK_BITS(a, 31, 24) + #define VE_DEC_H265_LOW_ADDR_SECONDARY_CHROMA(a) \ +diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +index 497b1199d3fe..178ad45b79d8 100644 +--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c ++++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c +@@ -367,17 +367,27 @@ static int cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, + { + struct cedrus_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; ++ unsigned int extra_size = 0; + +- if (V4L2_TYPE_IS_OUTPUT(vq->type)) ++ if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + pix_fmt = &ctx->src_fmt; +- else ++ } else { + pix_fmt = &ctx->dst_fmt; + ++ /* The HEVC decoder needs extra size on the output buffer. */ ++ if (ctx->src_fmt.pixelformat == V4L2_PIX_FMT_HEVC_SLICE) { ++ extra_size = DIV_ROUND_UP(pix_fmt->width, 4); ++ extra_size = ALIGN(extra_size, 32); ++ extra_size *= ALIGN(pix_fmt->height, 16) * 3; ++ extra_size /= 2; ++ } ++ } ++ + if (*nplanes) { +- if (sizes[0] < pix_fmt->sizeimage) +- return -EINVAL; ++ if (sizes[0] < (pix_fmt->sizeimage + extra_size)) ++ sizes[0] = pix_fmt->sizeimage + extra_size; + } else { +- sizes[0] = pix_fmt->sizeimage; ++ sizes[0] = pix_fmt->sizeimage + extra_size; + *nplanes = 1; + } + +-- +2.23.0 diff --git a/patch/kernel/sunxi-dev/0004-sun4i-i2s-improvements.patch b/patch/kernel/sunxi-dev/0004-sun4i-i2s-improvements.patch index 20fbec916..b2a56dce6 100644 --- a/patch/kernel/sunxi-dev/0004-sun4i-i2s-improvements.patch +++ b/patch/kernel/sunxi-dev/0004-sun4i-i2s-improvements.patch @@ -31,7 +31,7 @@ index d0a8d5810c0a..9a715a6bdbf9 100644 #define SUN4I_I2S_FIFO_TX_REG 0x0c #define SUN4I_I2S_FIFO_RX_REG 0x10 -@@ -97,33 +100,53 @@ +@@ -100,22 +100,25 @@ #define SUN8I_I2S_CTRL_MODE_PCM (0 << 4) #define SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK BIT(19) @@ -63,9 +63,8 @@ index d0a8d5810c0a..9a715a6bdbf9 100644 #define SUN8I_I2S_TX_CHAN_MAP_REG 0x44 #define SUN8I_I2S_TX_CHAN_SEL_REG 0x34 - #define SUN8I_I2S_TX_CHAN_OFFSET_MASK GENMASK(13, 12) --#define SUN8I_I2S_TX_CHAN_OFFSET(offset) (offset << 12) -+#define SUN8I_I2S_TX_CHAN_OFFSET(offset) ((offset) << 12) +@@ -123,10 +126,27 @@ + #define SUN8I_I2S_TX_CHAN_OFFSET(offset) ((offset) << 12) #define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4) #define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4) +#define SUN8I_I2S_TX_CHAN_SEL_MASK GENMASK(2, 0) @@ -485,18 +484,19 @@ index d0a8d5810c0a..9a715a6bdbf9 100644 break; default: -@@ -661,12 +928,8 @@ static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s, +@@ -913,12 +933,9 @@ static int sun8i_i2s_set_soc_fmt(struct sun4i_i2s *i2s, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, SUN8I_I2S_CTRL_MODE_MASK, mode); - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, - SUN8I_I2S_TX_CHAN_OFFSET_MASK, -- SUN8I_I2S_TX_CHAN_OFFSET(offset)); +- SUN8I_I2S_TX_CHAN_OFFSET(i2s->pinephone_hack ? 0 : offset)); - regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, - SUN8I_I2S_TX_CHAN_OFFSET_MASK, - SUN8I_I2S_TX_CHAN_OFFSET(offset)); + i2s->variant->set_txchanoffset(i2s, 0); + i2s->variant->set_rxchanoffset(i2s); ++ /* DAI clock master masks */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { diff --git a/patch/kernel/sunxi-dev/0533-allwinner-h6-add-AC200-EPHY-support.patch b/patch/kernel/sunxi-dev/0533-allwinner-h6-add-AC200-EPHY-support.patch new file mode 100644 index 000000000..a916a1b3d --- /dev/null +++ b/patch/kernel/sunxi-dev/0533-allwinner-h6-add-AC200-EPHY-support.patch @@ -0,0 +1,569 @@ +diff -Naur linux-5.3-rc2-old/drivers/mfd/ac200.c linux-5.3-rc2-old-new/drivers/mfd/ac200.c +--- linux-5.3-rc2-old/drivers/mfd/ac200.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-5.3-rc2-old-new/drivers/mfd/ac200.c 2019-08-03 15:24:25.929999997 +0200 +@@ -0,0 +1,223 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * MFD core driver for X-Powers' AC200 IC ++ * ++ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and ++ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse ++ * and RTC. ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ * ++ * Based on AC100 driver with following copyrights: ++ * Copyright (2016) Chen-Yu Tsai ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct ac200_dev { ++ struct clk *clk; ++ /* ++ * Lock is needed for serializing concurrent access to ++ * AC200 registers in order not to mess with register page. ++ */ ++ struct mutex lock; ++ struct regmap *regmap; ++}; ++ ++/* ++ * Register values can't be cached because registers are divided ++ * into multiple pages. ++ */ ++static const struct regmap_config ac200_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 16, ++}; ++ ++int ac200_reg_read(struct ac200_dev *ac200, u16 reg, u16 *value) ++{ ++ unsigned int val; ++ int ret; ++ ++ mutex_lock(&ac200->lock); ++ ret = regmap_write(ac200->regmap, AC200_TWI_REG_ADDR_H, reg >> 8); ++ if (ret) ++ goto read_reg_out; ++ ++ ret = regmap_read(ac200->regmap, reg & 0xff, &val); ++ *value = val; ++ ++read_reg_out: ++ mutex_unlock(&ac200->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ac200_reg_read); ++ ++int ac200_reg_write(struct ac200_dev *ac200, u16 reg, u16 value) ++{ ++ int ret; ++ ++ mutex_lock(&ac200->lock); ++ ret = regmap_write(ac200->regmap, AC200_TWI_REG_ADDR_H, reg >> 8); ++ if (ret) ++ goto write_reg_out; ++ ++ ret = regmap_write(ac200->regmap, reg & 0xff, value); ++ ++write_reg_out: ++ mutex_unlock(&ac200->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ac200_reg_write); ++ ++int ac200_reg_mod(struct ac200_dev *ac200, u16 reg, u16 mask, u16 value) ++{ ++ unsigned int val; ++ int ret; ++ ++ mutex_lock(&ac200->lock); ++ ret = regmap_write(ac200->regmap, AC200_TWI_REG_ADDR_H, reg >> 8); ++ if (ret) ++ goto mod_reg_out; ++ ++ ret = regmap_read(ac200->regmap, reg & 0xff, &val); ++ if (ret) ++ goto mod_reg_out; ++ ++ val &= ~mask; ++ val |= value; ++ ++ ret = regmap_write(ac200->regmap, reg & 0xff, val); ++ ++mod_reg_out: ++ mutex_unlock(&ac200->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ac200_reg_mod); ++ ++static struct mfd_cell ac200_cells[] = { ++ { ++ .name = "ac200-codec", ++ .of_compatible = "x-powers,ac200-codec", ++ }, { ++ .name = "ac200-efuse", ++ .of_compatible = "x-powers,ac200-efuse", ++ }, { ++ .name = "ac200-ephy", ++ .of_compatible = "x-powers,ac200-ephy", ++ }, { ++ .name = "ac200-rtc", ++ .of_compatible = "x-powers,ac200-rtc", ++ }, { ++ .name = "ac200-tve", ++ .of_compatible = "x-powers,ac200-tve", ++ }, ++}; ++ ++static int ac200_i2c_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &i2c->dev; ++ struct ac200_dev *ac200; ++ int ret; ++ ++ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL); ++ if (!ac200) ++ return -ENOMEM; ++ ++ mutex_init(&ac200->lock); ++ i2c_set_clientdata(i2c, ac200); ++ ++ ac200->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(ac200->clk)) { ++ ret = PTR_ERR(ac200->clk); ++ dev_err(dev, "Can't obtain the clock: %d\n", ret); ++ return ret; ++ } ++ ++ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config); ++ if (IS_ERR(ac200->regmap)) { ++ ret = PTR_ERR(ac200->regmap); ++ dev_err(dev, "Regmap init failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(ac200->clk); ++ if (ret) { ++ dev_err(dev, "Can't enable the clock: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_CONTROL, 0); ++ if (ret) { ++ dev_err(dev, "Can't put AC200 in reset: %d\n", ret); ++ goto err; ++ } ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_CONTROL, 1); ++ if (ret) { ++ dev_err(dev, "Can't put AC200 out of reset: %d\n", ret); ++ goto err; ++ } ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells, ++ ARRAY_SIZE(ac200_cells), NULL, 0, NULL); ++ if (ret) { ++ dev_err(dev, "Failed to add MFD devices: %d\n", ret); ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(ac200->clk); ++ return ret; ++} ++ ++static int ac200_i2c_remove(struct i2c_client *i2c) ++{ ++ struct ac200_dev *ac200 = i2c_get_clientdata(i2c); ++ ++ ac200_reg_write(ac200, AC200_SYS_CONTROL, 0); ++ ++ clk_disable_unprepare(ac200->clk); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id ac200_ids[] = { ++ { "ac200", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(i2c, ac200_ids); ++ ++static const struct of_device_id ac200_of_match[] = { ++ { .compatible = "x-powers,ac200" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ac200_of_match); ++ ++static struct i2c_driver ac200_i2c_driver = { ++ .driver = { ++ .name = "ac200", ++ .of_match_table = of_match_ptr(ac200_of_match), ++ }, ++ .probe = ac200_i2c_probe, ++ .remove = ac200_i2c_remove, ++ .id_table = ac200_ids, ++}; ++module_i2c_driver(ac200_i2c_driver); ++ ++MODULE_DESCRIPTION("MFD core driver for AC200"); ++MODULE_AUTHOR("Jernej Skrabec "); ++MODULE_LICENSE("GPL v2"); +diff -Naur linux-5.3-rc2-old/drivers/mfd/Kconfig linux-5.3-rc2-old-new/drivers/mfd/Kconfig +--- linux-5.3-rc2-old/drivers/mfd/Kconfig 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/mfd/Kconfig 2019-08-03 15:24:25.929999997 +0200 +@@ -178,6 +178,15 @@ + This driver include only the core APIs. You have to select individual + components like codecs or RTC under the corresponding menus. + ++config MFD_AC200 ++ tristate "X-Powers AC200" ++ select MFD_CORE ++ depends on I2C ++ help ++ If you say Y here you get support for the X-Powers AC200 IC. ++ This driver include only the core APIs. You have to select individual ++ components like Ethernet PHY or RTC under the corresponding menus. ++ + config MFD_AXP20X + tristate + select MFD_CORE +diff -Naur linux-5.3-rc2-old/drivers/mfd/Makefile linux-5.3-rc2-old-new/drivers/mfd/Makefile +--- linux-5.3-rc2-old/drivers/mfd/Makefile 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/mfd/Makefile 2019-08-03 15:24:25.929999997 +0200 +@@ -143,6 +143,7 @@ + obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o + + obj-$(CONFIG_MFD_AC100) += ac100.o ++obj-$(CONFIG_MFD_AC200) += ac200.o + obj-$(CONFIG_MFD_AXP20X) += axp20x.o + obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o + obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o +diff -Naur linux-5.3-rc2-old/drivers/net/phy/ac200.c linux-5.3-rc2-old-new/drivers/net/phy/ac200.c +--- linux-5.3-rc2-old/drivers/net/phy/ac200.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-5.3-rc2-old-new/drivers/net/phy/ac200.c 2019-08-03 15:24:25.929999997 +0200 +@@ -0,0 +1,231 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/** ++ * Driver for AC200 Ethernet PHY ++ * ++ * Copyright (c) 2019 Jernej Skrabec ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AC200_EPHY_ID 0x00441400 ++#define AC200_EPHY_ID_MASK 0x0ffffff0 ++ ++/* macros for system ephy control 0 register */ ++#define AC200_EPHY_RESET_INVALID BIT(0) ++#define AC200_EPHY_SYSCLK_GATING BIT(1) ++ ++/* macros for system ephy control 1 register */ ++#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0) ++#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1) ++#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2) ++#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3) ++ ++/* macros for ephy control register */ ++#define AC200_EPHY_SHUTDOWN BIT(0) ++#define AC200_EPHY_LED_POL BIT(1) ++#define AC200_EPHY_CLK_SEL BIT(2) ++#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4) ++#define AC200_EPHY_XMII_SEL BIT(11) ++#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12) ++ ++struct ac200_ephy_dev { ++ struct phy_driver *ephy; ++ struct ac200_dev *ac200; ++}; ++ ++static char *ac200_phy_name = "AC200 EPHY"; ++ ++static void disable_intelligent_ieee(struct phy_device *phydev) ++{ ++ unsigned int value; ++ ++ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */ ++ value = phy_read(phydev, 0x17); ++ value &= ~BIT(3); /* disable IEEE */ ++ phy_write(phydev, 0x17, value); ++ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */ ++} ++ ++static void disable_802_3az_ieee(struct phy_device *phydev) ++{ ++ unsigned int value; ++ ++ phy_write(phydev, 0xd, 0x7); ++ phy_write(phydev, 0xe, 0x3c); ++ phy_write(phydev, 0xd, BIT(14) | 0x7); ++ value = phy_read(phydev, 0xe); ++ value &= ~BIT(1); ++ phy_write(phydev, 0xd, 0x7); ++ phy_write(phydev, 0xe, 0x3c); ++ phy_write(phydev, 0xd, BIT(14) | 0x7); ++ phy_write(phydev, 0xe, value); ++ ++ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */ ++ phy_write(phydev, 0x18, 0x0000); ++} ++ ++static int ac200_ephy_config_init(struct phy_device *phydev) ++{ ++ const struct ac200_ephy_dev *priv = phydev->drv->driver_data; ++ u16 value; ++ ++ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */ ++ phy_write(phydev, 0x12, 0x4824); /* Disable APS */ ++ ++ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */ ++ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */ ++ ++ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */ ++ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */ ++ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */ ++ phy_write(phydev, 0x15, 0x1530); ++ ++ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 6 */ ++ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */ ++ ++ disable_intelligent_ieee(phydev); /* Disable Intelligent IEEE */ ++ disable_802_3az_ieee(phydev); /* Disable 802.3az IEEE */ ++ phy_write(phydev, 0x1f, 0x0000); /* Switch to Page 0 */ ++ ++ value = (phydev->interface == PHY_INTERFACE_MODE_RMII) ? ++ AC200_EPHY_XMII_SEL : 0; ++ ac200_reg_mod(priv->ac200, AC200_EPHY_CTL, AC200_EPHY_XMII_SEL, value); ++ ++ /* FIXME: This is probably H6 specific */ ++ value = phy_read(phydev, 0x13); ++ value |= BIT(12); ++ phy_write(phydev, 0x13, value); ++ ++ return 0; ++ ++} ++ ++static const struct mdio_device_id __maybe_unused ac200_ephy_phy_tbl[] = { ++ { AC200_EPHY_ID, AC200_EPHY_ID_MASK }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(mdio, ac200_ephy_phy_tbl); ++ ++static int ac200_ephy_probe(struct platform_device *pdev) ++{ ++ struct ac200_dev *ac200 = dev_get_drvdata(pdev->dev.parent); ++ struct device *dev = &pdev->dev; ++ struct ac200_ephy_dev *priv; ++ struct nvmem_cell *calcell; ++ struct phy_driver *ephy; ++ u16 *caldata, calib; ++ size_t callen; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ ephy = devm_kzalloc(dev, sizeof(*ephy), GFP_KERNEL); ++ if (!ephy) ++ return -ENOMEM; ++ ++ calcell = devm_nvmem_cell_get(dev, "ephy_calib"); ++ if (IS_ERR(calcell)) { ++ dev_err(dev, "Unable to find calibration data!\n"); ++ return PTR_ERR(calcell); ++ } ++ ++ caldata = nvmem_cell_read(calcell, &callen); ++ if (IS_ERR(caldata)) { ++ dev_err(dev, "Unable to read calibration data!\n"); ++ return PTR_ERR(caldata); ++ } ++ ++ if (callen != 2) { ++ dev_err(dev, "Calibration data has wrong length: 2 != %lu\n", ++ callen); ++ return -EINVAL; ++ } ++ ++ calib = *caldata + 3; ++ kfree(caldata); ++ ++ ephy->phy_id = AC200_EPHY_ID; ++ ephy->phy_id_mask = AC200_EPHY_ID_MASK; ++ ephy->name = ac200_phy_name; ++ ephy->driver_data = priv; ++ ephy->soft_reset = genphy_soft_reset; ++ ephy->config_init = ac200_ephy_config_init; ++ ephy->suspend = genphy_suspend; ++ ephy->resume = genphy_resume; ++ ++ priv->ac200 = ac200; ++ priv->ephy = ephy; ++ platform_set_drvdata(pdev, priv); ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_EPHY_CTL0, ++ AC200_EPHY_RESET_INVALID | ++ AC200_EPHY_SYSCLK_GATING); ++ if (ret) ++ return ret; ++ ++ ret = ac200_reg_write(ac200, AC200_SYS_EPHY_CTL1, ++ AC200_EPHY_E_EPHY_MII_IO_EN | ++ AC200_EPHY_E_LNK_LED_IO_EN | ++ AC200_EPHY_E_SPD_LED_IO_EN | ++ AC200_EPHY_E_DPX_LED_IO_EN); ++ if (ret) ++ return ret; ++ ++ ret = ac200_reg_write(ac200, AC200_EPHY_CTL, ++ AC200_EPHY_LED_POL | ++ AC200_EPHY_CLK_SEL | ++ AC200_EPHY_ADDR(1) | ++ AC200_EPHY_CALIB(calib)); ++ if (ret) ++ return ret; ++ ++ ret = phy_driver_register(priv->ephy, THIS_MODULE); ++ if (ret) { ++ dev_err(dev, "Unable to register phy\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ac200_ephy_remove(struct platform_device *pdev) ++{ ++ struct ac200_ephy_dev *priv = platform_get_drvdata(pdev); ++ ++ phy_driver_unregister(priv->ephy); ++ ++ ac200_reg_write(priv->ac200, AC200_EPHY_CTL, ++ AC200_EPHY_SHUTDOWN); ++ ac200_reg_write(priv->ac200, AC200_SYS_EPHY_CTL1, 0); ++ ac200_reg_write(priv->ac200, AC200_SYS_EPHY_CTL0, 0); ++ ++ return 0; ++} ++ ++static const struct of_device_id ac200_ephy_match[] = { ++ { .compatible = "x-powers,ac200-ephy" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ac200_ephy_match); ++ ++static struct platform_driver ac200_ephy_driver = { ++ .probe = ac200_ephy_probe, ++ .remove = ac200_ephy_remove, ++ .driver = { ++ .name = "ac200-ephy", ++ .of_match_table = ac200_ephy_match, ++ }, ++}; ++module_platform_driver(ac200_ephy_driver); ++ ++MODULE_AUTHOR("Jernej Skrabec >"); ++MODULE_DESCRIPTION("AC200 Ethernet PHY driver"); ++MODULE_LICENSE("GPL"); +diff -Naur linux-5.3-rc2-old/drivers/net/phy/Kconfig linux-5.3-rc2-old-new/drivers/net/phy/Kconfig +--- linux-5.3-rc2-old/drivers/net/phy/Kconfig 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/net/phy/Kconfig 2019-08-03 15:24:25.929999997 +0200 +@@ -244,6 +244,13 @@ + depends on HWMON || HWMON=n + select MDIO_I2C + ++config AC200_PHY ++ tristate "AC200 EPHY" ++ depends on NVMEM ++ depends on OF ++ help ++ Fast ethernet PHY as found in X-Powers AC200 multi-function device. ++ + config AMD_PHY + tristate "AMD PHYs" + ---help--- +diff -Naur linux-5.3-rc2-old/drivers/net/phy/Makefile linux-5.3-rc2-old-new/drivers/net/phy/Makefile +--- linux-5.3-rc2-old/drivers/net/phy/Makefile 2019-07-28 21:47:02.000000000 +0200 ++++ linux-5.3-rc2-old-new/drivers/net/phy/Makefile 2019-08-03 15:24:25.929999997 +0200 +@@ -46,6 +46,7 @@ + sfp-obj-$(CONFIG_SFP) += sfp-bus.o + obj-y += $(sfp-obj-y) $(sfp-obj-m) + ++obj-$(CONFIG_AC200_PHY) += ac200.o + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_AMD_PHY) += amd.o + aquantia-objs += aquantia_main.o + +diff -Naur linux-5.3-rc2-old/include/linux/mfd/ac200.h linux-5.3-rc2-old-new/include/linux/mfd/ac200.h +--- linux-5.3-rc2-old/include/linux/mfd/ac200.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-5.3-rc2-old-new/include/linux/mfd/ac200.h 2019-08-03 15:24:25.929999997 +0200 +@@ -0,0 +1,44 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Functions and registers to access AC200 IC. ++ * ++ * Copyright (C) 2019 Jernej Skrabec ++ */ ++ ++#ifndef __LINUX_MFD_AC200_H ++#define __LINUX_MFD_AC200_H ++ ++#include ++ ++struct ac200_dev; ++ ++/* interface registers (can be accessed from any page) */ ++#define AC200_TWI_CHANGE_TO_RSB 0x3E ++#define AC200_TWI_PAD_DELAY 0xC4 ++#define AC200_TWI_REG_ADDR_H 0xFE ++ ++/* General registers */ ++#define AC200_SYS_VERSION 0x0000 ++#define AC200_SYS_CONTROL 0x0002 ++#define AC200_SYS_IRQ_ENABLE 0x0004 ++#define AC200_SYS_IRQ_STATUS 0x0006 ++#define AC200_SYS_CLK_CTL 0x0008 ++#define AC200_SYS_DLDO_OSC_CTL 0x000A ++#define AC200_SYS_PLL_CTL0 0x000C ++#define AC200_SYS_PLL_CTL1 0x000E ++#define AC200_SYS_AUDIO_CTL0 0x0010 ++#define AC200_SYS_AUDIO_CTL1 0x0012 ++#define AC200_SYS_EPHY_CTL0 0x0014 ++#define AC200_SYS_EPHY_CTL1 0x0016 ++#define AC200_SYS_TVE_CTL0 0x0018 ++#define AC200_SYS_TVE_CTL1 0x001A ++ ++/* EPHY registers */ ++#define AC200_EPHY_CTL 0x6000 ++#define AC200_EPHY_BIST 0x6002 ++ ++int ac200_reg_read(struct ac200_dev *ac200, u16 reg, u16 *value); ++int ac200_reg_write(struct ac200_dev *ac200, u16 reg, u16 value); ++int ac200_reg_mod(struct ac200_dev *ac200, u16 reg, u16 mask, u16 value); ++ ++#endif /* __LINUX_MFD_AC200_H */ diff --git a/patch/kernel/sunxi-dev/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch b/patch/kernel/sunxi-dev/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch new file mode 100644 index 000000000..17a7edb2e --- /dev/null +++ b/patch/kernel/sunxi-dev/0534-arm64-dts-sun50i-h6-add-AC200-nodes.patch @@ -0,0 +1,79 @@ +diff -Naur linux-5.3-rc6-old/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi linux-5.3-rc6-new/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +--- linux-5.3-rc6-old/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 2019-09-02 12:17:46.116666674 +0200 ++++ linux-5.3-rc6-new/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 2019-09-02 12:31:05.253333344 +0200 +@@ -17,6 +17,16 @@ + #address-cells = <1>; + #size-cells = <1>; + ++ ac200_pwm_clk: ac200_clk { ++ compatible = "pwm-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm1_pin>; ++ pwms = <&pwm 1 42 0>; ++ status = "disabled"; ++ }; ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +@@ -241,7 +250,10 @@ + reg = <0x03006000 0x400>; + #address-cells = <1>; + #size-cells = <1>; + ++ ephy_calib: ephy_calib@2c { ++ reg = <0x2c 0x2>; ++ }; + speedbin_efuse: speed@1c { + reg = <0x1c 0x4>; + }; +@@ -326,6 +341,16 @@ + pins = "PH0", "PH1"; + function = "uart0"; + }; ++ ++ i2c3_pins: i2c3-pins { ++ pins = "PB17", "PB18"; ++ function = "i2c3"; ++ }; ++ ++ pwm1_pin: pwm1-pin { ++ pins = "PB19"; ++ function = "pwm1"; ++ }; + }; + + gic: interrupt-controller@3021000 { +@@ -431,6 +456,30 @@ + status = "disabled"; + }; + ++ i2c3: i2c@5002c00 { ++ compatible = "allwinner,sun6i-a31-i2c"; ++ reg = <0x05002c00 0x400>; ++ interrupts = ; ++ clocks = <&ccu CLK_BUS_I2C3>; ++ resets = <&ccu RST_BUS_I2C3>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c3_pins>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ac200: mfd@10 { ++ compatible = "x-powers,ac200"; ++ reg = <0x10>; ++ clocks = <&ac200_pwm_clk>; ++ ++ ac200_ephy: phy { ++ compatible = "x-powers,ac200-ephy"; ++ nvmem-cells = <&ephy_calib>; ++ nvmem-cell-names = "ephy_calib"; ++ }; ++ }; ++ }; ++ + emac: ethernet@5020000 { + compatible = "allwinner,sun50i-h6-emac", + "allwinner,sun50i-a64-emac"; diff --git a/patch/kernel/sunxi-dev/pwm-sun4i-Add-support-for-Allwinner-H6.patch b/patch/kernel/sunxi-dev/pwm-sun4i-Add-support-for-Allwinner-H6.patch new file mode 100644 index 000000000..561966adc --- /dev/null +++ b/patch/kernel/sunxi-dev/pwm-sun4i-Add-support-for-Allwinner-H6.patch @@ -0,0 +1,363 @@ +H6 PWM block is basically the same as A20 PWM, except that it also has +bus clock and reset line which needs to be handled accordingly. + +Expand Allwinner PWM binding with H6 PWM specifics. + +Signed-off-by: Jernej Skrabec +--- + .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 36 ++++++++++++++++++- + 1 file changed, 35 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +index 0ac52f83a58c..deca5d81802f 100644 +--- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml ++++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +@@ -30,13 +30,47 @@ properties: + - items: + - const: allwinner,sun50i-h5-pwm + - const: allwinner,sun5i-a13-pwm ++ - const: allwinner,sun50i-h6-pwm + + reg: + maxItems: 1 + +- clocks: ++ # Even though it only applies to subschemas under the conditionals, ++ # not listing them here will trigger a warning because of the ++ # additionalsProperties set to false. ++ clocks: true ++ clock-names: true ++ resets: + maxItems: 1 + ++allOf: ++ - if: ++ properties: ++ compatible: ++ contains: ++ const: allwinner,sun50i-h6-pwm ++ ++ then: ++ properties: ++ clocks: ++ items: ++ - description: Module Clock ++ - description: Bus Clock ++ ++ clock-names: ++ items: ++ - const: pwm ++ - const: bus ++ ++ required: ++ - clock-names ++ - resets ++ ++ else: ++ properties: ++ clocks: ++ maxItems: 1 ++ + required: + - "#pwm-cells" + - compatible + + +H6 PWM core needs deasserted reset line in order to work. + +Add a quirk for it. + +Signed-off-by: Jernej Skrabec +Acked-by: Maxime Ripard +--- + drivers/pwm/pwm-sun4i.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index de78c824bbfd..1b7be8fbde86 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -72,12 +73,14 @@ static const u32 prescaler_table[] = { + + struct sun4i_pwm_data { + bool has_prescaler_bypass; ++ bool has_reset; + unsigned int npwm; + }; + + struct sun4i_pwm_chip { + struct pwm_chip chip; + struct clk *clk; ++ struct reset_control *rst; + void __iomem *base; + spinlock_t ctrl_lock; + const struct sun4i_pwm_data *data; +@@ -371,6 +374,14 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + if (IS_ERR(pwm->clk)) + return PTR_ERR(pwm->clk); + ++ if (pwm->data->has_reset) { ++ pwm->rst = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(pwm->rst)) ++ return PTR_ERR(pwm->rst); ++ ++ reset_control_deassert(pwm->rst); ++ } ++ + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &sun4i_pwm_ops; + pwm->chip.base = -1; +@@ -383,19 +394,31 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); +- return ret; ++ goto err_pwm_add; + } + + platform_set_drvdata(pdev, pwm); + + return 0; ++ ++err_pwm_add: ++ reset_control_assert(pwm->rst); ++ ++ return ret; + } + + static int sun4i_pwm_remove(struct platform_device *pdev) + { + struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = pwmchip_remove(&pwm->chip); ++ if (ret) ++ return ret; + +- return pwmchip_remove(&pwm->chip); ++ reset_control_assert(pwm->rst); ++ ++ return 0; + } + + static struct platform_driver sun4i_pwm_driver = { + +H6 PWM core needs bus clock to be enabled in order to work. + +Add a quirk for it. + +Signed-off-by: Jernej Skrabec +--- + drivers/pwm/pwm-sun4i.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index 1b7be8fbde86..7d3ac3f2dc3f 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -72,6 +72,7 @@ static const u32 prescaler_table[] = { + }; + + struct sun4i_pwm_data { ++ bool has_bus_clock; + bool has_prescaler_bypass; + bool has_reset; + unsigned int npwm; +@@ -79,6 +80,7 @@ struct sun4i_pwm_data { + + struct sun4i_pwm_chip { + struct pwm_chip chip; ++ struct clk *bus_clk; + struct clk *clk; + struct reset_control *rst; + void __iomem *base; +@@ -382,6 +384,16 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + reset_control_deassert(pwm->rst); + } + ++ if (pwm->data->has_bus_clock) { ++ pwm->bus_clk = devm_clk_get(&pdev->dev, "bus"); ++ if (IS_ERR(pwm->bus_clk)) { ++ ret = PTR_ERR(pwm->bus_clk); ++ goto err_bus; ++ } ++ ++ clk_prepare_enable(pwm->bus_clk); ++ } ++ + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &sun4i_pwm_ops; + pwm->chip.base = -1; +@@ -402,6 +414,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev) + return 0; + + err_pwm_add: ++ clk_disable_unprepare(pwm->bus_clk); ++err_bus: + reset_control_assert(pwm->rst); + + return ret; +@@ -416,6 +430,7 @@ static int sun4i_pwm_remove(struct platform_device *pdev) + if (ret) + return ret; + ++ clk_disable_unprepare(pwm->bus_clk); + reset_control_assert(pwm->rst); + + return 0; + + +Note that while H6 PWM has two channels, only first one is wired to +output pin. Second channel is used as a clock source to companion AC200 +chip which is bundled into same package. + +Signed-off-by: Jernej Skrabec +--- + drivers/pwm/pwm-sun4i.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index 7d3ac3f2dc3f..9e0eca79ff88 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -331,6 +331,13 @@ static const struct sun4i_pwm_data sun4i_pwm_single_bypass = { + .npwm = 1, + }; + ++static const struct sun4i_pwm_data sun50i_pwm_dual_bypass_clk_rst = { ++ .has_bus_clock = true, ++ .has_prescaler_bypass = true, ++ .has_reset = true, ++ .npwm = 2, ++}; ++ + static const struct of_device_id sun4i_pwm_dt_ids[] = { + { + .compatible = "allwinner,sun4i-a10-pwm", +@@ -347,6 +354,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = { + }, { + .compatible = "allwinner,sun8i-h3-pwm", + .data = &sun4i_pwm_single_bypass, ++ }, { ++ .compatible = "allwinner,sun50i-h6-pwm", ++ .data = &sun50i_pwm_dual_bypass_clk_rst, + }, { + /* sentinel */ + }, + + +This mode of operation is needed to achieve high enough frequency to +serve as clock source for AC200 chip, which is integrated into same +package as H6 SoC. + +Signed-off-by: Jernej Skrabec +--- + drivers/pwm/pwm-sun4i.c | 31 ++++++++++++++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c +index 9e0eca79ff88..848cff26f385 100644 +--- a/drivers/pwm/pwm-sun4i.c ++++ b/drivers/pwm/pwm-sun4i.c +@@ -120,6 +120,19 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, + + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + ++ /* ++ * PWM chapter in H6 manual has a diagram which explains that if bypass ++ * bit is set, no other setting has any meaning. Even more, experiment ++ * proved that also enable bit is ignored in this case. ++ */ ++ if (val & BIT_CH(PWM_BYPASS, pwm->hwpwm)) { ++ state->period = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, clk_rate); ++ state->duty_cycle = state->period / 2; ++ state->polarity = PWM_POLARITY_NORMAL; ++ state->enabled = true; ++ return; ++ } ++ + if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) && + sun4i_pwm->data->has_prescaler_bypass) + prescaler = 1; +@@ -211,7 +224,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + { + struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + struct pwm_state cstate; +- u32 ctrl; ++ u32 ctrl, clk_rate; ++ bool bypass; + int ret; + unsigned int delay_us; + unsigned long now; +@@ -226,6 +240,16 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + } + } + ++ /* ++ * Although it would make much more sense to check for bypass in ++ * sun4i_pwm_calculate(), value of bypass bit also depends on "enabled". ++ * Period is allowed to be rounded up or down. ++ */ ++ clk_rate = clk_get_rate(sun4i_pwm->clk); ++ bypass = (state->period == NSEC_PER_SEC / clk_rate || ++ state->period == DIV_ROUND_UP(NSEC_PER_SEC, clk_rate)) && ++ state->enabled; ++ + spin_lock(&sun4i_pwm->ctrl_lock); + ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + +@@ -273,6 +297,11 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + } + ++ if (bypass) ++ ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm); ++ else ++ ctrl &= ~BIT_CH(PWM_BYPASS, pwm->hwpwm); ++ + sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); + + spin_unlock(&sun4i_pwm->ctrl_lock); + +Allwinner H6 PWM is similar to that in A20 except that it has additional +bus clock and reset line. + +Note that first PWM channel is connected to output pin and second +channel is used internally, as a clock source to AC200 co-packaged chip. +This means that any combination of these two channels can be used and +thus it doesn't make sense to add pinctrl nodes at this point. + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index e8bed58e7246..c1abd805cfdc 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -229,6 +229,16 @@ + status = "disabled"; + }; + ++ pwm: pwm@300a000 { ++ compatible = "allwinner,sun50i-h6-pwm"; ++ reg = <0x0300a000 0x400>; ++ clocks = <&osc24M>, <&ccu CLK_BUS_PWM>; ++ clock-names = "pwm", "bus"; ++ resets = <&ccu RST_BUS_PWM>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + pio: pinctrl@300b000 { + compatible = "allwinner,sun50i-h6-pinctrl"; + reg = <0x0300b000 0x400>;