* [PATCH 0/3] media: sunxi: Add H6 deinterlace driver
@ 2022-11-01 12:31 Jernej Skrabec
2022-11-01 12:31 ` [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding Jernej Skrabec
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Jernej Skrabec @ 2022-11-01 12:31 UTC (permalink / raw)
To: mchehab, hverkuil-cisco
Cc: robh+dt, krzysztof.kozlowski+dt, wens, samuel, linux-media,
devicetree, linux-arm-kernel, linux-sunxi, linux-kernel,
Jernej Skrabec
This series implements driver for H6 deinterlace core, which is
newer version of core, covered by sun8i-di (v2.3). Contrary to
older one, it doesn't support scaling, but it supports iommu,
has additional motion compensated deinterlacing algorithm and
supports different pixel formats.
v4l2-compliance 1.23.0-4961, 64 bits, 64-bit time_t
v4l2-compliance SHA: f86484524f32 2022-10-21 10:08:58
Compliance test for sun50i-di device /dev/video0:
Driver Info:
Driver name : sun50i-di
Card type : sun50i-di
Bus info : platform:sun50i-di
Driver version : 6.1.0
Capabilities : 0x84208000
Video Memory-to-Memory
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04208000
Video Memory-to-Memory
Streaming
Extended Pix Format
Required ioctls:
test VIDIOC_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 0 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Control ioctls:
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls:
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls:
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)
Total for sun50i-di device /dev/video0: 45, Succeeded: 45, Failed: 0, Warnings: 0
Best regards,
Jernej
Jernej Skrabec (3):
media: dt-bindings: media: Add Allwinner H6 Deinterlace binding
media: sunxi: Add H6 deinterlace driver
arm64: dts: allwinner: h6: Add deinterlace node
.../allwinner,sun50i-h6-deinterlace.yaml | 74 ++
MAINTAINERS | 4 +-
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 12 +
drivers/media/platform/sunxi/Kconfig | 1 +
drivers/media/platform/sunxi/Makefile | 1 +
.../media/platform/sunxi/sun50i-di/Kconfig | 15 +
.../media/platform/sunxi/sun50i-di/Makefile | 2 +
.../platform/sunxi/sun50i-di/sun50i-di.c | 1142 +++++++++++++++++
.../platform/sunxi/sun50i-di/sun50i-di.h | 175 +++
9 files changed, 1425 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml
create mode 100644 drivers/media/platform/sunxi/sun50i-di/Kconfig
create mode 100644 drivers/media/platform/sunxi/sun50i-di/Makefile
create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.c
create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.h
--
2.38.1
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding 2022-11-01 12:31 [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec @ 2022-11-01 12:31 ` Jernej Skrabec 2022-11-02 17:14 ` Rob Herring 2022-11-05 3:02 ` Samuel Holland 2022-11-01 12:32 ` [PATCH 2/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec ` (2 subsequent siblings) 3 siblings, 2 replies; 11+ messages in thread From: Jernej Skrabec @ 2022-11-01 12:31 UTC (permalink / raw) To: mchehab, hverkuil-cisco Cc: robh+dt, krzysztof.kozlowski+dt, wens, samuel, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel, Jernej Skrabec Allwinner H6 Deinterlace core is used for deinterlacing interlaced video content. Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> --- .../allwinner,sun50i-h6-deinterlace.yaml | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml diff --git a/Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml b/Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml new file mode 100644 index 000000000000..521f58559444 --- /dev/null +++ b/Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/allwinner,sun50i-h6-deinterlace.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner H6 Deinterlace + +maintainers: + - Jernej Skrabec <jernej.skrabec@gmail.com> + - Chen-Yu Tsai <wens@csie.org> + - Samuel Holland <samuel@sholland.org> + +description: |- + The Allwinner H6 has a deinterlace core used for deinterlacing + interlaced video content. + +properties: + compatible: + const: allwinner,sun50i-h6-deinterlace + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Deinterlace interface clock + - description: Deinterlace module clock + - description: Deinterlace DRAM clock + + clock-names: + items: + - const: bus + - const: mod + - const: ram + + resets: + maxItems: 1 + + iommus: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/sun50i-h6-ccu.h> + #include <dt-bindings/reset/sun50i-h6-ccu.h> + + deinterlace@1420000 { + compatible = "allwinner,sun50i-h6-deinterlace"; + reg = <0x01420000 0x2000>; + clocks = <&ccu CLK_BUS_DEINTERLACE>, + <&ccu CLK_DEINTERLACE>, + <&ccu CLK_MBUS_DEINTERLACE>; + clock-names = "bus", "mod", "ram"; + resets = <&ccu RST_BUS_DEINTERLACE>; + interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&iommu 2>; + }; + +... -- 2.38.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding 2022-11-01 12:31 ` [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding Jernej Skrabec @ 2022-11-02 17:14 ` Rob Herring 2022-11-05 3:02 ` Samuel Holland 1 sibling, 0 replies; 11+ messages in thread From: Rob Herring @ 2022-11-02 17:14 UTC (permalink / raw) To: Jernej Skrabec Cc: samuel, robh+dt, linux-kernel, krzysztof.kozlowski+dt, devicetree, linux-arm-kernel, linux-sunxi, hverkuil-cisco, wens, mchehab, linux-media On Tue, 01 Nov 2022 13:31:59 +0100, Jernej Skrabec wrote: > Allwinner H6 Deinterlace core is used for deinterlacing interlaced video > content. > > Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> > --- > .../allwinner,sun50i-h6-deinterlace.yaml | 74 +++++++++++++++++++ > 1 file changed, 74 insertions(+) > create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml > Reviewed-by: Rob Herring <robh@kernel.org> ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding 2022-11-01 12:31 ` [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding Jernej Skrabec 2022-11-02 17:14 ` Rob Herring @ 2022-11-05 3:02 ` Samuel Holland 1 sibling, 0 replies; 11+ messages in thread From: Samuel Holland @ 2022-11-05 3:02 UTC (permalink / raw) To: Jernej Skrabec, mchehab, hverkuil-cisco Cc: robh+dt, krzysztof.kozlowski+dt, wens, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel On 11/1/22 07:31, Jernej Skrabec wrote: > Allwinner H6 Deinterlace core is used for deinterlacing interlaced video > content. > > Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> > --- > .../allwinner,sun50i-h6-deinterlace.yaml | 74 +++++++++++++++++++ > 1 file changed, 74 insertions(+) > create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml Reviewed-by: Samuel Holland <samuel@sholland.org> ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/3] media: sunxi: Add H6 deinterlace driver 2022-11-01 12:31 [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec 2022-11-01 12:31 ` [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding Jernej Skrabec @ 2022-11-01 12:32 ` Jernej Skrabec 2022-11-05 3:28 ` Samuel Holland 2022-11-01 12:32 ` [PATCH 3/3] arm64: dts: allwinner: h6: Add deinterlace node Jernej Skrabec 2022-11-04 11:33 ` [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Hans Verkuil 3 siblings, 1 reply; 11+ messages in thread From: Jernej Skrabec @ 2022-11-01 12:32 UTC (permalink / raw) To: mchehab, hverkuil-cisco Cc: robh+dt, krzysztof.kozlowski+dt, wens, samuel, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel, Jernej Skrabec This driver covers H6 deinterlace core, which is marked in vendor driver as v2.3. Contrary to older cores, covered by sun8i-di, it doesn't support scaling, but it supports iommu and has additional motion compensated deinterlacing algorithm. Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> --- MAINTAINERS | 4 +- drivers/media/platform/sunxi/Kconfig | 1 + drivers/media/platform/sunxi/Makefile | 1 + .../media/platform/sunxi/sun50i-di/Kconfig | 15 + .../media/platform/sunxi/sun50i-di/Makefile | 2 + .../platform/sunxi/sun50i-di/sun50i-di.c | 1142 +++++++++++++++++ .../platform/sunxi/sun50i-di/sun50i-di.h | 175 +++ 7 files changed, 1339 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/sunxi/sun50i-di/Kconfig create mode 100644 drivers/media/platform/sunxi/sun50i-di/Makefile create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.c create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.h diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..df35acab10fc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5781,12 +5781,14 @@ M: "Maciej W. Rozycki" <macro@orcam.me.uk> S: Maintained F: drivers/net/fddi/defza.* -DEINTERLACE DRIVERS FOR ALLWINNER H3 +DEINTERLACE DRIVERS FOR ALLWINNER SOCS M: Jernej Skrabec <jernej.skrabec@gmail.com> L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml F: Documentation/devicetree/bindings/media/allwinner,sun8i-h3-deinterlace.yaml +F: drivers/media/platform/sunxi/sun50i-di/ F: drivers/media/platform/sunxi/sun8i-di/ DELL LAPTOP DRIVER diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig index 2dd15083a1d9..413a79d23625 100644 --- a/drivers/media/platform/sunxi/Kconfig +++ b/drivers/media/platform/sunxi/Kconfig @@ -7,4 +7,5 @@ source "drivers/media/platform/sunxi/sun6i-csi/Kconfig" source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig" source "drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig" source "drivers/media/platform/sunxi/sun8i-di/Kconfig" +source "drivers/media/platform/sunxi/sun50i-di/Kconfig" source "drivers/media/platform/sunxi/sun8i-rotate/Kconfig" diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile index 9aa01cb01883..f92927f49f93 100644 --- a/drivers/media/platform/sunxi/Makefile +++ b/drivers/media/platform/sunxi/Makefile @@ -5,4 +5,5 @@ obj-y += sun6i-csi/ obj-y += sun6i-mipi-csi2/ obj-y += sun8i-a83t-mipi-csi2/ obj-y += sun8i-di/ +obj-y += sun50i-di/ obj-y += sun8i-rotate/ diff --git a/drivers/media/platform/sunxi/sun50i-di/Kconfig b/drivers/media/platform/sunxi/sun50i-di/Kconfig new file mode 100644 index 000000000000..cc92f5086862 --- /dev/null +++ b/drivers/media/platform/sunxi/sun50i-di/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SUN50I_DEINTERLACE + tristate "Allwinner Deinterlace v2 driver" + depends on V4L_MEM2MEM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on COMMON_CLK && OF + depends on PM + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Allwinner deinterlace v2 unit found on + some SoCs, like H6. + To compile this driver as a module choose m here. The + module will be called sun50i-di. diff --git a/drivers/media/platform/sunxi/sun50i-di/Makefile b/drivers/media/platform/sunxi/sun50i-di/Makefile new file mode 100644 index 000000000000..225b3b808069 --- /dev/null +++ b/drivers/media/platform/sunxi/sun50i-di/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_SUN50I_DEINTERLACE) += sun50i-di.o diff --git a/drivers/media/platform/sunxi/sun50i-di/sun50i-di.c b/drivers/media/platform/sunxi/sun50i-di/sun50i-di.c new file mode 100644 index 000000000000..3ee8786cecc0 --- /dev/null +++ b/drivers/media/platform/sunxi/sun50i-di/sun50i-di.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner sun50i deinterlacer driver + * + * Copyright (C) 2022 Jernej Skrabec <jernej.skrabec@gmail.com> + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> + +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> + +#include "sun50i-di.h" + +#define FLAG_PITCH (DEINTERLACE_MAX_WIDTH / 4) +#define FLAG_SIZE (FLAG_PITCH * DEINTERLACE_MAX_HEIGHT) + +static const u32 deinterlace_formats[] = { + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_NV16, + V4L2_PIX_FMT_NV61, + V4L2_PIX_FMT_YUV422P +}; + +static u32 deinterlace_read(struct deinterlace_dev *dev, u32 reg) +{ + return readl(dev->base + reg); +} + +static void deinterlace_write(struct deinterlace_dev *dev, + u32 reg, u32 value) +{ + writel(value, dev->base + reg); +} + +static void deinterlace_device_run(void *priv) +{ + dma_addr_t buf, prev, curr, next, addr[4][3]; + struct deinterlace_ctx *ctx = priv; + struct deinterlace_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *src, *dst; + u32 reg, pitch[3], offset[2]; + unsigned int val; + bool motion; + + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + v4l2_m2m_buf_copy_metadata(src, dst, true); + + reg = DEINTERLACE_SIZE_WIDTH(ctx->src_fmt.width); + reg |= DEINTERLACE_SIZE_HEIGHT(ctx->src_fmt.height); + deinterlace_write(dev, DEINTERLACE_SIZE, reg); + + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + reg = DEINTERLACE_FORMAT_YUV420SP; + break; + case V4L2_PIX_FMT_YUV420: + reg = DEINTERLACE_FORMAT_YUV420P; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + reg = DEINTERLACE_FORMAT_YUV422SP; + break; + case V4L2_PIX_FMT_YUV422P: + reg = DEINTERLACE_FORMAT_YUV422P; + break; + } + deinterlace_write(dev, DEINTERLACE_FORMAT, reg); + + pitch[0] = ctx->src_fmt.bytesperline; + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YUV422P: + pitch[1] = pitch[0] / 2; + pitch[2] = pitch[1]; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + pitch[1] = pitch[0]; + pitch[2] = 0; + break; + } + + /* + * Deinterlacer assumes that each input has a single field. In + * reality, each input has two interlaced fields. By doubling + * pitches we satisfy deinterlacer's assumption. That way + * every second line will be skipped and it will appear to + * the deinterlacer as a single field. + */ + deinterlace_write(dev, DEINTERLACE_IN_PITCH0, pitch[0] * 2); + deinterlace_write(dev, DEINTERLACE_IN_PITCH1, pitch[1] * 2); + deinterlace_write(dev, DEINTERLACE_IN_PITCH2, pitch[2] * 2); + + deinterlace_write(dev, DEINTERLACE_OUT_PITCH0, pitch[0]); + deinterlace_write(dev, DEINTERLACE_OUT_PITCH1, pitch[1]); + deinterlace_write(dev, DEINTERLACE_OUT_PITCH2, pitch[2]); + + deinterlace_write(dev, DEINTERLACE_FLAG_PITCH, FLAG_PITCH); + + offset[0] = pitch[0] * ctx->src_fmt.height; + switch (ctx->src_fmt.pixelformat) { + case V4L2_PIX_FMT_YUV420: + offset[1] = offset[0] + offset[0] / 4; + break; + case V4L2_PIX_FMT_YUV422P: + offset[1] = offset[0] + offset[0] / 2; + break; + default: + offset[1] = 0; + break; + } + + /* + * Make sure that all inputs are valid. If history is not + * long enough, reuse last buffer as many times as needed. + */ + buf = vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0); + next = buf; + if (ctx->prev[0]) + buf = vb2_dma_contig_plane_dma_addr(&ctx->prev[0]->vb2_buf, 0); + curr = buf; + if (ctx->prev[1]) + buf = vb2_dma_contig_plane_dma_addr(&ctx->prev[1]->vb2_buf, 0); + prev = buf; + + if (ctx->first_field == 0) { + if (ctx->field == 0) { + addr[0][0] = prev; + addr[0][1] = prev; + addr[0][2] = prev; + addr[1][0] = prev + pitch[0]; + addr[1][1] = prev + pitch[1]; + addr[1][2] = prev + pitch[2]; + addr[2][0] = curr; + addr[2][1] = curr; + addr[2][2] = curr; + addr[3][0] = curr + pitch[0]; + addr[3][1] = curr + pitch[1]; + addr[3][2] = curr + pitch[2]; + } else { + addr[0][0] = prev + pitch[0]; + addr[0][1] = prev + pitch[1]; + addr[0][2] = prev + pitch[2]; + addr[1][0] = curr; + addr[1][1] = curr; + addr[1][2] = curr; + addr[2][0] = curr + pitch[0]; + addr[2][1] = curr + pitch[1]; + addr[2][2] = curr + pitch[2]; + addr[3][0] = next; + addr[3][1] = next; + addr[3][2] = next; + } + } else { + if (ctx->field == 0) { + addr[0][0] = prev; + addr[0][1] = prev; + addr[0][2] = prev; + addr[1][0] = curr + pitch[0]; + addr[1][1] = curr + pitch[1]; + addr[1][2] = curr + pitch[2]; + addr[2][0] = curr; + addr[2][1] = curr; + addr[2][2] = curr; + addr[3][0] = next + pitch[0]; + addr[3][1] = next + pitch[1]; + addr[3][2] = next + pitch[2]; + } else { + addr[0][0] = prev + pitch[0]; + addr[0][1] = prev + pitch[1]; + addr[0][2] = prev + pitch[2]; + addr[1][0] = prev; + addr[1][1] = prev; + addr[1][2] = prev; + addr[2][0] = curr + pitch[0]; + addr[2][1] = curr + pitch[1]; + addr[2][2] = curr + pitch[2]; + addr[3][0] = curr; + addr[3][1] = curr; + addr[3][2] = curr; + } + } + + deinterlace_write(dev, DEINTERLACE_IN0_ADDR0, addr[0][0]); + deinterlace_write(dev, DEINTERLACE_IN0_ADDR1, addr[0][1] + offset[0]); + deinterlace_write(dev, DEINTERLACE_IN0_ADDR2, addr[0][2] + offset[1]); + + deinterlace_write(dev, DEINTERLACE_IN1_ADDR0, addr[1][0]); + deinterlace_write(dev, DEINTERLACE_IN1_ADDR1, addr[1][1] + offset[0]); + deinterlace_write(dev, DEINTERLACE_IN1_ADDR2, addr[1][2] + offset[1]); + + deinterlace_write(dev, DEINTERLACE_IN2_ADDR0, addr[2][0]); + deinterlace_write(dev, DEINTERLACE_IN2_ADDR1, addr[2][1] + offset[0]); + deinterlace_write(dev, DEINTERLACE_IN2_ADDR2, addr[2][2] + offset[1]); + + deinterlace_write(dev, DEINTERLACE_IN3_ADDR0, addr[3][0]); + deinterlace_write(dev, DEINTERLACE_IN3_ADDR1, addr[3][1] + offset[0]); + deinterlace_write(dev, DEINTERLACE_IN3_ADDR2, addr[3][2] + offset[1]); + + buf = vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0); + deinterlace_write(dev, DEINTERLACE_OUT_ADDR0, buf); + deinterlace_write(dev, DEINTERLACE_OUT_ADDR1, buf + offset[0]); + deinterlace_write(dev, DEINTERLACE_OUT_ADDR2, buf + offset[1]); + + if (ctx->field) { + deinterlace_write(dev, DEINTERLACE_IN_FLAG_ADDR, + ctx->flag1_buf_dma); + deinterlace_write(dev, DEINTERLACE_OUT_FLAG_ADDR, + ctx->flag2_buf_dma); + } else { + deinterlace_write(dev, DEINTERLACE_IN_FLAG_ADDR, + ctx->flag2_buf_dma); + deinterlace_write(dev, DEINTERLACE_OUT_FLAG_ADDR, + ctx->flag1_buf_dma); + } + + if (ctx->first_field == 0) + val = 4; + else + val = 5; + + reg = DEINTERLACE_INTP_PARAM0_ANGLE_LIMIT(20); + reg |= DEINTERLACE_INTP_PARAM0_ANGLE_CONST_TH(5); + reg |= DEINTERLACE_INTP_PARAM0_LUMA_CUR_FAC_MODE(val); + reg |= DEINTERLACE_INTP_PARAM0_CHROMA_CUR_FAC_MODE(val); + deinterlace_write(dev, DEINTERLACE_INTP_PARAM0, reg); + + reg = DEINTERLACE_POLAR_FIELD(ctx->field); + deinterlace_write(dev, DEINTERLACE_POLAR, reg); + + motion = ctx->prev[0] && ctx->prev[1]; + reg = DEINTERLACE_MODE_DEINT_LUMA; + reg |= DEINTERLACE_MODE_DEINT_CHROMA; + reg |= DEINTERLACE_MODE_INTP_EN; + reg |= DEINTERLACE_MODE_AUTO_UPD_MODE(ctx->first_field); + if (motion) + reg |= DEINTERLACE_MODE_MOTION_EN; + else + reg |= DEINTERLACE_MODE_FIELD_MODE; + deinterlace_write(dev, DEINTERLACE_MODE, reg); + + /* Start the watchdog timer. */ + schedule_delayed_work(&dev->watchdog_work, msecs_to_jiffies(1000)); + + deinterlace_write(dev, DEINTERLACE_INT_CTRL, DEINTERLACE_INT_EN); + + reg = DEINTERLACE_CTRL_START; + if (device_iommu_mapped(dev->dev)) + reg |= DEINTERLACE_CTRL_IOMMU_EN; + deinterlace_write(dev, DEINTERLACE_CTRL, reg); +} + +static void deinterlace_end_job(struct deinterlace_dev *dev, + enum vb2_buffer_state state) +{ + struct deinterlace_ctx *ctx; + struct vb2_v4l2_buffer *dst; + + deinterlace_write(dev, DEINTERLACE_INT_CTRL, 0); + deinterlace_write(dev, DEINTERLACE_STATUS, DEINTERLACE_STATUS_FINISHED); + + /* vendor driver always resets the core, so do that here too */ + deinterlace_write(dev, DEINTERLACE_CTRL, DEINTERLACE_CTRL_RESET); + udelay(1); + deinterlace_write(dev, DEINTERLACE_CTRL, 0); + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + return; + } + + if (state == VB2_BUF_STATE_ERROR) { + memset(ctx->flag1_buf, 0, FLAG_SIZE); + memset(ctx->flag2_buf, 0, FLAG_SIZE); + } + + dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(dst, state); + + if (ctx->field != ctx->first_field || ctx->aborting) { + ctx->field = ctx->first_field; + + if (ctx->prev[1]) + v4l2_m2m_buf_done(ctx->prev[1], VB2_BUF_STATE_DONE); + ctx->prev[1] = ctx->prev[0]; + ctx->prev[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + + v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); + } else { + ctx->field = !ctx->first_field; + deinterlace_device_run(ctx); + } +} + +static irqreturn_t deinterlace_irq(int irq, void *data) +{ + struct deinterlace_dev *dev = data; + unsigned int status; + + status = deinterlace_read(dev, DEINTERLACE_STATUS); + if (!(status & DEINTERLACE_STATUS_FINISHED)) + return IRQ_NONE; + + /* + * If cancel_delayed_work returns false it means watchdog already + * executed and finished the job. + */ + if (!cancel_delayed_work(&dev->watchdog_work)) + return IRQ_HANDLED; + + deinterlace_end_job(dev, VB2_BUF_STATE_DONE); + + return IRQ_HANDLED; +} + +static void deinterlace_watchdog(struct work_struct *work) +{ + struct deinterlace_dev *dev; + + dev = container_of(to_delayed_work(work), + struct deinterlace_dev, watchdog_work); + + deinterlace_end_job(dev, VB2_BUF_STATE_ERROR); +} + +static void deinterlace_init(struct deinterlace_dev *dev) +{ + u32 reg; + + deinterlace_write(dev, DEINTERLACE_OUT_PATH, 0); + + reg = DEINTERLACE_MD_PARAM0_MIN_LUMA_TH(4); + reg |= DEINTERLACE_MD_PARAM0_MAX_LUMA_TH(12); + reg |= DEINTERLACE_MD_PARAM0_AVG_LUMA_SHIFT(6); + reg |= DEINTERLACE_MD_PARAM0_TH_SHIFT(1); + deinterlace_write(dev, DEINTERLACE_MD_PARAM0, reg); + + reg = DEINTERLACE_MD_PARAM1_MOV_FAC_NONEDGE(2); + deinterlace_write(dev, DEINTERLACE_MD_PARAM1, reg); + + reg = DEINTERLACE_MD_PARAM2_CHROMA_SPATIAL_TH(128); + reg |= DEINTERLACE_MD_PARAM2_CHROMA_DIFF_TH(5); + reg |= DEINTERLACE_MD_PARAM2_PIX_STATIC_TH(3); + deinterlace_write(dev, DEINTERLACE_MD_PARAM2, reg); + + reg = DEINTERLACE_MD_CH_PARAM_BLEND_MODE(1); + reg |= DEINTERLACE_MD_CH_PARAM_FONT_PRO_EN; + reg |= DEINTERLACE_MD_CH_PARAM_FONT_PRO_TH(48); + reg |= DEINTERLACE_MD_CH_PARAM_FONT_PRO_FAC(4); + deinterlace_write(dev, DEINTERLACE_MD_CH_PARAM, reg); + + reg = DEINTERLACE_INTP_PARAM1_A(4); + reg |= DEINTERLACE_INTP_PARAM1_EN; + reg |= DEINTERLACE_INTP_PARAM1_C(10); + reg |= DEINTERLACE_INTP_PARAM1_CMAX(64); + reg |= DEINTERLACE_INTP_PARAM1_MAXRAT(2); + deinterlace_write(dev, DEINTERLACE_INTP_PARAM1, reg); + + /* only 32-bit addresses are supported, so high bits are always 0 */ + deinterlace_write(dev, DEINTERLACE_IN0_ADDRH, 0); + deinterlace_write(dev, DEINTERLACE_IN1_ADDRH, 0); + deinterlace_write(dev, DEINTERLACE_IN2_ADDRH, 0); + deinterlace_write(dev, DEINTERLACE_IN3_ADDRH, 0); + deinterlace_write(dev, DEINTERLACE_OUT_ADDRH, 0); + deinterlace_write(dev, DEINTERLACE_FLAG_ADDRH, 0); +} + +static int deinterlace_job_ready(void *priv) +{ + struct deinterlace_ctx *ctx = priv; + + return v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) >= 1 && + v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) >= 2; +} + +static void deinterlace_job_abort(void *priv) +{ + struct deinterlace_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; +} + +static inline struct deinterlace_ctx *deinterlace_file2ctx(struct file *file) +{ + return container_of(file->private_data, struct deinterlace_ctx, fh); +} + +static bool deinterlace_check_format(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(deinterlace_formats); i++) + if (deinterlace_formats[i] == pixelformat) + return true; + + return false; +} + +static void deinterlace_prepare_format(struct v4l2_pix_format *pix_fmt) +{ + unsigned int bytesperline = pix_fmt->bytesperline; + unsigned int height = pix_fmt->height; + unsigned int width = pix_fmt->width; + unsigned int sizeimage; + + width = clamp(width, DEINTERLACE_MIN_WIDTH, + DEINTERLACE_MAX_WIDTH); + height = clamp(height, DEINTERLACE_MIN_HEIGHT, + DEINTERLACE_MAX_HEIGHT); + + /* try to respect userspace wishes about pitch */ + bytesperline = ALIGN(bytesperline, 2); + if (bytesperline < ALIGN(width, 2)) + bytesperline = ALIGN(width, 2); + + /* luma */ + sizeimage = bytesperline * height; + + /* chroma */ + switch (pix_fmt->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_YUV420: + sizeimage += bytesperline * height / 2; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_YUV422P: + sizeimage += bytesperline * height; + break; + } + + if (pix_fmt->sizeimage < sizeimage) + pix_fmt->sizeimage = sizeimage; + + pix_fmt->width = width; + pix_fmt->height = height; + pix_fmt->bytesperline = bytesperline; +} + +static int deinterlace_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, DEINTERLACE_NAME, sizeof(cap->driver)); + strscpy(cap->card, DEINTERLACE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", DEINTERLACE_NAME); + + return 0; +} + +static int deinterlace_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index < ARRAY_SIZE(deinterlace_formats)) { + f->pixelformat = deinterlace_formats[f->index]; + + return 0; + } + + return -EINVAL; +} + +static int deinterlace_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index != 0) + return -EINVAL; + + if (!deinterlace_check_format(fsize->pixel_format)) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = DEINTERLACE_MIN_WIDTH; + fsize->stepwise.min_height = DEINTERLACE_MIN_HEIGHT; + fsize->stepwise.max_width = DEINTERLACE_MAX_WIDTH; + fsize->stepwise.max_height = DEINTERLACE_MAX_HEIGHT; + fsize->stepwise.step_width = 2; + + switch (fsize->pixel_format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_YUV420: + fsize->stepwise.step_height = 2; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_YUV422P: + fsize->stepwise.step_height = 1; + break; + } + + return 0; +} + +static int deinterlace_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + + f->fmt.pix = ctx->dst_fmt; + + return 0; +} + +static int deinterlace_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + + f->fmt.pix = ctx->src_fmt; + + return 0; +} + +static void deinterlace_try_cap_format(struct deinterlace_ctx *ctx, + struct v4l2_pix_format *fmt) +{ + fmt->pixelformat = ctx->src_fmt.pixelformat; + fmt->field = V4L2_FIELD_NONE; + fmt->width = ctx->src_fmt.width; + fmt->height = ctx->src_fmt.height; + + deinterlace_prepare_format(fmt); +} + +static void deinterlace_try_out_format(struct v4l2_pix_format *fmt) +{ + if (!deinterlace_check_format(fmt->pixelformat)) + fmt->pixelformat = deinterlace_formats[0]; + + if (fmt->field != V4L2_FIELD_INTERLACED_TB && + fmt->field != V4L2_FIELD_INTERLACED_BT && + fmt->field != V4L2_FIELD_INTERLACED) + fmt->field = V4L2_FIELD_INTERLACED; + + deinterlace_prepare_format(fmt); +} + +static int deinterlace_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + deinterlace_try_cap_format(deinterlace_file2ctx(file), &f->fmt.pix); + + return 0; +} + +static int deinterlace_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + deinterlace_try_out_format(&f->fmt.pix); + + return 0; +} + +static void deinterlace_set_cap_format(struct deinterlace_ctx *ctx, + struct v4l2_pix_format *fmt) +{ + deinterlace_try_cap_format(ctx, fmt); + + ctx->dst_fmt = *fmt; +} + +static void deinterlace_set_out_format(struct deinterlace_ctx *ctx, + struct v4l2_pix_format *fmt) +{ + deinterlace_try_out_format(fmt); + + ctx->src_fmt = *fmt; + + /* Propagate colorspace information to capture. */ + ctx->dst_fmt.colorspace = fmt->colorspace; + ctx->dst_fmt.xfer_func = fmt->xfer_func; + ctx->dst_fmt.ycbcr_enc = fmt->ycbcr_enc; + ctx->dst_fmt.quantization = fmt->quantization; + + deinterlace_set_cap_format(ctx, &ctx->dst_fmt); +} + +static int deinterlace_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + deinterlace_set_cap_format(ctx, &f->fmt.pix); + + return 0; +} + +static int deinterlace_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct deinterlace_ctx *ctx = deinterlace_file2ctx(file); + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + /* + * Pixel format and size must be propagated to capture format too, + * so capture queue must not be busy. + */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (vb2_is_busy(vq)) + return -EBUSY; + + deinterlace_set_out_format(ctx, &f->fmt.pix); + + return 0; +} + +static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { + .vidioc_querycap = deinterlace_querycap, + + .vidioc_enum_framesizes = deinterlace_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = deinterlace_enum_fmt, + .vidioc_g_fmt_vid_cap = deinterlace_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = deinterlace_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = deinterlace_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = deinterlace_enum_fmt, + .vidioc_g_fmt_vid_out = deinterlace_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = deinterlace_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = deinterlace_s_fmt_vid_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, +}; + +static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + + if (*nplanes) { + if (sizes[0] < pix_fmt->sizeimage) + return -EINVAL; + } else { + sizes[0] = pix_fmt->sizeimage; + *nplanes = 1; + } + + return 0; +} + +static int deinterlace_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + + if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage); + + return 0; +} + +static void deinterlace_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void deinterlace_queue_cleanup(struct vb2_queue *vq, u32 state) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf; + + do { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (vbuf) + v4l2_m2m_buf_done(vbuf, state); + } while (vbuf); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (ctx->prev[0]) + v4l2_m2m_buf_done(ctx->prev[0], state); + if (ctx->prev[1]) + v4l2_m2m_buf_done(ctx->prev[1], state); + } +} + +static int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + struct device *dev = ctx->dev->dev; + int ret; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to enable module\n"); + + goto err_runtime_get; + } + + ctx->first_field = + ctx->src_fmt.field == V4L2_FIELD_INTERLACED_BT; + ctx->field = ctx->first_field; + + ctx->prev[0] = NULL; + ctx->prev[1] = NULL; + ctx->aborting = 0; + + ctx->flag1_buf = dma_alloc_coherent(dev, FLAG_SIZE, + &ctx->flag1_buf_dma, + GFP_KERNEL); + if (!ctx->flag1_buf) { + ret = -ENOMEM; + + goto err_no_mem1; + } + + ctx->flag2_buf = dma_alloc_coherent(dev, FLAG_SIZE, + &ctx->flag2_buf_dma, + GFP_KERNEL); + if (!ctx->flag2_buf) { + ret = -ENOMEM; + + goto err_no_mem2; + } + } + + return 0; + +err_no_mem2: + dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, + ctx->flag1_buf_dma); +err_no_mem1: + pm_runtime_put(dev); +err_runtime_get: + deinterlace_queue_cleanup(vq, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void deinterlace_stop_streaming(struct vb2_queue *vq) +{ + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + struct device *dev = ctx->dev->dev; + + dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, + ctx->flag1_buf_dma); + dma_free_coherent(dev, FLAG_SIZE, ctx->flag2_buf, + ctx->flag2_buf_dma); + + pm_runtime_put(dev); + } + + deinterlace_queue_cleanup(vq, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops deinterlace_qops = { + .queue_setup = deinterlace_queue_setup, + .buf_prepare = deinterlace_buf_prepare, + .buf_queue = deinterlace_buf_queue, + .start_streaming = deinterlace_start_streaming, + .stop_streaming = deinterlace_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct deinterlace_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->min_buffers_needed = 1; + src_vq->ops = &deinterlace_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = ctx->dev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->min_buffers_needed = 2; + dst_vq->ops = &deinterlace_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = ctx->dev->dev; + + ret = vb2_queue_init(dst_vq); + if (ret) + return ret; + + return 0; +} + +static int deinterlace_open(struct file *file) +{ + struct deinterlace_dev *dev = video_drvdata(file); + struct deinterlace_ctx *ctx = NULL; + int ret; + + if (mutex_lock_interruptible(&dev->dev_mutex)) + return -ERESTARTSYS; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + mutex_unlock(&dev->dev_mutex); + return -ENOMEM; + } + + /* set default output and capture format */ + deinterlace_set_out_format(ctx, &ctx->src_fmt); + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dev = dev; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + &deinterlace_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_free; + } + + v4l2_fh_add(&ctx->fh); + + mutex_unlock(&dev->dev_mutex); + + return 0; + +err_free: + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int deinterlace_release(struct file *file) +{ + struct deinterlace_dev *dev = video_drvdata(file); + struct deinterlace_ctx *ctx = container_of(file->private_data, + struct deinterlace_ctx, fh); + + mutex_lock(&dev->dev_mutex); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + kfree(ctx); + + mutex_unlock(&dev->dev_mutex); + + return 0; +} + +static const struct v4l2_file_operations deinterlace_fops = { + .owner = THIS_MODULE, + .open = deinterlace_open, + .release = deinterlace_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device deinterlace_video_device = { + .name = DEINTERLACE_NAME, + .vfl_dir = VFL_DIR_M2M, + .fops = &deinterlace_fops, + .ioctl_ops = &deinterlace_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_m2m_ops deinterlace_m2m_ops = { + .device_run = deinterlace_device_run, + .job_ready = deinterlace_job_ready, + .job_abort = deinterlace_job_abort, +}; + +static int deinterlace_probe(struct platform_device *pdev) +{ + struct deinterlace_dev *dev; + struct video_device *vfd; + int irq, ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->vfd = deinterlace_video_device; + dev->dev = &pdev->dev; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq; + + ret = devm_request_irq(dev->dev, irq, deinterlace_irq, + 0, dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "Failed to request IRQ\n"); + + return ret; + } + + dev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + dev->bus_clk = devm_clk_get(dev->dev, "bus"); + if (IS_ERR(dev->bus_clk)) + return dev_err_probe(dev->dev, PTR_ERR(dev->bus_clk), + "Failed to get bus clock\n"); + + dev->mod_clk = devm_clk_get(dev->dev, "mod"); + if (IS_ERR(dev->mod_clk)) + return dev_err_probe(dev->dev, PTR_ERR(dev->mod_clk), + "Failed to get mod clock\n"); + + dev->ram_clk = devm_clk_get(dev->dev, "ram"); + if (IS_ERR(dev->ram_clk)) + return dev_err_probe(dev->dev, PTR_ERR(dev->ram_clk), + "Failed to get ram clock\n"); + + dev->rstc = devm_reset_control_get(dev->dev, NULL); + if (IS_ERR(dev->rstc)) + return dev_err_probe(dev->dev, PTR_ERR(dev->rstc), + "Failed to get reset control\n"); + + dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(32)); + dma_set_max_seg_size(dev->dev, UINT_MAX); + + mutex_init(&dev->dev_mutex); + + INIT_DELAYED_WORK(&dev->watchdog_work, deinterlace_watchdog); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(dev->dev, "Failed to register V4L2 device\n"); + + return ret; + } + + vfd = &dev->vfd; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + snprintf(vfd->name, sizeof(vfd->name), "%s", + deinterlace_video_device.name); + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + + goto err_v4l2; + } + + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + + dev->m2m_dev = v4l2_m2m_init(&deinterlace_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, + "Failed to initialize V4L2 M2M device\n"); + ret = PTR_ERR(dev->m2m_dev); + + goto err_video; + } + + platform_set_drvdata(pdev, dev); + + pm_runtime_enable(dev->dev); + + return 0; + +err_video: + video_unregister_device(&dev->vfd); +err_v4l2: + v4l2_device_unregister(&dev->v4l2_dev); + + return ret; +} + +static int deinterlace_remove(struct platform_device *pdev) +{ + struct deinterlace_dev *dev = platform_get_drvdata(pdev); + + v4l2_m2m_release(dev->m2m_dev); + video_unregister_device(&dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + pm_runtime_force_suspend(&pdev->dev); + + return 0; +} + +static int deinterlace_runtime_resume(struct device *device) +{ + struct deinterlace_dev *dev = dev_get_drvdata(device); + int ret; + + ret = clk_prepare_enable(dev->bus_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable bus clock\n"); + + return ret; + } + + ret = clk_prepare_enable(dev->mod_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable mod clock\n"); + + goto err_bus_clk; + } + + ret = clk_prepare_enable(dev->ram_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable ram clock\n"); + + goto err_mod_clk; + } + + ret = reset_control_deassert(dev->rstc); + if (ret) { + dev_err(dev->dev, "Failed to apply reset\n"); + + goto err_ram_clk; + } + + deinterlace_init(dev); + + return 0; + +err_ram_clk: + clk_disable_unprepare(dev->ram_clk); +err_mod_clk: + clk_disable_unprepare(dev->mod_clk); +err_bus_clk: + clk_disable_unprepare(dev->bus_clk); + + return ret; +} + +static int deinterlace_runtime_suspend(struct device *device) +{ + struct deinterlace_dev *dev = dev_get_drvdata(device); + + reset_control_assert(dev->rstc); + + clk_disable_unprepare(dev->ram_clk); + clk_disable_unprepare(dev->mod_clk); + clk_disable_unprepare(dev->bus_clk); + + return 0; +} + +static const struct of_device_id deinterlace_dt_match[] = { + { .compatible = "allwinner,sun50i-h6-deinterlace" }, + { } +}; +MODULE_DEVICE_TABLE(of, deinterlace_dt_match); + +static const struct dev_pm_ops deinterlace_pm_ops = { + .runtime_resume = deinterlace_runtime_resume, + .runtime_suspend = deinterlace_runtime_suspend, +}; + +static struct platform_driver deinterlace_driver = { + .probe = deinterlace_probe, + .remove = deinterlace_remove, + .driver = { + .name = DEINTERLACE_NAME, + .of_match_table = deinterlace_dt_match, + .pm = &deinterlace_pm_ops, + }, +}; +module_platform_driver(deinterlace_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>"); +MODULE_DESCRIPTION("Allwinner sun50i deinterlace driver"); diff --git a/drivers/media/platform/sunxi/sun50i-di/sun50i-di.h b/drivers/media/platform/sunxi/sun50i-di/sun50i-di.h new file mode 100644 index 000000000000..c6eae30040e9 --- /dev/null +++ b/drivers/media/platform/sunxi/sun50i-di/sun50i-di.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Allwinner Deinterlace driver + * + * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@gmail.com> + */ + +#ifndef _SUN8I_DEINTERLACE_H_ +#define _SUN8I_DEINTERLACE_H_ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/workqueue.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-v4l2.h> + +#define DEINTERLACE_NAME "sun50i-di" + +#define DEINTERLACE_CTRL 0x00 +#define DEINTERLACE_CTRL_START BIT(0) +#define DEINTERLACE_CTRL_IOMMU_EN BIT(16) +#define DEINTERLACE_CTRL_RESET BIT(31) + +#define DEINTERLACE_INT_CTRL 0x04 +#define DEINTERLACE_INT_EN BIT(0) + +#define DEINTERLACE_STATUS 0x08 +#define DEINTERLACE_STATUS_FINISHED BIT(0) +#define DEINTERLACE_STATUS_BUSY BIT(8) + +#define DEINTERLACE_SIZE 0x10 +#define DEINTERLACE_SIZE_WIDTH(w) \ + (((w) - 1) & 0x7ff) +#define DEINTERLACE_SIZE_HEIGHT(h) \ + ((((h) - 1) & 0x7ff) << 16) + +#define DEINTERLACE_FORMAT 0x14 +#define DEINTERLACE_FORMAT_YUV420P 0 +#define DEINTERLACE_FORMAT_YUV420SP 1 +#define DEINTERLACE_FORMAT_YUV422P 2 +#define DEINTERLACE_FORMAT_YUV422SP 3 + +#define DEINTERLACE_POLAR 0x18 +#define DEINTERLACE_POLAR_FIELD(x) ((x) & 1) + +/* all pitch registers accept 16-bit values */ +#define DEINTERLACE_IN_PITCH0 0x20 +#define DEINTERLACE_IN_PITCH1 0x24 +#define DEINTERLACE_IN_PITCH2 0x28 +#define DEINTERLACE_OUT_PITCH0 0x30 +#define DEINTERLACE_OUT_PITCH1 0x34 +#define DEINTERLACE_OUT_PITCH2 0x38 +#define DEINTERLACE_FLAG_PITCH 0x40 +#define DEINTERLACE_IN0_ADDR0 0x50 +#define DEINTERLACE_IN0_ADDR1 0x54 +#define DEINTERLACE_IN0_ADDR2 0x58 +#define DEINTERLACE_IN0_ADDRH 0x5c +#define DEINTERLACE_IN1_ADDR0 0x60 +#define DEINTERLACE_IN1_ADDR1 0x64 +#define DEINTERLACE_IN1_ADDR2 0x68 +#define DEINTERLACE_IN1_ADDRH 0x6c +#define DEINTERLACE_IN2_ADDR0 0x70 +#define DEINTERLACE_IN2_ADDR1 0x74 +#define DEINTERLACE_IN2_ADDR2 0x78 +#define DEINTERLACE_IN2_ADDRH 0x7c +#define DEINTERLACE_IN3_ADDR0 0x80 +#define DEINTERLACE_IN3_ADDR1 0x84 +#define DEINTERLACE_IN3_ADDR2 0x88 +#define DEINTERLACE_IN3_ADDRH 0x8c +#define DEINTERLACE_OUT_ADDR0 0x90 +#define DEINTERLACE_OUT_ADDR1 0x94 +#define DEINTERLACE_OUT_ADDR2 0x98 +#define DEINTERLACE_OUT_ADDRH 0x9c +#define DEINTERLACE_IN_FLAG_ADDR 0xa0 +#define DEINTERLACE_OUT_FLAG_ADDR 0xa4 +#define DEINTERLACE_FLAG_ADDRH 0xa8 + +#define DEINTERLACE_ADDRH0(x) ((x) & 0xff) +#define DEINTERLACE_ADDRH1(x) (((x) & 0xff) << 8) +#define DEINTERLACE_ADDRH2(x) (((x) & 0xff) << 16) + +#define DEINTERLACE_MODE 0xb0 +#define DEINTERLACE_MODE_DEINT_LUMA BIT(0) +#define DEINTERLACE_MODE_MOTION_EN BIT(4) +#define DEINTERLACE_MODE_INTP_EN BIT(5) +#define DEINTERLACE_MODE_AUTO_UPD_MODE(x) (((x) & 3) << 12) +#define DEINTERLACE_MODE_DEINT_CHROMA BIT(16) +#define DEINTERLACE_MODE_FIELD_MODE BIT(31) + +#define DEINTERLACE_MD_PARAM0 0xb4 +#define DEINTERLACE_MD_PARAM0_MIN_LUMA_TH(x) ((x) & 0xff) +#define DEINTERLACE_MD_PARAM0_MAX_LUMA_TH(x) (((x) & 0xff) << 8) +#define DEINTERLACE_MD_PARAM0_AVG_LUMA_SHIFT(x) (((x) & 0xf) << 16) +#define DEINTERLACE_MD_PARAM0_TH_SHIFT(x) (((x) & 0xf) << 24) + +#define DEINTERLACE_MD_PARAM1 0xb8 +#define DEINTERLACE_MD_PARAM1_MOV_FAC_NONEDGE(x) (((x) & 0x3) << 28) + +#define DEINTERLACE_MD_PARAM2 0xbc +#define DEINTERLACE_MD_PARAM2_CHROMA_SPATIAL_TH(x) (((x) & 0xff) << 8) +#define DEINTERLACE_MD_PARAM2_CHROMA_DIFF_TH(x) (((x) & 0xff) << 16) +#define DEINTERLACE_MD_PARAM2_PIX_STATIC_TH(x) (((x) & 0x3) << 28) + +#define DEINTERLACE_INTP_PARAM0 0xc0 +#define DEINTERLACE_INTP_PARAM0_ANGLE_LIMIT(x) ((x) & 0x1f) +#define DEINTERLACE_INTP_PARAM0_ANGLE_CONST_TH(x) (((x) & 7) << 8) +#define DEINTERLACE_INTP_PARAM0_LUMA_CUR_FAC_MODE(x) (((x) & 7) << 16) +#define DEINTERLACE_INTP_PARAM0_CHROMA_CUR_FAC_MODE(x) (((x) & 7) << 20) + +#define DEINTERLACE_MD_CH_PARAM 0xc4 +#define DEINTERLACE_MD_CH_PARAM_BLEND_MODE(x) ((x) & 0xf) +#define DEINTERLACE_MD_CH_PARAM_FONT_PRO_EN BIT(8) +#define DEINTERLACE_MD_CH_PARAM_FONT_PRO_TH(x) (((x) & 0xff) << 16) +#define DEINTERLACE_MD_CH_PARAM_FONT_PRO_FAC(x) (((x) & 0x1f) << 24) + +#define DEINTERLACE_INTP_PARAM1 0xc8 +#define DEINTERLACE_INTP_PARAM1_A(x) ((x) & 7) +#define DEINTERLACE_INTP_PARAM1_EN BIT(3) +#define DEINTERLACE_INTP_PARAM1_C(x) (((x) & 0xf) << 4) +#define DEINTERLACE_INTP_PARAM1_CMAX(x) (((x) & 0xff) << 8) +#define DEINTERLACE_INTP_PARAM1_MAXRAT(x) (((x) & 3) << 16) + +/* there is no explanation what this register does */ +#define DEINTERLACE_OUT_PATH 0x200 + +#define DEINTERLACE_MIN_WIDTH 2U +#define DEINTERLACE_MIN_HEIGHT 2U +#define DEINTERLACE_MAX_WIDTH 2048U +#define DEINTERLACE_MAX_HEIGHT 1100U + +struct deinterlace_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd; + struct device *dev; + struct v4l2_m2m_dev *m2m_dev; + + /* Device file mutex */ + struct mutex dev_mutex; + + void __iomem *base; + + struct clk *bus_clk; + struct clk *mod_clk; + struct clk *ram_clk; + + struct reset_control *rstc; + + struct delayed_work watchdog_work; +}; + +struct deinterlace_ctx { + struct v4l2_fh fh; + struct deinterlace_dev *dev; + + struct v4l2_pix_format src_fmt; + struct v4l2_pix_format dst_fmt; + + void *flag1_buf; + dma_addr_t flag1_buf_dma; + + void *flag2_buf; + dma_addr_t flag2_buf_dma; + + struct vb2_v4l2_buffer *prev[2]; + + unsigned int first_field; + unsigned int field; + + int aborting; +}; + +#endif -- 2.38.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] media: sunxi: Add H6 deinterlace driver 2022-11-01 12:32 ` [PATCH 2/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec @ 2022-11-05 3:28 ` Samuel Holland 0 siblings, 0 replies; 11+ messages in thread From: Samuel Holland @ 2022-11-05 3:28 UTC (permalink / raw) To: Jernej Skrabec, mchehab, hverkuil-cisco Cc: robh+dt, krzysztof.kozlowski+dt, wens, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel Hi Jernej, On 11/1/22 07:32, Jernej Skrabec wrote: > This driver covers H6 deinterlace core, which is marked in vendor driver > as v2.3. Contrary to older cores, covered by sun8i-di, it doesn't > support scaling, but it supports iommu and has additional motion > compensated deinterlacing algorithm. > > Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> > --- > MAINTAINERS | 4 +- > drivers/media/platform/sunxi/Kconfig | 1 + > drivers/media/platform/sunxi/Makefile | 1 + > .../media/platform/sunxi/sun50i-di/Kconfig | 15 + > .../media/platform/sunxi/sun50i-di/Makefile | 2 + > .../platform/sunxi/sun50i-di/sun50i-di.c | 1142 +++++++++++++++++ > .../platform/sunxi/sun50i-di/sun50i-di.h | 175 +++ > 7 files changed, 1339 insertions(+), 1 deletion(-) > create mode 100644 drivers/media/platform/sunxi/sun50i-di/Kconfig > create mode 100644 drivers/media/platform/sunxi/sun50i-di/Makefile > create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.c > create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.h > > [...] > > +static int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count) > +{ > + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); > + struct device *dev = ctx->dev->dev; > + int ret; > + > + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { > + ret = pm_runtime_get_sync(dev); This should use pm_runtime_resume_and_get(). > + if (ret < 0) { > + dev_err(dev, "Failed to enable module\n"); > + > + goto err_runtime_get; > + } > + > + ctx->first_field = > + ctx->src_fmt.field == V4L2_FIELD_INTERLACED_BT; > + ctx->field = ctx->first_field; > + > + ctx->prev[0] = NULL; > + ctx->prev[1] = NULL; > + ctx->aborting = 0; > + > + ctx->flag1_buf = dma_alloc_coherent(dev, FLAG_SIZE, > + &ctx->flag1_buf_dma, > + GFP_KERNEL); > + if (!ctx->flag1_buf) { > + ret = -ENOMEM; > + > + goto err_no_mem1; > + } > + > + ctx->flag2_buf = dma_alloc_coherent(dev, FLAG_SIZE, > + &ctx->flag2_buf_dma, > + GFP_KERNEL); > + if (!ctx->flag2_buf) { > + ret = -ENOMEM; > + > + goto err_no_mem2; > + } > + } > + > + return 0; > + > +err_no_mem2: > + dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, > + ctx->flag1_buf_dma); > +err_no_mem1: > + pm_runtime_put(dev); > +err_runtime_get: > + deinterlace_queue_cleanup(vq, VB2_BUF_STATE_QUEUED); > + > + return ret; > +} > + > +static void deinterlace_stop_streaming(struct vb2_queue *vq) > +{ > + struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); > + > + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { > + struct device *dev = ctx->dev->dev; > + > + dma_free_coherent(dev, FLAG_SIZE, ctx->flag1_buf, > + ctx->flag1_buf_dma); > + dma_free_coherent(dev, FLAG_SIZE, ctx->flag2_buf, > + ctx->flag2_buf_dma); > + > + pm_runtime_put(dev); > + } > + > + deinterlace_queue_cleanup(vq, VB2_BUF_STATE_ERROR); > +} > + > +static const struct vb2_ops deinterlace_qops = { > + .queue_setup = deinterlace_queue_setup, > + .buf_prepare = deinterlace_buf_prepare, > + .buf_queue = deinterlace_buf_queue, > + .start_streaming = deinterlace_start_streaming, > + .stop_streaming = deinterlace_stop_streaming, > + .wait_prepare = vb2_ops_wait_prepare, > + .wait_finish = vb2_ops_wait_finish, > +}; > + > +static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq, > + struct vb2_queue *dst_vq) > +{ > + struct deinterlace_ctx *ctx = priv; > + int ret; > + > + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; > + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; > + src_vq->drv_priv = ctx; > + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); > + src_vq->min_buffers_needed = 1; > + src_vq->ops = &deinterlace_qops; > + src_vq->mem_ops = &vb2_dma_contig_memops; > + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + src_vq->lock = &ctx->dev->dev_mutex; > + src_vq->dev = ctx->dev->dev; > + > + ret = vb2_queue_init(src_vq); > + if (ret) > + return ret; > + > + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; > + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; > + dst_vq->drv_priv = ctx; > + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); > + dst_vq->min_buffers_needed = 2; > + dst_vq->ops = &deinterlace_qops; > + dst_vq->mem_ops = &vb2_dma_contig_memops; > + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; > + dst_vq->lock = &ctx->dev->dev_mutex; > + dst_vq->dev = ctx->dev->dev; > + > + ret = vb2_queue_init(dst_vq); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int deinterlace_open(struct file *file) > +{ > + struct deinterlace_dev *dev = video_drvdata(file); > + struct deinterlace_ctx *ctx = NULL; > + int ret; > + > + if (mutex_lock_interruptible(&dev->dev_mutex)) > + return -ERESTARTSYS; > + > + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); This memory allocation does not need to be inside the lock. > + if (!ctx) { > + mutex_unlock(&dev->dev_mutex); > + return -ENOMEM; > + } > + > + /* set default output and capture format */ > + deinterlace_set_out_format(ctx, &ctx->src_fmt); > + > + v4l2_fh_init(&ctx->fh, video_devdata(file)); > + file->private_data = &ctx->fh; > + ctx->dev = dev; > + > + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, > + &deinterlace_queue_init); > + if (IS_ERR(ctx->fh.m2m_ctx)) { > + ret = PTR_ERR(ctx->fh.m2m_ctx); > + goto err_free; > + } > + > + v4l2_fh_add(&ctx->fh); > + > + mutex_unlock(&dev->dev_mutex); > + > + return 0; > + > +err_free: > + kfree(ctx); > + mutex_unlock(&dev->dev_mutex); > + > + return ret; > +} > + > +static int deinterlace_release(struct file *file) > +{ > + struct deinterlace_dev *dev = video_drvdata(file); > + struct deinterlace_ctx *ctx = container_of(file->private_data, > + struct deinterlace_ctx, fh); > + > + mutex_lock(&dev->dev_mutex); > + > + v4l2_fh_del(&ctx->fh); > + v4l2_fh_exit(&ctx->fh); > + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); > + > + kfree(ctx); This does not need to be inside the lock either. > + > + mutex_unlock(&dev->dev_mutex); > + > + return 0; > +} > + > +static const struct v4l2_file_operations deinterlace_fops = { > + .owner = THIS_MODULE, > + .open = deinterlace_open, > + .release = deinterlace_release, > + .poll = v4l2_m2m_fop_poll, > + .unlocked_ioctl = video_ioctl2, > + .mmap = v4l2_m2m_fop_mmap, > +}; > + > +static const struct video_device deinterlace_video_device = { > + .name = DEINTERLACE_NAME, > + .vfl_dir = VFL_DIR_M2M, > + .fops = &deinterlace_fops, > + .ioctl_ops = &deinterlace_ioctl_ops, > + .minor = -1, > + .release = video_device_release_empty, > + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, > +}; > + > +static const struct v4l2_m2m_ops deinterlace_m2m_ops = { > + .device_run = deinterlace_device_run, > + .job_ready = deinterlace_job_ready, > + .job_abort = deinterlace_job_abort, > +}; > + > +static int deinterlace_probe(struct platform_device *pdev) > +{ > + struct deinterlace_dev *dev; > + struct video_device *vfd; > + int irq, ret; > + > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); > + if (!dev) > + return -ENOMEM; > + > + dev->vfd = deinterlace_video_device; > + dev->dev = &pdev->dev; > + > + irq = platform_get_irq(pdev, 0); > + if (irq <= 0) > + return irq; > + > + ret = devm_request_irq(dev->dev, irq, deinterlace_irq, > + 0, dev_name(dev->dev), dev); You should expect to receive an interrupt as soon as you call this function, but dev->base is not initialized yet. > + if (ret) { > + dev_err(dev->dev, "Failed to request IRQ\n"); > + > + return ret; > + } > + > + dev->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(dev->base)) > + return PTR_ERR(dev->base); > + > + dev->bus_clk = devm_clk_get(dev->dev, "bus"); > + if (IS_ERR(dev->bus_clk)) > + return dev_err_probe(dev->dev, PTR_ERR(dev->bus_clk), > + "Failed to get bus clock\n"); > + > + dev->mod_clk = devm_clk_get(dev->dev, "mod"); > + if (IS_ERR(dev->mod_clk)) > + return dev_err_probe(dev->dev, PTR_ERR(dev->mod_clk), > + "Failed to get mod clock\n"); > + > + dev->ram_clk = devm_clk_get(dev->dev, "ram"); > + if (IS_ERR(dev->ram_clk)) > + return dev_err_probe(dev->dev, PTR_ERR(dev->ram_clk), > + "Failed to get ram clock\n"); > + > + dev->rstc = devm_reset_control_get(dev->dev, NULL); > + if (IS_ERR(dev->rstc)) > + return dev_err_probe(dev->dev, PTR_ERR(dev->rstc), > + "Failed to get reset control\n"); > + > + dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(32)); > + dma_set_max_seg_size(dev->dev, UINT_MAX); > + > + mutex_init(&dev->dev_mutex); > + > + INIT_DELAYED_WORK(&dev->watchdog_work, deinterlace_watchdog); > + > + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); > + if (ret) { > + dev_err(dev->dev, "Failed to register V4L2 device\n"); > + > + return ret; > + } > + > + vfd = &dev->vfd; > + vfd->lock = &dev->dev_mutex; > + vfd->v4l2_dev = &dev->v4l2_dev; > + > + snprintf(vfd->name, sizeof(vfd->name), "%s", > + deinterlace_video_device.name); > + video_set_drvdata(vfd, dev); > + > + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); > + if (ret) { > + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); > + > + goto err_v4l2; > + } > + > + v4l2_info(&dev->v4l2_dev, > + "Device registered as /dev/video%d\n", vfd->num); > + > + dev->m2m_dev = v4l2_m2m_init(&deinterlace_m2m_ops); > + if (IS_ERR(dev->m2m_dev)) { > + v4l2_err(&dev->v4l2_dev, > + "Failed to initialize V4L2 M2M device\n"); > + ret = PTR_ERR(dev->m2m_dev); > + > + goto err_video; > + } > + > + platform_set_drvdata(pdev, dev); > + > + pm_runtime_enable(dev->dev); > + > + return 0; > + > +err_video: > + video_unregister_device(&dev->vfd); > +err_v4l2: > + v4l2_device_unregister(&dev->v4l2_dev); > + > + return ret; > +} > + > +static int deinterlace_remove(struct platform_device *pdev) > +{ > + struct deinterlace_dev *dev = platform_get_drvdata(pdev); > + > + v4l2_m2m_release(dev->m2m_dev); > + video_unregister_device(&dev->vfd); > + v4l2_device_unregister(&dev->v4l2_dev); > + > + pm_runtime_force_suspend(&pdev->dev); > + > + return 0; > +} > + > +static int deinterlace_runtime_resume(struct device *device) > +{ > + struct deinterlace_dev *dev = dev_get_drvdata(device); > + int ret; > + > + ret = clk_prepare_enable(dev->bus_clk); > + if (ret) { > + dev_err(dev->dev, "Failed to enable bus clock\n"); > + > + return ret; > + } > + > + ret = clk_prepare_enable(dev->mod_clk); > + if (ret) { > + dev_err(dev->dev, "Failed to enable mod clock\n"); > + > + goto err_bus_clk; > + } > + > + ret = clk_prepare_enable(dev->ram_clk); > + if (ret) { > + dev_err(dev->dev, "Failed to enable ram clock\n"); > + > + goto err_mod_clk; > + } > + > + ret = reset_control_deassert(dev->rstc); > + if (ret) { > + dev_err(dev->dev, "Failed to apply reset\n"); > + > + goto err_ram_clk; > + } The manuals generally recommend deasserting the reset before enabling the clock. Also, the BSP sets the module clock rate to 300 MHz (PLL_PERIPH0 / 2). Do you think we should do the same? I am guessing the default is 600 MHz (PLL_PERIPH0 / 1), which is fine as long as the module works at that frequency, although it may not be optimal for power usage. > + > + deinterlace_init(dev); > + > + return 0; > + > +err_ram_clk: > + clk_disable_unprepare(dev->ram_clk); > +err_mod_clk: > + clk_disable_unprepare(dev->mod_clk); > +err_bus_clk: > + clk_disable_unprepare(dev->bus_clk); > + > + return ret; > +} > + > +static int deinterlace_runtime_suspend(struct device *device) > +{ > + struct deinterlace_dev *dev = dev_get_drvdata(device); > + > + reset_control_assert(dev->rstc); > + > + clk_disable_unprepare(dev->ram_clk); > + clk_disable_unprepare(dev->mod_clk); > + clk_disable_unprepare(dev->bus_clk); > + > + return 0; > +} > + > +static const struct of_device_id deinterlace_dt_match[] = { > + { .compatible = "allwinner,sun50i-h6-deinterlace" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, deinterlace_dt_match); > + > +static const struct dev_pm_ops deinterlace_pm_ops = { > + .runtime_resume = deinterlace_runtime_resume, > + .runtime_suspend = deinterlace_runtime_suspend, > +}; > + > +static struct platform_driver deinterlace_driver = { > + .probe = deinterlace_probe, > + .remove = deinterlace_remove, > + .driver = { > + .name = DEINTERLACE_NAME, > + .of_match_table = deinterlace_dt_match, > + .pm = &deinterlace_pm_ops, > + }, > +}; > +module_platform_driver(deinterlace_driver); > + > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>"); > +MODULE_DESCRIPTION("Allwinner sun50i deinterlace driver"); > diff --git a/drivers/media/platform/sunxi/sun50i-di/sun50i-di.h b/drivers/media/platform/sunxi/sun50i-di/sun50i-di.h > new file mode 100644 > index 000000000000..c6eae30040e9 > --- /dev/null > +++ b/drivers/media/platform/sunxi/sun50i-di/sun50i-di.h > @@ -0,0 +1,175 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Allwinner Deinterlace driver > + * > + * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@gmail.com> > + */ > + > +#ifndef _SUN8I_DEINTERLACE_H_ > +#define _SUN8I_DEINTERLACE_H_ The header guard doesn't match the file. Regards, Samuel ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/3] arm64: dts: allwinner: h6: Add deinterlace node 2022-11-01 12:31 [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec 2022-11-01 12:31 ` [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding Jernej Skrabec 2022-11-01 12:32 ` [PATCH 2/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec @ 2022-11-01 12:32 ` Jernej Skrabec 2022-11-05 3:01 ` Samuel Holland 2022-11-04 11:33 ` [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Hans Verkuil 3 siblings, 1 reply; 11+ messages in thread From: Jernej Skrabec @ 2022-11-01 12:32 UTC (permalink / raw) To: mchehab, hverkuil-cisco Cc: robh+dt, krzysztof.kozlowski+dt, wens, samuel, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel, Jernej Skrabec H6 has deinterlace core. Add a node for it. Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> --- arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi index 0aa6dfafc197..badc6afd6941 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi @@ -154,6 +154,18 @@ mixer0_out_tcon_top_mixer0: endpoint { }; }; + deinterlace@1420000 { + compatible = "allwinner,sun50i-h6-deinterlace"; + reg = <0x01420000 0x2000>; + clocks = <&ccu CLK_BUS_DEINTERLACE>, + <&ccu CLK_DEINTERLACE>, + <&ccu CLK_MBUS_DEINTERLACE>; + clock-names = "bus", "mod", "ram"; + resets = <&ccu RST_BUS_DEINTERLACE>; + interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&iommu 2>; + }; + video-codec-g2@1c00000 { compatible = "allwinner,sun50i-h6-vpu-g2"; reg = <0x01c00000 0x1000>; -- 2.38.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] arm64: dts: allwinner: h6: Add deinterlace node 2022-11-01 12:32 ` [PATCH 3/3] arm64: dts: allwinner: h6: Add deinterlace node Jernej Skrabec @ 2022-11-05 3:01 ` Samuel Holland 0 siblings, 0 replies; 11+ messages in thread From: Samuel Holland @ 2022-11-05 3:01 UTC (permalink / raw) To: Jernej Skrabec, mchehab, hverkuil-cisco Cc: robh+dt, krzysztof.kozlowski+dt, wens, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel On 11/1/22 07:32, Jernej Skrabec wrote: > H6 has deinterlace core. Add a node for it. > > Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> Reviewed-by: Samuel Holland <samuel@sholland.org> > --- > arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 12 ++++++++++++ > 1 file changed, 12 insertions(+) > > diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi > index 0aa6dfafc197..badc6afd6941 100644 > --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi > +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi > @@ -154,6 +154,18 @@ mixer0_out_tcon_top_mixer0: endpoint { > }; > }; > > + deinterlace@1420000 { > + compatible = "allwinner,sun50i-h6-deinterlace"; > + reg = <0x01420000 0x2000>; Not that it matters, as long as the range is big enough, but how did you come up with this size? The only thing I find in the vendor devicetrees is 0x20c, the offset of the last documented register. Regards, Samuel > + clocks = <&ccu CLK_BUS_DEINTERLACE>, > + <&ccu CLK_DEINTERLACE>, > + <&ccu CLK_MBUS_DEINTERLACE>; > + clock-names = "bus", "mod", "ram"; > + resets = <&ccu RST_BUS_DEINTERLACE>; > + interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>; > + iommus = <&iommu 2>; > + }; > + > video-codec-g2@1c00000 { > compatible = "allwinner,sun50i-h6-vpu-g2"; > reg = <0x01c00000 0x1000>; ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 0/3] media: sunxi: Add H6 deinterlace driver 2022-11-01 12:31 [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec ` (2 preceding siblings ...) 2022-11-01 12:32 ` [PATCH 3/3] arm64: dts: allwinner: h6: Add deinterlace node Jernej Skrabec @ 2022-11-04 11:33 ` Hans Verkuil 2022-11-04 17:36 ` Jernej Škrabec 3 siblings, 1 reply; 11+ messages in thread From: Hans Verkuil @ 2022-11-04 11:33 UTC (permalink / raw) To: Jernej Skrabec, mchehab Cc: robh+dt, krzysztof.kozlowski+dt, wens, samuel, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel Hi Jernej, Can you add a patch updating the MAINTAINERS file? Just post a 4/3 patch :-) Regards, Hans On 01/11/2022 13:31, Jernej Skrabec wrote: > This series implements driver for H6 deinterlace core, which is > newer version of core, covered by sun8i-di (v2.3). Contrary to > older one, it doesn't support scaling, but it supports iommu, > has additional motion compensated deinterlacing algorithm and > supports different pixel formats. > > v4l2-compliance 1.23.0-4961, 64 bits, 64-bit time_t > v4l2-compliance SHA: f86484524f32 2022-10-21 10:08:58 > > Compliance test for sun50i-di device /dev/video0: > > Driver Info: > Driver name : sun50i-di > Card type : sun50i-di > Bus info : platform:sun50i-di > Driver version : 6.1.0 > Capabilities : 0x84208000 > Video Memory-to-Memory > Streaming > Extended Pix Format > Device Capabilities > Device Caps : 0x04208000 > Video Memory-to-Memory > Streaming > Extended Pix Format > > Required ioctls: > test VIDIOC_QUERYCAP: OK > test invalid ioctls: OK > > Allow for multiple opens: > test second /dev/video0 open: OK > test VIDIOC_QUERYCAP: OK > test VIDIOC_G/S_PRIORITY: OK > test for unlimited opens: OK > > Debug ioctls: > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported) > test VIDIOC_LOG_STATUS: OK (Not Supported) > > Input ioctls: > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported) > test VIDIOC_G/S_FREQUENCY: OK (Not Supported) > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported) > test VIDIOC_ENUMAUDIO: OK (Not Supported) > test VIDIOC_G/S/ENUMINPUT: OK (Not Supported) > test VIDIOC_G/S_AUDIO: OK (Not Supported) > Inputs: 0 Audio Inputs: 0 Tuners: 0 > > Output ioctls: > test VIDIOC_G/S_MODULATOR: OK (Not Supported) > test VIDIOC_G/S_FREQUENCY: OK (Not Supported) > test VIDIOC_ENUMAUDOUT: OK (Not Supported) > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported) > test VIDIOC_G/S_AUDOUT: OK (Not Supported) > Outputs: 0 Audio Outputs: 0 Modulators: 0 > > Input/Output configuration ioctls: > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported) > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported) > test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported) > test VIDIOC_G/S_EDID: OK (Not Supported) > > Control ioctls: > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported) > test VIDIOC_QUERYCTRL: OK (Not Supported) > test VIDIOC_G/S_CTRL: OK (Not Supported) > test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported) > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported) > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported) > Standard Controls: 0 Private Controls: 0 > > Format ioctls: > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK > test VIDIOC_G/S_PARM: OK (Not Supported) > test VIDIOC_G_FBUF: OK (Not Supported) > test VIDIOC_G_FMT: OK > test VIDIOC_TRY_FMT: OK > test VIDIOC_S_FMT: OK > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported) > test Cropping: OK (Not Supported) > test Composing: OK (Not Supported) > test Scaling: OK (Not Supported) > > Codec ioctls: > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported) > test VIDIOC_G_ENC_INDEX: OK (Not Supported) > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported) > > Buffer ioctls: > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK > test VIDIOC_EXPBUF: OK > test Requests: OK (Not Supported) > > Total for sun50i-di device /dev/video0: 45, Succeeded: 45, Failed: 0, Warnings: 0 > > Best regards, > Jernej > > Jernej Skrabec (3): > media: dt-bindings: media: Add Allwinner H6 Deinterlace binding > media: sunxi: Add H6 deinterlace driver > arm64: dts: allwinner: h6: Add deinterlace node > > .../allwinner,sun50i-h6-deinterlace.yaml | 74 ++ > MAINTAINERS | 4 +- > arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 12 + > drivers/media/platform/sunxi/Kconfig | 1 + > drivers/media/platform/sunxi/Makefile | 1 + > .../media/platform/sunxi/sun50i-di/Kconfig | 15 + > .../media/platform/sunxi/sun50i-di/Makefile | 2 + > .../platform/sunxi/sun50i-di/sun50i-di.c | 1142 +++++++++++++++++ > .../platform/sunxi/sun50i-di/sun50i-di.h | 175 +++ > 9 files changed, 1425 insertions(+), 1 deletion(-) > create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace.yaml > create mode 100644 drivers/media/platform/sunxi/sun50i-di/Kconfig > create mode 100644 drivers/media/platform/sunxi/sun50i-di/Makefile > create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.c > create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.h > > -- > 2.38.1 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 0/3] media: sunxi: Add H6 deinterlace driver 2022-11-04 11:33 ` [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Hans Verkuil @ 2022-11-04 17:36 ` Jernej Škrabec 2022-11-04 22:47 ` Hans Verkuil 0 siblings, 1 reply; 11+ messages in thread From: Jernej Škrabec @ 2022-11-04 17:36 UTC (permalink / raw) To: mchehab, Hans Verkuil Cc: robh+dt, krzysztof.kozlowski+dt, wens, samuel, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel Hi Hans, Dne petek, 04. november 2022 ob 12:33:50 CET je Hans Verkuil napisal(a): > Hi Jernej, > > Can you add a patch updating the MAINTAINERS file? Just post a 4/3 patch :-) It's already part of patch 2. I didn't see the reason to split it to separate patch. Best regards, Jernej > > Regards, > > Hans > > On 01/11/2022 13:31, Jernej Skrabec wrote: > > This series implements driver for H6 deinterlace core, which is > > newer version of core, covered by sun8i-di (v2.3). Contrary to > > older one, it doesn't support scaling, but it supports iommu, > > has additional motion compensated deinterlacing algorithm and > > supports different pixel formats. > > > > v4l2-compliance 1.23.0-4961, 64 bits, 64-bit time_t > > v4l2-compliance SHA: f86484524f32 2022-10-21 10:08:58 > > > > Compliance test for sun50i-di device /dev/video0: > > > > Driver Info: > > Driver name : sun50i-di > > Card type : sun50i-di > > Bus info : platform:sun50i-di > > Driver version : 6.1.0 > > Capabilities : 0x84208000 > > > > Video Memory-to-Memory > > Streaming > > Extended Pix Format > > Device Capabilities > > > > Device Caps : 0x04208000 > > > > Video Memory-to-Memory > > Streaming > > Extended Pix Format > > > > Required ioctls: > > test VIDIOC_QUERYCAP: OK > > test invalid ioctls: OK > > > > Allow for multiple opens: > > test second /dev/video0 open: OK > > test VIDIOC_QUERYCAP: OK > > test VIDIOC_G/S_PRIORITY: OK > > test for unlimited opens: OK > > > > Debug ioctls: > > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported) > > test VIDIOC_LOG_STATUS: OK (Not Supported) > > > > Input ioctls: > > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported) > > test VIDIOC_G/S_FREQUENCY: OK (Not Supported) > > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported) > > test VIDIOC_ENUMAUDIO: OK (Not Supported) > > test VIDIOC_G/S/ENUMINPUT: OK (Not Supported) > > test VIDIOC_G/S_AUDIO: OK (Not Supported) > > Inputs: 0 Audio Inputs: 0 Tuners: 0 > > > > Output ioctls: > > test VIDIOC_G/S_MODULATOR: OK (Not Supported) > > test VIDIOC_G/S_FREQUENCY: OK (Not Supported) > > test VIDIOC_ENUMAUDOUT: OK (Not Supported) > > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported) > > test VIDIOC_G/S_AUDOUT: OK (Not Supported) > > Outputs: 0 Audio Outputs: 0 Modulators: 0 > > > > Input/Output configuration ioctls: > > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported) > > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported) > > test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported) > > test VIDIOC_G/S_EDID: OK (Not Supported) > > > > Control ioctls: > > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported) > > test VIDIOC_QUERYCTRL: OK (Not Supported) > > test VIDIOC_G/S_CTRL: OK (Not Supported) > > test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported) > > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported) > > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported) > > Standard Controls: 0 Private Controls: 0 > > > > Format ioctls: > > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK > > test VIDIOC_G/S_PARM: OK (Not Supported) > > test VIDIOC_G_FBUF: OK (Not Supported) > > test VIDIOC_G_FMT: OK > > test VIDIOC_TRY_FMT: OK > > test VIDIOC_S_FMT: OK > > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported) > > test Cropping: OK (Not Supported) > > test Composing: OK (Not Supported) > > test Scaling: OK (Not Supported) > > > > Codec ioctls: > > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported) > > test VIDIOC_G_ENC_INDEX: OK (Not Supported) > > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported) > > > > Buffer ioctls: > > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK > > test VIDIOC_EXPBUF: OK > > test Requests: OK (Not Supported) > > > > Total for sun50i-di device /dev/video0: 45, Succeeded: 45, Failed: 0, > > Warnings: 0 > > > > Best regards, > > Jernej > > > > Jernej Skrabec (3): > > media: dt-bindings: media: Add Allwinner H6 Deinterlace binding > > media: sunxi: Add H6 deinterlace driver > > arm64: dts: allwinner: h6: Add deinterlace node > > > > .../allwinner,sun50i-h6-deinterlace.yaml | 74 ++ > > MAINTAINERS | 4 +- > > arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 12 + > > drivers/media/platform/sunxi/Kconfig | 1 + > > drivers/media/platform/sunxi/Makefile | 1 + > > .../media/platform/sunxi/sun50i-di/Kconfig | 15 + > > .../media/platform/sunxi/sun50i-di/Makefile | 2 + > > .../platform/sunxi/sun50i-di/sun50i-di.c | 1142 +++++++++++++++++ > > .../platform/sunxi/sun50i-di/sun50i-di.h | 175 +++ > > 9 files changed, 1425 insertions(+), 1 deletion(-) > > create mode 100644 > > Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace. > > yaml create mode 100644 drivers/media/platform/sunxi/sun50i-di/Kconfig > > create mode 100644 drivers/media/platform/sunxi/sun50i-di/Makefile > > create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.c > > create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.h > > > > -- > > 2.38.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 0/3] media: sunxi: Add H6 deinterlace driver 2022-11-04 17:36 ` Jernej Škrabec @ 2022-11-04 22:47 ` Hans Verkuil 0 siblings, 0 replies; 11+ messages in thread From: Hans Verkuil @ 2022-11-04 22:47 UTC (permalink / raw) To: Jernej Škrabec, mchehab Cc: robh+dt, krzysztof.kozlowski+dt, wens, samuel, linux-media, devicetree, linux-arm-kernel, linux-sunxi, linux-kernel On 04/11/2022 18:36, Jernej Škrabec wrote: > Hi Hans, > > Dne petek, 04. november 2022 ob 12:33:50 CET je Hans Verkuil napisal(a): >> Hi Jernej, >> >> Can you add a patch updating the MAINTAINERS file? Just post a 4/3 patch :-) > > It's already part of patch 2. I didn't see the reason to split it to separate > patch. I totally missed that. Apologies for the noise! Regards, Hans > > Best regards, > Jernej > >> >> Regards, >> >> Hans >> >> On 01/11/2022 13:31, Jernej Skrabec wrote: >>> This series implements driver for H6 deinterlace core, which is >>> newer version of core, covered by sun8i-di (v2.3). Contrary to >>> older one, it doesn't support scaling, but it supports iommu, >>> has additional motion compensated deinterlacing algorithm and >>> supports different pixel formats. >>> >>> v4l2-compliance 1.23.0-4961, 64 bits, 64-bit time_t >>> v4l2-compliance SHA: f86484524f32 2022-10-21 10:08:58 >>> >>> Compliance test for sun50i-di device /dev/video0: >>> >>> Driver Info: >>> Driver name : sun50i-di >>> Card type : sun50i-di >>> Bus info : platform:sun50i-di >>> Driver version : 6.1.0 >>> Capabilities : 0x84208000 >>> >>> Video Memory-to-Memory >>> Streaming >>> Extended Pix Format >>> Device Capabilities >>> >>> Device Caps : 0x04208000 >>> >>> Video Memory-to-Memory >>> Streaming >>> Extended Pix Format >>> >>> Required ioctls: >>> test VIDIOC_QUERYCAP: OK >>> test invalid ioctls: OK >>> >>> Allow for multiple opens: >>> test second /dev/video0 open: OK >>> test VIDIOC_QUERYCAP: OK >>> test VIDIOC_G/S_PRIORITY: OK >>> test for unlimited opens: OK >>> >>> Debug ioctls: >>> test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported) >>> test VIDIOC_LOG_STATUS: OK (Not Supported) >>> >>> Input ioctls: >>> test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported) >>> test VIDIOC_G/S_FREQUENCY: OK (Not Supported) >>> test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported) >>> test VIDIOC_ENUMAUDIO: OK (Not Supported) >>> test VIDIOC_G/S/ENUMINPUT: OK (Not Supported) >>> test VIDIOC_G/S_AUDIO: OK (Not Supported) >>> Inputs: 0 Audio Inputs: 0 Tuners: 0 >>> >>> Output ioctls: >>> test VIDIOC_G/S_MODULATOR: OK (Not Supported) >>> test VIDIOC_G/S_FREQUENCY: OK (Not Supported) >>> test VIDIOC_ENUMAUDOUT: OK (Not Supported) >>> test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported) >>> test VIDIOC_G/S_AUDOUT: OK (Not Supported) >>> Outputs: 0 Audio Outputs: 0 Modulators: 0 >>> >>> Input/Output configuration ioctls: >>> test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported) >>> test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported) >>> test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported) >>> test VIDIOC_G/S_EDID: OK (Not Supported) >>> >>> Control ioctls: >>> test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported) >>> test VIDIOC_QUERYCTRL: OK (Not Supported) >>> test VIDIOC_G/S_CTRL: OK (Not Supported) >>> test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported) >>> test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported) >>> test VIDIOC_G/S_JPEGCOMP: OK (Not Supported) >>> Standard Controls: 0 Private Controls: 0 >>> >>> Format ioctls: >>> test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK >>> test VIDIOC_G/S_PARM: OK (Not Supported) >>> test VIDIOC_G_FBUF: OK (Not Supported) >>> test VIDIOC_G_FMT: OK >>> test VIDIOC_TRY_FMT: OK >>> test VIDIOC_S_FMT: OK >>> test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported) >>> test Cropping: OK (Not Supported) >>> test Composing: OK (Not Supported) >>> test Scaling: OK (Not Supported) >>> >>> Codec ioctls: >>> test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported) >>> test VIDIOC_G_ENC_INDEX: OK (Not Supported) >>> test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported) >>> >>> Buffer ioctls: >>> test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK >>> test VIDIOC_EXPBUF: OK >>> test Requests: OK (Not Supported) >>> >>> Total for sun50i-di device /dev/video0: 45, Succeeded: 45, Failed: 0, >>> Warnings: 0 >>> >>> Best regards, >>> Jernej >>> >>> Jernej Skrabec (3): >>> media: dt-bindings: media: Add Allwinner H6 Deinterlace binding >>> media: sunxi: Add H6 deinterlace driver >>> arm64: dts: allwinner: h6: Add deinterlace node >>> >>> .../allwinner,sun50i-h6-deinterlace.yaml | 74 ++ >>> MAINTAINERS | 4 +- >>> arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 12 + >>> drivers/media/platform/sunxi/Kconfig | 1 + >>> drivers/media/platform/sunxi/Makefile | 1 + >>> .../media/platform/sunxi/sun50i-di/Kconfig | 15 + >>> .../media/platform/sunxi/sun50i-di/Makefile | 2 + >>> .../platform/sunxi/sun50i-di/sun50i-di.c | 1142 +++++++++++++++++ >>> .../platform/sunxi/sun50i-di/sun50i-di.h | 175 +++ >>> 9 files changed, 1425 insertions(+), 1 deletion(-) >>> create mode 100644 >>> Documentation/devicetree/bindings/media/allwinner,sun50i-h6-deinterlace. >>> yaml create mode 100644 drivers/media/platform/sunxi/sun50i-di/Kconfig >>> create mode 100644 drivers/media/platform/sunxi/sun50i-di/Makefile >>> create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.c >>> create mode 100644 drivers/media/platform/sunxi/sun50i-di/sun50i-di.h >>> >>> -- >>> 2.38.1 > > > > ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2022-11-05 3:28 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-11-01 12:31 [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec 2022-11-01 12:31 ` [PATCH 1/3] media: dt-bindings: media: Add Allwinner H6 Deinterlace binding Jernej Skrabec 2022-11-02 17:14 ` Rob Herring 2022-11-05 3:02 ` Samuel Holland 2022-11-01 12:32 ` [PATCH 2/3] media: sunxi: Add H6 deinterlace driver Jernej Skrabec 2022-11-05 3:28 ` Samuel Holland 2022-11-01 12:32 ` [PATCH 3/3] arm64: dts: allwinner: h6: Add deinterlace node Jernej Skrabec 2022-11-05 3:01 ` Samuel Holland 2022-11-04 11:33 ` [PATCH 0/3] media: sunxi: Add H6 deinterlace driver Hans Verkuil 2022-11-04 17:36 ` Jernej Škrabec 2022-11-04 22:47 ` Hans Verkuil
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).