Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1289376 > unrolled thread
| Started by | Tiffany Lin <tiffany.lin@mediatek.com> |
|---|---|
| First post | 2015-12-11 11:00 +0100 |
| Last post | 2015-12-11 11:10 +0100 |
| Articles | 5 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH v2 0/8] Add MT8173 Video Encoder Driver and VPU Driver Tiffany Lin <tiffany.lin@mediatek.com> - 2015-12-11 11:00 +0100
[PATCH v2 1/8] dt-bindings: Add a binding for Mediatek Video Processor Tiffany Lin <tiffany.lin@mediatek.com> - 2015-12-11 11:00 +0100
Re: [PATCH v2 1/8] dt-bindings: Add a binding for Mediatek Video Processor Rob Herring <robh@kernel.org> - 2015-12-11 18:30 +0100
[PATCH v2 7/8] [media] vcodec: mediatek: Add Mediatek VP8 Video Encoder Driver Tiffany Lin <tiffany.lin@mediatek.com> - 2015-12-11 11:00 +0100
[PATCH v2 3/8] [media] VPU: mediatek: support Mediatek VPU Tiffany Lin <tiffany.lin@mediatek.com> - 2015-12-11 11:10 +0100
| From | Tiffany Lin <tiffany.lin@mediatek.com> |
|---|---|
| Date | 2015-12-11 11:00 +0100 |
| Subject | [PATCH v2 0/8] Add MT8173 Video Encoder Driver and VPU Driver |
| Message-ID | <qEsEz-2v0-3@gated-at.bofh.it> |
============== Introduction ============== The purpose of this RFC is to discuss the driver for a hw video codec embedded in the Mediatek's MT8173 SoCs. Mediatek Video Codec is able to handle video encoding of in a range of formats. This RFC also include VPU driver. Mediatek Video Codec driver rely on VPU driver to load, communicate with VPU. Internally the driver uses videobuf2 framework and MTK IOMMU and MTK SMI. MTK IOMMU and MTK SMI have not yet been merged, but we wanted to start discussion about the driver earlier so it could be merged sooner. The driver posted here is the initial version, so I suppose it will require more work. [1]http://lists.infradead.org/pipermail/linux-mediatek/2015-October/002525.html ================== Device interface ================== In principle the driver bases on memory-to-memory framework: it provides a single video node and each opened file handle gets its own private context with separate buffer queues. Each context consist of 2 buffer queues: OUTPUT (for source buffers, i.e. raw video frames) and CAPTURE (for destination buffers, i.e. encoded video frames). The process of encoding video data from stream is a bit more complicated than typical memory-to-memory processing. We base on memory-to-memory framework and add the complicated part in our vb2 and v4l2 callback functionss. So we can base on well done m2m memory-to-memory framework, reduce duplicate code and make our driver code simple. ============================== VPU (Video Processor Unit) ============================== The VPU driver for hw video codec embedded in Mediatek's MT8173 SOCs. It is able to handle video decoding/encoding of in a range of formats. The driver provides with VPU firmware download, memory management and the communication interface between CPU and VPU. For VPU initialization, it will create virtual memory for CPU access and IOMMU address for vcodec hw device access. When a decode/encode instance opens a device node, vpu driver will download vpu firmware to the device. A decode/encode instant will decode/encode a frame using VPU interface to interrupt vpu to handle decoding/encoding jobs. Please have a look at the code and comments will be very much appreciated. Change in v2: Vcodec Part 1.Remove common and include directory in mtk-vcodec 2.Refine vb2ops_venc_start_streaming and vb2ops_venc_stop_streaming and state machine 3.Remove venc_if_init and venc_if_deinit 4.Refine debug message 5.Refine lab and vpu decription in mediatek-vcodec.txt VPU Part 1. Modify VPU Kconfig 2. Move encoder header files to other patch sets 3. Remove marcos for extended virtual/iova address 4. Change register and variable names 5. Add a reference counter for VPU watchdog 6. Remove one busy waiting in function vpu_ipi_send 7. Operate VPU clock in VPU driver (not called by encoder drivers) 8. Refine memory mapping, firmware download and extended memory allocation/free functions 9. Release more allocated resources in driver remove function Andrew-CT Chen (2): dt-bindings: Add a binding for Mediatek Video Processor [Media] vcodec: mediatek: Add Mediatek V4L2 Video Encoder Driver Tiffany Lin (6): arm64: dts: mediatek: Add node for Mediatek Video Processor Unit [media] VPU: mediatek: support Mediatek VPU dt-bindings: Add a binding for Mediatek Video Encoder arm64: dts: mediatek: Add Video Encoder for MT8173 [media] vcodec: mediatek: Add Mediatek VP8 Video Encoder Driver [media] vcodec: mediatek: Add Mediatek H264 Video Encoder Driver .../devicetree/bindings/media/mediatek-vcodec.txt | 58 + .../devicetree/bindings/media/mediatek-vpu.txt | 27 + arch/arm64/boot/dts/mediatek/mt8173.dtsi | 58 + drivers/media/platform/Kconfig | 21 + drivers/media/platform/Makefile | 4 + drivers/media/platform/mtk-vcodec/Makefile | 11 + .../media/platform/mtk-vcodec/h264_enc/Makefile | 8 + .../platform/mtk-vcodec/h264_enc/venc_h264_if.c | 495 ++++++ .../platform/mtk-vcodec/h264_enc/venc_h264_if.h | 161 ++ .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.c | 316 ++++ .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.h | 30 + drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h | 412 +++++ drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 1670 ++++++++++++++++++++ drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h | 45 + .../media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c | 469 ++++++ .../media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c | 122 ++ .../media/platform/mtk-vcodec/mtk_vcodec_intr.c | 102 ++ .../media/platform/mtk-vcodec/mtk_vcodec_intr.h | 29 + drivers/media/platform/mtk-vcodec/mtk_vcodec_pm.h | 26 + .../media/platform/mtk-vcodec/mtk_vcodec_util.c | 106 ++ .../media/platform/mtk-vcodec/mtk_vcodec_util.h | 85 + drivers/media/platform/mtk-vcodec/venc_drv_base.h | 62 + drivers/media/platform/mtk-vcodec/venc_drv_if.c | 108 ++ drivers/media/platform/mtk-vcodec/venc_drv_if.h | 174 ++ drivers/media/platform/mtk-vcodec/venc_ipi_msg.h | 212 +++ drivers/media/platform/mtk-vcodec/vp8_enc/Makefile | 8 + .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.c | 384 +++++ .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.h | 141 ++ .../platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c | 221 +++ .../platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.h | 28 + drivers/media/platform/mtk-vpu/Makefile | 1 + drivers/media/platform/mtk-vpu/mtk_vpu.c | 859 ++++++++++ drivers/media/platform/mtk-vpu/mtk_vpu.h | 149 ++ 33 files changed, 6602 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/mediatek-vcodec.txt create mode 100644 Documentation/devicetree/bindings/media/mediatek-vpu.txt create mode 100644 drivers/media/platform/mtk-vcodec/Makefile create mode 100644 drivers/media/platform/mtk-vcodec/h264_enc/Makefile create mode 100644 drivers/media/platform/mtk-vcodec/h264_enc/venc_h264_if.c create mode 100644 drivers/media/platform/mtk-vcodec/h264_enc/venc_h264_if.h create mode 100644 drivers/media/platform/mtk-vcodec/h264_enc/venc_h264_vpu.c create mode 100644 drivers/media/platform/mtk-vcodec/h264_enc/venc_h264_vpu.h create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_pm.h create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c create mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h create mode 100644 drivers/media/platform/mtk-vcodec/venc_drv_base.h create mode 100644 drivers/media/platform/mtk-vcodec/venc_drv_if.c create mode 100644 drivers/media/platform/mtk-vcodec/venc_drv_if.h create mode 100644 drivers/media/platform/mtk-vcodec/venc_ipi_msg.h create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/Makefile create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.c create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.h create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.h create mode 100644 drivers/media/platform/mtk-vpu/Makefile create mode 100644 drivers/media/platform/mtk-vpu/mtk_vpu.c create mode 100644 drivers/media/platform/mtk-vpu/mtk_vpu.h -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | Tiffany Lin <tiffany.lin@mediatek.com> |
|---|---|
| Date | 2015-12-11 11:00 +0100 |
| Subject | [PATCH v2 1/8] dt-bindings: Add a binding for Mediatek Video Processor |
| Message-ID | <qEsEz-2v0-23@gated-at.bofh.it> |
| In reply to | #1289376 |
From: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
Add a DT binding documentation of Video Processor Unit for the
MT8173 SoC from Mediatek.
Signed-off-by: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
.../devicetree/bindings/media/mediatek-vpu.txt | 27 ++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/mediatek-vpu.txt
diff --git a/Documentation/devicetree/bindings/media/mediatek-vpu.txt b/Documentation/devicetree/bindings/media/mediatek-vpu.txt
new file mode 100644
index 0000000..3c3a424
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek-vpu.txt
@@ -0,0 +1,27 @@
+* Mediatek Video Processor Unit
+
+Video Processor Unit is a HW video controller. It controls HW Codec including
+H.264/VP8/VP9 Decode, H.264/VP8 Encode and Image Processor (scale/rotate/color convert).
+
+Required properties:
+ - compatible: "mediatek,mt8173-vpu"
+ - reg: Must contain an entry for each entry in reg-names.
+ - reg-names: Must include the following entries:
+ "tcm": tcm base
+ "cfg_reg": Main configuration registers base
+ - interrupts: interrupt number to the cpu.
+ - clocks : clock name from clock manager
+ - clock-names: must be main. It is the main clock of VPU
+ - iommus : phandle and IOMMU spcifier for the IOMMU that serves the VPU.
+
+Example:
+ vpu: vpu@10020000 {
+ compatible = "mediatek,mt8173-vpu";
+ reg = <0 0x10020000 0 0x30000>,
+ <0 0x10050000 0 0x100>;
+ reg-names = "tcm", "cfg_reg";
+ interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&topckgen TOP_SCP_SEL>;
+ clock-names = "main";
+ iommus = <&iommu M4U_PORT_VENC_RCPU>;
+ };
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Rob Herring <robh@kernel.org> |
|---|---|
| Date | 2015-12-11 18:30 +0100 |
| Subject | Re: [PATCH v2 1/8] dt-bindings: Add a binding for Mediatek Video Processor |
| Message-ID | <qEzG2-7up-17@gated-at.bofh.it> |
| In reply to | #1289377 |
On Fri, Dec 11, 2015 at 05:55:36PM +0800, Tiffany Lin wrote:
> From: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
>
> Add a DT binding documentation of Video Processor Unit for the
> MT8173 SoC from Mediatek.
>
> Signed-off-by: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
> Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
> ---
> .../devicetree/bindings/media/mediatek-vpu.txt | 27 ++++++++++++++++++++
> 1 file changed, 27 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/mediatek-vpu.txt
>
> diff --git a/Documentation/devicetree/bindings/media/mediatek-vpu.txt b/Documentation/devicetree/bindings/media/mediatek-vpu.txt
> new file mode 100644
> index 0000000..3c3a424
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/mediatek-vpu.txt
> @@ -0,0 +1,27 @@
> +* Mediatek Video Processor Unit
> +
> +Video Processor Unit is a HW video controller. It controls HW Codec including
> +H.264/VP8/VP9 Decode, H.264/VP8 Encode and Image Processor (scale/rotate/color convert).
> +
> +Required properties:
> + - compatible: "mediatek,mt8173-vpu"
> + - reg: Must contain an entry for each entry in reg-names.
> + - reg-names: Must include the following entries:
> + "tcm": tcm base
> + "cfg_reg": Main configuration registers base
> + - interrupts: interrupt number to the cpu.
> + - clocks : clock name from clock manager
> + - clock-names: must be main. It is the main clock of VPU
> + - iommus : phandle and IOMMU spcifier for the IOMMU that serves the VPU.
> +
> +Example:
> + vpu: vpu@10020000 {
> + compatible = "mediatek,mt8173-vpu";
> + reg = <0 0x10020000 0 0x30000>,
> + <0 0x10050000 0 0x100>;
> + reg-names = "tcm", "cfg_reg";
> + interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&topckgen TOP_SCP_SEL>;
> + clock-names = "main";
> + iommus = <&iommu M4U_PORT_VENC_RCPU>;
> + };
> --
> 1.7.9.5
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tiffany Lin <tiffany.lin@mediatek.com> |
|---|---|
| Date | 2015-12-11 11:00 +0100 |
| Subject | [PATCH v2 7/8] [media] vcodec: mediatek: Add Mediatek VP8 Video Encoder Driver |
| Message-ID | <qEsEz-2v0-25@gated-at.bofh.it> |
| In reply to | #1289376 |
Add vp8 encoder driver for MT8173
Signed-off-by: Daniel Hsiao <daniel.hsiao@mediatek.com>
Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
drivers/media/platform/mtk-vcodec/Makefile | 2 +
drivers/media/platform/mtk-vcodec/venc_drv_if.c | 3 +
drivers/media/platform/mtk-vcodec/vp8_enc/Makefile | 8 +
.../platform/mtk-vcodec/vp8_enc/venc_vp8_if.c | 384 ++++++++++++++++++++
.../platform/mtk-vcodec/vp8_enc/venc_vp8_if.h | 141 +++++++
.../platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c | 221 +++++++++++
.../platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.h | 28 ++
7 files changed, 787 insertions(+)
create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/Makefile
create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.c
create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.h
create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c
create mode 100644 drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.h
diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mtk-vcodec/Makefile
index ce38689..f4ef502 100644
--- a/drivers/media/platform/mtk-vcodec/Makefile
+++ b/drivers/media/platform/mtk-vcodec/Makefile
@@ -5,4 +5,6 @@ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk_vcodec_intr.o \
mtk_vcodec_enc_pm.o \
venc_drv_if.o
+obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += vp8_enc/
+
ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c
index 63e7df0..384e0d2 100644
--- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c
+++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c
@@ -24,6 +24,7 @@
#include "mtk_vpu.h"
#include "venc_drv_base.h"
+#include "vp8_enc/venc_vp8_if.h"
int venc_if_create(struct mtk_vcodec_ctx *ctx, unsigned int fourcc)
{
@@ -34,6 +35,8 @@ int venc_if_create(struct mtk_vcodec_ctx *ctx, unsigned int fourcc)
switch (fourcc) {
case V4L2_PIX_FMT_VP8:
+ ctx->enc_if = get_vp8_enc_comm_if();
+ break;
case V4L2_PIX_FMT_H264:
default:
goto err_out;
diff --git a/drivers/media/platform/mtk-vcodec/vp8_enc/Makefile b/drivers/media/platform/mtk-vcodec/vp8_enc/Makefile
new file mode 100644
index 0000000..17a0dfe
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/vp8_enc/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += venc_vp8_if.o venc_vp8_vpu.o
+
+ccflags-y += \
+ -I$(srctree)/include/ \
+ -I$(srctree)/drivers/media/platform/mtk-vcodec/ \
+ -I$(srctree)/drivers/media/platform/mtk-vcodec/include \
+ -I$(srctree)/drivers/media/platform/mtk-vcodec/vp8_enc \
+ -I$(srctree)/drivers/media/platform/mtk-vpu
diff --git a/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.c
new file mode 100644
index 0000000..5201743
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ * PoChun Lin <pochun.lin@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_util.h"
+#include "mtk_vcodec_intr.h"
+#include "mtk_vcodec_enc.h"
+#include "mtk_vcodec_pm.h"
+#include "mtk_vpu.h"
+
+#include "venc_vp8_if.h"
+#include "venc_vp8_vpu.h"
+
+#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098
+#define VENC_PIC_BITSTREAM_BYTE_CNT1 0x00e8
+#define VENC_IRQ_STATUS_ENC_FRM_INT 0x04
+
+#define MAX_AC_TAG_SZ 10
+
+static inline void vp8_enc_write_reg(struct venc_vp8_inst *inst, u32 addr,
+ u32 val)
+{
+ writel(val, inst->hw_base + addr);
+}
+
+static inline u32 vp8_enc_read_reg(struct venc_vp8_inst *inst, u32 addr)
+{
+ return readl(inst->hw_base + addr);
+}
+
+static void vp8_enc_free_work_buf(struct venc_vp8_inst *inst)
+{
+ int i;
+
+ mtk_vcodec_debug_enter(inst);
+
+ /* Except the RC_CODEx buffers, other buffers need to be freed by AP. */
+ for (i = 0; i < VENC_VP8_VPU_WORK_BUF_RC_CODE; i++)
+ if (inst->work_bufs[i].va != NULL)
+ mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]);
+
+ mtk_vcodec_debug_leave(inst);
+}
+
+static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst)
+{
+ int i;
+ int ret = 0;
+ struct venc_vp8_vpu_buf *wb = inst->vpu_inst.drv->work_bufs;
+
+ mtk_vcodec_debug_enter(inst);
+ for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) {
+ /*
+ * Only temporal scalability mode will use RC_CODE2 & RC_CODE3
+ * Each three temporal layer has its own rate control code.
+ */
+ if ((i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 ||
+ i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) && !inst->ts_mode)
+ continue;
+
+ /*
+ * This 'wb' structure is set by VPU side and shared to AP for
+ * buffer allocation and IO virtual addr mapping. For most of
+ * the buffers, AP will allocate the buffer according to 'size'
+ * field and store the IO virtual addr in 'iova' field. For the
+ * RC_CODEx buffers, they are pre-allocated in the VPU side
+ * because they are inside VPU SRAM, and save the VPU addr in
+ * the 'vpua' field. The AP will translate the VPU addr to the
+ * corresponding IO virtual addr and store in 'iova' field.
+ */
+ if (i < VENC_VP8_VPU_WORK_BUF_RC_CODE) {
+ inst->work_bufs[i].size = wb[i].size;
+ ret = mtk_vcodec_mem_alloc(inst->ctx,
+ &inst->work_bufs[i]);
+ if (ret) {
+ mtk_vcodec_err(inst,
+ "cannot alloc work_bufs[%d]", i);
+ goto err_alloc;
+ }
+ wb[i].iova = inst->work_bufs[i].dma_addr;
+ } else {
+ inst->work_bufs[i].size = wb[i].size;
+ inst->work_bufs[i].va =
+ vpu_mapping_dm_addr(inst->dev, wb[i].vpua);
+ inst->work_bufs[i].dma_addr =
+ vpu_mapping_iommu_dm_addr(inst->dev,
+ wb[i].vpua);
+ wb[i].iova = inst->work_bufs[i].dma_addr;
+ }
+ mtk_vcodec_debug(inst,
+ "work_bufs[%d] va=0x%p,iova=0x%p,size=0x%lx",
+ i, inst->work_bufs[i].va,
+ (void *)inst->work_bufs[i].dma_addr,
+ inst->work_bufs[i].size);
+ }
+ mtk_vcodec_debug_leave(inst);
+
+ return ret;
+
+err_alloc:
+ vp8_enc_free_work_buf(inst);
+
+ return ret;
+}
+
+static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_inst *inst)
+{
+ struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx;
+ unsigned int irq_status;
+
+ mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
+ WAIT_INTR_TIMEOUT, true);
+ irq_status = ctx->irq_status;
+ mtk_vcodec_debug(inst, "isr return %x", irq_status);
+
+ return irq_status;
+}
+
+/*
+ * Compose ac_tag, bitstream header and bitstream payload into
+ * one bitstream buffer.
+ */
+static int vp8_enc_compose_one_frame(struct venc_vp8_inst *inst,
+ struct mtk_vcodec_mem *bs_buf,
+ unsigned int *bs_size)
+{
+ unsigned int is_key;
+ u32 bs_size_frm;
+ u32 bs_hdr_len;
+ unsigned int ac_tag_sz;
+ u8 ac_tag[MAX_AC_TAG_SZ];
+
+ bs_size_frm = vp8_enc_read_reg(inst,
+ VENC_PIC_BITSTREAM_BYTE_CNT);
+ bs_hdr_len = vp8_enc_read_reg(inst,
+ VENC_PIC_BITSTREAM_BYTE_CNT1);
+
+ /* if a frame is key frame, is_key is 0 */
+ is_key = (inst->frm_cnt %
+ inst->vpu_inst.drv->config.intra_period) ? 1 : 0;
+ *(u32 *)ac_tag = __cpu_to_le32((bs_hdr_len << 5) | 0x10 | is_key);
+ /* key frame */
+ if (is_key == 0) {
+ ac_tag[3] = 0x9d;
+ ac_tag[4] = 0x01;
+ ac_tag[5] = 0x2a;
+ ac_tag[6] = inst->vpu_inst.drv->config.pic_w;
+ ac_tag[7] = inst->vpu_inst.drv->config.pic_w >> 8;
+ ac_tag[8] = inst->vpu_inst.drv->config.pic_h;
+ ac_tag[9] = inst->vpu_inst.drv->config.pic_h >> 8;
+ }
+
+ if (is_key == 0)
+ ac_tag_sz = MAX_AC_TAG_SZ;
+ else
+ ac_tag_sz = 3;
+
+ if (bs_buf->size <= bs_hdr_len + bs_size_frm + ac_tag_sz) {
+ mtk_vcodec_err(inst, "bitstream buf size is too small(%ld)",
+ bs_buf->size);
+ return -EINVAL;
+ }
+
+ /*
+ * (1) The vp8 bitstream header and body are generated by the HW vp8
+ * encoder separately at the same time. We cannot know the bitstream
+ * header length in advance.
+ * (2) From the vp8 spec, there is no stuffing byte allowed between the
+ * ac tag, bitstream header and bitstream body.
+ */
+ memmove(bs_buf->va + bs_hdr_len + ac_tag_sz,
+ bs_buf->va, bs_size_frm);
+ memcpy(bs_buf->va + ac_tag_sz,
+ inst->work_bufs[VENC_VP8_VPU_WORK_BUF_BS_HD].va,
+ bs_hdr_len);
+ memcpy(bs_buf->va, ac_tag, ac_tag_sz);
+ *bs_size = bs_size_frm + bs_hdr_len + ac_tag_sz;
+
+ return 0;
+}
+
+static int vp8_enc_encode_frame(struct venc_vp8_inst *inst,
+ struct venc_frm_buf *frm_buf,
+ struct mtk_vcodec_mem *bs_buf,
+ unsigned int *bs_size)
+{
+ int ret = 0;
+ unsigned int irq_status;
+
+ mtk_vcodec_debug(inst, "->frm_cnt=%d", inst->frm_cnt);
+
+ ret = vp8_enc_vpu_encode(inst, frm_buf, bs_buf);
+ if (ret)
+ return ret;
+
+ irq_status = vp8_enc_wait_venc_done(inst);
+ if (irq_status != VENC_IRQ_STATUS_ENC_FRM_INT) {
+ mtk_vcodec_err(inst, "irq_status=%d failed", irq_status);
+ return -EINVAL;
+ }
+
+ if (vp8_enc_compose_one_frame(inst, bs_buf, bs_size)) {
+ mtk_vcodec_err(inst, "vp8_enc_compose_one_frame failed");
+ return -EINVAL;
+ }
+
+ inst->frm_cnt++;
+ mtk_vcodec_debug(inst, "<-size=%d", *bs_size);
+
+ return ret;
+}
+
+static int vp8_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle)
+{
+ int ret = 0;
+ struct venc_vp8_inst *inst;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ inst->ctx = ctx;
+ inst->dev = mtk_vcodec_get_plat_dev(ctx);
+ inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS);
+
+ ret = vp8_enc_vpu_init(inst);
+ if (ret)
+ kfree(inst);
+ else
+ (*handle) = (unsigned long)inst;
+
+ return ret;
+}
+
+static int vp8_enc_encode(unsigned long handle,
+ enum venc_start_opt opt,
+ struct venc_frm_buf *frm_buf,
+ struct mtk_vcodec_mem *bs_buf,
+ struct venc_done_result *result)
+{
+ int ret = 0;
+ struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle;
+ struct mtk_vcodec_ctx *ctx = inst->ctx ;
+
+ mtk_vcodec_debug_enter(inst);
+ enable_irq(ctx->dev->enc_lt_irq);
+
+ switch (opt) {
+ case VENC_START_OPT_ENCODE_FRAME:
+ ret = vp8_enc_encode_frame(inst, frm_buf, bs_buf,
+ &result->bs_size);
+ if (ret) {
+ result->msg = VENC_MESSAGE_ERR;
+ } else {
+ result->msg = VENC_MESSAGE_OK;
+ result->is_key_frm = ((*((unsigned char *)bs_buf->va) &
+ 0x01) == 0);
+ }
+ break;
+
+ default:
+ mtk_vcodec_err(inst, "opt not support:%d", opt);
+ ret = -EINVAL;
+ break;
+ }
+
+ disable_irq(ctx->dev->enc_lt_irq);
+ mtk_vcodec_debug_leave(inst);
+ return ret;
+}
+
+static int vp8_enc_set_param(unsigned long handle,
+ enum venc_set_param_type type, void *in)
+{
+ int ret = 0;
+ struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle;
+ struct venc_enc_prm *enc_prm;
+
+ mtk_vcodec_debug(inst, "->type=%d", type);
+
+ switch (type) {
+ case VENC_SET_PARAM_ENC:
+ enc_prm = in;
+ ret = vp8_enc_vpu_set_param(inst, type, enc_prm);
+ if (ret)
+ break;
+ if (inst->work_buf_allocated == 0) {
+ ret = vp8_enc_alloc_work_buf(inst);
+ if (ret)
+ break;
+ inst->work_buf_allocated = 1;
+ }
+ break;
+
+ case VENC_SET_PARAM_FORCE_INTRA:
+ ret = vp8_enc_vpu_set_param(inst, type, 0);
+ if (ret)
+ break;
+ inst->frm_cnt = 0;
+ break;
+
+ case VENC_SET_PARAM_ADJUST_BITRATE:
+ enc_prm = in;
+ ret = vp8_enc_vpu_set_param(inst, type, &enc_prm->bitrate);
+ break;
+
+ case VENC_SET_PARAM_ADJUST_FRAMERATE:
+ enc_prm = in;
+ ret = vp8_enc_vpu_set_param(inst, type, &enc_prm->frm_rate);
+ break;
+
+ case VENC_SET_PARAM_I_FRAME_INTERVAL:
+ ret = vp8_enc_vpu_set_param(inst, type, in);
+ if (ret)
+ break;
+ inst->frm_cnt = 0; /* reset counter */
+ break;
+
+ /*
+ * VENC_SET_PARAM_TS_MODE must be called before
+ * VENC_SET_PARAM_ENC
+ */
+ case VENC_SET_PARAM_TS_MODE:
+ inst->ts_mode = 1;
+ mtk_vcodec_debug(inst, "set ts_mode");
+ break;
+
+ default:
+ mtk_vcodec_err(inst, "type not support:%d", type);
+ ret = -EINVAL;
+ break;
+ }
+
+ mtk_vcodec_debug_leave(inst);
+ return ret;
+}
+
+static int vp8_enc_deinit(unsigned long handle)
+{
+ int ret = 0;
+ struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle;
+
+ mtk_vcodec_debug_enter(inst);
+
+ ret = vp8_enc_vpu_deinit(inst);
+
+ if (inst->work_buf_allocated)
+ vp8_enc_free_work_buf(inst);
+
+ mtk_vcodec_debug_leave(inst);
+ kfree(inst);
+
+ return ret;
+}
+
+struct venc_common_if venc_vp8_if = {
+ vp8_enc_init,
+ vp8_enc_encode,
+ vp8_enc_set_param,
+ vp8_enc_deinit,
+};
+
+struct venc_common_if *get_vp8_enc_comm_if(void)
+{
+ return &venc_vp8_if;
+}
diff --git a/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.h b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.h
new file mode 100644
index 0000000..5ee990c
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_if.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ * PoChun Lin <pochun.lin@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 _VENC_VP8_IF_H_
+#define _VENC_VP8_IF_H_
+
+#include "../venc_drv_base.h"
+
+/**
+ * enum venc_vp8_vpu_work_buf - vp8 encoder buffer index
+ */
+enum venc_vp8_vpu_work_buf {
+ VENC_VP8_VPU_WORK_BUF_LUMA,
+ VENC_VP8_VPU_WORK_BUF_LUMA2,
+ VENC_VP8_VPU_WORK_BUF_LUMA3,
+ VENC_VP8_VPU_WORK_BUF_CHROMA,
+ VENC_VP8_VPU_WORK_BUF_CHROMA2,
+ VENC_VP8_VPU_WORK_BUF_CHROMA3,
+ VENC_VP8_VPU_WORK_BUF_MV_INFO,
+ VENC_VP8_VPU_WORK_BUF_BS_HD,
+ VENC_VP8_VPU_WORK_BUF_PROB_BUF,
+ VENC_VP8_VPU_WORK_BUF_RC_INFO,
+ VENC_VP8_VPU_WORK_BUF_RC_CODE,
+ VENC_VP8_VPU_WORK_BUF_RC_CODE2,
+ VENC_VP8_VPU_WORK_BUF_RC_CODE3,
+ VENC_VP8_VPU_WORK_BUF_MAX,
+};
+
+/*
+ * struct venc_vp8_vpu_config - Structure for vp8 encoder configuration
+ * @input_fourcc: input fourcc
+ * @bitrate: target bitrate (in bps)
+ * @pic_w: picture width
+ * @pic_h: picture height
+ * @buf_w: buffer width (with 16 alignment)
+ * @buf_h: buffer height (with 16 alignment)
+ * @intra_period: intra frame period
+ * @framerate: frame rate
+ * @ts_mode: temporal scalability mode (0: disable, 1: enable)
+ * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps.
+ */
+struct venc_vp8_vpu_config {
+ u32 input_fourcc;
+ u32 bitrate;
+ u32 pic_w;
+ u32 pic_h;
+ u32 buf_w;
+ u32 buf_h;
+ u32 intra_period;
+ u32 framerate;
+ u32 ts_mode;
+};
+
+/*
+ * struct venc_vp8_vpu_buf -Structure for buffer information
+ * @align: buffer alignment (in bytes)
+ * @iova: IO virtual address
+ * @vpua: VPU side memory addr which is used by RC_CODE
+ * @size: buffer size (in bytes)
+ */
+struct venc_vp8_vpu_buf {
+ u32 align;
+ u32 iova;
+ u32 vpua;
+ u32 size;
+};
+
+/*
+ * struct venc_vp8_vpu_drv - Structure for VPU driver control and info share
+ * This structure is allocated in VPU side and shared to AP side.
+ * @config: vp8 encoder configuration
+ * @work_bufs: working buffer information in VPU side
+ * The work_bufs here is for storing the 'size' info shared to AP side.
+ * The similar item in struct venc_vp8_inst is for memory allocation
+ * in AP side. The AP driver will copy the 'size' from here to the one in
+ * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate
+ * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for
+ * register setting in VPU side.
+ */
+struct venc_vp8_vpu_drv {
+ struct venc_vp8_vpu_config config;
+ struct venc_vp8_vpu_buf work_bufs[VENC_VP8_VPU_WORK_BUF_MAX];
+};
+
+/*
+ * struct venc_vp8_vpu_inst - vp8 encoder VPU driver instance
+ * @wq_hd: wait queue used for vpu cmd trigger then wait vpu interrupt done
+ * @signaled: flag used for checking vpu interrupt done
+ * @failure: flag to show vpu cmd succeeds or not
+ * @id: VPU instance id
+ * @drv: driver structure allocated by VPU side and shared to AP side for
+ * control and info share
+ */
+struct venc_vp8_vpu_inst {
+ wait_queue_head_t wq_hd;
+ int signaled;
+ int failure;
+ unsigned int id;
+ struct venc_vp8_vpu_drv *drv;
+};
+
+/*
+ * struct venc_vp8_inst - vp8 encoder AP driver instance
+ * @hw_base: vp8 encoder hardware register base
+ * @work_bufs: working buffer
+ * @work_buf_allocated: working buffer allocated flag
+ * @frm_cnt: encoded frame count, it's used for I-frame judgement and
+ * reset when force intra cmd received.
+ * @ts_mode: temporal scalability mode (0: disable, 1: enable)
+ * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps.
+ * @vpu_inst: VPU instance to exchange information between AP and VPU
+ * @ctx: context for v4l2 layer integration
+ * @dev: device for v4l2 layer integration
+ */
+struct venc_vp8_inst {
+ void __iomem *hw_base;
+ struct mtk_vcodec_mem work_bufs[VENC_VP8_VPU_WORK_BUF_MAX];
+ bool work_buf_allocated;
+ unsigned int frm_cnt;
+ unsigned int ts_mode;
+ struct venc_vp8_vpu_inst vpu_inst;
+ void *ctx;
+ struct platform_device *dev;
+};
+
+struct venc_common_if *get_vp8_enc_comm_if(void);
+
+#endif
diff --git a/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c
new file mode 100644
index 0000000..796561d
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ * PoChun Lin <pochun.lin@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 "mtk_vpu.h"
+
+#include "venc_vp8_if.h"
+#include "venc_vp8_vpu.h"
+#include "venc_ipi_msg.h"
+
+#define VENC_VP8_WAIT_VPU_TIMEOUT_MS (2000)
+
+static void handle_vp8_enc_init_msg(struct venc_vp8_inst *inst, void *data)
+{
+ struct venc_vpu_ipi_msg_init *msg = data;
+
+ inst->vpu_inst.id = msg->inst_id;
+ inst->vpu_inst.drv = (struct venc_vp8_vpu_drv *)
+ vpu_mapping_dm_addr(inst->dev,
+ msg->inst_id);
+}
+
+static void vp8_enc_vpu_ipi_handler(void *data, unsigned int len, void *priv)
+{
+ struct venc_vpu_ipi_msg_common *msg = data;
+ struct venc_vp8_inst *inst = (struct venc_vp8_inst *)msg->venc_inst;
+
+ mtk_vcodec_debug(inst, "->msg_id=%x status=%d",
+ msg->msg_id, msg->status);
+
+ switch (msg->msg_id) {
+ case VPU_IPIMSG_VP8_ENC_INIT_DONE:
+ handle_vp8_enc_init_msg(inst, data);
+ break;
+ case VPU_IPIMSG_VP8_ENC_SET_PARAM_DONE:
+ case VPU_IPIMSG_VP8_ENC_ENCODE_DONE:
+ case VPU_IPIMSG_VP8_ENC_DEINIT_DONE:
+ break;
+ default:
+ mtk_vcodec_err(inst, "unknown msg id=%x", msg->msg_id);
+ break;
+ }
+
+ inst->vpu_inst.signaled = 1;
+ inst->vpu_inst.failure = (msg->status != VENC_IPI_MSG_STATUS_OK);
+
+ mtk_vcodec_debug_leave(inst);
+}
+
+static int vp8_enc_vpu_send_msg(struct venc_vp8_inst *inst, void *msg,
+ int len, int wait_ack)
+{
+ int status;
+
+ mtk_vcodec_debug_enter(inst);
+ status = vpu_ipi_send(inst->dev, IPI_VENC_VP8, (void *)msg, len);
+ if (status) {
+ mtk_vcodec_err(inst,
+ "vpu_ipi_send msg_id=%x len=%d failed status=%d",
+ *(unsigned int *)msg, len, status);
+ return -EINVAL;
+ }
+ mtk_vcodec_debug_leave(inst);
+ return 0;
+}
+
+int vp8_enc_vpu_init(struct venc_vp8_inst *inst)
+{
+ int status;
+ struct venc_ap_ipi_msg_init out;
+
+ mtk_vcodec_debug_enter(inst);
+ init_waitqueue_head(&inst->vpu_inst.wq_hd);
+ inst->vpu_inst.signaled = 0;
+ inst->vpu_inst.failure = 0;
+
+ status = vpu_ipi_register(inst->dev, IPI_VENC_VP8,
+ vp8_enc_vpu_ipi_handler,
+ "vp8_enc", NULL);
+ if (status) {
+ mtk_vcodec_err(inst,
+ "vpu_ipi_register failed status=%d", status);
+ return -EINVAL;
+ }
+
+ out.msg_id = AP_IPIMSG_VP8_ENC_INIT;
+ out.venc_inst = (unsigned long)inst;
+ if (vp8_enc_vpu_send_msg(inst, &out, sizeof(out), 1) ||
+ inst->vpu_inst.failure) {
+ mtk_vcodec_err(inst, "failed");
+ return -EINVAL;
+ }
+
+ mtk_vcodec_debug_leave(inst);
+ return 0;
+}
+
+int vp8_enc_vpu_set_param(struct venc_vp8_inst *inst, unsigned int id,
+ void *param)
+{
+ struct venc_ap_ipi_msg_set_param out;
+
+ mtk_vcodec_debug_enter(inst);
+ out.msg_id = AP_IPIMSG_VP8_ENC_SET_PARAM;
+ out.inst_id = inst->vpu_inst.id;
+ out.param_id = id;
+ switch (id) {
+ case VENC_SET_PARAM_ENC: {
+ struct venc_enc_prm *enc_param = (struct venc_enc_prm *)param;
+
+ inst->vpu_inst.drv->config.input_fourcc =
+ enc_param->input_fourcc;
+ inst->vpu_inst.drv->config.bitrate = enc_param->bitrate;
+ inst->vpu_inst.drv->config.pic_w = enc_param->width;
+ inst->vpu_inst.drv->config.pic_h = enc_param->height;
+ inst->vpu_inst.drv->config.buf_w = enc_param->buf_width;
+ inst->vpu_inst.drv->config.buf_h = enc_param->buf_height;
+ inst->vpu_inst.drv->config.intra_period =
+ enc_param->intra_period;
+ inst->vpu_inst.drv->config.framerate = enc_param->frm_rate;
+ inst->vpu_inst.drv->config.ts_mode = inst->ts_mode;
+ out.data_item = 0;
+ break;
+ }
+ case VENC_SET_PARAM_FORCE_INTRA:
+ out.data_item = 0;
+ break;
+ case VENC_SET_PARAM_ADJUST_BITRATE:
+ out.data_item = 1;
+ out.data[0] = *(unsigned int *)param;
+ break;
+ case VENC_SET_PARAM_ADJUST_FRAMERATE:
+ out.data_item = 1;
+ out.data[0] = *(unsigned int *)param;
+ break;
+ case VENC_SET_PARAM_I_FRAME_INTERVAL:
+ out.data_item = 1;
+ out.data[0] = *(unsigned int *)param;
+ break;
+ }
+ if (vp8_enc_vpu_send_msg(inst, &out, sizeof(out), 1) ||
+ inst->vpu_inst.failure) {
+ mtk_vcodec_err(inst, "failed");
+ return -EINVAL;
+ }
+
+ mtk_vcodec_debug_leave(inst);
+ return 0;
+}
+
+int vp8_enc_vpu_encode(struct venc_vp8_inst *inst,
+ struct venc_frm_buf *frm_buf,
+ struct mtk_vcodec_mem *bs_buf)
+{
+ struct venc_ap_ipi_msg_enc out;
+
+ mtk_vcodec_debug_enter(inst);
+ out.msg_id = AP_IPIMSG_VP8_ENC_ENCODE;
+ out.inst_id = inst->vpu_inst.id;
+ if (frm_buf) {
+ if ((frm_buf->fb_addr.dma_addr % 16 == 0) &&
+ (frm_buf->fb_addr1.dma_addr % 16 == 0) &&
+ (frm_buf->fb_addr2.dma_addr % 16 == 0)) {
+ out.input_addr[0] = frm_buf->fb_addr.dma_addr;
+ out.input_addr[1] = frm_buf->fb_addr1.dma_addr;
+ out.input_addr[2] = frm_buf->fb_addr2.dma_addr;
+ } else {
+ mtk_vcodec_err(inst, "dma_addr not align to 16");
+ return -EINVAL;
+ }
+ } else {
+ out.input_addr[0] = 0;
+ out.input_addr[1] = 0;
+ out.input_addr[2] = 0;
+ }
+ if (bs_buf) {
+ out.bs_addr = bs_buf->dma_addr;
+ out.bs_size = bs_buf->size;
+ } else {
+ out.bs_addr = 0;
+ out.bs_size = 0;
+ }
+
+ if (vp8_enc_vpu_send_msg(inst, &out, sizeof(out), 1) ||
+ inst->vpu_inst.failure) {
+ mtk_vcodec_err(inst, "failed");
+ return -EINVAL;
+ }
+
+ mtk_vcodec_debug_leave(inst);
+ return 0;
+}
+
+int vp8_enc_vpu_deinit(struct venc_vp8_inst *inst)
+{
+ struct venc_ap_ipi_msg_deinit out;
+
+ mtk_vcodec_debug_enter(inst);
+ out.msg_id = AP_IPIMSG_VP8_ENC_DEINIT;
+ out.inst_id = inst->vpu_inst.id;
+ if (vp8_enc_vpu_send_msg(inst, &out, sizeof(out), 1) ||
+ inst->vpu_inst.failure) {
+ mtk_vcodec_err(inst, "failed");
+ return -EINVAL;
+ }
+
+ mtk_vcodec_debug_leave(inst);
+ return 0;
+}
diff --git a/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.h b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.h
new file mode 100644
index 0000000..d7d79a8
--- /dev/null
+++ b/drivers/media/platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ * PoChun Lin <pochun.lin@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 _VENC_VP8_VPU_H_
+#define _VENC_VP8_VPU_H_
+
+int vp8_enc_vpu_init(struct venc_vp8_inst *inst);
+int vp8_enc_vpu_set_param(struct venc_vp8_inst *inst, unsigned int id,
+ void *param);
+int vp8_enc_vpu_encode(struct venc_vp8_inst *inst,
+ struct venc_frm_buf *frm_buf,
+ struct mtk_vcodec_mem *bs_buf);
+int vp8_enc_vpu_deinit(struct venc_vp8_inst *inst);
+
+#endif
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Tiffany Lin <tiffany.lin@mediatek.com> |
|---|---|
| Date | 2015-12-11 11:10 +0100 |
| Subject | [PATCH v2 3/8] [media] VPU: mediatek: support Mediatek VPU |
| Message-ID | <qEsOd-2Os-3@gated-at.bofh.it> |
| In reply to | #1289376 |
The VPU driver for hw video codec embedded in Mediatek's MT8173 SOCs.
It is able to handle video decoding/encoding of in a range of formats.
The driver provides with VPU firmware download, memory management and
the communication interface between CPU and VPU.
For VPU initialization, it will create virtual memory for CPU access and
IOMMU address for vcodec hw device access. When a decode/encode instance
opens a device node, vpu driver will download vpu firmware to the device.
A decode/encode instant will decode/encode a frame using VPU
interface to interrupt vpu to handle decoding/encoding jobs.
Signed-off-by: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
drivers/media/platform/Kconfig | 10 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/mtk-vpu/Makefile | 1 +
drivers/media/platform/mtk-vpu/mtk_vpu.c | 859 ++++++++++++++++++++++++++++++
drivers/media/platform/mtk-vpu/mtk_vpu.h | 149 ++++++
5 files changed, 1021 insertions(+)
create mode 100644 drivers/media/platform/mtk-vpu/Makefile
create mode 100644 drivers/media/platform/mtk-vpu/mtk_vpu.c
create mode 100644 drivers/media/platform/mtk-vpu/mtk_vpu.h
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ccbc974..f3776f5 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -148,6 +148,16 @@ config VIDEO_CODA
Coda is a range of video codec IPs that supports
H.264, MPEG-4, and other video formats.
+config VIDEO_MEDIATEK_VPU
+ tristate "Mediatek Video Processor Unit"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ ---help---
+ This driver provides downloading VPU firmware and
+ communicating with VPU. This driver for hw video
+ codec embedded in new Mediatek's SOCs. It is able
+ to handle video decoding/encoding in a range of formats.
+
config VIDEO_MEM2MEM_DEINTERLACE
tristate "Deinterlace support"
depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index efa0295..e5b19c6 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -55,3 +55,5 @@ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/
obj-$(CONFIG_VIDEO_XILINX) += xilinx/
ccflags-y += -I$(srctree)/drivers/media/i2c
+
+obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
diff --git a/drivers/media/platform/mtk-vpu/Makefile b/drivers/media/platform/mtk-vpu/Makefile
new file mode 100644
index 0000000..d890a66
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/Makefile
@@ -0,0 +1 @@
+obj-y += mtk_vpu.o
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c
new file mode 100644
index 0000000..8994f73
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -0,0 +1,859 @@
+/*
+* Copyright (c) 2015 MediaTek Inc.
+* Author: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+
+#include "mtk_vpu.h"
+
+/**
+ * VPU (video processor unit) is a tiny processor controlling video hardware
+ * related to video codec, scaling and color format converting.
+ * VPU interfaces with other blocks by share memory and interrupt.
+ **/
+
+#define INIT_TIMEOUT_MS 2000U
+#define IPI_TIMEOUT_MS 2000U
+#define VPU_FW_VER_LEN 16
+
+/* maximum program/data TCM (Tightly-Coupled Memory) size */
+#define VPU_PTCM_SIZE (96 * SZ_1K)
+#define VPU_DTCM_SIZE (32 * SZ_1K)
+/* the offset to get data tcm address */
+#define VPU_DTCM_OFFSET 0x18000UL
+/* daynamic allocated maximum extended memory size */
+#define VPU_EXT_P_SIZE SZ_1M
+#define VPU_EXT_D_SIZE SZ_4M
+/* maximum binary firmware size */
+#define VPU_P_FW_SIZE (VPU_PTCM_SIZE + VPU_EXT_P_SIZE)
+#define VPU_D_FW_SIZE (VPU_DTCM_SIZE + VPU_EXT_D_SIZE)
+/* the size of share buffer between Host and VPU */
+#define SHARE_BUF_SIZE 48
+
+/* binary firmware name */
+#define VPU_P_FW "vpu_p.bin"
+#define VPU_D_FW "vpu_d.bin"
+
+#define VPU_RESET 0x0
+#define VPU_TCM_CFG 0x0008
+#define VPU_PMEM_EXT0_ADDR 0x000C
+#define VPU_PMEM_EXT1_ADDR 0x0010
+#define VPU_TO_HOST 0x001C
+#define VPU_DMEM_EXT0_ADDR 0x0014
+#define VPU_DMEM_EXT1_ADDR 0x0018
+#define HOST_TO_VPU 0x0024
+#define VPU_PC_REG 0x0060
+#define VPU_WDT_REG 0x0084
+
+/* vpu inter-processor communication interrupt */
+#define VPU_IPC_INT BIT(8)
+
+/**
+ * enum vpu_fw_type - VPU firmware type
+ *
+ * @P_FW: program firmware
+ * @D_FW: data firmware
+ *
+ */
+enum vpu_fw_type {
+ P_FW,
+ D_FW,
+};
+
+/**
+ * struct vpu_mem - VPU extended program/data memory information
+ *
+ * @va: the kernel virtual memory address of VPU extended memory
+ * @iova: the iova memory address of VPU extended memory
+ *
+ */
+struct vpu_mem {
+ void *va;
+ dma_addr_t iova;
+};
+
+/**
+ * struct vpu_regs - VPU TCM and configuration registers
+ *
+ * @tcm: the register for VPU Tightly-Coupled Memory
+ * @cfg: the register for VPU configuration
+ * @irq: the irq number for VPU interrupt
+ */
+struct vpu_regs {
+ void __iomem *tcm;
+ void __iomem *cfg;
+ int irq;
+};
+
+/**
+ * struct vpu_run - VPU initialization status
+ *
+ * @signaled: the signal of vpu initialization completed
+ * @fw_ver: VPU firmware version
+ * @enc_capability: encoder capability
+ * @wq: wait queue for VPU initialization status
+ */
+struct vpu_run {
+ u32 signaled;
+ char fw_ver[VPU_FW_VER_LEN];
+ unsigned int enc_capability;
+ wait_queue_head_t wq;
+};
+
+/**
+ * struct vpu_ipi_desc - VPU IPI descriptor
+ *
+ * @handler: IPI handler
+ * @name: the name of IPI handler
+ * @priv: the private data of IPI handler
+ */
+struct vpu_ipi_desc {
+ ipi_handler_t handler;
+ const char *name;
+ void *priv;
+};
+
+/**
+ * struct share_obj - DTCM (Data Tightly-Coupled Memory) buffer shared with
+ * AP and VPU
+ *
+ * @id: IPI id
+ * @len: share buffer length
+ * @share_buf: share buffer data
+ */
+struct share_obj {
+ int32_t id;
+ uint32_t len;
+ unsigned char share_buf[SHARE_BUF_SIZE];
+};
+
+/**
+ * struct mtk_vpu - vpu driver data
+ * @extmem: VPU extended memory information
+ * @reg: VPU TCM and configuration registers
+ * @run: VPU initialization status
+ * @ipi_desc: VPU IPI descriptor
+ * @recv_buf: VPU DTCM share buffer for receiving. The
+ * receive buffer is only accessed in interrupt context.
+ * @send_buf: VPU DTCM share buffer for sending
+ * @dev: VPU struct device
+ * @clk: VPU clock on/off
+ * @vpu_mutex: protect mtk_vpu (except recv_buf) and ensure only
+ * one client to use VPU service at a time. For example,
+ * suppose a client is using VPU to decode VP8.
+ * If the other client wants to encode VP8,
+ * it has to wait until VP8 decode completes.
+ * @wdt_refcnt WDT reference count to make sure the watchdog can be
+ * disabled if no other client is using VPU service
+ * @ipi_ack_signaled: The ACKs for registered IPI function sending
+ * interrupt to VPU
+ * @ack_wq: The wait queue for each codec and mdp. When sleeping
+ * processes wake up, they will check the condition
+ * "ipi_ack_signaled" to run the corresponding action or
+ * go back to sleep.
+ *
+ */
+struct mtk_vpu {
+ struct vpu_mem extmem[2];
+ struct vpu_regs reg;
+ struct vpu_run run;
+ struct vpu_ipi_desc ipi_desc[IPI_MAX];
+ struct share_obj *recv_buf;
+ struct share_obj *send_buf;
+ struct device *dev;
+ struct clk *clk;
+ struct mutex vpu_mutex; /* for protecting vpu data data structure */
+ atomic_t wdt_refcnt;
+ wait_queue_head_t ack_wq;
+ bool ipi_id_ack[IPI_MAX];
+};
+
+static inline void vpu_cfg_writel(struct mtk_vpu *vpu, u32 val, u32 offset)
+{
+ writel(val, vpu->reg.cfg + offset);
+}
+
+static inline u32 vpu_cfg_readl(struct mtk_vpu *vpu, u32 offset)
+{
+ return readl(vpu->reg.cfg + offset);
+}
+
+static inline bool vpu_running(struct mtk_vpu *vpu)
+{
+ return vpu_cfg_readl(vpu, VPU_RESET) & BIT(0);
+}
+
+void vpu_clock_disable(struct mtk_vpu *vpu)
+{
+ /* Disable VPU watchdog */
+ if (atomic_dec_and_test(&vpu->wdt_refcnt))
+ vpu_cfg_writel(vpu,
+ vpu_cfg_readl(vpu, VPU_WDT_REG) & ~(1L<<31),
+ VPU_WDT_REG);
+
+ clk_disable(vpu->clk);
+}
+
+int vpu_clock_enable(struct mtk_vpu *vpu)
+{
+ int ret;
+
+ ret = clk_enable(vpu->clk);
+ if (ret)
+ return ret;
+ /* Enable VPU watchdog */
+ if (!atomic_read(&vpu->wdt_refcnt))
+ vpu_cfg_writel(vpu,
+ vpu_cfg_readl(vpu, VPU_WDT_REG) | (1L << 31),
+ VPU_WDT_REG);
+
+ atomic_inc(&vpu->wdt_refcnt);
+
+ return ret;
+}
+
+int vpu_ipi_register(struct platform_device *pdev,
+ enum ipi_id id, ipi_handler_t handler,
+ const char *name, void *priv)
+{
+ struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+ struct vpu_ipi_desc *ipi_desc;
+
+ if (!vpu) {
+ dev_err(&pdev->dev, "vpu device in not ready\n");
+ return -EPROBE_DEFER;
+ }
+
+ if (id < IPI_MAX && handler != NULL) {
+ ipi_desc = vpu->ipi_desc;
+ ipi_desc[id].name = name;
+ ipi_desc[id].handler = handler;
+ ipi_desc[id].priv = priv;
+ return 0;
+ }
+
+ dev_err(&pdev->dev, "register vpu ipi with invalid arguments\n");
+ return -EINVAL;
+}
+
+int vpu_ipi_send(struct platform_device *pdev,
+ enum ipi_id id, void *buf,
+ unsigned int len)
+{
+ struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+ struct share_obj *send_obj = vpu->send_buf;
+ unsigned long timeout;
+ int ret = 0;
+
+ if (id <= IPI_VPU_INIT || id >= IPI_MAX ||
+ len > sizeof(send_obj->share_buf) || buf == NULL) {
+ dev_err(vpu->dev, "failed to send ipi message\n");
+ return -EINVAL;
+ }
+
+ ret = vpu_clock_enable(vpu);
+ if (ret) {
+ dev_err(vpu->dev, "failed to enable vpu clock\n");
+ return ret;
+ }
+ if (!vpu_running(vpu)) {
+ dev_err(vpu->dev, "vpu_ipi_send: VPU is not running\n");
+ goto clock_disable;
+ }
+
+ mutex_lock(&vpu->vpu_mutex);
+
+ /* Wait until VPU receives the last command */
+ timeout = jiffies + msecs_to_jiffies(IPI_TIMEOUT_MS);
+ do {
+ if (time_after(jiffies, timeout)) {
+ dev_err(vpu->dev, "vpu_ipi_send: IPI timeout!\n");
+ ret = -EIO;
+ goto mut_unlock;
+ }
+ } while (vpu_cfg_readl(vpu, HOST_TO_VPU));
+
+ memcpy((void *)send_obj->share_buf, buf, len);
+ send_obj->len = len;
+ send_obj->id = id;
+
+ vpu->ipi_id_ack[id] = false;
+ /* send the command to VPU */
+ vpu_cfg_writel(vpu, 0x1, HOST_TO_VPU);
+
+ mutex_unlock(&vpu->vpu_mutex);
+
+ /* wait for VPU's ACK */
+ timeout = msecs_to_jiffies(IPI_TIMEOUT_MS);
+ ret = wait_event_interruptible_timeout(vpu->ack_wq,
+ vpu->ipi_id_ack[id], timeout);
+ vpu->ipi_id_ack[id] = false;
+ if (0 == ret) {
+ dev_err(vpu->dev, "vpu ipi %d ack time out !", id);
+ ret = -EIO;
+ goto clock_disable;
+ } else if (-ERESTARTSYS == ret) {
+ dev_err(vpu->dev, "vpu ipi %d ack wait interrupted by a signal",
+ id);
+ ret = -ERESTARTSYS;
+ goto clock_disable;
+ }
+ vpu_clock_disable(vpu);
+
+ return 0;
+
+mut_unlock:
+ vpu->ipi_id_ack[id] = false;
+ mutex_unlock(&vpu->vpu_mutex);
+clock_disable:
+ vpu_clock_disable(vpu);
+
+ return ret;
+}
+
+unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev)
+{
+ struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+
+ return vpu->run.enc_capability;
+}
+
+void *vpu_mapping_dm_addr(struct platform_device *pdev,
+ u32 dtcm_dmem_addr)
+{
+ struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+
+ if (!dtcm_dmem_addr ||
+ (dtcm_dmem_addr > (VPU_DTCM_SIZE + VPU_EXT_D_SIZE))) {
+ dev_err(vpu->dev, "invalid virtual data memory address\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (dtcm_dmem_addr < VPU_DTCM_SIZE)
+ return dtcm_dmem_addr + vpu->reg.tcm + VPU_DTCM_OFFSET;
+
+ return vpu->extmem[D_FW].va + (dtcm_dmem_addr - VPU_DTCM_SIZE);
+}
+
+dma_addr_t vpu_mapping_iommu_dm_addr(struct platform_device *pdev,
+ u32 dmem_addr)
+{
+ struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+
+ if (!dmem_addr ||
+ (dmem_addr < VPU_DTCM_SIZE) ||
+ (dmem_addr > (VPU_DTCM_SIZE + VPU_EXT_D_SIZE))) {
+ dev_err(vpu->dev, "invalid IOMMU data memory address\n");
+ return -EINVAL;
+ }
+
+ return vpu->extmem[D_FW].iova + (dmem_addr - VPU_DTCM_SIZE);
+}
+
+struct platform_device *vpu_get_plat_device(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *vpu_node;
+ struct platform_device *vpu_pdev;
+
+ vpu_node = of_parse_phandle(dev->of_node, "vpu", 0);
+ if (!vpu_node) {
+ dev_err(dev, "can't get vpu node\n");
+ return NULL;
+ }
+
+ vpu_pdev = of_find_device_by_node(vpu_node);
+ if (WARN_ON(!vpu_pdev)) {
+ dev_err(dev, "vpu pdev failed\n");
+ of_node_put(vpu_node);
+ return NULL;
+ }
+
+ return vpu_pdev;
+}
+
+/* load vpu program/data memory */
+static int load_requested_vpu(struct mtk_vpu *vpu,
+ const struct firmware *vpu_fw,
+ u8 fw_type)
+{
+ size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE;
+ size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE;
+ char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW;
+ size_t dl_size = 0;
+ size_t extra_fw_size = 0;
+ void *dest;
+ int ret;
+
+ ret = request_firmware(&vpu_fw, fw_name, vpu->dev);
+ if (ret < 0) {
+ dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret);
+ return ret;
+ }
+ dl_size = vpu_fw->size;
+ if (dl_size > fw_size) {
+ dev_err(vpu->dev, "fw %s size %zu is abnormal\n", fw_name,
+ dl_size);
+ release_firmware(vpu_fw);
+ return -EFBIG;
+ }
+ dev_dbg(vpu->dev, "Downloaded fw %s size: %zu.\n",
+ fw_name,
+ dl_size);
+ /* reset VPU */
+ vpu_cfg_writel(vpu, 0x0, VPU_RESET);
+
+ /* handle extended firmware size */
+ if (dl_size > tcm_size) {
+ dev_dbg(vpu->dev, "fw size %lx > limited fw size %lx\n",
+ dl_size, tcm_size);
+ extra_fw_size = dl_size - tcm_size;
+ dev_dbg(vpu->dev, "extra_fw_size %lx\n", extra_fw_size);
+ dl_size = tcm_size;
+ }
+ dest = vpu->reg.tcm;
+ if (fw_type == D_FW)
+ dest += VPU_DTCM_OFFSET;
+ memcpy(dest, vpu_fw->data, dl_size);
+ /* download to extended memory if need */
+ if (extra_fw_size > 0) {
+ dest = vpu->extmem[fw_type].va;
+ dev_dbg(vpu->dev, "download extended memory type %x\n",
+ fw_type);
+ memcpy(dest, vpu_fw->data + tcm_size, extra_fw_size);
+ }
+
+ release_firmware(vpu_fw);
+
+ return 0;
+}
+
+int vpu_load_firmware(struct platform_device *pdev)
+{
+ struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ struct vpu_run *run = &vpu->run;
+ const struct firmware *vpu_fw;
+ int ret;
+
+ if (!pdev) {
+ dev_err(dev, "VPU platform device is invalid\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&vpu->vpu_mutex);
+
+ ret = vpu_clock_enable(vpu);
+ if (ret) {
+ dev_err(dev, "enable clock failed %d\n", ret);
+ goto OUT_LOAD_FW;
+ }
+
+ if (vpu_running(vpu)) {
+ vpu_clock_disable(vpu);
+ mutex_unlock(&vpu->vpu_mutex);
+ dev_warn(dev, "vpu is running already\n");
+ return 0;
+ }
+
+ run->signaled = false;
+ dev_dbg(vpu->dev, "firmware request\n");
+ /* Downloading program firmware to device*/
+ ret = load_requested_vpu(vpu, vpu_fw, P_FW);
+ if (ret < 0) {
+ dev_err(dev, "Failed to request %s, %d\n", VPU_P_FW, ret);
+ goto OUT_LOAD_FW;
+ }
+
+ /* Downloading data firmware to device */
+ ret = load_requested_vpu(vpu, vpu_fw, D_FW);
+ if (ret < 0) {
+ dev_err(dev, "Failed to request %s, %d\n", VPU_D_FW, ret);
+ goto OUT_LOAD_FW;
+ }
+
+ /* boot up vpu */
+ vpu_cfg_writel(vpu, 0x1, VPU_RESET);
+
+ ret = wait_event_interruptible_timeout(run->wq,
+ run->signaled,
+ msecs_to_jiffies(INIT_TIMEOUT_MS)
+ );
+ if (0 == ret) {
+ ret = -ETIME;
+ dev_err(dev, "wait vpu initialization timout!\n");
+ goto OUT_LOAD_FW;
+ } else if (-ERESTARTSYS == ret) {
+ dev_err(dev, "wait vpu interrupted by a signal!\n");
+ goto OUT_LOAD_FW;
+ }
+
+ ret = 0;
+ dev_info(dev, "vpu is ready. Fw version %s\n", run->fw_ver);
+
+OUT_LOAD_FW:
+ vpu_clock_disable(vpu);
+ mutex_unlock(&vpu->vpu_mutex);
+
+ return ret;
+}
+
+static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv)
+{
+ struct mtk_vpu *vpu = (struct mtk_vpu *)priv;
+ struct vpu_run *run = (struct vpu_run *)data;
+
+ vpu->run.signaled = run->signaled;
+ strncpy(vpu->run.fw_ver, run->fw_ver, VPU_FW_VER_LEN);
+ vpu->run.enc_capability = run->enc_capability;
+ wake_up_interruptible(&vpu->run.wq);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int vpu_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t vpu_debug_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[256];
+ unsigned int len;
+ unsigned int running, pc, vpu_to_host, host_to_vpu, wdt;
+ int ret;
+ struct device *dev = file->private_data;
+ struct mtk_vpu *vpu = dev_get_drvdata(dev);
+
+ ret = vpu_clock_enable(vpu);
+ if (ret) {
+ dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret);
+ return 0;
+ }
+
+ /* vpu register status */
+ running = vpu_running(vpu);
+ pc = vpu_cfg_readl(vpu, VPU_PC_REG);
+ wdt = vpu_cfg_readl(vpu, VPU_WDT_REG);
+ host_to_vpu = vpu_cfg_readl(vpu, HOST_TO_VPU);
+ vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST);
+ vpu_clock_disable(vpu);
+
+ if (running) {
+ len = sprintf(buf, "VPU is running\n\n"
+ "FW Version: %s\n"
+ "PC: 0x%x\n"
+ "WDT: 0x%x\n"
+ "Host to VPU: 0x%x\n"
+ "VPU to Host: 0x%x\n",
+ vpu->run.fw_ver, pc, wdt,
+ host_to_vpu, vpu_to_host);
+ } else {
+ len = sprintf(buf, "VPU not running\n");
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations vpu_debug_fops = {
+ .open = vpu_debug_open,
+ .read = vpu_debug_read,
+};
+#endif /* CONFIG_DEBUG_FS */
+
+static void vpu_free_ext_mem(struct mtk_vpu *vpu, u8 fw_type)
+{
+ struct device *dev = vpu->dev;
+ size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE;
+ struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+
+ dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va,
+ vpu->extmem[fw_type].iova);
+
+ if (domain)
+ iommu_detach_device(domain, vpu->dev);
+}
+
+static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type)
+{
+ struct device *dev = vpu->dev;
+ struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+ phys_addr_t pa;
+ size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE;
+ u32 vpu_ext_mem0 = fw_type ? VPU_DMEM_EXT0_ADDR : VPU_PMEM_EXT0_ADDR;
+ u32 vpu_ext_mem1 = fw_type ? VPU_DMEM_EXT1_ADDR : VPU_PMEM_EXT1_ADDR;
+
+ vpu->extmem[fw_type].va = dma_alloc_coherent(dev,
+ fw_ext_size,
+ &vpu->extmem[fw_type].iova,
+ GFP_KERNEL);
+ if (vpu->extmem[fw_type].va == NULL) {
+ dev_err(dev, "Failed to allocate the extended program memory\n");
+ return PTR_ERR(vpu->extmem[fw_type].va);
+ }
+
+ pa = iommu_iova_to_phys(domain, vpu->extmem[fw_type].iova);
+ /* Disable extend0. Enable extend1 */
+ vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0);
+ vpu_cfg_writel(vpu, (pa & 0xFFFFF000), vpu_ext_mem1);
+
+ dev_info(dev, "Program extend memory phy=0x%llx virt=0x%p iova=0x%llx\n",
+ (unsigned long long)pa,
+ vpu->extmem[fw_type].va,
+ (unsigned long long)vpu->extmem[fw_type].iova);
+
+ return 0;
+}
+
+static void vpu_ipi_handler(struct mtk_vpu *vpu)
+{
+ struct share_obj *rcv_obj = vpu->recv_buf;
+ struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc;
+
+ if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) {
+ ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf,
+ rcv_obj->len,
+ ipi_desc[rcv_obj->id].priv);
+ if (rcv_obj->id > IPI_VPU_INIT) {
+ vpu->ipi_id_ack[rcv_obj->id] = true;
+ wake_up_interruptible(&vpu->ack_wq);
+ }
+ } else {
+ dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id);
+ }
+}
+
+static int vpu_ipi_init(struct mtk_vpu *vpu)
+{
+ /* Disable VPU to host interrupt */
+ vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST);
+
+ /* shared buffer initialization */
+ vpu->recv_buf = (struct share_obj *)(vpu->reg.tcm + VPU_DTCM_OFFSET);
+ vpu->send_buf = vpu->recv_buf + 1;
+ memset(vpu->recv_buf, 0, sizeof(struct share_obj));
+ memset(vpu->send_buf, 0, sizeof(struct share_obj));
+ mutex_init(&vpu->vpu_mutex);
+
+ return 0;
+}
+
+static irqreturn_t vpu_irq_handler(int irq, void *priv)
+{
+ struct mtk_vpu *vpu = priv;
+ uint32_t vpu_to_host;
+
+ /*
+ * Clock should have been enabled already.
+ * Enable again in case vpu_ipi_send times out
+ * and has disabled the clock.
+ */
+ clk_enable(vpu->clk);
+ vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST);
+ if (vpu_to_host & VPU_IPC_INT)
+ vpu_ipi_handler(vpu);
+ else
+ /*
+ * TO DO:
+ * Reset video hardware
+ * Reset and redownload vpu firmware
+ */
+ dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host);
+
+ /* VPU won't send another interrupt until we set VPU_TO_HOST to 0. */
+ vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST);
+ clk_disable(vpu->clk);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *vpu_debugfs;
+#endif
+static int mtk_vpu_probe(struct platform_device *pdev)
+{
+ struct mtk_vpu *vpu;
+ struct device *dev;
+ struct resource *res;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "initialization\n");
+
+ dev = &pdev->dev;
+ vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL);
+ if (!vpu)
+ return -ENOMEM;
+
+ vpu->dev = &pdev->dev;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcm");
+ vpu->reg.tcm = devm_ioremap_resource(dev, res);
+ if (IS_ERR(vpu->reg.tcm)) {
+ dev_err(dev, "devm_ioremap_resource vpu tcm failed.\n");
+ return PTR_ERR(vpu->reg.tcm);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_reg");
+ vpu->reg.cfg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(vpu->reg.cfg)) {
+ dev_err(dev, "devm_ioremap_resource vpu cfg failed.\n");
+ return PTR_ERR(vpu->reg.cfg);
+ }
+
+ /* Get VPU clock */
+ vpu->clk = devm_clk_get(dev, "main");
+ if (vpu->clk == NULL) {
+ dev_err(dev, "get vpu clock failed\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, vpu);
+
+ ret = clk_prepare(vpu->clk);
+ if (ret) {
+ dev_err(dev, "prepare vpu clock failed\n");
+ return ret;
+ }
+ ret = vpu_clock_enable(vpu);
+ if (ret) {
+ dev_err(dev, "enable vpu clock failed\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "vpu ipi init\n");
+ ret = vpu_ipi_init(vpu);
+ if (ret) {
+ dev_err(dev, "Failed to init ipi\n");
+ goto disable_vpu_clk;
+ }
+
+ platform_set_drvdata(pdev, vpu);
+
+ /* register vpu initialization IPI */
+ ret = vpu_ipi_register(pdev, IPI_VPU_INIT, vpu_init_ipi_handler,
+ "vpu_init", vpu);
+ if (ret) {
+ dev_err(dev, "Failed to register IPI_VPU_INIT\n");
+ goto vpu_mutex_destroy;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev,
+ &vpu_debug_fops);
+ if (!vpu_debugfs) {
+ ret = -ENOMEM;
+ goto cleanup_ipi;
+ }
+#endif
+
+ /* Set PTCM to 96K and DTCM to 32K */
+ vpu_cfg_writel(vpu, 0x2, VPU_TCM_CFG);
+
+ ret = vpu_alloc_ext_mem(vpu, P_FW);
+ if (ret) {
+ dev_err(dev, "Allocate PM failed\n");
+ goto remove_debugfs;
+ }
+
+ ret = vpu_alloc_ext_mem(vpu, D_FW);
+ if (ret) {
+ dev_err(dev, "Allocate DM failed\n");
+ goto free_p_mem;
+ }
+
+ init_waitqueue_head(&vpu->run.wq);
+ init_waitqueue_head(&vpu->ack_wq);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get IRQ resource failed.\n");
+ ret = -ENXIO;
+ goto free_d_mem;
+ }
+ vpu->reg.irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(dev, vpu->reg.irq, vpu_irq_handler, 0,
+ pdev->name, vpu);
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ goto free_d_mem;
+ }
+
+ vpu_clock_disable(vpu);
+ dev_dbg(dev, "initialization completed\n");
+
+ return 0;
+
+free_d_mem:
+ vpu_free_ext_mem(vpu, D_FW);
+free_p_mem:
+ vpu_free_ext_mem(vpu, P_FW);
+remove_debugfs:
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(vpu_debugfs);
+cleanup_ipi:
+#endif
+ memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc)*IPI_MAX);
+vpu_mutex_destroy:
+ mutex_destroy(&vpu->vpu_mutex);
+disable_vpu_clk:
+ vpu_clock_disable(vpu);
+
+ return ret;
+}
+
+static const struct of_device_id mtk_vpu_match[] = {
+ {
+ .compatible = "mediatek,mt8173-vpu",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_vpu_match);
+
+static int mtk_vpu_remove(struct platform_device *pdev)
+{
+ struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(vpu_debugfs);
+#endif
+ vpu_free_ext_mem(vpu, P_FW);
+ vpu_free_ext_mem(vpu, D_FW);
+ mutex_destroy(&vpu->vpu_mutex);
+ clk_unprepare(vpu->clk);
+
+ return 0;
+}
+
+static struct platform_driver mtk_vpu_driver = {
+ .probe = mtk_vpu_probe,
+ .remove = mtk_vpu_remove,
+ .driver = {
+ .name = "mtk_vpu",
+ .owner = THIS_MODULE,
+ .of_match_table = mtk_vpu_match,
+ },
+};
+
+module_platform_driver(mtk_vpu_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek Video Prosessor Unit driver");
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mtk-vpu/mtk_vpu.h
new file mode 100644
index 0000000..27024b3
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h
@@ -0,0 +1,149 @@
+/*
+* Copyright (c) 2015 MediaTek Inc.
+* Author: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*
+* 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 _MTK_VPU_H
+#define _MTK_VPU_H
+
+#include <linux/platform_device.h>
+
+/**
+ * VPU (video processor unit) is a tiny processor controlling video hardware
+ * related to video codec, scaling and color format converting.
+ * VPU interfaces with other blocks by share memory and interrupt.
+ **/
+
+typedef void (*ipi_handler_t) (void *data,
+ unsigned int len,
+ void *priv);
+
+/**
+ * enum ipi_id - the id of inter-processor interrupt
+ *
+ * @IPI_VPU_INIT: The interrupt from vpu is to notfiy kernel
+ VPU initialization completed.
+ IPI_VPU_INIT is sent from VPU when firmware is
+ loaded. AP doesn't need to send IPI_VPU_INIT
+ command to VPU.
+ For other IPI below, AP should send the request
+ to VPU to trigger the interrupt.
+ * @IPI_VENC_H264: The interrupt from vpu is to notify kernel to
+ handle H264 video encoder job, and vice versa.
+ * @IPI_VENC_VP8: The interrupt fro vpu is to notify kernel to
+ handle VP8 video encoder job,, and vice versa.
+ * @IPI_MAX: The maximum IPI number
+ */
+
+enum ipi_id {
+ IPI_VPU_INIT = 0,
+ IPI_VENC_H264,
+ IPI_VENC_VP8,
+ IPI_MAX,
+};
+
+/**
+ * vpu_ipi_register - register an ipi function
+ *
+ * @pdev: VPU platform device
+ * @id: IPI ID
+ * @handler: IPI handler
+ * @name: IPI name
+ * @priv: private data for IPI handler
+ *
+ * Register an ipi function to receive ipi interrupt from VPU.
+ *
+ * Return: Return 0 if ipi registers successfully, otherwise it is failed.
+ */
+int vpu_ipi_register(struct platform_device *pdev, enum ipi_id id,
+ ipi_handler_t handler, const char *name, void *priv);
+
+/**
+ * vpu_ipi_send - send data from AP to vpu.
+ *
+ * @pdev: VPU platform device
+ * @id: IPI ID
+ * @buf: the data buffer
+ * @len: the data buffer length
+ *
+ * This function is thread-safe. When this function returns,
+ * VPU has received the data and starts the processing.
+ * When the processing completes, IPI handler registered
+ * by vpu_ipi_register will be called in interrupt context.
+ *
+ * Return: Return 0 if sending data successfully, otherwise it is failed.
+ **/
+int vpu_ipi_send(struct platform_device *pdev,
+ enum ipi_id id, void *buf,
+ unsigned int len);
+
+/**
+ * vpu_get_plat_device - get VPU's platform device
+ *
+ * @pdev: the platform device of the module requesting VPU platform
+ * device for using VPU API.
+ *
+ * Return: Return NULL if it is failed.
+ * otherwise it is VPU's platform device
+ **/
+struct platform_device *vpu_get_plat_device(struct platform_device *pdev);
+
+/**
+ * vpu_get_venc_hw_capa - get video encoder hardware capability
+ *
+ * @pdev: VPU platform device
+ *
+ * Return: video encoder hardware capability
+ **/
+unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev);
+
+/**
+ * vpu_load_firmware - download VPU firmware and boot it
+ *
+ * @pdev: VPU platform device
+ *
+ * Return: Return 0 if downloading firmware successfully,
+ * otherwise it is failed
+ **/
+int vpu_load_firmware(struct platform_device *pdev);
+
+
+/**
+ * vpu_mapping_dm_addr - Mapping DTCM/DMEM to kernel virtual address
+ *
+ * @pdev: VPU platform device
+ * @dmem_addr: VPU's data memory address
+ *
+ * Mapping the VPU's DTCM (Data Tightly-Coupled Memory) /
+ * DMEM (Data Extended Memory) memory address to
+ * kernel virtual address.
+ *
+ * Return: Return ERR_PTR(-EINVAL) if mapping failed,
+ * otherwise the mapped kernel virtual address
+ **/
+void *vpu_mapping_dm_addr(struct platform_device *pdev,
+ u32 dtcm_dmem_addr);
+
+/**
+ * vpu_mapping_iommu_dm_addr - Mapping to iommu address
+ *
+ * @pdev: VPU platform device
+ * @dmem_addr: VPU's extended data memory address
+ *
+ * Mapping the VPU's extended data address to iommu address
+ *
+ * Return: Return ERR_PTR(-EINVAL) if mapping failed,
+ * otherwise the mapped iommu address
+ **/
+dma_addr_t vpu_mapping_iommu_dm_addr(struct platform_device *pdev,
+ u32 dmem_addr);
+#endif /* _MTK_VPU_H */
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web