From patchwork Thu Aug 25 09:39:40 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711059 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933256AbcHYJm0 (ORCPT ); Thu, 25 Aug 2016 05:42:26 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34161 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1757777AbcHYJkE (ORCPT ); Thu, 25 Aug 2016 05:40:04 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 01/10] clk: sunxi-ng: Add a couple of A13 clocks Date: Thu, 25 Aug 2016 11:39:40 +0200 Message-Id: <1472117989-21455-2-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11513 Lines: 324 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 --- .../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 #include #include +#include / { 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 +#include + +#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 + * + * 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 +#include + +#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 + * + * 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 + * + * 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_ */ From patchwork Thu Aug 25 09:39:41 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711058 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933138AbcHYJmM (ORCPT ); Thu, 25 Aug 2016 05:42:12 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34171 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1757885AbcHYJkE (ORCPT ); Thu, 25 Aug 2016 05:40:04 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 02/10] v4l: Add private compound control type. Date: Thu, 25 Aug 2016 11:39:41 +0200 Message-Id: <1472117989-21455-3-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 1370 Lines: 43 From: Pawel Osciak 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 Signed-off-by: Jung Zhao Signed-off-by: Florent Revest --- 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 */ From patchwork Thu Aug 25 09:39:42 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711051 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758923AbcHYJkN (ORCPT ); Thu, 25 Aug 2016 05:40:13 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34179 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758718AbcHYJkL (ORCPT ); Thu, 25 Aug 2016 05:40:11 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 03/10] v4l: Add sunxi Video Engine pixel format Date: Thu, 25 Aug 2016 11:39:42 +0200 Message-Id: <1472117989-21455-4-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 1140 Lines: 25 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 --- 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 */ From patchwork Thu Aug 25 09:39:43 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711050 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758784AbcHYJkK (ORCPT ); Thu, 25 Aug 2016 05:40:10 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34187 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1757887AbcHYJkI (ORCPT ); Thu, 25 Aug 2016 05:40:08 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 04/10] v4l: Add MPEG2 low-level decoder API control Date: Thu, 25 Aug 2016 11:39:43 +0200 Message-Id: <1472117989-21455-5-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5301 Lines: 139 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 --- 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, }; From patchwork Thu Aug 25 09:39:44 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711054 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759139AbcHYJkd (ORCPT ); Thu, 25 Aug 2016 05:40:33 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34231 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1759034AbcHYJka (ORCPT ); Thu, 25 Aug 2016 05:40:30 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 05/10] v4l: Add MPEG4 low-level decoder API control Date: Thu, 25 Aug 2016 11:39:44 +0200 Message-Id: <1472117989-21455-6-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5754 Lines: 154 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 --- 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, }; From patchwork Thu Aug 25 09:39:45 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711057 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933084AbcHYJle (ORCPT ); Thu, 25 Aug 2016 05:41:34 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34197 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758928AbcHYJkV (ORCPT ); Thu, 25 Aug 2016 05:40:21 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 06/10] media: platform: Add Sunxi Cedrus decoder driver Date: Thu, 25 Aug 2016 11:39:45 +0200 Message-Id: <1472117989-21455-7-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 39950 Lines: 1387 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 --- 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 + * + * Based on vim2m + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, + * Marek Szyprowski, + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 + * + * Based on vim2m + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, + * Marek Szyprowski, + * + * 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 +#include + +#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 + * + * Based on vim2m + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, + * Marek Szyprowski, + * + * 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 +#include +#include +#include +#include +#include + +#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 + * + * Based on vim2m + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, + * Marek Szyprowski, + * + * 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 + * + * Based on vim2m + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, + * Marek Szyprowski, + * + * And reverse engineering efforts of the 'Cedrus' project + * Copyright (c) 2013-2014 Jens Kuske + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +/* + * 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 + * + * Based on vim2m + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, + * Marek Szyprowski, + * + * 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 + * + * Based on Cedrus + * + * Copyright (c) 2013 Jens Kuske + * + * 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 */ From patchwork Thu Aug 25 09:39:46 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711052 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758966AbcHYJkZ (ORCPT ); Thu, 25 Aug 2016 05:40:25 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34204 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758941AbcHYJkW (ORCPT ); Thu, 25 Aug 2016 05:40:22 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 07/10] sunxi-cedrus: Add a MPEG 2 codec Date: Thu, 25 Aug 2016 11:39:46 +0200 Message-Id: <1472117989-21455-8-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 12799 Lines: 335 This patch introduces the support of MPEG2 video decoding to the sunxi-cedrus video decoder driver. Signed-off-by: Florent Revest --- 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 + * + * Based on reverse engineering efforts of the 'Cedrus' project + * Copyright (c) 2013-2014 Jens Kuske + * + * 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 + +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); +} From patchwork Thu Aug 25 09:39:47 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711055 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759323AbcHYJkw (ORCPT ); Thu, 25 Aug 2016 05:40:52 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34217 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758992AbcHYJk2 (ORCPT ); Thu, 25 Aug 2016 05:40:28 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 08/10] sunxi-cedrus: Add a MPEG 4 codec Date: Thu, 25 Aug 2016 11:39:47 +0200 Message-Id: <1472117989-21455-9-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 12626 Lines: 322 This patch introduces the support of MPEG4 video decoding to the sunxi-cedrus video decoder driver. Signed-off-by: Florent Revest --- 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 + * + * Based on reverse engineering efforts of the 'Cedrus' project + * Copyright (c) 2013-2014 Jens Kuske + * + * 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 + +#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); +} From patchwork Thu Aug 25 09:39:48 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711056 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759295AbcHYJkv (ORCPT ); Thu, 25 Aug 2016 05:40:51 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34220 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758997AbcHYJk2 (ORCPT ); Thu, 25 Aug 2016 05:40:28 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 09/10] ARM: dts: sun5i: Use video-engine node Date: Thu, 25 Aug 2016 11:39:48 +0200 Message-Id: <1472117989-21455-10-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 1480 Lines: 63 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 --- 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>; From patchwork Thu Aug 25 09:39:49 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florent Revest X-Patchwork-Id: 711053 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759049AbcHYJka (ORCPT ); Thu, 25 Aug 2016 05:40:30 -0400 Received: from down.free-electrons.com ([37.187.137.238]:34209 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758962AbcHYJk0 (ORCPT ); Thu, 25 Aug 2016 05:40:26 -0400 From: Florent Revest To: linux-media@vger.kernel.org Cc: florent.revest@free-electrons.com, linux-sunxi@googlegroups.com, maxime.ripard@free-electrons.com, posciak@chromium.org, hans.verkuil@cisco.com, thomas.petazzoni@free-electrons.com, mchehab@kernel.org, linux-kernel@vger.kernel.org, wens@csie.org Subject: [RFC 10/10] sunxi-cedrus: Add device tree binding document Date: Thu, 25 Aug 2016 11:39:49 +0200 Message-Id: <1472117989-21455-11-git-send-email-florent.revest@free-electrons.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> References: <1472117989-21455-1-git-send-email-florent.revest@free-electrons.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 1708 Lines: 60 Device Tree bindings for the Allwinner's video engine Signed-off-by: Florent Revest --- .../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>; +};