All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver
@ 2016-08-25  9:39 Florent Revest
  2016-08-25  9:39 ` [RFC 01/10] clk: sunxi-ng: Add a couple of A13 clocks Florent Revest
                   ` (10 more replies)
  0 siblings, 11 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

Hi,

This series adds a v4l2 memory2memory decoder driver for Allwinner's
VPU found in the A13 SoC. It follows the reverse engineering effort
of the Cedrus [1] project.

The VPU is able to decode a bunch of formats but currently only MPEG2
and a subset of MPEG4 are supported by the driver, more will come
eventually.

This VPU needs a frame-by-frame registers programming and implements
the idea of "Frame API" [2] proposed by Pawel Osciak. (i.e: binding
standard frame headers with frames via the "Request API") The patchset
includes both a new control for MPEG2 and MPEG4 frame headers. The
MPEG2 control should be generic enough for every other drivers but the
MPEG4 control sticks to the bare minimum needed by sunxi-cedrus.

The "Frame API" relies on the controls features of the "Request API".
[3] Since the latest Request API RFCs don't support controls and
given the time I had to work on this driver, I chose to use an older
RFC from Hans. [4] Of course, this is definitely not meant to be kept
and as soon as a newer Request API will support controls I'll stick
to the newest code base.

If you are interested in testing this driver, you can find a recently
(v4.8rc3) rebased version of this request API along my patchset in
this repository. [5] I also developed a libVA backend interfacing
with my proposal of MPEG2 and MPEG4 Frame API. [6] It's called 
"sunxi-cedrus-drv-video" but the only sunxi-cedrus specific part is
the format conversion code. Overall it should be generic enough for
any other v4l driver using the Frame API and as soon as the support
of DRM planes for this pixel format will be added it could be
renamed to something more generic.

[1] http://linux-sunxi.org/Cedrus
[2] https://docs.google.com/presentation/d/1RLkH3QxdmrcW_t41KllEvUmVsrHMbMOgd6CqAgzR7U4/pub?slide=id.p
[3] https://lwn.net/Articles/688585/
[4] https://lwn.net/Articles/641204/
[5] https://github.com/FlorentRevest/linux-sunxi-cedrus
[6] https://github.com/FlorentRevest/sunxi-cedrus-drv-video

Florent Revest (9):
  clk: sunxi-ng: Add a couple of A13 clocks
  v4l: Add sunxi Video Engine pixel format
  v4l: Add MPEG2 low-level decoder API control
  v4l: Add MPEG4 low-level decoder API control
  media: platform: Add Sunxi Cedrus decoder driver
  sunxi-cedrus: Add a MPEG 2 codec
  sunxi-cedrus: Add a MPEG 4 codec
  ARM: dts: sun5i: Use video-engine node
  sunxi-cedrus: Add device tree binding document

Pawel Osciak (1):
  v4l: Add private compound control type.

 .../devicetree/bindings/clock/sunxi-ccu.txt        |   1 +
 .../devicetree/bindings/media/sunxi-cedrus.txt     |  44 ++
 arch/arm/boot/dts/sun5i-a13.dtsi                   |  42 ++
 drivers/clk/sunxi-ng/Kconfig                       |  11 +
 drivers/clk/sunxi-ng/Makefile                      |   1 +
 drivers/clk/sunxi-ng/ccu-sun5i-a13.c               |  80 +++
 drivers/clk/sunxi-ng/ccu-sun5i-a13.h               |  25 +
 drivers/media/platform/Kconfig                     |  13 +
 drivers/media/platform/Makefile                    |   1 +
 drivers/media/platform/sunxi-cedrus/Makefile       |   3 +
 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c | 285 ++++++++++
 .../platform/sunxi-cedrus/sunxi_cedrus_common.h    | 101 ++++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c | 588 +++++++++++++++++++++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.h |  33 ++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.c  | 166 ++++++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |  39 ++
 .../platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c     | 152 ++++++
 .../platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c     | 140 +++++
 .../platform/sunxi-cedrus/sunxi_cedrus_regs.h      | 170 ++++++
 drivers/media/v4l2-core/v4l2-ctrls.c               |  23 +
 drivers/media/v4l2-core/v4l2-ioctl.c               |   2 +
 include/dt-bindings/clock/sun5i-a13-ccu.h          |  49 ++
 include/dt-bindings/reset/sun5i-a13-ccu.h          |  48 ++
 include/uapi/linux/v4l2-controls.h                 |  68 +++
 include/uapi/linux/videodev2.h                     |   9 +
 25 files changed, 2094 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/sunxi-cedrus.txt
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun5i-a13.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun5i-a13.h
 create mode 100644 drivers/media/platform/sunxi-cedrus/Makefile
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h
 create mode 100644 include/dt-bindings/clock/sun5i-a13-ccu.h
 create mode 100644 include/dt-bindings/reset/sun5i-a13-ccu.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC 01/10] clk: sunxi-ng: Add a couple of A13 clocks
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-08-25  9:39 ` [RFC 02/10] v4l: Add private compound control type Florent Revest
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

Add a new style driver for the clock control unit in Allwinner A13.

Only AVS and VE are supported since they weren't provided until now and are
needed for "sunxi-cedrus".

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 .../devicetree/bindings/clock/sunxi-ccu.txt        |  1 +
 arch/arm/boot/dts/sun5i-a13.dtsi                   | 11 +++
 drivers/clk/sunxi-ng/Kconfig                       | 11 +++
 drivers/clk/sunxi-ng/Makefile                      |  1 +
 drivers/clk/sunxi-ng/ccu-sun5i-a13.c               | 80 ++++++++++++++++++++++
 drivers/clk/sunxi-ng/ccu-sun5i-a13.h               | 25 +++++++
 include/dt-bindings/clock/sun5i-a13-ccu.h          | 49 +++++++++++++
 include/dt-bindings/reset/sun5i-a13-ccu.h          | 48 +++++++++++++
 8 files changed, 226 insertions(+)
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun5i-a13.c
 create mode 100644 drivers/clk/sunxi-ng/ccu-sun5i-a13.h
 create mode 100644 include/dt-bindings/clock/sun5i-a13-ccu.h
 create mode 100644 include/dt-bindings/reset/sun5i-a13-ccu.h

diff --git a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
index cb91507..7bb7a6a 100644
--- a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt
@@ -4,6 +4,7 @@ Allwinner Clock Control Unit Binding
 Required properties :
 - compatible: must contain one of the following compatible:
 		- "allwinner,sun8i-h3-ccu"
+		- "allwinner,sun5i-a13-ccu"
 
 - reg: Must contain the registers base address and length
 - clocks: phandle to the oscillators feeding the CCU. Two are needed:
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index e012890..2afe05fb 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -46,8 +46,10 @@
 
 #include "sun5i.dtsi"
 
+#include <dt-bindings/clock/sun5i-a13-ccu.h>
 #include <dt-bindings/pinctrl/sun4i-a10.h>
 #include <dt-bindings/thermal/thermal.h>
+#include <dt-bindings/reset/sun5i-a13-ccu.h>
 
 / {
 	interrupt-parent = <&intc>;
@@ -327,6 +329,15 @@
 				};
 			};
 		};
+
+		ccu: clock@01c20000 {
+			compatible = "allwinner,sun5i-a13-ccu";
+			reg = <0x01c20000 0x400>;
+			clocks = <&osc24M>, <&osc32k>;
+			clock-names = "hosc", "losc";
+			#clock-cells = <1>;
+			#reset-cells = <1>;
+		};
 	};
 };
 
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 2afcbd3..8faba4e 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -51,6 +51,17 @@ config SUNXI_CCU_MP
 
 # SoC Drivers
 
+config SUN5I_A13_CCU
+	bool "Support for the Allwinner A13 CCU"
+	select SUNXI_CCU_DIV
+	select SUNXI_CCU_NK
+	select SUNXI_CCU_NKM
+	select SUNXI_CCU_NKMP
+	select SUNXI_CCU_NM
+	select SUNXI_CCU_MP
+	select SUNXI_CCU_PHASE
+	default ARCH_SUN5I
+
 config SUN8I_H3_CCU
 	bool "Support for the Allwinner H3 CCU"
 	select SUNXI_CCU_DIV
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 633ce64..1710745 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -17,4 +17,5 @@ obj-$(CONFIG_SUNXI_CCU_NM)	+= ccu_nm.o
 obj-$(CONFIG_SUNXI_CCU_MP)	+= ccu_mp.o
 
 # SoC support
+obj-$(CONFIG_SUN5I_A13_CCU)	+= ccu-sun5i-a13.o
 obj-$(CONFIG_SUN8I_H3_CCU)	+= ccu-sun8i-h3.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun5i-a13.c b/drivers/clk/sunxi-ng/ccu-sun5i-a13.c
new file mode 100644
index 0000000..7f1da20
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun5i-a13.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016 Maxime Ripard. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun5i-a13.h"
+
+static SUNXI_CCU_GATE(ve_clk, "ve", "pll4",
+		      0x13c, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(avs_clk,	"avs",	"osc24M",
+		      0x144, BIT(31), 0);
+
+static struct ccu_common *sun5i_a13_ccu_clks[] = {
+	&ve_clk.common,
+	&avs_clk.common,
+};
+
+static struct clk_hw_onecell_data sun5i_a13_hw_clks = {
+	.hws	= {
+		[CLK_VE]		= &ve_clk.common.hw,
+		[CLK_AVS]		= &avs_clk.common.hw,
+	},
+	.num	= CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun5i_a13_ccu_resets[] = {
+	[RST_VE]		=  { 0x13c, BIT(0) },
+};
+
+static const struct sunxi_ccu_desc sun5i_a13_ccu_desc = {
+	.ccu_clks	= sun5i_a13_ccu_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sun5i_a13_ccu_clks),
+
+	.hw_clks	= &sun5i_a13_hw_clks,
+
+	.resets		= sun5i_a13_ccu_resets,
+	.num_resets	= ARRAY_SIZE(sun5i_a13_ccu_resets),
+};
+
+static void __init sun5i_a13_ccu_setup(struct device_node *node)
+{
+	void __iomem *reg;
+
+	reg = of_iomap(node, 0);
+	if (IS_ERR(reg)) {
+		pr_err("%s: Could not map the clock registers\n",
+		       of_node_full_name(node));
+		return;
+	}
+
+	sunxi_ccu_probe(node, reg, &sun5i_a13_ccu_desc);
+}
+
+CLK_OF_DECLARE(sun5i_A13_ccu, "allwinner,sun5i-a13-ccu",
+	       sun5i_a13_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun5i-a13.h b/drivers/clk/sunxi-ng/ccu-sun5i-a13.h
new file mode 100644
index 0000000..a52af0b
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun5i-a13.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN5I_A13_H_
+#define _CCU_SUN5I_A13_H_
+
+#include <dt-bindings/clock/sun5i-a13-ccu.h>
+#include <dt-bindings/reset/sun5i-a13-ccu.h>
+
+#define CLK_NUMBER		2
+
+#endif /* _CCU_SUN5I_A13_H_ */
diff --git a/include/dt-bindings/clock/sun5i-a13-ccu.h b/include/dt-bindings/clock/sun5i-a13-ccu.h
new file mode 100644
index 0000000..1218338
--- /dev/null
+++ b/include/dt-bindings/clock/sun5i-a13-ccu.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_CLK_SUN8I_a13_H_
+#define _DT_BINDINGS_CLK_SUN8I_a13_H_
+
+#define CLK_VE			0
+#define CLK_AVS			1
+
+#endif /* _DT_BINDINGS_CLK_SUN8I_A13_H_ */
diff --git a/include/dt-bindings/reset/sun5i-a13-ccu.h b/include/dt-bindings/reset/sun5i-a13-ccu.h
new file mode 100644
index 0000000..f20b4f6
--- /dev/null
+++ b/include/dt-bindings/reset/sun5i-a13-ccu.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DT_BINDINGS_RST_SUN5I_A13_H_
+#define _DT_BINDINGS_RST_SUN5I_A13_H_
+
+#define RST_VE			0
+
+#endif /* _DT_BINDINGS_RST_SUN5I_A13_H_ */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 02/10] v4l: Add private compound control type.
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
  2016-08-25  9:39 ` [RFC 01/10] clk: sunxi-ng: Add a couple of A13 clocks Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-08-25  9:39 ` [RFC 03/10] v4l: Add sunxi Video Engine pixel format Florent Revest
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

From: Pawel Osciak <posciak@chromium.org>

V4L2_CTRL_TYPE_PRIVATE is to be used for private driver compound
controls that use the "ptr" member of struct v4l2_ext_control.

Signed-off-by: Pawel Osciak <posciak@chromium.org>
Signed-off-by: Jung Zhao <jung.zhao@rock-chips.com>
Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 4 ++++
 include/uapi/linux/videodev2.h       | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index f7333fe..60056b0 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1543,6 +1543,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
 			return -ERANGE;
 		return 0;
 
+	/* FIXME:just return 0 for now */
+	case V4L2_CTRL_TYPE_PRIVATE:
+		return 0;
+
 	default:
 		return -EINVAL;
 	}
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 3eafd3f..904c44c 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1521,6 +1521,8 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_U8	     = 0x0100,
 	V4L2_CTRL_TYPE_U16	     = 0x0101,
 	V4L2_CTRL_TYPE_U32	     = 0x0102,
+
+	V4L2_CTRL_TYPE_PRIVATE       = 0xffff,
 };
 
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 03/10] v4l: Add sunxi Video Engine pixel format
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
  2016-08-25  9:39 ` [RFC 01/10] clk: sunxi-ng: Add a couple of A13 clocks Florent Revest
  2016-08-25  9:39 ` [RFC 02/10] v4l: Add private compound control type Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-11-14 10:01   ` Hans Verkuil
  2016-08-25  9:39 ` [RFC 04/10] v4l: Add MPEG2 low-level decoder API control Florent Revest
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

Add support for the allwinner's proprietary pixel format described in
details here: http://linux-sunxi.org/File:Ve_tile_format_v1.pdf

This format is similar to V4L2_PIX_FMT_NV12M but the planes are divided
in tiles of 32x32px.

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 include/uapi/linux/videodev2.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 904c44c..96e034d 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -627,6 +627,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_Y8I      v4l2_fourcc('Y', '8', 'I', ' ') /* Greyscale 8-bit L/R interleaved */
 #define V4L2_PIX_FMT_Y12I     v4l2_fourcc('Y', '1', '2', 'I') /* Greyscale 12-bit L/R interleaved */
 #define V4L2_PIX_FMT_Z16      v4l2_fourcc('Z', '1', '6', ' ') /* Depth data 16-bit */
+#define V4L2_PIX_FMT_SUNXI    v4l2_fourcc('S', 'X', 'I', 'Y') /* Sunxi VE's 32x32 tiled NV12 */
 
 /* SDR formats - used only for Software Defined Radio devices */
 #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 04/10] v4l: Add MPEG2 low-level decoder API control
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (2 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 03/10] v4l: Add sunxi Video Engine pixel format Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-08-25  9:39 ` [RFC 05/10] v4l: Add MPEG4 " Florent Revest
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

This control is to be used with the new low-level decoder API for
MPEG2 to provide additional parameters for the hardware that cannot parse
the input stream.

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c | 11 +++++++++++
 drivers/media/v4l2-core/v4l2-ioctl.c |  1 +
 include/uapi/linux/v4l2-controls.h   | 26 ++++++++++++++++++++++++++
 include/uapi/linux/videodev2.h       |  3 +++
 4 files changed, 41 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 60056b0..331d009 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -760,6 +760,8 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER:		return "Repeat Sequence Header";
 	case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:		return "Force Key Frame";
 
+	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:		return "MPEG2 Frame Header";
+
 	/* VPX controls */
 	case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS:		return "VPX Number of Partitions";
 	case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4:		return "VPX Intra Mode Decision Disable";
@@ -1143,6 +1145,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_RDS_TX_ALT_FREQS:
 		*type = V4L2_CTRL_TYPE_U32;
 		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
+		*type = V4L2_CTRL_TYPE_MPEG2_FRAME_HDR;
+		break;
 	default:
 		*type = V4L2_CTRL_TYPE_INTEGER;
 		break;
@@ -1543,6 +1548,9 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
 			return -ERANGE;
 		return 0;
 
+	case V4L2_CTRL_TYPE_MPEG2_FRAME_HDR:
+		return 0;
+
 	/* FIXME:just return 0 for now */
 	case V4L2_CTRL_TYPE_PRIVATE:
 		return 0;
@@ -2096,6 +2104,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	case V4L2_CTRL_TYPE_U32:
 		elem_size = sizeof(u32);
 		break;
+	case V4L2_CTRL_TYPE_MPEG2_FRAME_HDR:
+		elem_size = sizeof(struct v4l2_ctrl_mpeg2_frame_hdr);
+		break;
 	default:
 		if (type < V4L2_CTRL_COMPOUND_TYPES)
 			elem_size = sizeof(s32);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index f19b666..de382a1 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1273,6 +1273,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 		case V4L2_PIX_FMT_VC1_ANNEX_G:	descr = "VC-1 (SMPTE 412M Annex G)"; break;
 		case V4L2_PIX_FMT_VC1_ANNEX_L:	descr = "VC-1 (SMPTE 412M Annex L)"; break;
 		case V4L2_PIX_FMT_VP8:		descr = "VP8"; break;
+		case V4L2_PIX_FMT_MPEG2_FRAME:	descr = "MPEG2 FRAME"; break;
 		case V4L2_PIX_FMT_CPIA1:	descr = "GSPCA CPiA YUV"; break;
 		case V4L2_PIX_FMT_WNVA:		descr = "WNVA"; break;
 		case V4L2_PIX_FMT_SN9C10X:	descr = "GSPCA SN9C10X"; break;
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b6a357a..cdf9497 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -547,6 +547,8 @@ enum v4l2_mpeg_video_mpeg4_profile {
 };
 #define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL		(V4L2_CID_MPEG_BASE+407)
 
+#define V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR     (V4L2_CID_MPEG_BASE+450)
+
 /*  Control IDs for VP8 streams
  *  Although VP8 is not part of MPEG we add these controls to the MPEG class
  *  as that class is already handling other video compression standards
@@ -974,4 +976,28 @@ enum v4l2_detect_md_mode {
 #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
 #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
 
+struct v4l2_ctrl_mpeg2_frame_hdr {
+	__u32 slice_len;
+	__u32 slice_pos;
+	enum { MPEG1, MPEG2 } type;
+
+	__u16 width;
+	__u16 height;
+
+	enum { PCT_I = 1, PCT_P, PCT_B, PCT_D } picture_coding_type;
+	__u8 f_code[2][2];
+
+	__u8 intra_dc_precision;
+	__u8 picture_structure;
+	__u8 top_field_first;
+	__u8 frame_pred_frame_dct;
+	__u8 concealment_motion_vectors;
+	__u8 q_scale_type;
+	__u8 intra_vlc_format;
+	__u8 alternate_scan;
+
+	__u8 backward_index;
+	__u8 forward_index;
+};
+
 #endif
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 96e034d..feff200 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -596,6 +596,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
 #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
 #define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
+#define V4L2_PIX_FMT_MPEG2_FRAME  v4l2_fourcc('M', 'G', '2', 'F') /* MPEG2 frame */
 
 /*  Vendor-specific formats   */
 #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
@@ -1478,6 +1479,7 @@ struct v4l2_ext_control {
 		__u8 __user *p_u8;
 		__u16 __user *p_u16;
 		__u32 __user *p_u32;
+		struct v4l2_ctrl_mpeg2_frame_hdr __user *p_mpeg2_frame_hdr;
 		void __user *ptr;
 	};
 } __attribute__ ((packed));
@@ -1522,6 +1524,7 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_U8	     = 0x0100,
 	V4L2_CTRL_TYPE_U16	     = 0x0101,
 	V4L2_CTRL_TYPE_U32	     = 0x0102,
+	V4L2_CTRL_TYPE_MPEG2_FRAME_HDR  = 0x0109,
 
 	V4L2_CTRL_TYPE_PRIVATE       = 0xffff,
 };
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 05/10] v4l: Add MPEG4 low-level decoder API control
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (3 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 04/10] v4l: Add MPEG2 low-level decoder API control Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-08-25  9:39 ` [RFC 06/10] media: platform: Add Sunxi Cedrus decoder driver Florent Revest
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

This control is to be used with the new low-level decoder API for MPEG4
to provide additional parameters for the hardware that cannot parse the
input stream.

Some fields are still missing for this structure to be complete.

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 drivers/media/v4l2-core/v4l2-ctrls.c |  8 +++++++
 drivers/media/v4l2-core/v4l2-ioctl.c |  1 +
 include/uapi/linux/v4l2-controls.h   | 42 ++++++++++++++++++++++++++++++++++++
 include/uapi/linux/videodev2.h       |  3 +++
 4 files changed, 54 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 331d009..302c744 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -761,6 +761,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:		return "Force Key Frame";
 
 	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:		return "MPEG2 Frame Header";
+	case V4L2_CID_MPEG_VIDEO_MPEG4_FRAME_HDR:		return "MPEG4 Frame Header";
 
 	/* VPX controls */
 	case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS:		return "VPX Number of Partitions";
@@ -1148,6 +1149,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
 		*type = V4L2_CTRL_TYPE_MPEG2_FRAME_HDR;
 		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_FRAME_HDR:
+		*type = V4L2_CTRL_TYPE_MPEG4_FRAME_HDR;
+		break;
 	default:
 		*type = V4L2_CTRL_TYPE_INTEGER;
 		break;
@@ -1549,6 +1553,7 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
 		return 0;
 
 	case V4L2_CTRL_TYPE_MPEG2_FRAME_HDR:
+	case V4L2_CTRL_TYPE_MPEG4_FRAME_HDR:
 		return 0;
 
 	/* FIXME:just return 0 for now */
@@ -2107,6 +2112,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	case V4L2_CTRL_TYPE_MPEG2_FRAME_HDR:
 		elem_size = sizeof(struct v4l2_ctrl_mpeg2_frame_hdr);
 		break;
+	case V4L2_CTRL_TYPE_MPEG4_FRAME_HDR:
+		elem_size = sizeof(struct v4l2_ctrl_mpeg4_frame_hdr);
+		break;
 	default:
 		if (type < V4L2_CTRL_COMPOUND_TYPES)
 			elem_size = sizeof(s32);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index de382a1..be7973e 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1274,6 +1274,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 		case V4L2_PIX_FMT_VC1_ANNEX_L:	descr = "VC-1 (SMPTE 412M Annex L)"; break;
 		case V4L2_PIX_FMT_VP8:		descr = "VP8"; break;
 		case V4L2_PIX_FMT_MPEG2_FRAME:	descr = "MPEG2 FRAME"; break;
+		case V4L2_PIX_FMT_MPEG4_FRAME:	descr = "MPEG4 FRAME"; break;
 		case V4L2_PIX_FMT_CPIA1:	descr = "GSPCA CPiA YUV"; break;
 		case V4L2_PIX_FMT_WNVA:		descr = "WNVA"; break;
 		case V4L2_PIX_FMT_SN9C10X:	descr = "GSPCA SN9C10X"; break;
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index cdf9497..af466ca 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -548,6 +548,7 @@ enum v4l2_mpeg_video_mpeg4_profile {
 #define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL		(V4L2_CID_MPEG_BASE+407)
 
 #define V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR     (V4L2_CID_MPEG_BASE+450)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_FRAME_HDR     (V4L2_CID_MPEG_BASE+451)
 
 /*  Control IDs for VP8 streams
  *  Although VP8 is not part of MPEG we add these controls to the MPEG class
@@ -1000,4 +1001,45 @@ struct v4l2_ctrl_mpeg2_frame_hdr {
 	__u8 forward_index;
 };
 
+struct v4l2_ctrl_mpeg4_frame_hdr {
+	__u32 slice_len;
+	__u32 slice_pos;
+	unsigned char quant_scale;
+
+	__u16 width;
+	__u16 height;
+
+	struct {
+		unsigned int short_video_header		: 1;
+		unsigned int chroma_format			: 2;
+		unsigned int interlaced			: 1;
+		unsigned int obmc_disable			: 1;
+		unsigned int sprite_enable			: 2;
+		unsigned int sprite_warping_accuracy	: 2;
+		unsigned int quant_type			: 1;
+		unsigned int quarter_sample			: 1;
+		unsigned int data_partitioned		: 1;
+		unsigned int reversible_vlc			: 1;
+		unsigned int resync_marker_disable		: 1;
+	} vol_fields;
+
+	struct {
+		unsigned int vop_coding_type		: 2;
+		unsigned int backward_reference_vop_coding_type	: 2;
+		unsigned int vop_rounding_type		: 1;
+		unsigned int intra_dc_vlc_thr		: 3;
+		unsigned int top_field_first		: 1;
+		unsigned int alternate_vertical_scan_flag	: 1;
+	} vop_fields;
+
+	unsigned char vop_fcode_forward;
+	unsigned char vop_fcode_backward;
+
+	short trb;
+	short trd;
+
+	__u8 backward_index;
+	__u8 forward_index;
+};
+
 #endif
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index feff200..18958e2 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -597,6 +597,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
 #define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
 #define V4L2_PIX_FMT_MPEG2_FRAME  v4l2_fourcc('M', 'G', '2', 'F') /* MPEG2 frame */
+#define V4L2_PIX_FMT_MPEG4_FRAME  v4l2_fourcc('M', 'G', '4', 'F') /* MPEG4 frame */
 
 /*  Vendor-specific formats   */
 #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
@@ -1480,6 +1481,7 @@ struct v4l2_ext_control {
 		__u16 __user *p_u16;
 		__u32 __user *p_u32;
 		struct v4l2_ctrl_mpeg2_frame_hdr __user *p_mpeg2_frame_hdr;
+		struct v4l2_ctrl_mpeg4_frame_hdr __user *p_mpeg4_frame_hdr;
 		void __user *ptr;
 	};
 } __attribute__ ((packed));
@@ -1525,6 +1527,7 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_U16	     = 0x0101,
 	V4L2_CTRL_TYPE_U32	     = 0x0102,
 	V4L2_CTRL_TYPE_MPEG2_FRAME_HDR  = 0x0109,
+	V4L2_CTRL_TYPE_MPEG4_FRAME_HDR  = 0x010A,
 
 	V4L2_CTRL_TYPE_PRIVATE       = 0xffff,
 };
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 06/10] media: platform: Add Sunxi Cedrus decoder driver
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (4 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 05/10] v4l: Add MPEG4 " Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-11-14 10:07   ` Hans Verkuil
  2016-08-25  9:39 ` [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec Florent Revest
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

This patch adds a "sunxi-cedrus" v4l2 m2m decoder driver for
Allwinner's Video Processing Unit. This VPU has a low-level interface
which requires manual registers writing for frame headers. Hence, it
depends on the Request API to synchronize buffers with controls.

Most of the reverse engineering on which I based my work comes from the
"Cedrus" project: http://linux-sunxi.org/Cedrus

The driver currently only runs on the A13 and this patch doesn't
include any codec.

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 drivers/media/platform/Kconfig                     |  13 +
 drivers/media/platform/Makefile                    |   1 +
 drivers/media/platform/sunxi-cedrus/Makefile       |   2 +
 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c | 248 ++++++++++
 .../platform/sunxi-cedrus/sunxi_cedrus_common.h    |  86 ++++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c | 544 +++++++++++++++++++++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.h |  33 ++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.c  | 153 ++++++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |  32 ++
 .../platform/sunxi-cedrus/sunxi_cedrus_regs.h      | 170 +++++++
 10 files changed, 1282 insertions(+)
 create mode 100644 drivers/media/platform/sunxi-cedrus/Makefile
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index f25344b..92c92d3 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -315,6 +315,19 @@ config VIDEO_TI_VPE
 	  Support for the TI VPE(Video Processing Engine) block
 	  found on DRA7XX SoC.
 
+config VIDEO_SUNXI_CEDRUS
+	tristate "Sunxi CEDRUS VPU driver"
+	depends on VIDEO_DEV && VIDEO_V4L2
+	depends on ARCH_SUNXI
+	depends on HAS_DMA
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	---help---
+	  Support for the VPU video codec found on Sunxi SoC.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called sunxi-cedrus.
+
 config VIDEO_TI_VPE_DEBUG
 	bool "VPE debug messages"
 	depends on VIDEO_TI_VPE
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 21771c1..1419749 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
 obj-y	+= omap/
 
 obj-$(CONFIG_VIDEO_AM437X_VPFE)		+= am437x/
+obj-$(CONFIG_VIDEO_SUNXI_CEDRUS)	+= sunxi-cedrus/
 
 obj-$(CONFIG_VIDEO_XILINX)		+= xilinx/
 
diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
new file mode 100644
index 0000000..14c2f7a
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
+				    sunxi_cedrus_dec.o
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
new file mode 100644
index 0000000..17af34c
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
@@ -0,0 +1,248 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on vim2m
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sunxi_cedrus_common.h"
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "sunxi_cedrus_dec.h"
+#include "sunxi_cedrus_hw.h"
+
+static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct sunxi_cedrus_ctx *ctx =
+		container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);
+
+	v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+	return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops sunxi_cedrus_ctrl_ops = {
+	.s_ctrl = sunxi_cedrus_s_ctrl,
+};
+
+/*
+ * File operations
+ */
+static int sunxi_cedrus_open(struct file *file)
+{
+	struct sunxi_cedrus_dev *dev = video_drvdata(file);
+	struct sunxi_cedrus_ctx *ctx = NULL;
+	struct v4l2_ctrl_handler *hdl;
+	int rc = 0;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		rc = -ENOMEM;
+		goto open_unlock;
+	}
+
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	ctx->dev = dev;
+	hdl = &ctx->hdl;
+	v4l2_ctrl_handler_init(hdl, 1);
+
+	if (hdl->error) {
+		rc = hdl->error;
+		v4l2_ctrl_handler_free(hdl);
+		goto open_unlock;
+	}
+	ctx->fh.ctrl_handler = hdl;
+	v4l2_ctrl_handler_setup(hdl);
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		rc = PTR_ERR(ctx->fh.m2m_ctx);
+
+		v4l2_ctrl_handler_free(hdl);
+		kfree(ctx);
+		goto open_unlock;
+	}
+
+	v4l2_fh_add(&ctx->fh);
+
+	dev_dbg(dev->dev, "Created instance: %p, m2m_ctx: %p\n",
+		ctx, ctx->fh.m2m_ctx);
+
+open_unlock:
+	mutex_unlock(&dev->dev_mutex);
+	return rc;
+}
+
+static int sunxi_cedrus_release(struct file *file)
+{
+	struct sunxi_cedrus_dev *dev = video_drvdata(file);
+	struct sunxi_cedrus_ctx *ctx = container_of(file->private_data,
+			struct sunxi_cedrus_ctx, fh);
+
+	dev_dbg(dev->dev, "Releasing instance %p\n", ctx);
+
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	v4l2_ctrl_handler_free(&ctx->hdl);
+	mutex_lock(&dev->dev_mutex);
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+	mutex_unlock(&dev->dev_mutex);
+	kfree(ctx);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations sunxi_cedrus_fops = {
+	.owner		= THIS_MODULE,
+	.open		= sunxi_cedrus_open,
+	.release	= sunxi_cedrus_release,
+	.poll		= v4l2_m2m_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= v4l2_m2m_fop_mmap,
+};
+
+static struct video_device sunxi_cedrus_viddev = {
+	.name		= SUNXI_CEDRUS_NAME,
+	.vfl_dir	= VFL_DIR_M2M,
+	.fops		= &sunxi_cedrus_fops,
+	.ioctl_ops	= &sunxi_cedrus_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release_empty,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+	.device_run	= device_run,
+	.job_abort	= job_abort,
+};
+
+static int sunxi_cedrus_probe(struct platform_device *pdev)
+{
+	struct sunxi_cedrus_dev *dev;
+	struct video_device *vfd;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->dev = &pdev->dev;
+	dev->pdev = pdev;
+
+	ret = sunxi_cedrus_hw_probe(dev);
+	if (ret) {
+		dev_err(&pdev->dev, "sunxi_cedrus_hw_probe failed\n");
+		return ret;
+	}
+
+	spin_lock_init(&dev->irqlock);
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		return ret;
+
+	mutex_init(&dev->dev_mutex);
+
+	dev->vfd = sunxi_cedrus_viddev;
+	vfd = &dev->vfd;
+	vfd->lock = &dev->dev_mutex;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		goto unreg_dev;
+	}
+
+	video_set_drvdata(vfd, dev);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", sunxi_cedrus_viddev.name);
+	v4l2_info(&dev->v4l2_dev,
+		  "Device registered as /dev/video%d\n", vfd->num);
+
+	platform_set_drvdata(pdev, dev);
+
+	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR(dev->m2m_dev)) {
+		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(dev->m2m_dev);
+		goto err_m2m;
+	}
+
+	return 0;
+
+err_m2m:
+	v4l2_m2m_release(dev->m2m_dev);
+	video_unregister_device(&dev->vfd);
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	return ret;
+}
+
+static int sunxi_cedrus_remove(struct platform_device *pdev)
+{
+	struct sunxi_cedrus_dev *dev = platform_get_drvdata(pdev);
+
+	v4l2_info(&dev->v4l2_dev, "Removing " SUNXI_CEDRUS_NAME);
+	v4l2_m2m_release(dev->m2m_dev);
+	video_unregister_device(&dev->vfd);
+	v4l2_device_unregister(&dev->v4l2_dev);
+	sunxi_cedrus_hw_remove(dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_sunxi_cedrus_match[] = {
+	{ .compatible = "allwinner,sun5i-a13-video-engine" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_sunxi_cedrus_match);
+#endif
+
+static struct platform_driver sunxi_cedrus_driver = {
+	.probe		= sunxi_cedrus_probe,
+	.remove		= sunxi_cedrus_remove,
+	.driver		= {
+		.name	= SUNXI_CEDRUS_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(of_sunxi_cedrus_match),
+	},
+};
+module_platform_driver(sunxi_cedrus_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
+MODULE_DESCRIPTION("Sunxi Cedrus codec driver");
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
new file mode 100644
index 0000000..6b8d87a
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
@@ -0,0 +1,86 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on vim2m
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef SUNXI_CEDRUS_COMMON_H_
+#define SUNXI_CEDRUS_COMMON_H_
+
+#include "sunxi_cedrus_regs.h"
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#define SUNXI_CEDRUS_NAME		"sunxi-cedrus"
+
+struct sunxi_cedrus_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	vfd;
+	struct platform_device	*pdev;
+	struct device		*dev;
+	struct v4l2_m2m_dev	*m2m_dev;
+
+	/* Mutex for device file */
+	struct mutex		dev_mutex;
+	/* Spinlock for interrupt */
+	spinlock_t		irqlock;
+
+	struct clk *mod_clk;
+	struct clk *ahb_clk;
+	struct clk *ram_clk;
+
+	struct reset_control *rstc;
+
+	char *base;
+};
+
+struct sunxi_cedrus_fmt {
+	u32	fourcc;
+	int	depth;
+	u32	types;
+	unsigned int num_planes;
+};
+
+struct sunxi_cedrus_ctx {
+	struct v4l2_fh		fh;
+	struct sunxi_cedrus_dev	*dev;
+
+	struct sunxi_cedrus_fmt *vpu_src_fmt;
+	struct v4l2_pix_format_mplane src_fmt;
+	struct sunxi_cedrus_fmt *vpu_dst_fmt;
+	struct v4l2_pix_format_mplane dst_fmt;
+
+	struct v4l2_ctrl_handler hdl;
+
+	struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
+};
+
+static inline void sunxi_cedrus_write(struct sunxi_cedrus_dev *vpu,
+				      u32 val, u32 reg)
+{
+	writel(val, vpu->base + reg);
+}
+
+static inline u32 sunxi_cedrus_read(struct sunxi_cedrus_dev *vpu, u32 reg)
+{
+	return readl(vpu->base + reg);
+}
+
+#endif /* SUNXI_CEDRUS_COMMON_H_ */
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
new file mode 100644
index 0000000..71ef34b
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
@@ -0,0 +1,544 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on vim2m
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sunxi_cedrus_common.h"
+
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "sunxi_cedrus_dec.h"
+#include "sunxi_cedrus_hw.h"
+
+/* Flags that indicate a format can be used for capture/output */
+#define SUNXI_CEDRUS_CAPTURE	BIT(0)
+#define SUNXI_CEDRUS_OUTPUT	BIT(1)
+
+#define SUNXI_CEDRUS_MIN_WIDTH 16U
+#define SUNXI_CEDRUS_MIN_HEIGHT 16U
+#define SUNXI_CEDRUS_MAX_WIDTH 3840U
+#define SUNXI_CEDRUS_MAX_HEIGHT 2160U
+
+static struct sunxi_cedrus_fmt formats[] = {
+	{
+		.fourcc = V4L2_PIX_FMT_SUNXI,
+		.types	= SUNXI_CEDRUS_CAPTURE,
+		.depth = 8,
+		.num_planes = 2,
+	},
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct sunxi_cedrus_fmt *find_format(struct v4l2_format *f)
+{
+	struct sunxi_cedrus_fmt *fmt;
+	unsigned int k;
+
+	for (k = 0; k < NUM_FORMATS; k++) {
+		fmt = &formats[k];
+		if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
+			break;
+	}
+
+	if (k == NUM_FORMATS)
+		return NULL;
+
+	return &formats[k];
+}
+
+static inline struct sunxi_cedrus_ctx *file2ctx(struct file *file)
+{
+	return container_of(file->private_data, struct sunxi_cedrus_ctx, fh);
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+void job_abort(void *priv)
+{}
+
+/*
+ * device_run() - prepares and starts processing
+ */
+void device_run(void *priv)
+{
+	struct sunxi_cedrus_ctx *ctx = priv;
+	struct vb2_v4l2_buffer *in_vb, *out_vb;
+	dma_addr_t in_buf, out_luma, out_chroma;
+
+	in_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	out_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+	v4l2_ctrl_apply_request(&ctx->hdl, in_vb->request);
+
+	in_buf = vb2_dma_contig_plane_dma_addr(&in_vb->vb2_buf, 0);
+	out_luma = vb2_dma_contig_plane_dma_addr(&out_vb->vb2_buf, 0);
+	out_chroma = vb2_dma_contig_plane_dma_addr(&out_vb->vb2_buf, 1);
+	if (!in_buf || !out_luma || !out_chroma) {
+		v4l2_err(&ctx->dev->v4l2_dev,
+			 "Acquiring kernel pointers to buffers failed\n");
+		return;
+	}
+
+	/*
+	 * The VPU is only able to handle bus addresses so we have to subtract
+	 * the RAM offset to the physcal addresses
+	 */
+	in_buf     -= PHYS_OFFSET;
+	out_luma   -= PHYS_OFFSET;
+	out_chroma -= PHYS_OFFSET;
+
+	out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp;
+	if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE)
+		out_vb->timecode = in_vb->timecode;
+	out_vb->field = in_vb->field;
+	out_vb->flags = in_vb->flags & (V4L2_BUF_FLAG_TIMECODE |
+		 V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
+		 V4L2_BUF_FLAG_BFRAME   | V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
+
+	v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
+	v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, SUNXI_CEDRUS_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, SUNXI_CEDRUS_NAME, sizeof(cap->card) - 1);
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", SUNXI_CEDRUS_NAME);
+	cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+	int i, num;
+	struct sunxi_cedrus_fmt *fmt;
+
+	num = 0;
+
+	for (i = 0; i < NUM_FORMATS; ++i) {
+		if (formats[i].types & type) {
+			/* index-th format of type type found ? */
+			if (num == f->index)
+				break;
+			/*
+			 * Correct type but haven't reached our index yet,
+			 * just increment per-type index
+			 */
+			++num;
+		}
+	}
+
+	if (i < NUM_FORMATS) {
+		/* Format found */
+		fmt = &formats[i];
+		f->pixelformat = fmt->fourcc;
+		return 0;
+	}
+
+	/* Format not found */
+	return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	return enum_fmt(f, SUNXI_CEDRUS_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	return enum_fmt(f, SUNXI_CEDRUS_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct sunxi_cedrus_ctx *ctx, struct v4l2_format *f)
+{
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		f->fmt.pix_mp = ctx->dst_fmt;
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		f->fmt.pix_mp = ctx->src_fmt;
+		break;
+	default:
+		dev_dbg(ctx->dev->dev, "invalid buf type\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(file2ctx(file), f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	return vidioc_g_fmt(file2ctx(file), f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f, struct sunxi_cedrus_fmt *fmt)
+{
+	int i;
+	__u32 bpl;
+
+	f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+	f->fmt.pix_mp.num_planes = fmt->num_planes;
+
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0)
+			return -EINVAL;
+
+		f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		/* Limit to hardware min/max. */
+		f->fmt.pix_mp.width = clamp(f->fmt.pix_mp.width,
+			SUNXI_CEDRUS_MIN_WIDTH, SUNXI_CEDRUS_MAX_WIDTH);
+		f->fmt.pix_mp.height = clamp(f->fmt.pix_mp.height,
+			SUNXI_CEDRUS_MIN_HEIGHT, SUNXI_CEDRUS_MAX_HEIGHT);
+
+		for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) {
+			bpl = (f->fmt.pix_mp.width * fmt->depth) >> 3;
+			f->fmt.pix_mp.plane_fmt[i].bytesperline = bpl;
+			f->fmt.pix_mp.plane_fmt[i].sizeimage =
+				f->fmt.pix_mp.height * bpl;
+		}
+		break;
+	}
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct sunxi_cedrus_fmt *fmt;
+	struct sunxi_cedrus_ctx *ctx = file2ctx(file);
+
+	fmt = find_format(f);
+	if (!fmt) {
+		f->fmt.pix_mp.pixelformat = formats[0].fourcc;
+		fmt = find_format(f);
+	}
+	if (!(fmt->types & SUNXI_CEDRUS_CAPTURE)) {
+		v4l2_err(&ctx->dev->v4l2_dev,
+			 "Fourcc format (0x%08x) invalid.\n",
+			 f->fmt.pix_mp.pixelformat);
+		return -EINVAL;
+	}
+
+	return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct sunxi_cedrus_fmt *fmt;
+	struct sunxi_cedrus_ctx *ctx = file2ctx(file);
+
+	fmt = find_format(f);
+	if (!fmt) {
+		f->fmt.pix_mp.pixelformat = formats[0].fourcc;
+		fmt = find_format(f);
+	}
+	if (!(fmt->types & SUNXI_CEDRUS_OUTPUT)) {
+		v4l2_err(&ctx->dev->v4l2_dev,
+			 "Fourcc format (0x%08x) invalid.\n",
+			 f->fmt.pix_mp.pixelformat);
+		return -EINVAL;
+	}
+
+	return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_s_fmt(struct sunxi_cedrus_ctx *ctx, struct v4l2_format *f)
+{
+	struct sunxi_cedrus_dev *dev = ctx->dev;
+	struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+	struct sunxi_cedrus_fmt *fmt;
+	int i, ret = 0;
+
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ctx->vpu_src_fmt = find_format(f);
+		ctx->src_fmt = *pix_fmt_mp;
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		fmt = find_format(f);
+		ctx->vpu_dst_fmt = fmt;
+
+		for (i = 0; i < fmt->num_planes; ++i) {
+			pix_fmt_mp->plane_fmt[i].bytesperline =
+				pix_fmt_mp->width * fmt->depth;
+			pix_fmt_mp->plane_fmt[i].sizeimage =
+				pix_fmt_mp->plane_fmt[i].bytesperline
+				* pix_fmt_mp->height;
+		}
+		ctx->dst_fmt = *pix_fmt_mp;
+		break;
+	default:
+		dev_dbg(ctx->dev->dev, "invalid buf type\n");
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	int ret;
+
+	ret = vidioc_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	return vidioc_s_fmt(file2ctx(file), f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	int ret;
+
+	ret = vidioc_try_fmt_vid_out(file, priv, f);
+	if (ret)
+		return ret;
+
+	ret = vidioc_s_fmt(file2ctx(file), f);
+	return ret;
+}
+
+const struct v4l2_ioctl_ops sunxi_cedrus_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap	= vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap_mplane	= vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap_mplane	= vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap_mplane	= vidioc_s_fmt_vid_cap,
+
+	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_out_mplane	= vidioc_g_fmt_vid_out,
+	.vidioc_try_fmt_vid_out_mplane	= vidioc_try_fmt_vid_out,
+	.vidioc_s_fmt_vid_out_mplane	= vidioc_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,
+
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * Queue operations
+ */
+
+static int sunxi_cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
+				    unsigned int *nplanes, unsigned int sizes[],
+				    struct device *alloc_devs[])
+{
+	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(vq);
+
+	if (*nbufs < 1)
+		*nbufs = 1;
+
+	if (*nbufs > VIDEO_MAX_FRAME)
+		*nbufs = VIDEO_MAX_FRAME;
+
+	switch (vq->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		*nplanes = ctx->vpu_src_fmt->num_planes;
+
+		sizes[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		*nplanes = ctx->vpu_dst_fmt->num_planes;
+
+		sizes[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8);
+		sizes[1] = sizes[0];
+		break;
+
+	default:
+		dev_dbg(ctx->dev->dev, "invalid queue type: %d\n", vq->type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sunxi_cedrus_buf_init(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct sunxi_cedrus_ctx *ctx = container_of(vq->drv_priv,
+			struct sunxi_cedrus_ctx, fh);
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		ctx->dst_bufs[vb->index] = vb;
+
+	return 0;
+}
+
+static void sunxi_cedrus_buf_cleanup(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct sunxi_cedrus_ctx *ctx = container_of(vq->drv_priv,
+			struct sunxi_cedrus_ctx, fh);
+
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		ctx->dst_bufs[vb->index] = NULL;
+}
+
+static int sunxi_cedrus_buf_prepare(struct vb2_buffer *vb)
+{
+	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vb2_queue *vq = vb->vb2_queue;
+	int i;
+
+	dev_dbg(ctx->dev->dev, "type: %d\n", vb->vb2_queue->type);
+
+	switch (vq->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (vb2_plane_size(vb, 0)
+		    < ctx->src_fmt.plane_fmt[0].sizeimage) {
+			dev_dbg(ctx->dev->dev, "plane too small for output\n");
+			return -EINVAL;
+		}
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) {
+			if (vb2_plane_size(vb, i)
+			    < ctx->dst_fmt.plane_fmt[i].sizeimage) {
+				dev_dbg(ctx->dev->dev,
+					"plane %d too small for capture\n", i);
+				break;
+			}
+		}
+
+		if (i != ctx->vpu_dst_fmt->num_planes)
+			return -EINVAL;
+		break;
+
+	default:
+		dev_dbg(ctx->dev->dev, "invalid queue type: %d\n", vq->type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void sunxi_cedrus_stop_streaming(struct vb2_queue *q)
+{
+	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(q);
+	struct vb2_v4l2_buffer *vbuf;
+	unsigned long flags;
+
+	for (;;) {
+		if (V4L2_TYPE_IS_OUTPUT(q->type))
+			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		else
+			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+		if (!vbuf)
+			return;
+		spin_lock_irqsave(&ctx->dev->irqlock, flags);
+		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+		spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+	}
+}
+
+static void sunxi_cedrus_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static struct vb2_ops sunxi_cedrus_qops = {
+	.queue_setup	 = sunxi_cedrus_queue_setup,
+	.buf_prepare	 = sunxi_cedrus_buf_prepare,
+	.buf_init	 = sunxi_cedrus_buf_init,
+	.buf_cleanup	 = sunxi_cedrus_buf_cleanup,
+	.buf_queue	 = sunxi_cedrus_buf_queue,
+	.stop_streaming  = sunxi_cedrus_stop_streaming,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
+};
+
+int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+	struct sunxi_cedrus_ctx *ctx = priv;
+	int ret;
+
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	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->ops = &sunxi_cedrus_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->v4l2_allow_requests = true;
+	src_vq->dev = ctx->dev->dev;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	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->ops = &sunxi_cedrus_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->v4l2_allow_requests = true;
+	dst_vq->dev = ctx->dev->dev;
+
+	return vb2_queue_init(dst_vq);
+}
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h
new file mode 100644
index 0000000..f0ac921
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h
@@ -0,0 +1,33 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on vim2m
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef SUNXI_CEDRUS_DEC_H_
+#define SUNXI_CEDRUS_DEC_H_
+
+int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq);
+
+void job_abort(void *priv);
+void device_run(void *priv);
+
+extern const struct v4l2_ioctl_ops sunxi_cedrus_ioctl_ops;
+
+#endif /* SUNXI_CEDRUS_DEC_H_ */
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
new file mode 100644
index 0000000..72b9df4
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
@@ -0,0 +1,153 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on vim2m
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * And reverse engineering efforts of the 'Cedrus' project
+ * Copyright (c) 2013-2014 Jens Kuske <jenskuske@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sunxi_cedrus_common.h"
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/reset.h>
+
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+
+/*
+ * Interrupt handlers.
+ */
+
+static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
+{
+	struct sunxi_cedrus_dev *vpu = dev_id;
+	struct sunxi_cedrus_ctx *curr_ctx;
+	struct vb2_v4l2_buffer *src_vb, *dst_vb;
+	int val;
+	unsigned long flags;
+
+	curr_ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev);
+
+	if (!curr_ctx) {
+		pr_err("Instance released before the end of transaction\n");
+		return IRQ_HANDLED;
+	}
+
+	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
+	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
+
+	spin_lock_irqsave(&vpu->irqlock, flags);
+	v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
+	v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
+	spin_unlock_irqrestore(&vpu->irqlock, flags);
+
+	v4l2_m2m_job_finish(vpu->m2m_dev, curr_ctx->fh.m2m_ctx);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Initialization/clean-up.
+ */
+
+int sunxi_cedrus_hw_probe(struct sunxi_cedrus_dev *vpu)
+{
+	struct resource *res;
+	int irq_dec;
+	int ret;
+
+	irq_dec = platform_get_irq(vpu->pdev, 0);
+	if (irq_dec <= 0) {
+		dev_err(vpu->dev, "could not get ve IRQ\n");
+		return -ENXIO;
+	}
+	ret = devm_request_irq(vpu->dev, irq_dec, sunxi_cedrus_ve_irq, 0,
+			       dev_name(vpu->dev), vpu);
+	if (ret) {
+		dev_err(vpu->dev, "could not request ve IRQ\n");
+		return -ENXIO;
+	}
+
+	ret = of_reserved_mem_device_init(vpu->dev);
+	if (ret) {
+		dev_err(vpu->dev, "could not reserve memory\n");
+		return -ENODEV;
+	}
+
+	vpu->ahb_clk = devm_clk_get(vpu->dev, "ahb");
+	if (IS_ERR(vpu->ahb_clk)) {
+		dev_err(vpu->dev, "failed to get ahb clock\n");
+		return PTR_ERR(vpu->ahb_clk);
+	}
+	vpu->mod_clk = devm_clk_get(vpu->dev, "mod");
+	if (IS_ERR(vpu->mod_clk)) {
+		dev_err(vpu->dev, "failed to get mod clock\n");
+		return PTR_ERR(vpu->mod_clk);
+	}
+	vpu->ram_clk = devm_clk_get(vpu->dev, "ram");
+	if (IS_ERR(vpu->ram_clk)) {
+		dev_err(vpu->dev, "failed to get ram clock\n");
+		return PTR_ERR(vpu->ram_clk);
+	}
+
+	vpu->rstc = devm_reset_control_get(vpu->dev, NULL);
+
+	res = platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0);
+	vpu->base = devm_ioremap_resource(vpu->dev, res);
+	if (!vpu->base)
+		dev_err(vpu->dev, "could not maps MACC registers\n");
+
+	ret = clk_prepare_enable(vpu->ahb_clk);
+	if (ret) {
+		dev_err(vpu->dev, "could not enable ahb clock\n");
+		return -EFAULT;
+	}
+	ret = clk_prepare_enable(vpu->mod_clk);
+	if (ret) {
+		clk_disable_unprepare(vpu->ahb_clk);
+		dev_err(vpu->dev, "could not enable mod clock\n");
+		return -EFAULT;
+	}
+	ret = clk_prepare_enable(vpu->ram_clk);
+	if (ret) {
+		clk_disable_unprepare(vpu->mod_clk);
+		clk_disable_unprepare(vpu->ahb_clk);
+		dev_err(vpu->dev, "could not enable ram clock\n");
+		return -EFAULT;
+	}
+
+	reset_control_assert(vpu->rstc);
+	reset_control_deassert(vpu->rstc);
+
+	return 0;
+}
+
+void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu)
+{
+	clk_disable_unprepare(vpu->ram_clk);
+	clk_disable_unprepare(vpu->mod_clk);
+	clk_disable_unprepare(vpu->ahb_clk);
+
+	of_reserved_mem_device_release(vpu->dev);
+}
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
new file mode 100644
index 0000000..3c9199b
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
@@ -0,0 +1,32 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on vim2m
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef SUNXI_CEDRUS_HW_H_
+#define SUNXI_CEDRUS_HW_H_
+
+struct sunxi_cedrus_dev;
+struct sunxi_cedrus_ctx;
+
+int sunxi_cedrus_hw_probe(struct sunxi_cedrus_dev *vpu);
+void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu);
+
+#endif /* SUNXI_CEDRUS_HW_H_ */
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h
new file mode 100644
index 0000000..7384daa
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h
@@ -0,0 +1,170 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on Cedrus
+ *
+ * Copyright (c) 2013 Jens Kuske <jenskuske@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef SUNXI_CEDRUS_REGS_H
+#define SUNXI_CEDRUS_REGS_H
+
+/*
+ * For more information consult http://linux-sunxi.org/VE_Register_guide
+ */
+
+/* Special registers values */
+
+/* VE_CTRL:
+ * The first 3 bits indicate the engine (0 for MPEG, 1 for H264, b for AVC...)
+ * The 16th and 17th bits indicate the memory type (3 for DDR3 32 bits)
+ * The 20th bit is unknown but needed
+ */
+#define VE_CTRL_MPEG		0x130000
+#define VE_CTRL_H264		0x130001
+#define VE_CTRL_AVC		0x13000b
+#define VE_CTRL_REINIT		0x130007
+
+/* VE_MPEG_CTRL:
+ * The bit 3 (0x8) is used to enable IRQs
+ * The other bits are unknown but needed
+ */
+#define VE_MPEG_CTRL_MPEG2	0x800001b8
+#define VE_MPEG_CTRL_MPEG4	(0x80084118 | BIT(7))
+#define VE_MPEG_CTRL_MPEG4_P	(VE_MPEG_CTRL_MPEG4 | BIT(12))
+
+/* VE_MPEG_VLD_ADDR:
+ * The bits 27 to 4 are used for the address
+ * The bits 31 to 28 (0x7) are used to select the MPEG or JPEG engine
+ */
+#define VE_MPEG_VLD_ADDR_VAL(x)	((x & 0x0ffffff0) | (x >> 28) | (0x7 << 28))
+
+/* VE_MPEG_TRIGGER:
+ * The first three bits are used to trigger the engine
+ * The bits 24 to 26 are used to select the input format (1 for MPEG1, 2 for 
+ *                           MPEG2, 4 for MPEG4)
+ * The bit 21 (0x8) is used to disable bitstream error handling
+ *
+ * In MPEG4 the w*h value is somehow used for an offset, unknown but needed
+ */
+#define VE_TRIG_MPEG1		0x8100000f
+#define VE_TRIG_MPEG2		0x8200000f
+#define VE_TRIG_MPEG4(w, h)	(0x8400000d | ((w * h) << 8))
+
+/* VE_MPEG_SDROT_CTRL:
+ * The bit 8 at zero is used to disable x downscaling
+ * The bit 10 at 0 is used to disable y downscaling
+ * The other bits are unknown but needed
+ */
+#define VE_NO_SDROT_CTRL	0x40620000
+
+/* Decent size fo video buffering verifier */
+#define VBV_SIZE		(1024 * 1024)
+
+/* Registers addresses */
+#define VE_CTRL				0x000
+#define VE_VERSION			0x0f0
+
+#define VE_MPEG_PIC_HDR			0x100
+#define VE_MPEG_VOP_HDR			0x104
+#define VE_MPEG_SIZE			0x108
+#define VE_MPEG_FRAME_SIZE		0x10c
+#define VE_MPEG_MBA			0x110
+#define VE_MPEG_CTRL			0x114
+#define VE_MPEG_TRIGGER			0x118
+#define VE_MPEG_STATUS			0x11c
+#define VE_MPEG_TRBTRD_FIELD		0x120
+#define VE_MPEG_TRBTRD_FRAME		0x124
+#define VE_MPEG_VLD_ADDR		0x128
+#define VE_MPEG_VLD_OFFSET		0x12c
+#define VE_MPEG_VLD_LEN			0x130
+#define VE_MPEG_VLD_END			0x134
+#define VE_MPEG_MBH_ADDR		0x138
+#define VE_MPEG_DCAC_ADDR		0x13c
+#define VE_MPEG_NCF_ADDR		0x144
+#define VE_MPEG_REC_LUMA		0x148
+#define VE_MPEG_REC_CHROMA		0x14c
+#define VE_MPEG_FWD_LUMA		0x150
+#define VE_MPEG_FWD_CHROMA		0x154
+#define VE_MPEG_BACK_LUMA		0x158
+#define VE_MPEG_BACK_CHROMA		0x15c
+#define VE_MPEG_IQ_MIN_INPUT		0x180
+#define VE_MPEG_QP_INPUT		0x184
+#define VE_MPEG_JPEG_SIZE		0x1b8
+#define VE_MPEG_JPEG_RES_INT		0x1c0
+#define VE_MPEG_ERROR			0x1c4
+#define VE_MPEG_CTR_MB			0x1c8
+#define VE_MPEG_ROT_LUMA		0x1cc
+#define VE_MPEG_ROT_CHROMA		0x1d0
+#define VE_MPEG_SDROT_CTRL		0x1d4
+#define VE_MPEG_RAM_WRITE_PTR		0x1e0
+#define VE_MPEG_RAM_WRITE_DATA		0x1e4
+
+#define VE_H264_FRAME_SIZE		0x200
+#define VE_H264_PIC_HDR			0x204
+#define VE_H264_SLICE_HDR		0x208
+#define VE_H264_SLICE_HDR2		0x20c
+#define VE_H264_PRED_WEIGHT		0x210
+#define VE_H264_QP_PARAM		0x21c
+#define VE_H264_CTRL			0x220
+#define VE_H264_TRIGGER			0x224
+#define VE_H264_STATUS			0x228
+#define VE_H264_CUR_MB_NUM		0x22c
+#define VE_H264_VLD_ADDR		0x230
+#define VE_H264_VLD_OFFSET		0x234
+#define VE_H264_VLD_LEN			0x238
+#define VE_H264_VLD_END			0x23c
+#define VE_H264_SDROT_CTRL		0x240
+#define VE_H264_OUTPUT_FRAME_IDX	0x24c
+#define VE_H264_EXTRA_BUFFER1		0x250
+#define VE_H264_EXTRA_BUFFER2		0x254
+#define VE_H264_BASIC_BITS		0x2dc
+#define VE_H264_RAM_WRITE_PTR		0x2e0
+#define VE_H264_RAM_WRITE_DATA		0x2e4
+
+#define VE_SRAM_H264_PRED_WEIGHT_TABLE	0x000
+#define VE_SRAM_H264_FRAMEBUFFER_LIST	0x400
+#define VE_SRAM_H264_REF_LIST0		0x640
+#define VE_SRAM_H264_REF_LIST1		0x664
+#define VE_SRAM_H264_SCALING_LISTS	0x800
+
+#define VE_ISP_INPUT_SIZE		0xa00
+#define VE_ISP_INPUT_STRIDE		0xa04
+#define VE_ISP_CTRL			0xa08
+#define VE_ISP_INPUT_LUMA		0xa78
+#define VE_ISP_INPUT_CHROMA		0xa7c
+
+#define VE_AVC_PARAM			0xb04
+#define VE_AVC_QP			0xb08
+#define VE_AVC_MOTION_EST		0xb10
+#define VE_AVC_CTRL			0xb14
+#define VE_AVC_TRIGGER			0xb18
+#define VE_AVC_STATUS			0xb1c
+#define VE_AVC_BASIC_BITS		0xb20
+#define VE_AVC_UNK_BUF			0xb60
+#define VE_AVC_VLE_ADDR			0xb80
+#define VE_AVC_VLE_END			0xb84
+#define VE_AVC_VLE_OFFSET		0xb88
+#define VE_AVC_VLE_MAX			0xb8c
+#define VE_AVC_VLE_LENGTH		0xb90
+#define VE_AVC_REF_LUMA			0xba0
+#define VE_AVC_REF_CHROMA		0xba4
+#define VE_AVC_REC_LUMA			0xbb0
+#define VE_AVC_REC_CHROMA		0xbb4
+#define VE_AVC_REF_SLUMA		0xbb8
+#define VE_AVC_REC_SLUMA		0xbbc
+#define VE_AVC_MB_INFO			0xbc0
+
+#endif /* SUNXI_CEDRUS_REGS_H */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (5 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 06/10] media: platform: Add Sunxi Cedrus decoder driver Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-11-14 10:18   ` Hans Verkuil
  2016-08-25  9:39 ` [RFC 08/10] sunxi-cedrus: Add a MPEG 4 codec Florent Revest
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

This patch introduces the support of MPEG2 video decoding to the
sunxi-cedrus video decoder driver.

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 drivers/media/platform/sunxi-cedrus/Makefile       |   2 +-
 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c |  26 +++-
 .../platform/sunxi-cedrus/sunxi_cedrus_common.h    |   2 +
 .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c |  15 +-
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.c  |  17 ++-
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |   4 +
 .../platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c     | 152 +++++++++++++++++++++
 7 files changed, 211 insertions(+), 7 deletions(-)
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c

diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
index 14c2f7a..2d495a2 100644
--- a/drivers/media/platform/sunxi-cedrus/Makefile
+++ b/drivers/media/platform/sunxi-cedrus/Makefile
@@ -1,2 +1,2 @@
 obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
-				    sunxi_cedrus_dec.o
+				    sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
index 17af34c..d1c957a 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
@@ -46,14 +46,31 @@ static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
 	struct sunxi_cedrus_ctx *ctx =
 		container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);
 
-	v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
-	return -EINVAL;
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
+		/* This is kept in memory and used directly. */
+		break;
+	default:
+		v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+
+	return 0;
 }
 
 static const struct v4l2_ctrl_ops sunxi_cedrus_ctrl_ops = {
 	.s_ctrl = sunxi_cedrus_s_ctrl,
 };
 
+static const struct v4l2_ctrl_config sunxi_cedrus_ctrl_mpeg2_frame_hdr = {
+	.ops = &sunxi_cedrus_ctrl_ops,
+	.id = V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR,
+	.type = V4L2_CTRL_TYPE_PRIVATE,
+	.name = "MPEG2 Frame Header Parameters",
+	.max_reqs = VIDEO_MAX_FRAME,
+	.elem_size = sizeof(struct v4l2_ctrl_mpeg2_frame_hdr),
+};
+
 /*
  * File operations
  */
@@ -78,6 +95,10 @@ static int sunxi_cedrus_open(struct file *file)
 	hdl = &ctx->hdl;
 	v4l2_ctrl_handler_init(hdl, 1);
 
+	ctx->mpeg2_frame_hdr_ctrl = v4l2_ctrl_new_custom(hdl,
+			&sunxi_cedrus_ctrl_mpeg2_frame_hdr, NULL);
+	ctx->mpeg2_frame_hdr_ctrl->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
+
 	if (hdl->error) {
 		rc = hdl->error;
 		v4l2_ctrl_handler_free(hdl);
@@ -117,6 +138,7 @@ static int sunxi_cedrus_release(struct file *file)
 	v4l2_fh_del(&ctx->fh);
 	v4l2_fh_exit(&ctx->fh);
 	v4l2_ctrl_handler_free(&ctx->hdl);
+	ctx->mpeg2_frame_hdr_ctrl = NULL;
 	mutex_lock(&dev->dev_mutex);
 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
 	mutex_unlock(&dev->dev_mutex);
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
index 6b8d87a..e715184 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
@@ -70,6 +70,8 @@ struct sunxi_cedrus_ctx {
 	struct v4l2_ctrl_handler hdl;
 
 	struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
+
+	struct v4l2_ctrl *mpeg2_frame_hdr_ctrl;
 };
 
 static inline void sunxi_cedrus_write(struct sunxi_cedrus_dev *vpu,
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
index 71ef34b..38e8a3a 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
@@ -48,6 +48,11 @@ static struct sunxi_cedrus_fmt formats[] = {
 		.depth = 8,
 		.num_planes = 2,
 	},
+	{
+		.fourcc = V4L2_PIX_FMT_MPEG2_FRAME,
+		.types	= SUNXI_CEDRUS_OUTPUT,
+		.num_planes = 1,
+	},
 };
 
 #define NUM_FORMATS ARRAY_SIZE(formats)
@@ -120,8 +125,14 @@ void device_run(void *priv)
 		 V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
 		 V4L2_BUF_FLAG_BFRAME   | V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
 
-	v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
-	v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
+	if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG2_FRAME) {
+		struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr =
+				ctx->mpeg2_frame_hdr_ctrl->p_new.p;
+		process_mpeg2(ctx, in_buf, out_luma, out_chroma, frame_hdr);
+	} else {
+		v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
+		v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
+	}
 }
 
 /*
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
index 72b9df4..160de17 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
@@ -47,6 +47,13 @@ static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
 	int val;
 	unsigned long flags;
 
+	/* Disable MPEG interrupts and stop the MPEG engine */
+	val = sunxi_cedrus_read(vpu, VE_MPEG_CTRL);
+	sunxi_cedrus_write(vpu, val & (~0xf), VE_MPEG_CTRL);
+	val = sunxi_cedrus_read(vpu, VE_MPEG_STATUS);
+	sunxi_cedrus_write(vpu, 0x0000c00f, VE_MPEG_STATUS);
+	sunxi_cedrus_write(vpu, VE_CTRL_REINIT, VE_CTRL);
+
 	curr_ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev);
 
 	if (!curr_ctx) {
@@ -57,9 +64,15 @@ static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
 	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
 	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
 
+	/* First bit of MPEG_STATUS means success */
 	spin_lock_irqsave(&vpu->irqlock, flags);
-	v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
-	v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
+	if (val & 0x1) {
+		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+	} else {
+		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
+		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
+	}
 	spin_unlock_irqrestore(&vpu->irqlock, flags);
 
 	v4l2_m2m_job_finish(vpu->m2m_dev, curr_ctx->fh.m2m_ctx);
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
index 3c9199b..78625e5 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
@@ -29,4 +29,8 @@ struct sunxi_cedrus_ctx;
 int sunxi_cedrus_hw_probe(struct sunxi_cedrus_dev *vpu);
 void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu);
 
+void process_mpeg2(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
+		   dma_addr_t out_luma, dma_addr_t out_chroma,
+		   struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr);
+
 #endif /* SUNXI_CEDRUS_HW_H_ */
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
new file mode 100644
index 0000000..9381c63
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
@@ -0,0 +1,152 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on reverse engineering efforts of the 'Cedrus' project
+ * Copyright (c) 2013-2014 Jens Kuske <jenskuske@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sunxi_cedrus_common.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+static const u8 mpeg_default_intra_quant[64] = {
+	 8, 16, 16, 19, 16, 19, 22, 22,
+	22, 22, 22, 22, 26, 24, 26, 27,
+	27, 27, 26, 26, 26, 26, 27, 27,
+	27, 29, 29, 29, 34, 34, 34, 29,
+	29, 29, 27, 27, 29, 29, 32, 32,
+	34, 34, 37, 38, 37, 35, 35, 34,
+	35, 38, 38, 40, 40, 40, 48, 48,
+	46, 46, 56, 56, 58, 69, 69, 83
+};
+
+#define m_iq(i) (((64 + i) << 8) | mpeg_default_intra_quant[i])
+
+static const u8 mpeg_default_non_intra_quant[64] = {
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 16, 16, 16, 16
+};
+
+#define m_niq(i) ((i << 8) | mpeg_default_non_intra_quant[i])
+
+void process_mpeg2(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
+		   dma_addr_t out_luma, dma_addr_t out_chroma,
+		   struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr)
+{
+	struct sunxi_cedrus_dev *dev = ctx->dev;
+
+	u16 width = DIV_ROUND_UP(frame_hdr->width, 16);
+	u16 height = DIV_ROUND_UP(frame_hdr->height, 16);
+
+	u32 pic_header = 0;
+	u32 vld_len = frame_hdr->slice_len - frame_hdr->slice_pos;
+	int i;
+
+	struct vb2_buffer *fwd_vb2_buf, *bwd_vb2_buf;
+	dma_addr_t fwd_luma = 0, fwd_chroma = 0, bwd_luma = 0, bwd_chroma = 0;
+
+	/*
+	 * The VPU is only able to handle bus addresses so we have to subtract
+	 * the RAM offset to the physcal addresses
+	 */
+	fwd_vb2_buf = ctx->dst_bufs[frame_hdr->forward_index];
+	if (fwd_vb2_buf) {
+		fwd_luma   = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 0);
+		fwd_chroma = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 1);
+		fwd_luma   -= PHYS_OFFSET;
+		fwd_chroma -= PHYS_OFFSET;
+	}
+
+	bwd_vb2_buf = ctx->dst_bufs[frame_hdr->backward_index];
+	if (bwd_vb2_buf) {
+		bwd_luma   = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 0);
+		bwd_chroma = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 1);
+		bwd_chroma -= PHYS_OFFSET;
+		bwd_luma   -= PHYS_OFFSET;
+	}
+
+	/* Activates MPEG engine */
+	sunxi_cedrus_write(dev, VE_CTRL_MPEG, VE_CTRL);
+
+	/* Upload quantization matrices */
+	for (i = 0; i < 64; i++) {
+		sunxi_cedrus_write(dev, m_iq(i),  VE_MPEG_IQ_MIN_INPUT);
+		sunxi_cedrus_write(dev, m_niq(i), VE_MPEG_IQ_MIN_INPUT);
+	}
+
+	/* Image's dimensions */
+	sunxi_cedrus_write(dev, width << 8  | height,      VE_MPEG_SIZE);
+	sunxi_cedrus_write(dev, width << 20 | height << 4, VE_MPEG_FRAME_SIZE);
+
+	/* MPEG picture's header */
+	pic_header |= (frame_hdr->picture_coding_type        & 0xf) << 28;
+	pic_header |= (frame_hdr->f_code[0][0]               & 0xf) << 24;
+	pic_header |= (frame_hdr->f_code[0][1]               & 0xf) << 20;
+	pic_header |= (frame_hdr->f_code[1][0]               & 0xf) << 16;
+	pic_header |= (frame_hdr->f_code[1][1]               & 0xf) << 12;
+	pic_header |= (frame_hdr->intra_dc_precision         & 0x3) << 10;
+	pic_header |= (frame_hdr->picture_structure          & 0x3) << 8;
+	pic_header |= (frame_hdr->top_field_first            & 0x1) << 7;
+	pic_header |= (frame_hdr->frame_pred_frame_dct       & 0x1) << 6;
+	pic_header |= (frame_hdr->concealment_motion_vectors & 0x1) << 5;
+	pic_header |= (frame_hdr->q_scale_type               & 0x1) << 4;
+	pic_header |= (frame_hdr->intra_vlc_format           & 0x1) << 3;
+	pic_header |= (frame_hdr->alternate_scan             & 0x1) << 2;
+	sunxi_cedrus_write(dev, pic_header, VE_MPEG_PIC_HDR);
+
+	/* Enable interrupt and an unknown control flag */
+	sunxi_cedrus_write(dev, VE_MPEG_CTRL_MPEG2, VE_MPEG_CTRL);
+
+	/* Macroblock address */
+	sunxi_cedrus_write(dev, 0, VE_MPEG_MBA);
+
+	/* Clear previous errors */
+	sunxi_cedrus_write(dev, 0, VE_MPEG_ERROR);
+
+	/* Unknown register */
+	sunxi_cedrus_write(dev, 0, VE_MPEG_CTR_MB);
+
+	/* Forward and backward prediction buffers (cached in dst_bufs) */
+	sunxi_cedrus_write(dev, fwd_luma,   VE_MPEG_FWD_LUMA);
+	sunxi_cedrus_write(dev, fwd_chroma, VE_MPEG_FWD_CHROMA);
+	sunxi_cedrus_write(dev, bwd_luma,   VE_MPEG_BACK_LUMA);
+	sunxi_cedrus_write(dev, bwd_chroma, VE_MPEG_BACK_CHROMA);
+
+	/* Output luma and chroma buffers */
+	sunxi_cedrus_write(dev, out_luma,   VE_MPEG_REC_LUMA);
+	sunxi_cedrus_write(dev, out_chroma, VE_MPEG_REC_CHROMA);
+	sunxi_cedrus_write(dev, out_luma,   VE_MPEG_ROT_LUMA);
+	sunxi_cedrus_write(dev, out_chroma, VE_MPEG_ROT_CHROMA);
+
+	/* Input offset and length in bits */
+	sunxi_cedrus_write(dev, frame_hdr->slice_pos, VE_MPEG_VLD_OFFSET);
+	sunxi_cedrus_write(dev, vld_len, VE_MPEG_VLD_LEN);
+
+	/* Input beginning and end addresses */
+	sunxi_cedrus_write(dev, VE_MPEG_VLD_ADDR_VAL(in_buf), VE_MPEG_VLD_ADDR);
+	sunxi_cedrus_write(dev, in_buf + VBV_SIZE - 1, VE_MPEG_VLD_END);
+
+	/* Starts the MPEG engine */
+	if (frame_hdr->type == MPEG2)
+		sunxi_cedrus_write(dev, VE_TRIG_MPEG2, VE_MPEG_TRIGGER);
+	else
+		sunxi_cedrus_write(dev, VE_TRIG_MPEG1, VE_MPEG_TRIGGER);
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 08/10] sunxi-cedrus: Add a MPEG 4 codec
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (6 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-08-25  9:39 ` [RFC 09/10] ARM: dts: sun5i: Use video-engine node Florent Revest
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

This patch introduces the support of MPEG4 video decoding to the
sunxi-cedrus video decoder driver.

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 drivers/media/platform/sunxi-cedrus/Makefile       |   3 +-
 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c |  15 +++
 .../platform/sunxi-cedrus/sunxi_cedrus_common.h    |  13 ++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c |  33 +++++
 .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |   3 +
 .../platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c     | 140 +++++++++++++++++++++
 6 files changed, 206 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c

diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
index 2d495a2..823d611 100644
--- a/drivers/media/platform/sunxi-cedrus/Makefile
+++ b/drivers/media/platform/sunxi-cedrus/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
-				    sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o
+				    sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o \
+				    sunxi_cedrus_mpeg4.o
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
index d1c957a..3001440 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
@@ -47,6 +47,7 @@ static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
 		container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);
 
 	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_MPEG4_FRAME_HDR:
 	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
 		/* This is kept in memory and used directly. */
 		break;
@@ -71,6 +72,15 @@ static const struct v4l2_ctrl_config sunxi_cedrus_ctrl_mpeg2_frame_hdr = {
 	.elem_size = sizeof(struct v4l2_ctrl_mpeg2_frame_hdr),
 };
 
+static const struct v4l2_ctrl_config sunxi_cedrus_ctrl_mpeg4_frame_hdr = {
+	.ops = &sunxi_cedrus_ctrl_ops,
+	.id = V4L2_CID_MPEG_VIDEO_MPEG4_FRAME_HDR,
+	.type = V4L2_CTRL_TYPE_PRIVATE,
+	.name = "MPEG4 Frame Header Parameters",
+	.max_reqs = VIDEO_MAX_FRAME,
+	.elem_size = sizeof(struct v4l2_ctrl_mpeg4_frame_hdr),
+};
+
 /*
  * File operations
  */
@@ -99,6 +109,10 @@ static int sunxi_cedrus_open(struct file *file)
 			&sunxi_cedrus_ctrl_mpeg2_frame_hdr, NULL);
 	ctx->mpeg2_frame_hdr_ctrl->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
 
+	ctx->mpeg4_frame_hdr_ctrl = v4l2_ctrl_new_custom(hdl,
+			&sunxi_cedrus_ctrl_mpeg4_frame_hdr, NULL);
+	ctx->mpeg4_frame_hdr_ctrl->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
+
 	if (hdl->error) {
 		rc = hdl->error;
 		v4l2_ctrl_handler_free(hdl);
@@ -139,6 +153,7 @@ static int sunxi_cedrus_release(struct file *file)
 	v4l2_fh_exit(&ctx->fh);
 	v4l2_ctrl_handler_free(&ctx->hdl);
 	ctx->mpeg2_frame_hdr_ctrl = NULL;
+	ctx->mpeg4_frame_hdr_ctrl = NULL;
 	mutex_lock(&dev->dev_mutex);
 	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
 	mutex_unlock(&dev->dev_mutex);
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
index e715184..33fa891 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
@@ -49,6 +49,18 @@ struct sunxi_cedrus_dev {
 	struct reset_control *rstc;
 
 	char *base;
+
+	unsigned int mbh_buf;
+	unsigned int dcac_buf;
+	unsigned int ncf_buf;
+
+	void *mbh_buf_virt;
+	void *dcac_buf_virt;
+	void *ncf_buf_virt;
+
+	unsigned int mbh_buf_size;
+	unsigned int dcac_buf_size;
+	unsigned int ncf_buf_size;
 };
 
 struct sunxi_cedrus_fmt {
@@ -72,6 +84,7 @@ struct sunxi_cedrus_ctx {
 	struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
 
 	struct v4l2_ctrl *mpeg2_frame_hdr_ctrl;
+	struct v4l2_ctrl *mpeg4_frame_hdr_ctrl;
 };
 
 static inline void sunxi_cedrus_write(struct sunxi_cedrus_dev *vpu,
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
index 38e8a3a..8ce635d 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
@@ -53,6 +53,11 @@ static struct sunxi_cedrus_fmt formats[] = {
 		.types	= SUNXI_CEDRUS_OUTPUT,
 		.num_planes = 1,
 	},
+	{
+		.fourcc = V4L2_PIX_FMT_MPEG4_FRAME,
+		.types	= SUNXI_CEDRUS_OUTPUT,
+		.num_planes = 1,
+	},
 };
 
 #define NUM_FORMATS ARRAY_SIZE(formats)
@@ -129,6 +134,10 @@ void device_run(void *priv)
 		struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr =
 				ctx->mpeg2_frame_hdr_ctrl->p_new.p;
 		process_mpeg2(ctx, in_buf, out_luma, out_chroma, frame_hdr);
+	} else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG4_FRAME) {
+		struct v4l2_ctrl_mpeg4_frame_hdr *frame_hdr =
+				ctx->mpeg4_frame_hdr_ctrl->p_new.p;
+		process_mpeg4(ctx, in_buf, out_luma, out_chroma, frame_hdr);
 	} else {
 		v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
 		v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
@@ -306,8 +315,32 @@ static int vidioc_s_fmt(struct sunxi_cedrus_ctx *ctx, struct v4l2_format *f)
 
 	switch (f->type) {
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (ctx->vpu_src_fmt && ctx->vpu_src_fmt->fourcc ==
+		       V4L2_PIX_FMT_MPEG4_FRAME && dev->mbh_buf_virt) {
+			dma_free_coherent(dev->dev, dev->mbh_buf_size,
+					  dev->mbh_buf_virt, dev->mbh_buf);
+			dma_free_coherent(dev->dev, dev->dcac_buf_size,
+					  dev->dcac_buf_virt, dev->dcac_buf);
+			dma_free_coherent(dev->dev, dev->ncf_buf_size,
+					  dev->ncf_buf_virt, dev->ncf_buf);
+		}
+
 		ctx->vpu_src_fmt = find_format(f);
 		ctx->src_fmt = *pix_fmt_mp;
+
+		if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG4_FRAME) {
+			dev->mbh_buf_size  = pix_fmt_mp->height * 2048;
+			dev->dcac_buf_size = 2 * pix_fmt_mp->width *
+					       pix_fmt_mp->height;
+			dev->ncf_buf_size  = 4 * 1024;
+
+			dev->mbh_buf_virt = dma_alloc_coherent(dev->dev,
+			       dev->mbh_buf_size, &dev->mbh_buf, GFP_KERNEL);
+			dev->dcac_buf_virt = dma_alloc_coherent(dev->dev,
+			       dev->dcac_buf_size, &dev->dcac_buf, GFP_KERNEL);
+			dev->ncf_buf_virt = dma_alloc_coherent(dev->dev,
+			       dev->ncf_buf_size, &dev->ncf_buf, GFP_KERNEL);
+		}
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 		fmt = find_format(f);
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
index 78625e5..d5f8b47 100644
--- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
@@ -32,5 +32,8 @@ void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu);
 void process_mpeg2(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
 		   dma_addr_t out_luma, dma_addr_t out_chroma,
 		   struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr);
+void process_mpeg4(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
+		   dma_addr_t out_luma, dma_addr_t out_chroma,
+		   struct v4l2_ctrl_mpeg4_frame_hdr *frame_hdr);
 
 #endif /* SUNXI_CEDRUS_HW_H_ */
diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c
new file mode 100644
index 0000000..656fb6f
--- /dev/null
+++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg4.c
@@ -0,0 +1,140 @@
+/*
+ * Sunxi Cedrus codec driver
+ *
+ * Copyright (C) 2016 Florent Revest
+ * Florent Revest <florent.revest@free-electrons.com>
+ *
+ * Based on reverse engineering efforts of the 'Cedrus' project
+ * Copyright (c) 2013-2014 Jens Kuske <jenskuske@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sunxi_cedrus_common.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+#define VOP_I	0
+#define VOP_P	1
+#define VOP_B	2
+#define VOP_S	3
+
+void process_mpeg4(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
+		   dma_addr_t out_luma, dma_addr_t out_chroma,
+		   struct v4l2_ctrl_mpeg4_frame_hdr *frame_hdr)
+{
+	struct sunxi_cedrus_dev *dev = ctx->dev;
+
+	u16 width = DIV_ROUND_UP(frame_hdr->width, 16);
+	u16 height = DIV_ROUND_UP(frame_hdr->height, 16);
+
+	u32 vop_header = 0;
+	u32 vld_len = frame_hdr->slice_len - frame_hdr->slice_pos;
+
+	struct vb2_buffer *fwd_vb2_buf, *bwd_vb2_buf;
+	dma_addr_t fwd_luma = 0, fwd_chroma = 0, bwd_luma = 0, bwd_chroma = 0;
+
+	/*
+	 * The VPU is only able to handle bus addresses so we have to subtract
+	 * the RAM offset to the physcal addresses
+	 */
+	fwd_vb2_buf = ctx->dst_bufs[frame_hdr->forward_index];
+	if (fwd_vb2_buf) {
+		fwd_luma   = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 0);
+		fwd_chroma = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 1);
+		fwd_luma   -= PHYS_OFFSET;
+		fwd_chroma -= PHYS_OFFSET;
+	}
+
+	bwd_vb2_buf = ctx->dst_bufs[frame_hdr->backward_index];
+	if (bwd_vb2_buf) {
+		bwd_luma   = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 0);
+		bwd_chroma = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 1);
+		bwd_chroma -= PHYS_OFFSET;
+		bwd_luma   -= PHYS_OFFSET;
+	}
+
+	/* Activates MPEG engine */
+	sunxi_cedrus_write(dev, VE_CTRL_MPEG, VE_CTRL);
+
+	/* Quantization parameter */
+	sunxi_cedrus_write(dev, frame_hdr->quant_scale, VE_MPEG_QP_INPUT);
+
+	/* Intermediate buffers needed by the VPU */
+	sunxi_cedrus_write(dev, dev->mbh_buf  - PHYS_OFFSET, VE_MPEG_MBH_ADDR);
+	sunxi_cedrus_write(dev, dev->dcac_buf - PHYS_OFFSET, VE_MPEG_DCAC_ADDR);
+	sunxi_cedrus_write(dev, dev->ncf_buf  - PHYS_OFFSET, VE_MPEG_NCF_ADDR);
+
+	/* Image's dimensions */
+	sunxi_cedrus_write(dev, width << 8  | height,      VE_MPEG_SIZE);
+	sunxi_cedrus_write(dev, width << 20 | height << 4, VE_MPEG_FRAME_SIZE);
+
+	/* MPEG VOP's header */
+	vop_header |= (frame_hdr->vop_fields.vop_coding_type == VOP_B) << 28;
+	vop_header |= frame_hdr->vol_fields.quant_type << 24;
+	vop_header |= frame_hdr->vol_fields.quarter_sample << 23;
+	vop_header |= frame_hdr->vol_fields.resync_marker_disable << 22;
+	vop_header |= frame_hdr->vop_fields.vop_coding_type << 18;
+	vop_header |= frame_hdr->vop_fields.vop_rounding_type << 17;
+	vop_header |= frame_hdr->vop_fields.intra_dc_vlc_thr << 8;
+	vop_header |= frame_hdr->vop_fields.top_field_first << 7;
+	vop_header |= frame_hdr->vop_fields.alternate_vertical_scan_flag << 6;
+	if (frame_hdr->vop_fields.vop_coding_type != VOP_I)
+		vop_header |= frame_hdr->vop_fcode_forward << 3;
+	if (frame_hdr->vop_fields.vop_coding_type == VOP_B)
+		vop_header |= frame_hdr->vop_fcode_backward << 0;
+	sunxi_cedrus_write(dev, vop_header, VE_MPEG_VOP_HDR);
+
+	/* Enable interrupt and an unknown control flag */
+	if (frame_hdr->vop_fields.vop_coding_type == VOP_P)
+		sunxi_cedrus_write(dev, VE_MPEG_CTRL_MPEG4_P, VE_MPEG_CTRL);
+	else
+		sunxi_cedrus_write(dev, VE_MPEG_CTRL_MPEG4, VE_MPEG_CTRL);
+
+	/* Temporal distances of B frames */
+	if (frame_hdr->vop_fields.vop_coding_type == VOP_B) {
+		u32 trbtrd = (frame_hdr->trb << 16) | frame_hdr->trd;
+
+		sunxi_cedrus_write(dev, trbtrd, VE_MPEG_TRBTRD_FRAME);
+		sunxi_cedrus_write(dev, 0, VE_MPEG_TRBTRD_FIELD);
+	}
+
+	/* Don't rotate or scale buffer */
+	sunxi_cedrus_write(dev, VE_NO_SDROT_CTRL, VE_MPEG_SDROT_CTRL);
+
+	/* Macroblock number */
+	sunxi_cedrus_write(dev, 0, VE_MPEG_MBA);
+
+	/* Clear previous status */
+	sunxi_cedrus_write(dev, 0xffffffff, VE_MPEG_STATUS);
+
+	/* Forward and backward prediction buffers (cached in dst_bufs) */
+	sunxi_cedrus_write(dev, fwd_luma,   VE_MPEG_FWD_LUMA);
+	sunxi_cedrus_write(dev, fwd_chroma, VE_MPEG_FWD_CHROMA);
+	sunxi_cedrus_write(dev, bwd_luma,   VE_MPEG_BACK_LUMA);
+	sunxi_cedrus_write(dev, bwd_chroma, VE_MPEG_BACK_CHROMA);
+
+	/* Output luma and chroma buffers */
+	sunxi_cedrus_write(dev, out_luma,   VE_MPEG_REC_LUMA);
+	sunxi_cedrus_write(dev, out_chroma, VE_MPEG_REC_CHROMA);
+	sunxi_cedrus_write(dev, out_luma,   VE_MPEG_ROT_LUMA);
+	sunxi_cedrus_write(dev, out_chroma, VE_MPEG_ROT_CHROMA);
+
+	/* Input offset and length in bits */
+	sunxi_cedrus_write(dev, frame_hdr->slice_pos, VE_MPEG_VLD_OFFSET);
+	sunxi_cedrus_write(dev, vld_len, VE_MPEG_VLD_LEN);
+
+	/* Input beginning and end addresses */
+	sunxi_cedrus_write(dev, VE_MPEG_VLD_ADDR_VAL(in_buf), VE_MPEG_VLD_ADDR);
+	sunxi_cedrus_write(dev, in_buf + VBV_SIZE - 1, VE_MPEG_VLD_END);
+
+	/* Starts the MPEG engine */
+	sunxi_cedrus_write(dev, VE_TRIG_MPEG4(width, height), VE_MPEG_TRIGGER);
+}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 09/10] ARM: dts: sun5i: Use video-engine node
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (7 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 08/10] sunxi-cedrus: Add a MPEG 4 codec Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-08-25  9:39 ` [RFC 10/10] sunxi-cedrus: Add device tree binding document Florent Revest
  2016-08-25 12:12 ` [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Stefan Monnier
  10 siblings, 0 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

Now that we have a driver matching "allwinner,sun5i-a13-video-engine" we
can load it.

The "video-engine" node depends on the new sunxi-ng's CCU clock and
reset bindings. This patch also includes a ve_reserved DMA pool for
videobuf2 buffer allocations in sunxi-cedrus.

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 arch/arm/boot/dts/sun5i-a13.dtsi | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 2afe05fb..384b645 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -69,6 +69,19 @@
 		};
 	};
 
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		ve_reserved: cma {
+			compatible = "shared-dma-pool";
+			reg = <0x43d00000 0x9000000>;
+			no-map;
+			linux,cma-default;
+		};
+	};
+
 	thermal-zones {
 		cpu_thermal {
 			/* milliseconds */
@@ -330,6 +343,24 @@
 			};
 		};
 
+		video-engine {
+			compatible = "allwinner,sun5i-a13-video-engine";
+			memory-region = <&ve_reserved>;
+
+			clocks = <&ahb_gates 32>, <&ccu CLK_VE>,
+				 <&dram_gates 0>;
+			clock-names = "ahb", "mod", "ram";
+
+			assigned-clocks = <&ccu CLK_VE>;
+			assigned-clock-rates = <320000000>;
+
+			resets = <&ccu RST_VE>;
+
+			interrupts = <53>;
+
+			reg = <0x01c0e000 4096>;
+		};
+
 		ccu: clock@01c20000 {
 			compatible = "allwinner,sun5i-a13-ccu";
 			reg = <0x01c20000 0x400>;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC 10/10] sunxi-cedrus: Add device tree binding document
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (8 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 09/10] ARM: dts: sun5i: Use video-engine node Florent Revest
@ 2016-08-25  9:39 ` Florent Revest
  2016-08-25 12:12 ` [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Stefan Monnier
  10 siblings, 0 replies; 16+ messages in thread
From: Florent Revest @ 2016-08-25  9:39 UTC (permalink / raw)
  To: linux-media
  Cc: florent.revest, linux-sunxi, maxime.ripard, posciak,
	hans.verkuil, thomas.petazzoni, mchehab, linux-kernel, wens

Device Tree bindings for the Allwinner's video engine

Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
---
 .../devicetree/bindings/media/sunxi-cedrus.txt     | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/sunxi-cedrus.txt

diff --git a/Documentation/devicetree/bindings/media/sunxi-cedrus.txt b/Documentation/devicetree/bindings/media/sunxi-cedrus.txt
new file mode 100644
index 0000000..26f2e09
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/sunxi-cedrus.txt
@@ -0,0 +1,44 @@
+Device-Tree bindings for SUNXI video engine found in sunXi SoC family
+
+Required properties:
+- compatible	    : "allwinner,sun5i-a13-video-engine";
+- memory-region     : DMA pool for buffers allocation;
+- clocks	    : list of clock specifiers, corresponding to
+		      entries in clock-names property;
+- clock-names	    : should contain "ahb", "mod" and "ram" entries;
+- resets	    : phandle for reset;
+- interrupts	    : should contain VE interrupt number;
+- reg		    : should contain register base and length of VE.
+
+Example:
+
+reserved-memory {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges;
+
+	ve_reserved: cma {
+		compatible = "shared-dma-pool";
+		reg = <0x43d00000 0x9000000>;
+		no-map;
+		linux,cma-default;
+	};
+};
+
+video-engine {
+	compatible = "allwinner,sun5i-a13-video-engine";
+	memory-region = <&ve_reserved>;
+
+	clocks = <&ahb_gates 32>, <&ccu CLK_VE>,
+		 <&dram_gates 0>;
+	clock-names = "ahb", "mod", "ram";
+
+	assigned-clocks = <&ccu CLK_VE>;
+	assigned-clock-rates = <320000000>;
+
+	resets = <&ccu RST_VE>;
+
+	interrupts = <53>;
+
+	reg = <0x01c0e000 4096>;
+};
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver
  2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
                   ` (9 preceding siblings ...)
  2016-08-25  9:39 ` [RFC 10/10] sunxi-cedrus: Add device tree binding document Florent Revest
@ 2016-08-25 12:12 ` Stefan Monnier
  10 siblings, 0 replies; 16+ messages in thread
From: Stefan Monnier @ 2016-08-25 12:12 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-media, linux-sunxi

> This series adds a v4l2 memory2memory decoder driver for Allwinner's
> VPU found in the A13 SoC. It follows the reverse engineering effort
> of the Cedrus [1] project.

Thank you so much for this work, I really look forward to seeing this
support in mainline kernel!


        Stefan

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC 03/10] v4l: Add sunxi Video Engine pixel format
  2016-08-25  9:39 ` [RFC 03/10] v4l: Add sunxi Video Engine pixel format Florent Revest
@ 2016-11-14 10:01   ` Hans Verkuil
  0 siblings, 0 replies; 16+ messages in thread
From: Hans Verkuil @ 2016-11-14 10:01 UTC (permalink / raw)
  To: Florent Revest, linux-media
  Cc: linux-sunxi, maxime.ripard, posciak, hans.verkuil,
	thomas.petazzoni, mchehab, linux-kernel, wens

On 08/25/2016 11:39 AM, Florent Revest wrote:
> Add support for the allwinner's proprietary pixel format described in
> details here: http://linux-sunxi.org/File:Ve_tile_format_v1.pdf
> 
> This format is similar to V4L2_PIX_FMT_NV12M but the planes are divided
> in tiles of 32x32px.
> 
> Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
> ---
>  include/uapi/linux/videodev2.h | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 904c44c..96e034d 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -627,6 +627,7 @@ struct v4l2_pix_format {
>  #define V4L2_PIX_FMT_Y8I      v4l2_fourcc('Y', '8', 'I', ' ') /* Greyscale 8-bit L/R interleaved */
>  #define V4L2_PIX_FMT_Y12I     v4l2_fourcc('Y', '1', '2', 'I') /* Greyscale 12-bit L/R interleaved */
>  #define V4L2_PIX_FMT_Z16      v4l2_fourcc('Z', '1', '6', ' ') /* Depth data 16-bit */
> +#define V4L2_PIX_FMT_SUNXI    v4l2_fourcc('S', 'X', 'I', 'Y') /* Sunxi VE's 32x32 tiled NV12 */

This is very similar to V4L2_PIX_FMT_NV12MT_16X16. I think it can be added as
V4L2_PIX_FMT_NV12MT_32X32.

This needs to be documented in the V4L2 spec as well, of course.

Regards,

	Hans

>  
>  /* SDR formats - used only for Software Defined Radio devices */
>  #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC 06/10] media: platform: Add Sunxi Cedrus decoder driver
  2016-08-25  9:39 ` [RFC 06/10] media: platform: Add Sunxi Cedrus decoder driver Florent Revest
@ 2016-11-14 10:07   ` Hans Verkuil
  0 siblings, 0 replies; 16+ messages in thread
From: Hans Verkuil @ 2016-11-14 10:07 UTC (permalink / raw)
  To: Florent Revest, linux-media
  Cc: linux-sunxi, maxime.ripard, posciak, hans.verkuil,
	thomas.petazzoni, mchehab, linux-kernel, wens

On 08/25/2016 11:39 AM, Florent Revest wrote:
> This patch adds a "sunxi-cedrus" v4l2 m2m decoder driver for
> Allwinner's Video Processing Unit. This VPU has a low-level interface
> which requires manual registers writing for frame headers. Hence, it
> depends on the Request API to synchronize buffers with controls.
> 
> Most of the reverse engineering on which I based my work comes from the
> "Cedrus" project: http://linux-sunxi.org/Cedrus
> 
> The driver currently only runs on the A13 and this patch doesn't
> include any codec.
> 
> Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
> ---
>  drivers/media/platform/Kconfig                     |  13 +
>  drivers/media/platform/Makefile                    |   1 +
>  drivers/media/platform/sunxi-cedrus/Makefile       |   2 +
>  drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c | 248 ++++++++++
>  .../platform/sunxi-cedrus/sunxi_cedrus_common.h    |  86 ++++
>  .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c | 544 +++++++++++++++++++++
>  .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.h |  33 ++
>  .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.c  | 153 ++++++
>  .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |  32 ++
>  .../platform/sunxi-cedrus/sunxi_cedrus_regs.h      | 170 +++++++
>  10 files changed, 1282 insertions(+)
>  create mode 100644 drivers/media/platform/sunxi-cedrus/Makefile
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index f25344b..92c92d3 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -315,6 +315,19 @@ config VIDEO_TI_VPE
>  	  Support for the TI VPE(Video Processing Engine) block
>  	  found on DRA7XX SoC.
>  
> +config VIDEO_SUNXI_CEDRUS
> +	tristate "Sunxi CEDRUS VPU driver"
> +	depends on VIDEO_DEV && VIDEO_V4L2
> +	depends on ARCH_SUNXI
> +	depends on HAS_DMA
> +	select VIDEOBUF2_DMA_CONTIG
> +	select V4L2_MEM2MEM_DEV
> +	---help---
> +	  Support for the VPU video codec found on Sunxi SoC.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called sunxi-cedrus.
> +
>  config VIDEO_TI_VPE_DEBUG
>  	bool "VPE debug messages"
>  	depends on VIDEO_TI_VPE
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 21771c1..1419749 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -53,6 +53,7 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
>  obj-y	+= omap/
>  
>  obj-$(CONFIG_VIDEO_AM437X_VPFE)		+= am437x/
> +obj-$(CONFIG_VIDEO_SUNXI_CEDRUS)	+= sunxi-cedrus/
>  
>  obj-$(CONFIG_VIDEO_XILINX)		+= xilinx/
>  
> diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
> new file mode 100644
> index 0000000..14c2f7a
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
> +				    sunxi_cedrus_dec.o
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
> new file mode 100644
> index 0000000..17af34c
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
> @@ -0,0 +1,248 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on vim2m
> + *
> + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <pawel@osciak.com>
> + * Marek Szyprowski, <m.szyprowski@samsung.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include "sunxi_cedrus_common.h"
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +
> +#include <linux/platform_device.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "sunxi_cedrus_dec.h"
> +#include "sunxi_cedrus_hw.h"
> +
> +static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct sunxi_cedrus_ctx *ctx =
> +		container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);
> +
> +	v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
> +	return -EINVAL;
> +}
> +
> +static const struct v4l2_ctrl_ops sunxi_cedrus_ctrl_ops = {
> +	.s_ctrl = sunxi_cedrus_s_ctrl,
> +};
> +
> +/*
> + * File operations
> + */
> +static int sunxi_cedrus_open(struct file *file)
> +{
> +	struct sunxi_cedrus_dev *dev = video_drvdata(file);
> +	struct sunxi_cedrus_ctx *ctx = NULL;
> +	struct v4l2_ctrl_handler *hdl;
> +	int rc = 0;
> +
> +	if (mutex_lock_interruptible(&dev->dev_mutex))
> +		return -ERESTARTSYS;
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx) {
> +		rc = -ENOMEM;
> +		goto open_unlock;
> +	}
> +
> +	v4l2_fh_init(&ctx->fh, video_devdata(file));
> +	file->private_data = &ctx->fh;
> +	ctx->dev = dev;
> +	hdl = &ctx->hdl;
> +	v4l2_ctrl_handler_init(hdl, 1);
> +
> +	if (hdl->error) {
> +		rc = hdl->error;
> +		v4l2_ctrl_handler_free(hdl);
> +		goto open_unlock;
> +	}
> +	ctx->fh.ctrl_handler = hdl;
> +	v4l2_ctrl_handler_setup(hdl);
> +
> +	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
> +
> +	if (IS_ERR(ctx->fh.m2m_ctx)) {
> +		rc = PTR_ERR(ctx->fh.m2m_ctx);
> +
> +		v4l2_ctrl_handler_free(hdl);
> +		kfree(ctx);
> +		goto open_unlock;
> +	}
> +
> +	v4l2_fh_add(&ctx->fh);
> +
> +	dev_dbg(dev->dev, "Created instance: %p, m2m_ctx: %p\n",
> +		ctx, ctx->fh.m2m_ctx);
> +
> +open_unlock:
> +	mutex_unlock(&dev->dev_mutex);
> +	return rc;
> +}
> +
> +static int sunxi_cedrus_release(struct file *file)
> +{
> +	struct sunxi_cedrus_dev *dev = video_drvdata(file);
> +	struct sunxi_cedrus_ctx *ctx = container_of(file->private_data,
> +			struct sunxi_cedrus_ctx, fh);
> +
> +	dev_dbg(dev->dev, "Releasing instance %p\n", ctx);
> +
> +	v4l2_fh_del(&ctx->fh);
> +	v4l2_fh_exit(&ctx->fh);
> +	v4l2_ctrl_handler_free(&ctx->hdl);
> +	mutex_lock(&dev->dev_mutex);
> +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> +	mutex_unlock(&dev->dev_mutex);
> +	kfree(ctx);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations sunxi_cedrus_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= sunxi_cedrus_open,
> +	.release	= sunxi_cedrus_release,
> +	.poll		= v4l2_m2m_fop_poll,
> +	.unlocked_ioctl	= video_ioctl2,
> +	.mmap		= v4l2_m2m_fop_mmap,
> +};
> +
> +static struct video_device sunxi_cedrus_viddev = {
> +	.name		= SUNXI_CEDRUS_NAME,
> +	.vfl_dir	= VFL_DIR_M2M,
> +	.fops		= &sunxi_cedrus_fops,
> +	.ioctl_ops	= &sunxi_cedrus_ioctl_ops,
> +	.minor		= -1,
> +	.release	= video_device_release_empty,
> +};
> +
> +static struct v4l2_m2m_ops m2m_ops = {
> +	.device_run	= device_run,
> +	.job_abort	= job_abort,
> +};
> +
> +static int sunxi_cedrus_probe(struct platform_device *pdev)
> +{
> +	struct sunxi_cedrus_dev *dev;
> +	struct video_device *vfd;
> +	int ret;
> +
> +	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->dev = &pdev->dev;
> +	dev->pdev = pdev;
> +
> +	ret = sunxi_cedrus_hw_probe(dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "sunxi_cedrus_hw_probe failed\n");
> +		return ret;
> +	}
> +
> +	spin_lock_init(&dev->irqlock);
> +
> +	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> +	if (ret)
> +		return ret;
> +
> +	mutex_init(&dev->dev_mutex);
> +
> +	dev->vfd = sunxi_cedrus_viddev;
> +	vfd = &dev->vfd;

Don't embed video_device, but dynamically allocate it using video_device_alloc.

There are subtle problems with embedding it and new drivers shouldn't do that
anymore.

> +	vfd->lock = &dev->dev_mutex;
> +	vfd->v4l2_dev = &dev->v4l2_dev;
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> +	if (ret) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
> +		goto unreg_dev;
> +	}
> +
> +	video_set_drvdata(vfd, dev);
> +	snprintf(vfd->name, sizeof(vfd->name), "%s", sunxi_cedrus_viddev.name);
> +	v4l2_info(&dev->v4l2_dev,
> +		  "Device registered as /dev/video%d\n", vfd->num);
> +
> +	platform_set_drvdata(pdev, dev);
> +
> +	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
> +	if (IS_ERR(dev->m2m_dev)) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
> +		ret = PTR_ERR(dev->m2m_dev);
> +		goto err_m2m;
> +	}
> +
> +	return 0;
> +
> +err_m2m:
> +	v4l2_m2m_release(dev->m2m_dev);
> +	video_unregister_device(&dev->vfd);
> +unreg_dev:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +
> +	return ret;
> +}
> +
> +static int sunxi_cedrus_remove(struct platform_device *pdev)
> +{
> +	struct sunxi_cedrus_dev *dev = platform_get_drvdata(pdev);
> +
> +	v4l2_info(&dev->v4l2_dev, "Removing " SUNXI_CEDRUS_NAME);
> +	v4l2_m2m_release(dev->m2m_dev);
> +	video_unregister_device(&dev->vfd);
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +	sunxi_cedrus_hw_remove(dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id of_sunxi_cedrus_match[] = {
> +	{ .compatible = "allwinner,sun5i-a13-video-engine" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, of_sunxi_cedrus_match);
> +#endif
> +
> +static struct platform_driver sunxi_cedrus_driver = {
> +	.probe		= sunxi_cedrus_probe,
> +	.remove		= sunxi_cedrus_remove,
> +	.driver		= {
> +		.name	= SUNXI_CEDRUS_NAME,
> +		.owner = THIS_MODULE,
> +		.of_match_table = of_match_ptr(of_sunxi_cedrus_match),
> +	},
> +};
> +module_platform_driver(sunxi_cedrus_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Florent Revest <florent.revest@free-electrons.com>");
> +MODULE_DESCRIPTION("Sunxi Cedrus codec driver");
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
> new file mode 100644
> index 0000000..6b8d87a
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
> @@ -0,0 +1,86 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on vim2m
> + *
> + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <pawel@osciak.com>
> + * Marek Szyprowski, <m.szyprowski@samsung.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef SUNXI_CEDRUS_COMMON_H_
> +#define SUNXI_CEDRUS_COMMON_H_
> +
> +#include "sunxi_cedrus_regs.h"
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ctrls.h>
> +
> +#define SUNXI_CEDRUS_NAME		"sunxi-cedrus"
> +
> +struct sunxi_cedrus_dev {
> +	struct v4l2_device	v4l2_dev;
> +	struct video_device	vfd;
> +	struct platform_device	*pdev;
> +	struct device		*dev;
> +	struct v4l2_m2m_dev	*m2m_dev;
> +
> +	/* Mutex for device file */
> +	struct mutex		dev_mutex;
> +	/* Spinlock for interrupt */
> +	spinlock_t		irqlock;
> +
> +	struct clk *mod_clk;
> +	struct clk *ahb_clk;
> +	struct clk *ram_clk;
> +
> +	struct reset_control *rstc;
> +
> +	char *base;
> +};
> +
> +struct sunxi_cedrus_fmt {
> +	u32	fourcc;
> +	int	depth;
> +	u32	types;
> +	unsigned int num_planes;
> +};
> +
> +struct sunxi_cedrus_ctx {
> +	struct v4l2_fh		fh;
> +	struct sunxi_cedrus_dev	*dev;
> +
> +	struct sunxi_cedrus_fmt *vpu_src_fmt;
> +	struct v4l2_pix_format_mplane src_fmt;
> +	struct sunxi_cedrus_fmt *vpu_dst_fmt;
> +	struct v4l2_pix_format_mplane dst_fmt;
> +
> +	struct v4l2_ctrl_handler hdl;
> +
> +	struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
> +};
> +
> +static inline void sunxi_cedrus_write(struct sunxi_cedrus_dev *vpu,
> +				      u32 val, u32 reg)
> +{
> +	writel(val, vpu->base + reg);
> +}
> +
> +static inline u32 sunxi_cedrus_read(struct sunxi_cedrus_dev *vpu, u32 reg)
> +{
> +	return readl(vpu->base + reg);
> +}
> +
> +#endif /* SUNXI_CEDRUS_COMMON_H_ */
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
> new file mode 100644
> index 0000000..71ef34b
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
> @@ -0,0 +1,544 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on vim2m
> + *
> + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <pawel@osciak.com>
> + * Marek Szyprowski, <m.szyprowski@samsung.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include "sunxi_cedrus_common.h"
> +
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "sunxi_cedrus_dec.h"
> +#include "sunxi_cedrus_hw.h"
> +
> +/* Flags that indicate a format can be used for capture/output */
> +#define SUNXI_CEDRUS_CAPTURE	BIT(0)
> +#define SUNXI_CEDRUS_OUTPUT	BIT(1)
> +
> +#define SUNXI_CEDRUS_MIN_WIDTH 16U
> +#define SUNXI_CEDRUS_MIN_HEIGHT 16U
> +#define SUNXI_CEDRUS_MAX_WIDTH 3840U
> +#define SUNXI_CEDRUS_MAX_HEIGHT 2160U
> +
> +static struct sunxi_cedrus_fmt formats[] = {
> +	{
> +		.fourcc = V4L2_PIX_FMT_SUNXI,
> +		.types	= SUNXI_CEDRUS_CAPTURE,
> +		.depth = 8,
> +		.num_planes = 2,
> +	},
> +};
> +
> +#define NUM_FORMATS ARRAY_SIZE(formats)
> +
> +static struct sunxi_cedrus_fmt *find_format(struct v4l2_format *f)
> +{
> +	struct sunxi_cedrus_fmt *fmt;
> +	unsigned int k;
> +
> +	for (k = 0; k < NUM_FORMATS; k++) {
> +		fmt = &formats[k];
> +		if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
> +			break;
> +	}
> +
> +	if (k == NUM_FORMATS)
> +		return NULL;
> +
> +	return &formats[k];
> +}
> +
> +static inline struct sunxi_cedrus_ctx *file2ctx(struct file *file)
> +{
> +	return container_of(file->private_data, struct sunxi_cedrus_ctx, fh);
> +}
> +
> +/*
> + * mem2mem callbacks
> + */
> +
> +void job_abort(void *priv)
> +{}
> +
> +/*
> + * device_run() - prepares and starts processing
> + */
> +void device_run(void *priv)
> +{
> +	struct sunxi_cedrus_ctx *ctx = priv;
> +	struct vb2_v4l2_buffer *in_vb, *out_vb;
> +	dma_addr_t in_buf, out_luma, out_chroma;
> +
> +	in_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> +	out_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> +
> +	v4l2_ctrl_apply_request(&ctx->hdl, in_vb->request);
> +
> +	in_buf = vb2_dma_contig_plane_dma_addr(&in_vb->vb2_buf, 0);
> +	out_luma = vb2_dma_contig_plane_dma_addr(&out_vb->vb2_buf, 0);
> +	out_chroma = vb2_dma_contig_plane_dma_addr(&out_vb->vb2_buf, 1);
> +	if (!in_buf || !out_luma || !out_chroma) {
> +		v4l2_err(&ctx->dev->v4l2_dev,
> +			 "Acquiring kernel pointers to buffers failed\n");
> +		return;
> +	}
> +
> +	/*
> +	 * The VPU is only able to handle bus addresses so we have to subtract
> +	 * the RAM offset to the physcal addresses
> +	 */
> +	in_buf     -= PHYS_OFFSET;
> +	out_luma   -= PHYS_OFFSET;
> +	out_chroma -= PHYS_OFFSET;
> +
> +	out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp;
> +	if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE)
> +		out_vb->timecode = in_vb->timecode;
> +	out_vb->field = in_vb->field;
> +	out_vb->flags = in_vb->flags & (V4L2_BUF_FLAG_TIMECODE |
> +		 V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
> +		 V4L2_BUF_FLAG_BFRAME   | V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
> +
> +	v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
> +	v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
> +}
> +
> +/*
> + * video ioctls
> + */
> +static int vidioc_querycap(struct file *file, void *priv,
> +			   struct v4l2_capability *cap)
> +{
> +	strncpy(cap->driver, SUNXI_CEDRUS_NAME, sizeof(cap->driver) - 1);
> +	strncpy(cap->card, SUNXI_CEDRUS_NAME, sizeof(cap->card) - 1);
> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
> +		 "platform:%s", SUNXI_CEDRUS_NAME);
> +	cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;

Don't set this here, instead fill in device_caps in struct video_device. The
V4L2 core will fill in the device_caps and capabilities for you.

> +	return 0;
> +}
> +
> +static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
> +{
> +	int i, num;
> +	struct sunxi_cedrus_fmt *fmt;
> +
> +	num = 0;
> +
> +	for (i = 0; i < NUM_FORMATS; ++i) {
> +		if (formats[i].types & type) {
> +			/* index-th format of type type found ? */
> +			if (num == f->index)
> +				break;
> +			/*
> +			 * Correct type but haven't reached our index yet,
> +			 * just increment per-type index
> +			 */
> +			++num;
> +		}
> +	}
> +
> +	if (i < NUM_FORMATS) {
> +		/* Format found */
> +		fmt = &formats[i];
> +		f->pixelformat = fmt->fourcc;
> +		return 0;
> +	}
> +
> +	/* Format not found */
> +	return -EINVAL;
> +}
> +
> +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
> +				   struct v4l2_fmtdesc *f)
> +{
> +	return enum_fmt(f, SUNXI_CEDRUS_CAPTURE);
> +}
> +
> +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
> +				   struct v4l2_fmtdesc *f)
> +{
> +	return enum_fmt(f, SUNXI_CEDRUS_OUTPUT);
> +}
> +
> +static int vidioc_g_fmt(struct sunxi_cedrus_ctx *ctx, struct v4l2_format *f)
> +{
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		f->fmt.pix_mp = ctx->dst_fmt;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		f->fmt.pix_mp = ctx->src_fmt;
> +		break;
> +	default:
> +		dev_dbg(ctx->dev->dev, "invalid buf type\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
> +				struct v4l2_format *f)
> +{
> +	return vidioc_g_fmt(file2ctx(file), f);
> +}
> +
> +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
> +				struct v4l2_format *f)
> +{
> +	return vidioc_g_fmt(file2ctx(file), f);
> +}
> +
> +static int vidioc_try_fmt(struct v4l2_format *f, struct sunxi_cedrus_fmt *fmt)
> +{
> +	int i;
> +	__u32 bpl;
> +
> +	f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> +	f->fmt.pix_mp.num_planes = fmt->num_planes;
> +
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0)
> +			return -EINVAL;
> +
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		/* Limit to hardware min/max. */
> +		f->fmt.pix_mp.width = clamp(f->fmt.pix_mp.width,
> +			SUNXI_CEDRUS_MIN_WIDTH, SUNXI_CEDRUS_MAX_WIDTH);
> +		f->fmt.pix_mp.height = clamp(f->fmt.pix_mp.height,
> +			SUNXI_CEDRUS_MIN_HEIGHT, SUNXI_CEDRUS_MAX_HEIGHT);
> +
> +		for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) {
> +			bpl = (f->fmt.pix_mp.width * fmt->depth) >> 3;
> +			f->fmt.pix_mp.plane_fmt[i].bytesperline = bpl;
> +			f->fmt.pix_mp.plane_fmt[i].sizeimage =
> +				f->fmt.pix_mp.height * bpl;
> +		}
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
> +				  struct v4l2_format *f)
> +{
> +	struct sunxi_cedrus_fmt *fmt;
> +	struct sunxi_cedrus_ctx *ctx = file2ctx(file);
> +
> +	fmt = find_format(f);
> +	if (!fmt) {
> +		f->fmt.pix_mp.pixelformat = formats[0].fourcc;
> +		fmt = find_format(f);
> +	}
> +	if (!(fmt->types & SUNXI_CEDRUS_CAPTURE)) {
> +		v4l2_err(&ctx->dev->v4l2_dev,
> +			 "Fourcc format (0x%08x) invalid.\n",
> +			 f->fmt.pix_mp.pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	return vidioc_try_fmt(f, fmt);
> +}
> +
> +static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
> +				  struct v4l2_format *f)
> +{
> +	struct sunxi_cedrus_fmt *fmt;
> +	struct sunxi_cedrus_ctx *ctx = file2ctx(file);
> +
> +	fmt = find_format(f);
> +	if (!fmt) {
> +		f->fmt.pix_mp.pixelformat = formats[0].fourcc;
> +		fmt = find_format(f);
> +	}
> +	if (!(fmt->types & SUNXI_CEDRUS_OUTPUT)) {
> +		v4l2_err(&ctx->dev->v4l2_dev,
> +			 "Fourcc format (0x%08x) invalid.\n",
> +			 f->fmt.pix_mp.pixelformat);
> +		return -EINVAL;
> +	}
> +
> +	return vidioc_try_fmt(f, fmt);
> +}
> +
> +static int vidioc_s_fmt(struct sunxi_cedrus_ctx *ctx, struct v4l2_format *f)
> +{
> +	struct sunxi_cedrus_dev *dev = ctx->dev;
> +	struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
> +	struct sunxi_cedrus_fmt *fmt;
> +	int i, ret = 0;
> +
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		ctx->vpu_src_fmt = find_format(f);
> +		ctx->src_fmt = *pix_fmt_mp;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		fmt = find_format(f);
> +		ctx->vpu_dst_fmt = fmt;
> +
> +		for (i = 0; i < fmt->num_planes; ++i) {
> +			pix_fmt_mp->plane_fmt[i].bytesperline =
> +				pix_fmt_mp->width * fmt->depth;
> +			pix_fmt_mp->plane_fmt[i].sizeimage =
> +				pix_fmt_mp->plane_fmt[i].bytesperline
> +				* pix_fmt_mp->height;
> +		}
> +		ctx->dst_fmt = *pix_fmt_mp;
> +		break;
> +	default:
> +		dev_dbg(ctx->dev->dev, "invalid buf type\n");
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
> +				struct v4l2_format *f)
> +{
> +	int ret;
> +
> +	ret = vidioc_try_fmt_vid_cap(file, priv, f);
> +	if (ret)
> +		return ret;
> +
> +	return vidioc_s_fmt(file2ctx(file), f);
> +}
> +
> +static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
> +				struct v4l2_format *f)
> +{
> +	int ret;
> +
> +	ret = vidioc_try_fmt_vid_out(file, priv, f);
> +	if (ret)
> +		return ret;
> +
> +	ret = vidioc_s_fmt(file2ctx(file), f);
> +	return ret;
> +}
> +
> +const struct v4l2_ioctl_ops sunxi_cedrus_ioctl_ops = {
> +	.vidioc_querycap	= vidioc_querycap,
> +
> +	.vidioc_enum_fmt_vid_cap	= vidioc_enum_fmt_vid_cap,
> +	.vidioc_g_fmt_vid_cap_mplane	= vidioc_g_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap_mplane	= vidioc_try_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap_mplane	= vidioc_s_fmt_vid_cap,
> +
> +	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out,
> +	.vidioc_g_fmt_vid_out_mplane	= vidioc_g_fmt_vid_out,
> +	.vidioc_try_fmt_vid_out_mplane	= vidioc_try_fmt_vid_out,
> +	.vidioc_s_fmt_vid_out_mplane	= vidioc_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,
> +
> +	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +/*
> + * Queue operations
> + */
> +
> +static int sunxi_cedrus_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
> +				    unsigned int *nplanes, unsigned int sizes[],
> +				    struct device *alloc_devs[])
> +{
> +	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(vq);
> +
> +	if (*nbufs < 1)
> +		*nbufs = 1;
> +
> +	if (*nbufs > VIDEO_MAX_FRAME)
> +		*nbufs = VIDEO_MAX_FRAME;
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		*nplanes = ctx->vpu_src_fmt->num_planes;
> +
> +		sizes[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> +		break;
> +
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		*nplanes = ctx->vpu_dst_fmt->num_planes;
> +
> +		sizes[0] = round_up(ctx->dst_fmt.plane_fmt[0].sizeimage, 8);
> +		sizes[1] = sizes[0];
> +		break;
> +
> +	default:
> +		dev_dbg(ctx->dev->dev, "invalid queue type: %d\n", vq->type);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sunxi_cedrus_buf_init(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct sunxi_cedrus_ctx *ctx = container_of(vq->drv_priv,
> +			struct sunxi_cedrus_ctx, fh);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +		ctx->dst_bufs[vb->index] = vb;
> +
> +	return 0;
> +}
> +
> +static void sunxi_cedrus_buf_cleanup(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct sunxi_cedrus_ctx *ctx = container_of(vq->drv_priv,
> +			struct sunxi_cedrus_ctx, fh);
> +
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +		ctx->dst_bufs[vb->index] = NULL;
> +}
> +
> +static int sunxi_cedrus_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	int i;
> +
> +	dev_dbg(ctx->dev->dev, "type: %d\n", vb->vb2_queue->type);
> +
> +	switch (vq->type) {
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		if (vb2_plane_size(vb, 0)
> +		    < ctx->src_fmt.plane_fmt[0].sizeimage) {
> +			dev_dbg(ctx->dev->dev, "plane too small for output\n");
> +			return -EINVAL;
> +		}
> +		break;
> +
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		for (i = 0; i < ctx->vpu_dst_fmt->num_planes; ++i) {
> +			if (vb2_plane_size(vb, i)
> +			    < ctx->dst_fmt.plane_fmt[i].sizeimage) {
> +				dev_dbg(ctx->dev->dev,
> +					"plane %d too small for capture\n", i);
> +				break;
> +			}
> +		}
> +
> +		if (i != ctx->vpu_dst_fmt->num_planes)
> +			return -EINVAL;
> +		break;
> +
> +	default:
> +		dev_dbg(ctx->dev->dev, "invalid queue type: %d\n", vq->type);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void sunxi_cedrus_stop_streaming(struct vb2_queue *q)
> +{
> +	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(q);
> +	struct vb2_v4l2_buffer *vbuf;
> +	unsigned long flags;
> +
> +	for (;;) {
> +		if (V4L2_TYPE_IS_OUTPUT(q->type))
> +			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +		else
> +			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +		if (!vbuf)
> +			return;
> +		spin_lock_irqsave(&ctx->dev->irqlock, flags);
> +		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +		spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
> +	}
> +}
> +
> +static void sunxi_cedrus_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static struct vb2_ops sunxi_cedrus_qops = {
> +	.queue_setup	 = sunxi_cedrus_queue_setup,
> +	.buf_prepare	 = sunxi_cedrus_buf_prepare,
> +	.buf_init	 = sunxi_cedrus_buf_init,
> +	.buf_cleanup	 = sunxi_cedrus_buf_cleanup,
> +	.buf_queue	 = sunxi_cedrus_buf_queue,
> +	.stop_streaming  = sunxi_cedrus_stop_streaming,
> +	.wait_prepare	 = vb2_ops_wait_prepare,
> +	.wait_finish	 = vb2_ops_wait_finish,
> +};
> +
> +int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
> +{
> +	struct sunxi_cedrus_ctx *ctx = priv;
> +	int ret;
> +
> +	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +	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->ops = &sunxi_cedrus_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->v4l2_allow_requests = true;
> +	src_vq->dev = ctx->dev->dev;
> +
> +	ret = vb2_queue_init(src_vq);
> +	if (ret)
> +		return ret;
> +
> +	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +	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->ops = &sunxi_cedrus_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->v4l2_allow_requests = true;
> +	dst_vq->dev = ctx->dev->dev;
> +
> +	return vb2_queue_init(dst_vq);
> +}
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h
> new file mode 100644
> index 0000000..f0ac921
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.h
> @@ -0,0 +1,33 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on vim2m
> + *
> + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <pawel@osciak.com>
> + * Marek Szyprowski, <m.szyprowski@samsung.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef SUNXI_CEDRUS_DEC_H_
> +#define SUNXI_CEDRUS_DEC_H_
> +
> +int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq);
> +
> +void job_abort(void *priv);
> +void device_run(void *priv);
> +
> +extern const struct v4l2_ioctl_ops sunxi_cedrus_ioctl_ops;
> +
> +#endif /* SUNXI_CEDRUS_DEC_H_ */
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
> new file mode 100644
> index 0000000..72b9df4
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
> @@ -0,0 +1,153 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on vim2m
> + *
> + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <pawel@osciak.com>
> + * Marek Szyprowski, <m.szyprowski@samsung.com>
> + *
> + * And reverse engineering efforts of the 'Cedrus' project
> + * Copyright (c) 2013-2014 Jens Kuske <jenskuske@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include "sunxi_cedrus_common.h"
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/reset.h>
> +
> +#include <media/v4l2-mem2mem.h>
> +#include <media/videobuf2-core.h>
> +
> +/*
> + * Interrupt handlers.
> + */
> +
> +static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
> +{
> +	struct sunxi_cedrus_dev *vpu = dev_id;
> +	struct sunxi_cedrus_ctx *curr_ctx;
> +	struct vb2_v4l2_buffer *src_vb, *dst_vb;
> +	int val;
> +	unsigned long flags;
> +
> +	curr_ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev);
> +
> +	if (!curr_ctx) {
> +		pr_err("Instance released before the end of transaction\n");
> +		return IRQ_HANDLED;
> +	}
> +
> +	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
> +	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
> +
> +	spin_lock_irqsave(&vpu->irqlock, flags);
> +	v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
> +	v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
> +	spin_unlock_irqrestore(&vpu->irqlock, flags);
> +
> +	v4l2_m2m_job_finish(vpu->m2m_dev, curr_ctx->fh.m2m_ctx);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Initialization/clean-up.
> + */
> +
> +int sunxi_cedrus_hw_probe(struct sunxi_cedrus_dev *vpu)
> +{
> +	struct resource *res;
> +	int irq_dec;
> +	int ret;
> +
> +	irq_dec = platform_get_irq(vpu->pdev, 0);
> +	if (irq_dec <= 0) {
> +		dev_err(vpu->dev, "could not get ve IRQ\n");
> +		return -ENXIO;
> +	}
> +	ret = devm_request_irq(vpu->dev, irq_dec, sunxi_cedrus_ve_irq, 0,
> +			       dev_name(vpu->dev), vpu);
> +	if (ret) {
> +		dev_err(vpu->dev, "could not request ve IRQ\n");
> +		return -ENXIO;
> +	}
> +
> +	ret = of_reserved_mem_device_init(vpu->dev);
> +	if (ret) {
> +		dev_err(vpu->dev, "could not reserve memory\n");
> +		return -ENODEV;
> +	}
> +
> +	vpu->ahb_clk = devm_clk_get(vpu->dev, "ahb");
> +	if (IS_ERR(vpu->ahb_clk)) {
> +		dev_err(vpu->dev, "failed to get ahb clock\n");
> +		return PTR_ERR(vpu->ahb_clk);
> +	}
> +	vpu->mod_clk = devm_clk_get(vpu->dev, "mod");
> +	if (IS_ERR(vpu->mod_clk)) {
> +		dev_err(vpu->dev, "failed to get mod clock\n");
> +		return PTR_ERR(vpu->mod_clk);
> +	}
> +	vpu->ram_clk = devm_clk_get(vpu->dev, "ram");
> +	if (IS_ERR(vpu->ram_clk)) {
> +		dev_err(vpu->dev, "failed to get ram clock\n");
> +		return PTR_ERR(vpu->ram_clk);
> +	}
> +
> +	vpu->rstc = devm_reset_control_get(vpu->dev, NULL);
> +
> +	res = platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0);
> +	vpu->base = devm_ioremap_resource(vpu->dev, res);
> +	if (!vpu->base)
> +		dev_err(vpu->dev, "could not maps MACC registers\n");
> +
> +	ret = clk_prepare_enable(vpu->ahb_clk);
> +	if (ret) {
> +		dev_err(vpu->dev, "could not enable ahb clock\n");
> +		return -EFAULT;
> +	}
> +	ret = clk_prepare_enable(vpu->mod_clk);
> +	if (ret) {
> +		clk_disable_unprepare(vpu->ahb_clk);
> +		dev_err(vpu->dev, "could not enable mod clock\n");
> +		return -EFAULT;
> +	}
> +	ret = clk_prepare_enable(vpu->ram_clk);
> +	if (ret) {
> +		clk_disable_unprepare(vpu->mod_clk);
> +		clk_disable_unprepare(vpu->ahb_clk);
> +		dev_err(vpu->dev, "could not enable ram clock\n");
> +		return -EFAULT;
> +	}
> +
> +	reset_control_assert(vpu->rstc);
> +	reset_control_deassert(vpu->rstc);
> +
> +	return 0;
> +}
> +
> +void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu)
> +{
> +	clk_disable_unprepare(vpu->ram_clk);
> +	clk_disable_unprepare(vpu->mod_clk);
> +	clk_disable_unprepare(vpu->ahb_clk);
> +
> +	of_reserved_mem_device_release(vpu->dev);
> +}
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
> new file mode 100644
> index 0000000..3c9199b
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
> @@ -0,0 +1,32 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on vim2m
> + *
> + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <pawel@osciak.com>
> + * Marek Szyprowski, <m.szyprowski@samsung.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef SUNXI_CEDRUS_HW_H_
> +#define SUNXI_CEDRUS_HW_H_
> +
> +struct sunxi_cedrus_dev;
> +struct sunxi_cedrus_ctx;
> +
> +int sunxi_cedrus_hw_probe(struct sunxi_cedrus_dev *vpu);
> +void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu);
> +
> +#endif /* SUNXI_CEDRUS_HW_H_ */
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h
> new file mode 100644
> index 0000000..7384daa
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_regs.h
> @@ -0,0 +1,170 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on Cedrus
> + *
> + * Copyright (c) 2013 Jens Kuske <jenskuske@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef SUNXI_CEDRUS_REGS_H
> +#define SUNXI_CEDRUS_REGS_H
> +
> +/*
> + * For more information consult http://linux-sunxi.org/VE_Register_guide
> + */
> +
> +/* Special registers values */
> +
> +/* VE_CTRL:
> + * The first 3 bits indicate the engine (0 for MPEG, 1 for H264, b for AVC...)
> + * The 16th and 17th bits indicate the memory type (3 for DDR3 32 bits)
> + * The 20th bit is unknown but needed
> + */
> +#define VE_CTRL_MPEG		0x130000
> +#define VE_CTRL_H264		0x130001
> +#define VE_CTRL_AVC		0x13000b
> +#define VE_CTRL_REINIT		0x130007
> +
> +/* VE_MPEG_CTRL:
> + * The bit 3 (0x8) is used to enable IRQs
> + * The other bits are unknown but needed
> + */
> +#define VE_MPEG_CTRL_MPEG2	0x800001b8
> +#define VE_MPEG_CTRL_MPEG4	(0x80084118 | BIT(7))
> +#define VE_MPEG_CTRL_MPEG4_P	(VE_MPEG_CTRL_MPEG4 | BIT(12))
> +
> +/* VE_MPEG_VLD_ADDR:
> + * The bits 27 to 4 are used for the address
> + * The bits 31 to 28 (0x7) are used to select the MPEG or JPEG engine
> + */
> +#define VE_MPEG_VLD_ADDR_VAL(x)	((x & 0x0ffffff0) | (x >> 28) | (0x7 << 28))
> +
> +/* VE_MPEG_TRIGGER:
> + * The first three bits are used to trigger the engine
> + * The bits 24 to 26 are used to select the input format (1 for MPEG1, 2 for 
> + *                           MPEG2, 4 for MPEG4)
> + * The bit 21 (0x8) is used to disable bitstream error handling
> + *
> + * In MPEG4 the w*h value is somehow used for an offset, unknown but needed
> + */
> +#define VE_TRIG_MPEG1		0x8100000f
> +#define VE_TRIG_MPEG2		0x8200000f
> +#define VE_TRIG_MPEG4(w, h)	(0x8400000d | ((w * h) << 8))
> +
> +/* VE_MPEG_SDROT_CTRL:
> + * The bit 8 at zero is used to disable x downscaling
> + * The bit 10 at 0 is used to disable y downscaling
> + * The other bits are unknown but needed
> + */
> +#define VE_NO_SDROT_CTRL	0x40620000
> +
> +/* Decent size fo video buffering verifier */
> +#define VBV_SIZE		(1024 * 1024)
> +
> +/* Registers addresses */
> +#define VE_CTRL				0x000
> +#define VE_VERSION			0x0f0
> +
> +#define VE_MPEG_PIC_HDR			0x100
> +#define VE_MPEG_VOP_HDR			0x104
> +#define VE_MPEG_SIZE			0x108
> +#define VE_MPEG_FRAME_SIZE		0x10c
> +#define VE_MPEG_MBA			0x110
> +#define VE_MPEG_CTRL			0x114
> +#define VE_MPEG_TRIGGER			0x118
> +#define VE_MPEG_STATUS			0x11c
> +#define VE_MPEG_TRBTRD_FIELD		0x120
> +#define VE_MPEG_TRBTRD_FRAME		0x124
> +#define VE_MPEG_VLD_ADDR		0x128
> +#define VE_MPEG_VLD_OFFSET		0x12c
> +#define VE_MPEG_VLD_LEN			0x130
> +#define VE_MPEG_VLD_END			0x134
> +#define VE_MPEG_MBH_ADDR		0x138
> +#define VE_MPEG_DCAC_ADDR		0x13c
> +#define VE_MPEG_NCF_ADDR		0x144
> +#define VE_MPEG_REC_LUMA		0x148
> +#define VE_MPEG_REC_CHROMA		0x14c
> +#define VE_MPEG_FWD_LUMA		0x150
> +#define VE_MPEG_FWD_CHROMA		0x154
> +#define VE_MPEG_BACK_LUMA		0x158
> +#define VE_MPEG_BACK_CHROMA		0x15c
> +#define VE_MPEG_IQ_MIN_INPUT		0x180
> +#define VE_MPEG_QP_INPUT		0x184
> +#define VE_MPEG_JPEG_SIZE		0x1b8
> +#define VE_MPEG_JPEG_RES_INT		0x1c0
> +#define VE_MPEG_ERROR			0x1c4
> +#define VE_MPEG_CTR_MB			0x1c8
> +#define VE_MPEG_ROT_LUMA		0x1cc
> +#define VE_MPEG_ROT_CHROMA		0x1d0
> +#define VE_MPEG_SDROT_CTRL		0x1d4
> +#define VE_MPEG_RAM_WRITE_PTR		0x1e0
> +#define VE_MPEG_RAM_WRITE_DATA		0x1e4
> +
> +#define VE_H264_FRAME_SIZE		0x200
> +#define VE_H264_PIC_HDR			0x204
> +#define VE_H264_SLICE_HDR		0x208
> +#define VE_H264_SLICE_HDR2		0x20c
> +#define VE_H264_PRED_WEIGHT		0x210
> +#define VE_H264_QP_PARAM		0x21c
> +#define VE_H264_CTRL			0x220
> +#define VE_H264_TRIGGER			0x224
> +#define VE_H264_STATUS			0x228
> +#define VE_H264_CUR_MB_NUM		0x22c
> +#define VE_H264_VLD_ADDR		0x230
> +#define VE_H264_VLD_OFFSET		0x234
> +#define VE_H264_VLD_LEN			0x238
> +#define VE_H264_VLD_END			0x23c
> +#define VE_H264_SDROT_CTRL		0x240
> +#define VE_H264_OUTPUT_FRAME_IDX	0x24c
> +#define VE_H264_EXTRA_BUFFER1		0x250
> +#define VE_H264_EXTRA_BUFFER2		0x254
> +#define VE_H264_BASIC_BITS		0x2dc
> +#define VE_H264_RAM_WRITE_PTR		0x2e0
> +#define VE_H264_RAM_WRITE_DATA		0x2e4
> +
> +#define VE_SRAM_H264_PRED_WEIGHT_TABLE	0x000
> +#define VE_SRAM_H264_FRAMEBUFFER_LIST	0x400
> +#define VE_SRAM_H264_REF_LIST0		0x640
> +#define VE_SRAM_H264_REF_LIST1		0x664
> +#define VE_SRAM_H264_SCALING_LISTS	0x800
> +
> +#define VE_ISP_INPUT_SIZE		0xa00
> +#define VE_ISP_INPUT_STRIDE		0xa04
> +#define VE_ISP_CTRL			0xa08
> +#define VE_ISP_INPUT_LUMA		0xa78
> +#define VE_ISP_INPUT_CHROMA		0xa7c
> +
> +#define VE_AVC_PARAM			0xb04
> +#define VE_AVC_QP			0xb08
> +#define VE_AVC_MOTION_EST		0xb10
> +#define VE_AVC_CTRL			0xb14
> +#define VE_AVC_TRIGGER			0xb18
> +#define VE_AVC_STATUS			0xb1c
> +#define VE_AVC_BASIC_BITS		0xb20
> +#define VE_AVC_UNK_BUF			0xb60
> +#define VE_AVC_VLE_ADDR			0xb80
> +#define VE_AVC_VLE_END			0xb84
> +#define VE_AVC_VLE_OFFSET		0xb88
> +#define VE_AVC_VLE_MAX			0xb8c
> +#define VE_AVC_VLE_LENGTH		0xb90
> +#define VE_AVC_REF_LUMA			0xba0
> +#define VE_AVC_REF_CHROMA		0xba4
> +#define VE_AVC_REC_LUMA			0xbb0
> +#define VE_AVC_REC_CHROMA		0xbb4
> +#define VE_AVC_REF_SLUMA		0xbb8
> +#define VE_AVC_REC_SLUMA		0xbbc
> +#define VE_AVC_MB_INFO			0xbc0
> +
> +#endif /* SUNXI_CEDRUS_REGS_H */
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec
  2016-08-25  9:39 ` [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec Florent Revest
@ 2016-11-14 10:18   ` Hans Verkuil
  2016-11-14 10:28     ` Hans Verkuil
  0 siblings, 1 reply; 16+ messages in thread
From: Hans Verkuil @ 2016-11-14 10:18 UTC (permalink / raw)
  To: Florent Revest, linux-media
  Cc: linux-sunxi, maxime.ripard, posciak, hans.verkuil,
	thomas.petazzoni, mchehab, linux-kernel, wens

On 08/25/2016 11:39 AM, Florent Revest wrote:
> This patch introduces the support of MPEG2 video decoding to the
> sunxi-cedrus video decoder driver.
> 
> Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
> ---
>  drivers/media/platform/sunxi-cedrus/Makefile       |   2 +-
>  drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c |  26 +++-
>  .../platform/sunxi-cedrus/sunxi_cedrus_common.h    |   2 +
>  .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c |  15 +-
>  .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.c  |  17 ++-
>  .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |   4 +
>  .../platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c     | 152 +++++++++++++++++++++
>  7 files changed, 211 insertions(+), 7 deletions(-)
>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
> 
> diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
> index 14c2f7a..2d495a2 100644
> --- a/drivers/media/platform/sunxi-cedrus/Makefile
> +++ b/drivers/media/platform/sunxi-cedrus/Makefile
> @@ -1,2 +1,2 @@
>  obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
> -				    sunxi_cedrus_dec.o
> +				    sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
> index 17af34c..d1c957a 100644
> --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
> @@ -46,14 +46,31 @@ static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
>  	struct sunxi_cedrus_ctx *ctx =
>  		container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);
>  
> -	v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
> -	return -EINVAL;
> +	switch (ctrl->id) {
> +	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
> +		/* This is kept in memory and used directly. */
> +		break;
> +	default:
> +		v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");

Drop this, it's pointless since this cannot happen, and even if it could, there
is nothing wrong about userspace passing an unknown control, that should just result in
-EINVAL.

> +		return -EINVAL;
> +	}
> +
> +	return 0;
>  }
>  
>  static const struct v4l2_ctrl_ops sunxi_cedrus_ctrl_ops = {
>  	.s_ctrl = sunxi_cedrus_s_ctrl,
>  };
>  
> +static const struct v4l2_ctrl_config sunxi_cedrus_ctrl_mpeg2_frame_hdr = {
> +	.ops = &sunxi_cedrus_ctrl_ops,
> +	.id = V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR,
> +	.type = V4L2_CTRL_TYPE_PRIVATE,
> +	.name = "MPEG2 Frame Header Parameters",
> +	.max_reqs = VIDEO_MAX_FRAME,
> +	.elem_size = sizeof(struct v4l2_ctrl_mpeg2_frame_hdr),
> +};
> +

I understand that this is not yet finalized. In any case, what should happen is
that we have standard compound controls for the MPEG2 and MPEG4 headers that are
based on the MPEG-2 and MPEG-4 standards. Any HW specific information should be
done through driver-specific controls.

I am not sure if V4L2_CTRL_TYPE_PRIVATE is the way to go for private compound
controls, but it may not be needed at all once these MPEG controls are standardized.

>  /*
>   * File operations
>   */
> @@ -78,6 +95,10 @@ static int sunxi_cedrus_open(struct file *file)
>  	hdl = &ctx->hdl;
>  	v4l2_ctrl_handler_init(hdl, 1);
>  
> +	ctx->mpeg2_frame_hdr_ctrl = v4l2_ctrl_new_custom(hdl,
> +			&sunxi_cedrus_ctrl_mpeg2_frame_hdr, NULL);
> +	ctx->mpeg2_frame_hdr_ctrl->flags |= V4L2_CTRL_FLAG_REQ_KEEP;
> +
>  	if (hdl->error) {
>  		rc = hdl->error;
>  		v4l2_ctrl_handler_free(hdl);
> @@ -117,6 +138,7 @@ static int sunxi_cedrus_release(struct file *file)
>  	v4l2_fh_del(&ctx->fh);
>  	v4l2_fh_exit(&ctx->fh);
>  	v4l2_ctrl_handler_free(&ctx->hdl);
> +	ctx->mpeg2_frame_hdr_ctrl = NULL;
>  	mutex_lock(&dev->dev_mutex);
>  	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
>  	mutex_unlock(&dev->dev_mutex);
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
> index 6b8d87a..e715184 100644
> --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_common.h
> @@ -70,6 +70,8 @@ struct sunxi_cedrus_ctx {
>  	struct v4l2_ctrl_handler hdl;
>  
>  	struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME];
> +
> +	struct v4l2_ctrl *mpeg2_frame_hdr_ctrl;
>  };
>  
>  static inline void sunxi_cedrus_write(struct sunxi_cedrus_dev *vpu,
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
> index 71ef34b..38e8a3a 100644
> --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_dec.c
> @@ -48,6 +48,11 @@ static struct sunxi_cedrus_fmt formats[] = {
>  		.depth = 8,
>  		.num_planes = 2,
>  	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_MPEG2_FRAME,
> +		.types	= SUNXI_CEDRUS_OUTPUT,
> +		.num_planes = 1,
> +	},
>  };
>  
>  #define NUM_FORMATS ARRAY_SIZE(formats)
> @@ -120,8 +125,14 @@ void device_run(void *priv)
>  		 V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
>  		 V4L2_BUF_FLAG_BFRAME   | V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
>  
> -	v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
> -	v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
> +	if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_MPEG2_FRAME) {
> +		struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr =
> +				ctx->mpeg2_frame_hdr_ctrl->p_new.p;
> +		process_mpeg2(ctx, in_buf, out_luma, out_chroma, frame_hdr);
> +	} else {
> +		v4l2_m2m_buf_done(in_vb, VB2_BUF_STATE_ERROR);
> +		v4l2_m2m_buf_done(out_vb, VB2_BUF_STATE_ERROR);
> +	}
>  }
>  
>  /*
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
> index 72b9df4..160de17 100644
> --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.c
> @@ -47,6 +47,13 @@ static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
>  	int val;
>  	unsigned long flags;
>  
> +	/* Disable MPEG interrupts and stop the MPEG engine */
> +	val = sunxi_cedrus_read(vpu, VE_MPEG_CTRL);
> +	sunxi_cedrus_write(vpu, val & (~0xf), VE_MPEG_CTRL);
> +	val = sunxi_cedrus_read(vpu, VE_MPEG_STATUS);
> +	sunxi_cedrus_write(vpu, 0x0000c00f, VE_MPEG_STATUS);
> +	sunxi_cedrus_write(vpu, VE_CTRL_REINIT, VE_CTRL);
> +
>  	curr_ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev);
>  
>  	if (!curr_ctx) {
> @@ -57,9 +64,15 @@ static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
>  	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
>  	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
>  
> +	/* First bit of MPEG_STATUS means success */
>  	spin_lock_irqsave(&vpu->irqlock, flags);
> -	v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
> -	v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
> +	if (val & 0x1) {
> +		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
> +		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
> +	} else {
> +		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
> +		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
> +	}
>  	spin_unlock_irqrestore(&vpu->irqlock, flags);
>  
>  	v4l2_m2m_job_finish(vpu->m2m_dev, curr_ctx->fh.m2m_ctx);
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
> index 3c9199b..78625e5 100644
> --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_hw.h
> @@ -29,4 +29,8 @@ struct sunxi_cedrus_ctx;
>  int sunxi_cedrus_hw_probe(struct sunxi_cedrus_dev *vpu);
>  void sunxi_cedrus_hw_remove(struct sunxi_cedrus_dev *vpu);
>  
> +void process_mpeg2(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
> +		   dma_addr_t out_luma, dma_addr_t out_chroma,
> +		   struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr);
> +
>  #endif /* SUNXI_CEDRUS_HW_H_ */
> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
> new file mode 100644
> index 0000000..9381c63
> --- /dev/null
> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
> @@ -0,0 +1,152 @@
> +/*
> + * Sunxi Cedrus codec driver
> + *
> + * Copyright (C) 2016 Florent Revest
> + * Florent Revest <florent.revest@free-electrons.com>
> + *
> + * Based on reverse engineering efforts of the 'Cedrus' project
> + * Copyright (c) 2013-2014 Jens Kuske <jenskuske@gmail.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include "sunxi_cedrus_common.h"
> +
> +#include <media/videobuf2-dma-contig.h>
> +
> +static const u8 mpeg_default_intra_quant[64] = {
> +	 8, 16, 16, 19, 16, 19, 22, 22,
> +	22, 22, 22, 22, 26, 24, 26, 27,
> +	27, 27, 26, 26, 26, 26, 27, 27,
> +	27, 29, 29, 29, 34, 34, 34, 29,
> +	29, 29, 27, 27, 29, 29, 32, 32,
> +	34, 34, 37, 38, 37, 35, 35, 34,
> +	35, 38, 38, 40, 40, 40, 48, 48,
> +	46, 46, 56, 56, 58, 69, 69, 83
> +};
> +
> +#define m_iq(i) (((64 + i) << 8) | mpeg_default_intra_quant[i])
> +
> +static const u8 mpeg_default_non_intra_quant[64] = {
> +	16, 16, 16, 16, 16, 16, 16, 16,
> +	16, 16, 16, 16, 16, 16, 16, 16,
> +	16, 16, 16, 16, 16, 16, 16, 16,
> +	16, 16, 16, 16, 16, 16, 16, 16,
> +	16, 16, 16, 16, 16, 16, 16, 16,
> +	16, 16, 16, 16, 16, 16, 16, 16,
> +	16, 16, 16, 16, 16, 16, 16, 16,
> +	16, 16, 16, 16, 16, 16, 16, 16
> +};
> +
> +#define m_niq(i) ((i << 8) | mpeg_default_non_intra_quant[i])
> +
> +void process_mpeg2(struct sunxi_cedrus_ctx *ctx, dma_addr_t in_buf,
> +		   dma_addr_t out_luma, dma_addr_t out_chroma,
> +		   struct v4l2_ctrl_mpeg2_frame_hdr *frame_hdr)
> +{
> +	struct sunxi_cedrus_dev *dev = ctx->dev;
> +
> +	u16 width = DIV_ROUND_UP(frame_hdr->width, 16);
> +	u16 height = DIV_ROUND_UP(frame_hdr->height, 16);
> +
> +	u32 pic_header = 0;
> +	u32 vld_len = frame_hdr->slice_len - frame_hdr->slice_pos;
> +	int i;
> +
> +	struct vb2_buffer *fwd_vb2_buf, *bwd_vb2_buf;
> +	dma_addr_t fwd_luma = 0, fwd_chroma = 0, bwd_luma = 0, bwd_chroma = 0;
> +
> +	/*
> +	 * The VPU is only able to handle bus addresses so we have to subtract
> +	 * the RAM offset to the physcal addresses
> +	 */
> +	fwd_vb2_buf = ctx->dst_bufs[frame_hdr->forward_index];
> +	if (fwd_vb2_buf) {
> +		fwd_luma   = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 0);
> +		fwd_chroma = vb2_dma_contig_plane_dma_addr(fwd_vb2_buf, 1);
> +		fwd_luma   -= PHYS_OFFSET;
> +		fwd_chroma -= PHYS_OFFSET;
> +	}
> +
> +	bwd_vb2_buf = ctx->dst_bufs[frame_hdr->backward_index];
> +	if (bwd_vb2_buf) {
> +		bwd_luma   = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 0);
> +		bwd_chroma = vb2_dma_contig_plane_dma_addr(bwd_vb2_buf, 1);
> +		bwd_chroma -= PHYS_OFFSET;
> +		bwd_luma   -= PHYS_OFFSET;
> +	}
> +
> +	/* Activates MPEG engine */
> +	sunxi_cedrus_write(dev, VE_CTRL_MPEG, VE_CTRL);
> +
> +	/* Upload quantization matrices */
> +	for (i = 0; i < 64; i++) {
> +		sunxi_cedrus_write(dev, m_iq(i),  VE_MPEG_IQ_MIN_INPUT);
> +		sunxi_cedrus_write(dev, m_niq(i), VE_MPEG_IQ_MIN_INPUT);
> +	}
> +
> +	/* Image's dimensions */
> +	sunxi_cedrus_write(dev, width << 8  | height,      VE_MPEG_SIZE);
> +	sunxi_cedrus_write(dev, width << 20 | height << 4, VE_MPEG_FRAME_SIZE);
> +
> +	/* MPEG picture's header */
> +	pic_header |= (frame_hdr->picture_coding_type        & 0xf) << 28;
> +	pic_header |= (frame_hdr->f_code[0][0]               & 0xf) << 24;
> +	pic_header |= (frame_hdr->f_code[0][1]               & 0xf) << 20;
> +	pic_header |= (frame_hdr->f_code[1][0]               & 0xf) << 16;
> +	pic_header |= (frame_hdr->f_code[1][1]               & 0xf) << 12;
> +	pic_header |= (frame_hdr->intra_dc_precision         & 0x3) << 10;
> +	pic_header |= (frame_hdr->picture_structure          & 0x3) << 8;
> +	pic_header |= (frame_hdr->top_field_first            & 0x1) << 7;
> +	pic_header |= (frame_hdr->frame_pred_frame_dct       & 0x1) << 6;
> +	pic_header |= (frame_hdr->concealment_motion_vectors & 0x1) << 5;
> +	pic_header |= (frame_hdr->q_scale_type               & 0x1) << 4;
> +	pic_header |= (frame_hdr->intra_vlc_format           & 0x1) << 3;
> +	pic_header |= (frame_hdr->alternate_scan             & 0x1) << 2;
> +	sunxi_cedrus_write(dev, pic_header, VE_MPEG_PIC_HDR);
> +
> +	/* Enable interrupt and an unknown control flag */
> +	sunxi_cedrus_write(dev, VE_MPEG_CTRL_MPEG2, VE_MPEG_CTRL);
> +
> +	/* Macroblock address */
> +	sunxi_cedrus_write(dev, 0, VE_MPEG_MBA);
> +
> +	/* Clear previous errors */
> +	sunxi_cedrus_write(dev, 0, VE_MPEG_ERROR);
> +
> +	/* Unknown register */
> +	sunxi_cedrus_write(dev, 0, VE_MPEG_CTR_MB);
> +
> +	/* Forward and backward prediction buffers (cached in dst_bufs) */
> +	sunxi_cedrus_write(dev, fwd_luma,   VE_MPEG_FWD_LUMA);
> +	sunxi_cedrus_write(dev, fwd_chroma, VE_MPEG_FWD_CHROMA);
> +	sunxi_cedrus_write(dev, bwd_luma,   VE_MPEG_BACK_LUMA);
> +	sunxi_cedrus_write(dev, bwd_chroma, VE_MPEG_BACK_CHROMA);
> +
> +	/* Output luma and chroma buffers */
> +	sunxi_cedrus_write(dev, out_luma,   VE_MPEG_REC_LUMA);
> +	sunxi_cedrus_write(dev, out_chroma, VE_MPEG_REC_CHROMA);
> +	sunxi_cedrus_write(dev, out_luma,   VE_MPEG_ROT_LUMA);
> +	sunxi_cedrus_write(dev, out_chroma, VE_MPEG_ROT_CHROMA);
> +
> +	/* Input offset and length in bits */
> +	sunxi_cedrus_write(dev, frame_hdr->slice_pos, VE_MPEG_VLD_OFFSET);
> +	sunxi_cedrus_write(dev, vld_len, VE_MPEG_VLD_LEN);
> +
> +	/* Input beginning and end addresses */
> +	sunxi_cedrus_write(dev, VE_MPEG_VLD_ADDR_VAL(in_buf), VE_MPEG_VLD_ADDR);
> +	sunxi_cedrus_write(dev, in_buf + VBV_SIZE - 1, VE_MPEG_VLD_END);
> +
> +	/* Starts the MPEG engine */
> +	if (frame_hdr->type == MPEG2)
> +		sunxi_cedrus_write(dev, VE_TRIG_MPEG2, VE_MPEG_TRIGGER);
> +	else
> +		sunxi_cedrus_write(dev, VE_TRIG_MPEG1, VE_MPEG_TRIGGER);
> +}
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec
  2016-11-14 10:18   ` Hans Verkuil
@ 2016-11-14 10:28     ` Hans Verkuil
  0 siblings, 0 replies; 16+ messages in thread
From: Hans Verkuil @ 2016-11-14 10:28 UTC (permalink / raw)
  To: Florent Revest, linux-media
  Cc: linux-sunxi, maxime.ripard, posciak, hans.verkuil,
	thomas.petazzoni, mchehab, linux-kernel, wens

On 11/14/2016 11:18 AM, Hans Verkuil wrote:
> On 08/25/2016 11:39 AM, Florent Revest wrote:
>> This patch introduces the support of MPEG2 video decoding to the
>> sunxi-cedrus video decoder driver.
>>
>> Signed-off-by: Florent Revest <florent.revest@free-electrons.com>
>> ---
>>  drivers/media/platform/sunxi-cedrus/Makefile       |   2 +-
>>  drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c |  26 +++-
>>  .../platform/sunxi-cedrus/sunxi_cedrus_common.h    |   2 +
>>  .../media/platform/sunxi-cedrus/sunxi_cedrus_dec.c |  15 +-
>>  .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.c  |  17 ++-
>>  .../media/platform/sunxi-cedrus/sunxi_cedrus_hw.h  |   4 +
>>  .../platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c     | 152 +++++++++++++++++++++
>>  7 files changed, 211 insertions(+), 7 deletions(-)
>>  create mode 100644 drivers/media/platform/sunxi-cedrus/sunxi_cedrus_mpeg2.c
>>
>> diff --git a/drivers/media/platform/sunxi-cedrus/Makefile b/drivers/media/platform/sunxi-cedrus/Makefile
>> index 14c2f7a..2d495a2 100644
>> --- a/drivers/media/platform/sunxi-cedrus/Makefile
>> +++ b/drivers/media/platform/sunxi-cedrus/Makefile
>> @@ -1,2 +1,2 @@
>>  obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi_cedrus.o sunxi_cedrus_hw.o \
>> -				    sunxi_cedrus_dec.o
>> +				    sunxi_cedrus_dec.o sunxi_cedrus_mpeg2.o
>> diff --git a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
>> index 17af34c..d1c957a 100644
>> --- a/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
>> +++ b/drivers/media/platform/sunxi-cedrus/sunxi_cedrus.c
>> @@ -46,14 +46,31 @@ static int sunxi_cedrus_s_ctrl(struct v4l2_ctrl *ctrl)
>>  	struct sunxi_cedrus_ctx *ctx =
>>  		container_of(ctrl->handler, struct sunxi_cedrus_ctx, hdl);
>>  
>> -	v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
>> -	return -EINVAL;
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_MPEG_VIDEO_MPEG2_FRAME_HDR:
>> +		/* This is kept in memory and used directly. */
>> +		break;
>> +	default:
>> +		v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
> 
> Drop this, it's pointless since this cannot happen, and even if it could, there
> is nothing wrong about userspace passing an unknown control, that should just result in
> -EINVAL.
> 

Just to be clear: 'this' == the v4l2_err call.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2016-11-14 10:28 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-25  9:39 [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Florent Revest
2016-08-25  9:39 ` [RFC 01/10] clk: sunxi-ng: Add a couple of A13 clocks Florent Revest
2016-08-25  9:39 ` [RFC 02/10] v4l: Add private compound control type Florent Revest
2016-08-25  9:39 ` [RFC 03/10] v4l: Add sunxi Video Engine pixel format Florent Revest
2016-11-14 10:01   ` Hans Verkuil
2016-08-25  9:39 ` [RFC 04/10] v4l: Add MPEG2 low-level decoder API control Florent Revest
2016-08-25  9:39 ` [RFC 05/10] v4l: Add MPEG4 " Florent Revest
2016-08-25  9:39 ` [RFC 06/10] media: platform: Add Sunxi Cedrus decoder driver Florent Revest
2016-11-14 10:07   ` Hans Verkuil
2016-08-25  9:39 ` [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec Florent Revest
2016-11-14 10:18   ` Hans Verkuil
2016-11-14 10:28     ` Hans Verkuil
2016-08-25  9:39 ` [RFC 08/10] sunxi-cedrus: Add a MPEG 4 codec Florent Revest
2016-08-25  9:39 ` [RFC 09/10] ARM: dts: sun5i: Use video-engine node Florent Revest
2016-08-25  9:39 ` [RFC 10/10] sunxi-cedrus: Add device tree binding document Florent Revest
2016-08-25 12:12 ` [RFC 00/10] Add Sunxi Cedrus Video Decoder Driver Stefan Monnier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.