mirror of
https://github.com/Fishwaldo/build.git
synced 2025-06-06 14:21:48 +00:00
[Allwinner] Add analogue audio to H6, enable Cedrus (#1750)
* [Allwinner] Add analogue audio to H6, enable Cedrus, remove deprecated patches, adjust config * Cleanup
This commit is contained in:
parent
cc7ab6a6b1
commit
fc23107e1c
34 changed files with 7927 additions and 49 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
From 98800eeb2244387e821f4af8d21ccf2deaf18da0 Mon Sep 17 00:00:00 2001
|
||||
From: Icenowy Zheng <icenowy@aosc.io>
|
||||
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 <icenowy@aosc.io>
|
||||
---
|
||||
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
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
From 61fd036d01111679b01e4b92e6bd0cdd33809aea Mon Sep 17 00:00:00 2001
|
||||
From: Ricardo Ribalda Delgado <ribalda@kernel.org>
|
||||
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 <p.zabel@pengutronix.de>
|
||||
Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,165 @@
|
|||
|
||||
From d1dc49370f8371b00e682ac409aa1987ce641e93 Mon Sep 17 00:00:00 2001
|
||||
From: Ricardo Ribalda Delgado <ribalda@kernel.org>
|
||||
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 <jacopo@jmondi.org>
|
||||
Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,265 @@
|
|||
|
||||
From f8cca8c97a63d77f48334cde81d15014f43530ef Mon Sep 17 00:00:00 2001
|
||||
From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
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 <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,101 @@
|
|||
|
||||
From bef41d93aac64b54c3008ca6170bec54f85784f5 Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
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 <jernej.skrabec@siol.net>
|
||||
Co-developed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
From f07602ac388723233e9e3c5a05b54baf34e0a3e9 Mon Sep 17 00:00:00 2001
|
||||
From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
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 <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,199 @@
|
|||
|
||||
From 137272cdf7cc5be835f44216e6003769d1638480 Mon Sep 17 00:00:00 2001
|
||||
From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
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 <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
From bac06ec36ea2012ff0daa9767d0f77bf9c6064ec Mon Sep 17 00:00:00 2001
|
||||
From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
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 <boris.brezillon@collabora.com>
|
||||
Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
From 7119ecef4e5ec51e45e6fbe1b5da4385fcae8ded Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
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 <jernej.skrabec@siol.net>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
---
|
||||
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
|
|
@ -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 <jonas@kwiboo.se>
|
||||
---
|
||||
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;
|
|
@ -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 <hverkuil@xs4all.nl>
|
||||
Suggested-by: Maxime Jourdan <mjourdan@baylibre.com>
|
||||
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
||||
---
|
||||
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,
|
|
@ -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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
.../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 <jernej.skrabec@siol.net>
|
||||
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
---
|
||||
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 <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
+#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/time.h>
|
||||
@@ -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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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>;
|
|
@ -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 <jernej.skrabec@siol.net>
|
||||
+ *
|
||||
+ * Based on AC100 driver with following copyrights:
|
||||
+ * Copyright (2016) Chen-Yu Tsai
|
||||
+ */
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/mfd/core.h>
|
||||
+#include <linux/mfd/ac200.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+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 <jernej.skrabec@siol.net>");
|
||||
+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 <jernej.skrabec@siol.net>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mfd/ac200.h>
|
||||
+#include <linux/nvmem-consumer.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#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 <jernej.skrabec@siol.net>>");
|
||||
+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 <jernej.skrabec@siol.net>
|
||||
+ */
|
||||
+
|
||||
+#ifndef __LINUX_MFD_AC200_H
|
||||
+#define __LINUX_MFD_AC200_H
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+
|
||||
+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 */
|
|
@ -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 = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ 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";
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
From dec555256f2cb61ee94975727ec2d4a8d592ac92 Mon Sep 17 00:00:00 2001
|
||||
From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
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 <hverkuil-cisco@xs4all.nl>
|
||||
Reviewed-by: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
.../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
|
|
@ -0,0 +1,135 @@
|
|||
|
||||
From 965c71e8adcff315e16b58c00cd312598fc0222c Mon Sep 17 00:00:00 2001
|
||||
From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
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 <hverkuil-cisco@xs4all.nl>
|
||||
Reviewed-by: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
From eabf10e5e3009e0c7e9a9b98a7f8299e690bcc55 Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
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 <jernej.skrabec@siol.net>
|
||||
[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 <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
From c3b32900fbf5178473c6b39260e891e19067edc2 Mon Sep 17 00:00:00 2001
|
||||
From: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
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 <paul.kocialkowski@bootlin.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,110 @@
|
|||
|
||||
From b4e33e09e7938513bfaba034731c7e84e73c6a5b Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
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 <paul.kocialkowski@bootlin.com>
|
||||
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
---
|
||||
.../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 <linux/delay.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
@@ -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
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
From e2c02ba9bd0068b00628d7874d8a8d1eb5168177 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Karlman <jonas@kwiboo.se>
|
||||
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 <jonas@kwiboo.se>
|
||||
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,118 @@
|
|||
|
||||
From 3aef46bd5bf24a845e05d2531ed61f53ee8c7797 Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
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 <jernej.skrabec@siol.net>
|
||||
Acked-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
|
||||
---
|
||||
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
|
|
@ -0,0 +1,211 @@
|
|||
|
||||
From 03e612e701a61aa9cc9fd8e25cd47d8d685ef675 Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
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 <jernej.skrabec@siol.net>
|
||||
Acked-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
From 0b3e5c15f9cb8b56599c50e6bf4f46ee1c1253bc Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
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 <jernej.skrabec@siol.net>
|
||||
Acked-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
|
||||
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||
---
|
||||
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
|
||||
|
1395
patch/kernel/sunxi-current/0622-media-cedrus-improvements.patch
Normal file
1395
patch/kernel/sunxi-current/0622-media-cedrus-improvements.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,89 @@
|
|||
From 60808cc1810d47f91c368de8ffb7db59cabceaf9 Mon Sep 17 00:00:00 2001
|
||||
From: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
Date: Tue, 28 May 2019 21:05:34 +0200
|
||||
Subject: [PATCH] 10-bit HEVC hack
|
||||
|
||||
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
---
|
||||
.../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
|
|
@ -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) {
|
||||
|
|
|
@ -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 <jernej.skrabec@siol.net>
|
||||
+ *
|
||||
+ * Based on AC100 driver with following copyrights:
|
||||
+ * Copyright (2016) Chen-Yu Tsai
|
||||
+ */
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/mfd/core.h>
|
||||
+#include <linux/mfd/ac200.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/regmap.h>
|
||||
+
|
||||
+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 <jernej.skrabec@siol.net>");
|
||||
+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 <jernej.skrabec@siol.net>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mfd/ac200.h>
|
||||
+#include <linux/nvmem-consumer.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#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 <jernej.skrabec@siol.net>>");
|
||||
+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 <jernej.skrabec@siol.net>
|
||||
+ */
|
||||
+
|
||||
+#ifndef __LINUX_MFD_AC200_H
|
||||
+#define __LINUX_MFD_AC200_H
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+
|
||||
+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 */
|
|
@ -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 = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
+ 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";
|
|
@ -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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
.../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 <jernej.skrabec@siol.net>
|
||||
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
---
|
||||
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 <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
+#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/time.h>
|
||||
@@ -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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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 <jernej.skrabec@siol.net>
|
||||
---
|
||||
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>;
|
Loading…
Add table
Reference in a new issue