[PATCH v4 0/8] Add MT8173 Video Encoder Driver and VPU Driver

STALE3810d

Revision v4 of 10 in this series.

18 messages, 6 authors, 2016-02-16 · open the first message on its own page

[PATCH v4 0/8] Add MT8173 Video Encoder Driver and VPU Driver

From: Tiffany Lin <tiffany.lin@mediatek.com>
Date: 2016-02-04 11:36:11

==============
 Introduction
==============

The purpose of this series is to add the driver for video codec hw embedded in the Mediatek's MT8173 SoCs.
Mediatek Video Codec is able to handle video encoding of in a range of formats.

This patch series 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[1] and MTK SMI[2] have not yet been merged, but we wanted to start discussion about the driver earlier so it could be merged sooner.

[1]https://patchwork.kernel.org/patch/8120921/
[2]https://patchwork.kernel.org/patch/8120941/


==================
 Device interface
==================

In principle the driver bases on v4l2 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).

==============================
 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 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 physical address for VPU 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 v4:
Vcodec Part
1. Remove MTK_ENCODE_PARAM_SKIP_FRAME support
2. Remove MTK_ENCODE_PARAM_FRAME_TYPE and change to use V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME[3]
3. Refine Encoder HW clock source
4. Refine debug log
5. Add watchdog support
6. With patch "media: v4l2-compat-ioctl32: fix missing length copy in put_v4l2_buffer32"[4], v4l2-compliance test passed[5] in v4.4-rc5 

VPU Part
1. These two patches were Acked-by: Rob Herring [off-list ref] in v3
   [PATCH v3 1/8] dt-bindings: Add a binding for Mediatek Video Processor
   [PATCH v3 3/8] arm64: dts: mediatek: Add node for Mediatek Video Processor Unit
   Because we were wrong about how the hardware works, there is no connection between VPU and IOMMU HW
   We remove VPU attaching to IOMMU
2. Support VPU running on 4GB DRAM system
3. Support VPU watchdog reset
4. Refine for coding style 

[3]https://patchwork.linuxtv.org/patch/32670/
[4] https://patchwork.linuxtv.org/patch/32631/
[5]localhost ~ # /usr/bin/v4l2-compliance -d /dev/video1
Driver Info:
        Driver name   : mtk-vcodec-en
        Card type     : platform:mt817
        Bus info      : platform:mt817
        Driver version: 4.4.0
        Capabilities  : 0x84204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format

Compliance test for device /dev/video1 (not using libv4l2):

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second video open: OK
        test VIDIOC_QUERYCAP: OK
        test VIDIOC_G/S_PRIORITY: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

        Control ioctls:
                test VIDIOC_QUERYCTRL/MENU: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 12 Private Controls: 0

        Format ioctls:
                test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
                test VIDIOC_G/S_PARM: OK (Not Supported)
                test VIDIOC_G_FBUF: OK (Not Supported)
                test VIDIOC_G_FMT: OK
                test VIDIOC_TRY_FMT: OK
                test VIDIOC_S_FMT: OK
                test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                warn: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(475): VIDIOC_CREATE_BUFS not supported
                warn: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(475): VIDIOC_CREATE_BUFS not supported
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
                test VIDIOC_EXPBUF: OK

Total: 38, Succeeded: 38, Failed: 0, Warnings: 2


Change in v3:
1.Refine code to pass v4l2-compliance test, now it still has 2 issues 2.Refine code according to latest MTK IOMMU patches[1] 3.Remove MFC51 specific CIDs and add MTK specific CIDs for for keyframe and
  skip I-frame
4.Refine code according to review comments

Below is the v1.6 version v4l2-compliance report for the mt8173 encoder driver.
Now there are still 2 test fail in v1.6.
For VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF, we directly use v4l2_m2m_ioctl_* functions, but it still fail. It pass in kernel 3.18 but fail in kernel 4.4.
We will try v1.8 in next version.
VIDIOC_EXPBUF is becuase we support all three memory types VB2_DMABUF, VB2_MMAP and VB2_USERPTR. VIDIOC_EXPBUF only allowed when only VB2_MMAP supported.
localhost ~ # /usr/bin/v4l2-compliance -d /dev/video1 Driver Info:
        Driver name   : mtk-vcodec-en
        Card type     : platform:mt817
        Bus info      : platform:mt817
        Driver version: 4.4.0
        Capabilities  : 0x84204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format

Compliance test for device /dev/video1 (not using libv4l2):

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second video open: OK
        test VIDIOC_QUERYCAP: OK
        test VIDIOC_G/S_PRIORITY: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

        Control ioctls:
                test VIDIOC_QUERYCTRL/MENU: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 11 Private Controls: 2

        Format ioctls:
                test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
                test VIDIOC_G/S_PARM: OK (Not Supported)
                test VIDIOC_G_FBUF: OK (Not Supported)
                test VIDIOC_G_FMT: OK
                test VIDIOC_TRY_FMT: OK
                test VIDIOC_S_FMT: OK
                test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(266): vp->length == 0
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(335): buf.check(Unqueued, i)
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(420): testQueryBuf(node, i, q.g_buffers())
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: FAIL
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(500): q.has_expbuf(node)
                test VIDIOC_EXPBUF: FAIL
Total: 38, Succeeded: 36, Failed: 2, Warnings: 0


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):
  [media] VPU: mediatek: support Mediatek VPU
  arm64: dts: mediatek: Add node for Mediatek Video Processor Unit
  dt-bindings: Add a binding for Mediatek Video Encoder
  [media] vcodec: mediatek: Add Mediatek VP8 Video Encoder Driver
  [media] vcodec: mediatek: Add Mediatek H264 Video Encoder Driver
  arm64: dts: mediatek: Add Video Encoder for MT8173

 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 +
 .../devicetree/bindings/media/mediatek-vpu.txt     |   31 +
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |   62 +
 drivers/media/platform/Kconfig                     |   20 +
 drivers/media/platform/Makefile                    |    4 +
 drivers/media/platform/mtk-vcodec/Makefile         |   11 +
 .../media/platform/mtk-vcodec/h264_enc/Makefile    |    6 +
 .../platform/mtk-vcodec/h264_enc/venc_h264_if.c    |  540 ++++++++
 .../platform/mtk-vcodec/h264_enc/venc_h264_if.h    |  165 +++
 .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.c   |  309 +++++
 .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.h   |   30 +
 drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h |  388 ++++++
 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 1380 ++++++++++++++++++++
 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h |   46 +
 .../media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c |  476 +++++++
 .../media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c  |  132 ++
 .../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    |  106 ++
 drivers/media/platform/mtk-vcodec/venc_drv_if.h    |  175 +++
 drivers/media/platform/mtk-vcodec/venc_ipi_msg.h   |  212 +++
 drivers/media/platform/mtk-vcodec/vp8_enc/Makefile |    6 +
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.c      |  422 ++++++
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.h      |  149 +++
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c     |  240 ++++
 .../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           |  994 ++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h           |  167 +++
 include/uapi/linux/v4l2-controls.h                 |    4 +
 34 files changed, 6573 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
 mode change 100644 => 100755 include/uapi/linux/v4l2-controls.h

-- 
1.7.9.5

[PATCH v4 1/8] dt-bindings: Add a binding for Mediatek Video Processor

From: Tiffany Lin <tiffany.lin@mediatek.com>
Date: 2016-02-04 11:35:43

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     |   31 ++++++++++++++++++++
 1 file changed, 31 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..2a5bac3
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek-vpu.txt
@@ -0,0 +1,31 @@
+* 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
+
+Optional properties:
+  - memory-region: phandle to a node describing memory (see
+    Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt)
+    to be used for VPU extended memory; if not present, VPU may be located
+    anywhere in the memory
+
+Example:
+	vpu: vpu at 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";
+	};
-- 
1.7.9.5

[PATCH v4 2/8] [media] VPU: mediatek: support Mediatek VPU

From: Tiffany Lin <tiffany.lin@mediatek.com>
Date: 2016-02-04 11:41:33

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           |    9 +
 drivers/media/platform/Makefile          |    2 +
 drivers/media/platform/mtk-vpu/Makefile  |    1 +
 drivers/media/platform/mtk-vpu/mtk_vpu.c |  994 ++++++++++++++++++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h |  167 +++++
 5 files changed, 1173 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..ba812d6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -148,6 +148,15 @@ 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 && ARCH_MEDIATEK
+	---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..f54fd89
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -0,0 +1,994 @@
+/*
+* 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/bootmem.h>
+#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/of_reserved_mem.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
+ * @pa:		the physical memory address of VPU extended memory
+ *
+ */
+struct vpu_mem {
+	void *va;
+	dma_addr_t pa;
+};
+
+/**
+ * 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_wdt_handler - VPU watchdog reset handler
+ *
+ * @reset_func:	reset handler
+ * @priv:	private data
+ */
+struct vpu_wdt_handler {
+	void (*reset_func)(void *);
+	void *priv;
+};
+
+/**
+ * struct vpu_wdt - VPU watchdog workqueue
+ *
+ * @handler:	VPU watchdog reset handler
+ * @ws:		workstruct for VPU watchdog
+ * @wq:		workqueue for VPU watchdog
+ */
+struct vpu_wdt {
+	struct vpu_wdt_handler handler[VPU_RST_MAX];
+	struct work_struct ws;
+	struct workqueue_struct *wq;
+};
+
+/**
+ * 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 {
+	s32 id;
+	u32 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
+ * @enable_4GB:		VPU 4GB mode 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_wdt wdt;
+	struct vpu_ipi_desc ipi_desc[IPI_MAX];
+	struct share_obj *recv_buf;
+	struct share_obj *send_buf;
+	struct device *dev;
+	struct clk *clk;
+	bool enable_4GB;
+	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) {
+		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) {
+		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");
+		ret = -EINVAL;
+		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 (ret == 0) {
+		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;
+}
+
+static void vpu_wdt_reset_func(struct work_struct *ws)
+{
+	struct vpu_wdt *wdt = container_of(ws, struct vpu_wdt, ws);
+	struct mtk_vpu *vpu = container_of(wdt, struct mtk_vpu, wdt);
+	struct vpu_wdt_handler *handler = wdt->handler;
+	int index, ret;
+
+	dev_info(vpu->dev, "vpu reset\n");
+	mutex_lock(&vpu->vpu_mutex);
+	ret = vpu_clock_enable(vpu);
+	if (ret) {
+		dev_err(vpu->dev, "[VPU] wdt enables clock failed %d\n", ret);
+		return;
+	}
+	vpu_cfg_writel(vpu, 0x0, VPU_RESET);
+	vpu_clock_disable(vpu);
+	mutex_unlock(&vpu->vpu_mutex);
+
+	for (index = 0; index < VPU_RST_MAX; index++) {
+		if (handler[index].reset_func) {
+			handler[index].reset_func(handler[index].priv);
+			dev_dbg(vpu->dev, "wdt handler func %d\n", index);
+		}
+	}
+}
+
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+			void wdt_reset(void *),
+			void *priv, enum rst_id id)
+{
+	struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+	struct vpu_wdt_handler *handler = vpu->wdt.handler;
+
+	if (!vpu) {
+		dev_err(vpu->dev, "vpu device in not ready\n");
+		return -EPROBE_DEFER;
+	}
+
+	if (id < VPU_RST_MAX && wdt_reset != NULL) {
+		dev_dbg(vpu->dev, "wdt register id %d\n", id);
+		mutex_lock(&vpu->vpu_mutex);
+		handler[id].reset_func = wdt_reset;
+		handler[id].priv = priv;
+		mutex_unlock(&vpu->vpu_mutex);
+		return 0;
+	}
+
+	dev_err(vpu->dev, "register vpu wdt handler failed\n");
+	return -EINVAL;
+}
+
+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);
+}
+
+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, "mediatek,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 (ret == 0) {
+		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;
+}
+
+int vpu_compare_version(struct platform_device *pdev,
+			const char *expected_version)
+{
+	struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+	int cur_major, cur_minor, cur_build, cur_rel, cur_ver_num;
+	int major, minor, build, rel, ver_num;
+	char *cur_version = vpu->run.fw_ver;
+
+	cur_ver_num = sscanf(cur_version, "%d.%d.%d-rc%d",
+			     &cur_major, &cur_minor, &cur_build, &cur_rel);
+	if (cur_ver_num < 3)
+		return -1;
+	ver_num = sscanf(expected_version, "%d.%d.%d-rc%d",
+			 &major, &minor, &build, &rel);
+	if (ver_num < 3)
+		return -1;
+
+	if (cur_major < major)
+		return -1;
+	if (cur_major > major)
+		return 1;
+
+	if (cur_minor < minor)
+		return -1;
+	if (cur_minor > minor)
+		return 1;
+
+	if (cur_build < build)
+		return -1;
+	if (cur_build > build)
+		return 1;
+
+	if (cur_ver_num < ver_num)
+		return -1;
+	if (cur_ver_num > ver_num)
+		return 1;
+
+	if (ver_num > 3) {
+		if (cur_rel < rel)
+			return -1;
+		if (cur_rel > rel)
+			return 1;
+	}
+
+	return 0;
+}
+
+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;
+
+	dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va,
+			  vpu->extmem[fw_type].pa);
+}
+
+static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type)
+{
+	struct device *dev = vpu->dev;
+	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;
+	u32 offset_4gb = vpu->enable_4GB ? 0x40000000 : 0;
+
+	vpu->extmem[fw_type].va = dma_alloc_coherent(dev,
+					       fw_ext_size,
+					       &vpu->extmem[fw_type].pa,
+					       GFP_KERNEL);
+	if (!vpu->extmem[fw_type].va) {
+		dev_err(dev, "Failed to allocate the extended program memory\n");
+		return PTR_ERR(vpu->extmem[fw_type].va);
+	}
+
+	/* Disable extend0. Enable extend1 */
+	vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0);
+	vpu_cfg_writel(vpu, (vpu->extmem[fw_type].pa & 0xFFFFF000) + offset_4gb,
+		       vpu_ext_mem1);
+
+	dev_info(dev, "%s extend memory phy=0x%llx virt=0x%p\n",
+		 fw_type ? "Data" : "Program",
+		 (unsigned long long)vpu->extmem[fw_type].pa,
+		 vpu->extmem[fw_type].va);
+
+	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;
+	u32 vpu_to_host;
+	int ret;
+
+	/*
+	 * Clock should have been enabled already.
+	 * Enable again in case vpu_ipi_send times out
+	 * and has disabled the clock.
+	 */
+	ret = clk_enable(vpu->clk);
+	if (ret) {
+		dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret);
+		return IRQ_NONE;
+	}
+	vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST);
+	if (vpu_to_host & VPU_IPC_INT) {
+		vpu_ipi_handler(vpu);
+	} else {
+		dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host);
+		if (vpu->wdt.wq)
+			queue_work(vpu->wdt.wq, &vpu->wdt.ws);
+	}
+
+	/* 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) {
+		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;
+	}
+
+	/* VPU watchdog */
+	vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt");
+	if (!vpu->wdt.wq) {
+		dev_err(dev, "initialize wdt workqueue failed\n");
+		return -ENOMEM;
+	}
+	INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func);
+
+	ret = vpu_clock_enable(vpu);
+	if (ret) {
+		dev_err(dev, "enable vpu clock failed\n");
+		goto workqueue_destroy;
+	}
+
+	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;
+	}
+
+	/* 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);
+
+	vpu->enable_4GB = !!(max_pfn > (0xffffffffUL >> PAGE_SHIFT));
+	dev_dbg(dev, "4GB mode %u\n", vpu->enable_4GB);
+
+	if (vpu->enable_4GB) {
+		ret = of_reserved_mem_device_init(dev);
+		if (ret)
+			dev_info(dev, "init reserved memory failed\n");
+			/* continue to use dynamic allocation if failed */
+	}
+
+	ret = vpu_alloc_ext_mem(vpu, D_FW);
+	if (ret) {
+		dev_err(dev, "Allocate DM failed\n");
+		goto remove_debugfs;
+	}
+
+	ret = vpu_alloc_ext_mem(vpu, P_FW);
+	if (ret) {
+		dev_err(dev, "Allocate PM failed\n");
+		goto free_d_mem;
+	}
+
+	init_waitqueue_head(&vpu->run.wq);
+	init_waitqueue_head(&vpu->ack_wq);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(dev, "get IRQ resource failed.\n");
+		ret = -ENXIO;
+		goto free_p_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_p_mem;
+	}
+
+	vpu_clock_disable(vpu);
+	dev_dbg(dev, "initialization completed\n");
+
+	return 0;
+
+free_p_mem:
+	vpu_free_ext_mem(vpu, P_FW);
+free_d_mem:
+	vpu_free_ext_mem(vpu, D_FW);
+remove_debugfs:
+	of_reserved_mem_device_release(dev);
+#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);
+workqueue_destroy:
+	destroy_workqueue(vpu->wdt.wq);
+
+	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
+	if (vpu->wdt.wq) {
+		flush_workqueue(vpu->wdt.wq);
+		destroy_workqueue(vpu->wdt.wq);
+	}
+	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..d9c3cde
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h
@@ -0,0 +1,167 @@
+/*
+* 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,
+};
+
+/**
+ * enum rst_id - reset id to register reset function for VPU watchdog timeout
+ *
+ * @VPU_RST_DEC: decoder reset id
+ * @VPU_RST_ENC: encoder reset id
+ * @VPU_RST_MDP: MDP (Media Data Path) reset id
+ * @VPU_RST_MAX: maximum reset id
+ */
+enum rst_id {
+	VPU_RST_ENC,
+	VPU_RST_DEC,
+	VPU_RST_MDP,
+	VPU_RST_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_wdt_reg_handler - register a VPU watchdog handler
+ *
+ * @pdev:               VPU platform device
+ * @vpu_wdt_reset_func:	the callback reset function
+ * @private_data:       the private data for reset function
+ * @rst_id:		reset id
+ *
+ * Register a handler performing own tasks when vpu reset by watchdog
+ *
+ * Return: Return 0 if the handler is added successfully,
+ * otherwise it is failed.
+ *
+ **/
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+			void vpu_wdt_reset_func(void *),
+			void *priv, enum rst_id id);
+
+/**
+ * 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);
+#endif /* _MTK_VPU_H */
-- 
1.7.9.5

[PATCH v4 3/8] arm64: dts: mediatek: Add node for Mediatek Video Processor Unit

From: Tiffany Lin <tiffany.lin@mediatek.com>
Date: 2016-02-04 11:36:08

Add VPU drivers for MT8173

Signed-off-by: Andrew-CT Chen <andrew-ct.chen@mediatek.com>
Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8173.dtsi |   23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 60a1284..5b0b38a 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -200,6 +200,18 @@
 		clock-output-names = "cpum_ck";
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		vpu_dma_reserved: vpu_dma_mem_region {
+			compatible = "shared-dma-pool";
+			reg = <0 0xb7000000 0 0x500000>;
+			alignment = <0x1000>;
+			no-map;
+		};
+	};
+
 	thermal-zones {
 		cpu_thermal: cpu_thermal {
 			polling-delay-passive = <1000>; /* milliseconds */
@@ -422,6 +434,17 @@
 			clocks = <&infracfg CLK_INFRA_CEC>;
 		};
 
+		vpu: vpu at 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 CLK_TOP_SCP_SEL>;
+			clock-names = "main";
+			memory-region = <&vpu_dma_reserved>;
+		};
+
 		sysirq: intpol-controller at 10200620 {
 			compatible = "mediatek,mt8173-sysirq",
 				     "mediatek,mt6577-sysirq";
-- 
1.7.9.5

[PATCH v4 4/8] dt-bindings: Add a binding for Mediatek Video Encoder

From: Tiffany Lin <tiffany.lin@mediatek.com>
Date: 2016-02-04 11:35:46

Add a DT binding documentation of Video Encoder for the
MT8173 SoC from Mediatek.

Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 ++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek-vcodec.txt
diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
new file mode 100644
index 0000000..572bfdd
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
@@ -0,0 +1,59 @@
+Mediatek Video Codec
+
+Mediatek Video Codec is the video codec hw present in Mediatek SoCs which
+supports high resolution encoding functionalities.
+
+Required properties:
+- compatible : "mediatek,mt8173-vcodec-enc" for encoder
+- reg : Physical base address of the video codec registers and length of
+  memory mapped region.
+- interrupts : interrupt number to the cpu.
+- mediatek,larb : must contain the local arbiters in the current Socs.
+- clocks : list of clock specifiers, corresponding to entries in
+  the clock-names property.
+- clock-names: encoder must contain "vencpll_d2", "venc_sel", "univpll1_d2",
+  "venc_lt_sel".
+- iommus : should point to the respective IOMMU block with master port as
+  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- mediatek,vpu : the node of video processor unit
+
+Example:
+vcodec_enc: vcodec at 0x18002000 {
+    compatible = "mediatek,mt8173-vcodec-enc";
+    reg = <0 0x18002000 0 0x1000>,    /*VENC_SYS*/
+          <0 0x19002000 0 0x1000>;    /*VENC_LT_SYS*/
+    interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
+           <GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
+    mediatek,larb = <&larb3>,
+		    <&larb5>;
+    iommus = <&iommu M4U_PORT_VENC_RCPU>,
+             <&iommu M4U_PORT_VENC_REC>,
+             <&iommu M4U_PORT_VENC_BSDMA>,
+             <&iommu M4U_PORT_VENC_SV_COMV>,
+             <&iommu M4U_PORT_VENC_RD_COMV>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA>,
+             <&iommu M4U_PORT_VENC_REF_LUMA>,
+             <&iommu M4U_PORT_VENC_REF_CHROMA>,
+             <&iommu M4U_PORT_VENC_NBM_RDMA>,
+             <&iommu M4U_PORT_VENC_NBM_WDMA>,
+             <&iommu M4U_PORT_VENC_RCPU_SET2>,
+             <&iommu M4U_PORT_VENC_REC_FRM_SET2>,
+             <&iommu M4U_PORT_VENC_BSDMA_SET2>,
+             <&iommu M4U_PORT_VENC_SV_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_RD_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
+             <&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
+    mediatek,vpu = <&vpu>;
+    clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
+             <&topckgen CLK_TOP_VENC_SEL>,
+             <&topckgen CLK_TOP_UNIVPLL1_D2>,
+             <&topckgen CLK_TOP_VENC_LT_SEL>;
+    clock-names = "vencpll_d2",
+                  "venc_sel",
+                  "univpll1_d2",
+                  "venc_lt_sel";
+  };
-- 
1.7.9.5

Re: [PATCH v4 4/8] dt-bindings: Add a binding for Mediatek Video Encoder

From: Rob Herring <robh@kernel.org>
Date: 2016-02-08 18:56:21

On Thu, Feb 04, 2016 at 07:34:59PM +0800, Tiffany Lin wrote:
Add a DT binding documentation of Video Encoder for the
MT8173 SoC from Mediatek.

Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 ++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek-vcodec.txt
Acked-by: Rob Herring <robh@kernel.org>

Re: [PATCH v4 4/8] dt-bindings: Add a binding for Mediatek Video Encoder

From: Daniel Kurtz <hidden>
Date: 2016-02-09 11:29:50

Hi Tiffany,

On Thu, Feb 4, 2016 at 7:34 PM, Tiffany Lin [off-list ref] wrote:
quoted hunk
Add a DT binding documentation of Video Encoder for the
MT8173 SoC from Mediatek.

Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 ++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek-vcodec.txt
diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
new file mode 100644
index 0000000..572bfdd
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
@@ -0,0 +1,59 @@
+Mediatek Video Codec
+
+Mediatek Video Codec is the video codec hw present in Mediatek SoCs which
+supports high resolution encoding functionalities.
+
+Required properties:
+- compatible : "mediatek,mt8173-vcodec-enc" for encoder
+- reg : Physical base address of the video codec registers and length of
+  memory mapped region.
+- interrupts : interrupt number to the cpu.
+- mediatek,larb : must contain the local arbiters in the current Socs.
+- clocks : list of clock specifiers, corresponding to entries in
+  the clock-names property.
+- clock-names: encoder must contain "vencpll_d2", "venc_sel", "univpll1_d2",
+  "venc_lt_sel".
+- iommus : should point to the respective IOMMU block with master port as
+  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- mediatek,vpu : the node of video processor unit
+
+Example:
+vcodec_enc: vcodec at 0x18002000 {
+    compatible = "mediatek,mt8173-vcodec-enc";
+    reg = <0 0x18002000 0 0x1000>,    /*VENC_SYS*/
+          <0 0x19002000 0 0x1000>;    /*VENC_LT_SYS*/
This really looks like two encoder devices combined into a single
device tree node.
There are two register sets, two irqs, two sets of iommus, and two
sets of clocks.

If possible, please split this node into two, one for each encoder.

+    interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
+           <GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
+    mediatek,larb = <&larb3>,
+                   <&larb5>;
+    iommus = <&iommu M4U_PORT_VENC_RCPU>,
+             <&iommu M4U_PORT_VENC_REC>,
+             <&iommu M4U_PORT_VENC_BSDMA>,
+             <&iommu M4U_PORT_VENC_SV_COMV>,
+             <&iommu M4U_PORT_VENC_RD_COMV>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA>,
+             <&iommu M4U_PORT_VENC_REF_LUMA>,
+             <&iommu M4U_PORT_VENC_REF_CHROMA>,
+             <&iommu M4U_PORT_VENC_NBM_RDMA>,
+             <&iommu M4U_PORT_VENC_NBM_WDMA>,
+             <&iommu M4U_PORT_VENC_RCPU_SET2>,
+             <&iommu M4U_PORT_VENC_REC_FRM_SET2>,
+             <&iommu M4U_PORT_VENC_BSDMA_SET2>,
+             <&iommu M4U_PORT_VENC_SV_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_RD_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
+             <&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
+    mediatek,vpu = <&vpu>;
+    clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
+             <&topckgen CLK_TOP_VENC_SEL>,
+             <&topckgen CLK_TOP_UNIVPLL1_D2>,
+             <&topckgen CLK_TOP_VENC_LT_SEL>;
+    clock-names = "vencpll_d2",
+                  "venc_sel",
+                  "univpll1_d2",
+                  "venc_lt_sel";
The names of these clocks should be from the perspective of the
encoder, not the clock provider.

-Dan
+  };
--
1.7.9.5

Re: [PATCH v4 4/8] dt-bindings: Add a binding for Mediatek Video Encoder

From: Daniel Kurtz <hidden>
Date: 2016-02-15 10:43:15

On Tue, Feb 9, 2016 at 7:29 PM, Daniel Kurtz [off-list ref] wrote:
Hi Tiffany,

On Thu, Feb 4, 2016 at 7:34 PM, Tiffany Lin [off-list ref] wrote:
quoted
Add a DT binding documentation of Video Encoder for the
MT8173 SoC from Mediatek.

Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 ++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek-vcodec.txt
diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
new file mode 100644
index 0000000..572bfdd
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
@@ -0,0 +1,59 @@
+Mediatek Video Codec
+
+Mediatek Video Codec is the video codec hw present in Mediatek SoCs which
+supports high resolution encoding functionalities.
+
+Required properties:
+- compatible : "mediatek,mt8173-vcodec-enc" for encoder
+- reg : Physical base address of the video codec registers and length of
+  memory mapped region.
+- interrupts : interrupt number to the cpu.
+- mediatek,larb : must contain the local arbiters in the current Socs.
+- clocks : list of clock specifiers, corresponding to entries in
+  the clock-names property.
+- clock-names: encoder must contain "vencpll_d2", "venc_sel", "univpll1_d2",
+  "venc_lt_sel".
+- iommus : should point to the respective IOMMU block with master port as
+  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- mediatek,vpu : the node of video processor unit
+
+Example:
+vcodec_enc: vcodec at 0x18002000 {
+    compatible = "mediatek,mt8173-vcodec-enc";
+    reg = <0 0x18002000 0 0x1000>,    /*VENC_SYS*/
+          <0 0x19002000 0 0x1000>;    /*VENC_LT_SYS*/
This really looks like two encoder devices combined into a single
device tree node.
There are two register sets, two irqs, two sets of iommus, and two
sets of clocks.

If possible, please split this node into two, one for each encoder.
I chatted offline with Mediatek.  They explained that there really is
just one encoder hardware, that happens to support multiple formats.
The encoder cannot encode with both formats at the same time.  The
Mediatek HW designers added a new format to an existing encoder by
adding a second interface (register set, irq, iommus, clocks) without
modifying the original interface.  However in the hardware itself
there is really just one encoder device.

So, although this node looks like it is for two encoder devices (one
for each format), really there is just one device that supports each
format through its large interface.

So, I'm fine with this being a single device node.
quoted
+    interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
+           <GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
+    mediatek,larb = <&larb3>,
+                   <&larb5>;
+    iommus = <&iommu M4U_PORT_VENC_RCPU>,
+             <&iommu M4U_PORT_VENC_REC>,
+             <&iommu M4U_PORT_VENC_BSDMA>,
+             <&iommu M4U_PORT_VENC_SV_COMV>,
+             <&iommu M4U_PORT_VENC_RD_COMV>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA>,
+             <&iommu M4U_PORT_VENC_REF_LUMA>,
+             <&iommu M4U_PORT_VENC_REF_CHROMA>,
+             <&iommu M4U_PORT_VENC_NBM_RDMA>,
+             <&iommu M4U_PORT_VENC_NBM_WDMA>,
+             <&iommu M4U_PORT_VENC_RCPU_SET2>,
+             <&iommu M4U_PORT_VENC_REC_FRM_SET2>,
+             <&iommu M4U_PORT_VENC_BSDMA_SET2>,
+             <&iommu M4U_PORT_VENC_SV_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_RD_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
+             <&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
+    mediatek,vpu = <&vpu>;
+    clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
+             <&topckgen CLK_TOP_VENC_SEL>,
+             <&topckgen CLK_TOP_UNIVPLL1_D2>,
+             <&topckgen CLK_TOP_VENC_LT_SEL>;
+    clock-names = "vencpll_d2",
+                  "venc_sel",
+                  "univpll1_d2",
+                  "venc_lt_sel";
The names of these clocks should be from the perspective of the
encoder, not the clock provider.
I still think these clock names should be updated, however.

-Dan

Re: [PATCH v4 4/8] dt-bindings: Add a binding for Mediatek Video Encoder

From: tiffany lin <tiffany.lin@mediatek.com>
Date: 2016-02-16 02:09:36

Hi Daniel,

On Mon, 2016-02-15 at 18:42 +0800, Daniel Kurtz wrote:
On Tue, Feb 9, 2016 at 7:29 PM, Daniel Kurtz [off-list ref] wrote:
quoted
Hi Tiffany,

On Thu, Feb 4, 2016 at 7:34 PM, Tiffany Lin [off-list ref] wrote:
quoted
Add a DT binding documentation of Video Encoder for the
MT8173 SoC from Mediatek.

Signed-off-by: Tiffany Lin <tiffany.lin@mediatek.com>
---
 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 ++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek-vcodec.txt
diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
new file mode 100644
index 0000000..572bfdd
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
@@ -0,0 +1,59 @@
+Mediatek Video Codec
+
+Mediatek Video Codec is the video codec hw present in Mediatek SoCs which
+supports high resolution encoding functionalities.
+
+Required properties:
+- compatible : "mediatek,mt8173-vcodec-enc" for encoder
+- reg : Physical base address of the video codec registers and length of
+  memory mapped region.
+- interrupts : interrupt number to the cpu.
+- mediatek,larb : must contain the local arbiters in the current Socs.
+- clocks : list of clock specifiers, corresponding to entries in
+  the clock-names property.
+- clock-names: encoder must contain "vencpll_d2", "venc_sel", "univpll1_d2",
+  "venc_lt_sel".
+- iommus : should point to the respective IOMMU block with master port as
+  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- mediatek,vpu : the node of video processor unit
+
+Example:
+vcodec_enc: vcodec at 0x18002000 {
+    compatible = "mediatek,mt8173-vcodec-enc";
+    reg = <0 0x18002000 0 0x1000>,    /*VENC_SYS*/
+          <0 0x19002000 0 0x1000>;    /*VENC_LT_SYS*/
This really looks like two encoder devices combined into a single
device tree node.
There are two register sets, two irqs, two sets of iommus, and two
sets of clocks.

If possible, please split this node into two, one for each encoder.
I chatted offline with Mediatek.  They explained that there really is
just one encoder hardware, that happens to support multiple formats.
The encoder cannot encode with both formats at the same time.  The
Mediatek HW designers added a new format to an existing encoder by
adding a second interface (register set, irq, iommus, clocks) without
modifying the original interface.  However in the hardware itself
there is really just one encoder device.

So, although this node looks like it is for two encoder devices (one
for each format), really there is just one device that supports each
format through its large interface.

So, I'm fine with this being a single device node.
quoted
quoted
+    interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
+           <GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
+    mediatek,larb = <&larb3>,
+                   <&larb5>;
+    iommus = <&iommu M4U_PORT_VENC_RCPU>,
+             <&iommu M4U_PORT_VENC_REC>,
+             <&iommu M4U_PORT_VENC_BSDMA>,
+             <&iommu M4U_PORT_VENC_SV_COMV>,
+             <&iommu M4U_PORT_VENC_RD_COMV>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA>,
+             <&iommu M4U_PORT_VENC_REF_LUMA>,
+             <&iommu M4U_PORT_VENC_REF_CHROMA>,
+             <&iommu M4U_PORT_VENC_NBM_RDMA>,
+             <&iommu M4U_PORT_VENC_NBM_WDMA>,
+             <&iommu M4U_PORT_VENC_RCPU_SET2>,
+             <&iommu M4U_PORT_VENC_REC_FRM_SET2>,
+             <&iommu M4U_PORT_VENC_BSDMA_SET2>,
+             <&iommu M4U_PORT_VENC_SV_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_RD_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
+             <&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
+    mediatek,vpu = <&vpu>;
+    clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
+             <&topckgen CLK_TOP_VENC_SEL>,
+             <&topckgen CLK_TOP_UNIVPLL1_D2>,
+             <&topckgen CLK_TOP_VENC_LT_SEL>;
+    clock-names = "vencpll_d2",
+                  "venc_sel",
+                  "univpll1_d2",
+                  "venc_lt_sel";
The names of these clocks should be from the perspective of the
encoder, not the clock provider.
I still think these clock names should be updated, however.
Got it. We will fix this in next version.
-Dan

Re: [PATCH v4 2/8] [media] VPU: mediatek: support Mediatek VPU

From: Hans Verkuil <hidden>
Date: 2016-02-15 10:07:53

Hi Tiffany,

A small review comment below:

On 02/04/2016 12:34 PM, Tiffany Lin wrote:
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           |    9 +
 drivers/media/platform/Makefile          |    2 +
 drivers/media/platform/mtk-vpu/Makefile  |    1 +
 drivers/media/platform/mtk-vpu/mtk_vpu.c |  994 ++++++++++++++++++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h |  167 +++++
 5 files changed, 1173 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
<snip>
quoted hunk
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..f54fd89
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -0,0 +1,994 @@
<snip>
+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"
Please use snprintf.
+		"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");
Ditto.

sprintf always makes me feel uncomfortable :-)
+	}
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
Regards,

	Hans

Re: [PATCH v4 2/8] [media] VPU: mediatek: support Mediatek VPU

From: tiffany lin <tiffany.lin@mediatek.com>
Date: 2016-02-15 11:20:23

Hi Hans,

On Mon, 2016-02-15 at 11:07 +0100, Hans Verkuil wrote:
Hi Tiffany,

A small review comment below:

On 02/04/2016 12:34 PM, Tiffany Lin wrote:
quoted
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           |    9 +
 drivers/media/platform/Makefile          |    2 +
 drivers/media/platform/mtk-vpu/Makefile  |    1 +
 drivers/media/platform/mtk-vpu/mtk_vpu.c |  994 ++++++++++++++++++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h |  167 +++++
 5 files changed, 1173 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
<snip>
quoted
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..f54fd89
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -0,0 +1,994 @@
<snip>
quoted
+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"
Please use snprintf.
Got it, we will fix this in next version.
quoted
+		"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");
Ditto.

sprintf always makes me feel uncomfortable :-)
Sorry about that, we will check patch series and remove all sprintf in
next version.
quoted
+	}
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
Regards,

	Hans

Re: [PATCH v4 2/8] [media] VPU: mediatek: support Mediatek VPU

From: Hans Verkuil <hidden>
Date: 2016-02-15 10:13:33

On 02/04/2016 12:34 PM, Tiffany Lin wrote:
quoted hunk
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           |    9 +
 drivers/media/platform/Makefile          |    2 +
 drivers/media/platform/mtk-vpu/Makefile  |    1 +
 drivers/media/platform/mtk-vpu/mtk_vpu.c |  994 ++++++++++++++++++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h |  167 +++++
 5 files changed, 1173 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..ba812d6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -148,6 +148,15 @@ 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 && ARCH_MEDIATEK
+	---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.
Can you be more specific in this text and mention for which Mediatek SoCs
this driver is for? Just like you did in the commit log.

Also add something like this:

          To compile this driver as a module, choose M here: the module
          will be called mtk-vpu.

I always find it useful if the Kconfig text mentions the module name.

Regards,

	Hans
+
 config VIDEO_MEM2MEM_DEINTERLACE
 	tristate "Deinterlace support"
 	depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE

Re: [PATCH v4 2/8] [media] VPU: mediatek: support Mediatek VPU

From: tiffany lin <tiffany.lin@mediatek.com>
Date: 2016-02-15 11:27:44

On Mon, 2016-02-15 at 11:13 +0100, Hans Verkuil wrote:
On 02/04/2016 12:34 PM, Tiffany Lin wrote:
quoted
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           |    9 +
 drivers/media/platform/Makefile          |    2 +
 drivers/media/platform/mtk-vpu/Makefile  |    1 +
 drivers/media/platform/mtk-vpu/mtk_vpu.c |  994 ++++++++++++++++++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h |  167 +++++
 5 files changed, 1173 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..ba812d6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -148,6 +148,15 @@ 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 && ARCH_MEDIATEK
+	---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.
Can you be more specific in this text and mention for which Mediatek SoCs
this driver is for? Just like you did in the commit log.
Got it. We will add more specific description in Kconfig in next
version.
Also add something like this:

          To compile this driver as a module, choose M here: the module
          will be called mtk-vpu.

I always find it useful if the Kconfig text mentions the module name.
Got it. We will fix this. Thanks for sharing experience.


best regards,
Tiffany
Regards,

	Hans
quoted
+
 config VIDEO_MEM2MEM_DEINTERLACE
 	tristate "Deinterlace support"
 	depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE

Re: [PATCH v4 2/8] [media] VPU: mediatek: support Mediatek VPU

From: Wu-Cheng Li (李務誠) <hidden>
Date: 2016-02-15 13:59:30

On Thu, Feb 4, 2016 at 7:34 PM, Tiffany Lin [off-list ref] wrote:
quoted hunk
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           |    9 +
 drivers/media/platform/Makefile          |    2 +
 drivers/media/platform/mtk-vpu/Makefile  |    1 +
 drivers/media/platform/mtk-vpu/mtk_vpu.c |  994 ++++++++++++++++++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h |  167 +++++
 5 files changed, 1173 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..ba812d6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -148,6 +148,15 @@ 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 && ARCH_MEDIATEK
+       ---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..f54fd89
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -0,0 +1,994 @@
+/*
+* 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/bootmem.h>
+#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/of_reserved_mem.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
+ * @pa:                the physical memory address of VPU extended memory
This should be device memory address? If yes, s/physical/device/ and
s/pa/dma_addr/.
+ *
+ */
+struct vpu_mem {
+       void *va;
+       dma_addr_t pa;
+};
+
+/**
+ * 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_wdt_handler - VPU watchdog reset handler
+ *
+ * @reset_func:        reset handler
+ * @priv:      private data
+ */
+struct vpu_wdt_handler {
+       void (*reset_func)(void *);
+       void *priv;
+};
+
+/**
+ * struct vpu_wdt - VPU watchdog workqueue
+ *
+ * @handler:   VPU watchdog reset handler
+ * @ws:                workstruct for VPU watchdog
+ * @wq:                workqueue for VPU watchdog
+ */
+struct vpu_wdt {
+       struct vpu_wdt_handler handler[VPU_RST_MAX];
+       struct work_struct ws;
+       struct workqueue_struct *wq;
+};
+
+/**
+ * struct vpu_run - VPU initialization status
+ *
+ * @signaled:          the signal of vpu initialization completed
+ * @fw_ver:            VPU firmware version
+ * @enc_capability:    encoder capability
We should document the meaning of the bit used. Since no bit is used
by the codec driver, document this is not used for now and the value
is reserved for future use.
+ * @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 {
+       s32 id;
+       u32 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
+ * @enable_4GB:                VPU 4GB mode 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
s/ipi_ack_signaled/ipi_id_ack/. Move to after |ack_wq| to be
consistent with the order of variable declaration.
+ * @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_wdt wdt;
+       struct vpu_ipi_desc ipi_desc[IPI_MAX];
+       struct share_obj *recv_buf;
+       struct share_obj *send_buf;
+       struct device *dev;
+       struct clk *clk;
+       bool enable_4GB;
+       struct mutex vpu_mutex; /* for protecting vpu data data structure */
Remove the comment here. It should be documented in function comment above.
+       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))
Checking wdt_refcnt and doing vpu_cfg_writel should be done
atomically. For example, if vpu_clock_enable is called after
atomic_dec_and_test(&vpu->wdt_refcnt) and before vpu_cfg_writel(...),
the state of watchdog could be wrong. Adding a spinlock to protect
wdt_refcnt and vpu_cfg_writel together. Then wdt_refcnt doesn't need
to be atomic_t.
+               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))
Do we need to enable the watchdog other than vpu_ipi_send? The only
place that AP waits for VPU is in vpu_ipi_send. Right? If yes, can we
just enable and disable the watchdog in vpu_ipi_send?
+               vpu_cfg_writel(vpu,
+                              vpu_cfg_readl(vpu, VPU_WDT_REG) | (1L << 31),
+                              VPU_WDT_REG);
+
+       atomic_inc(&vpu->wdt_refcnt);
Same above. atomic_read, vpu_cfg_writel, and atomic_inc should be done
atomically.
+
+       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) {
Check if id >= 0 because type of enum is implementation dependent.
+               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");
print id
+       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) {
+               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");
+               ret = -EINVAL;
+               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 (ret == 0) {
+               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;
I don't see why we need to set it to false here. Remove if not needed.
+       mutex_unlock(&vpu->vpu_mutex);
+clock_disable:
+       vpu_clock_disable(vpu);
+
+       return ret;
+}
+
+static void vpu_wdt_reset_func(struct work_struct *ws)
+{
+       struct vpu_wdt *wdt = container_of(ws, struct vpu_wdt, ws);
+       struct mtk_vpu *vpu = container_of(wdt, struct mtk_vpu, wdt);
+       struct vpu_wdt_handler *handler = wdt->handler;
+       int index, ret;
+
+       dev_info(vpu->dev, "vpu reset\n");
+       mutex_lock(&vpu->vpu_mutex);
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
This is missing mutex_unlock. Move mutex_lock right before
vpu_cfg_writel(vpu, 0x0, VPU_RESET);. So we don't need to unlock when
vpu_clock_enable fails.
+               dev_err(vpu->dev, "[VPU] wdt enables clock failed %d\n", ret);
+               return;
+       }
+       vpu_cfg_writel(vpu, 0x0, VPU_RESET);
+       vpu_clock_disable(vpu);
+       mutex_unlock(&vpu->vpu_mutex);
+
+       for (index = 0; index < VPU_RST_MAX; index++) {
+               if (handler[index].reset_func) {
+                       handler[index].reset_func(handler[index].priv);
+                       dev_dbg(vpu->dev, "wdt handler func %d\n", index);
+               }
+       }
+}
+
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+                       void wdt_reset(void *),
+                       void *priv, enum rst_id id)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       struct vpu_wdt_handler *handler = vpu->wdt.handler;
+
+       if (!vpu) {
+               dev_err(vpu->dev, "vpu device in not ready\n");
+               return -EPROBE_DEFER;
+       }
+
+       if (id < VPU_RST_MAX && wdt_reset != NULL) {
Check id >= 0
+               dev_dbg(vpu->dev, "wdt register id %d\n", id);
+               mutex_lock(&vpu->vpu_mutex);
+               handler[id].reset_func = wdt_reset;
+               handler[id].priv = priv;
+               mutex_unlock(&vpu->vpu_mutex);
+               return 0;
+       }
+
+       dev_err(vpu->dev, "register vpu wdt handler failed\n");
+       return -EINVAL;
+}
+
+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);
+}
+
+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, "mediatek,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 (ret == 0) {
+               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;
+}
+
+int vpu_compare_version(struct platform_device *pdev,
+                       const char *expected_version)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       int cur_major, cur_minor, cur_build, cur_rel, cur_ver_num;
+       int major, minor, build, rel, ver_num;
+       char *cur_version = vpu->run.fw_ver;
+
+       cur_ver_num = sscanf(cur_version, "%d.%d.%d-rc%d",
+                            &cur_major, &cur_minor, &cur_build, &cur_rel);
+       if (cur_ver_num < 3)
+               return -1;
+       ver_num = sscanf(expected_version, "%d.%d.%d-rc%d",
+                        &major, &minor, &build, &rel);
+       if (ver_num < 3)
+               return -1;
+
+       if (cur_major < major)
+               return -1;
+       if (cur_major > major)
+               return 1;
+
+       if (cur_minor < minor)
+               return -1;
+       if (cur_minor > minor)
+               return 1;
+
+       if (cur_build < build)
+               return -1;
+       if (cur_build > build)
+               return 1;
+
+       if (cur_ver_num < ver_num)
+               return -1;
+       if (cur_ver_num > ver_num)
+               return 1;
+
+       if (ver_num > 3) {
+               if (cur_rel < rel)
+                       return -1;
+               if (cur_rel > rel)
+                       return 1;
+       }
+
+       return 0;
+}
+
+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;
+
+       dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va,
+                         vpu->extmem[fw_type].pa);
+}
+
+static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type)
+{
+       struct device *dev = vpu->dev;
+       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;
+       u32 offset_4gb = vpu->enable_4GB ? 0x40000000 : 0;
+
+       vpu->extmem[fw_type].va = dma_alloc_coherent(dev,
+                                              fw_ext_size,
+                                              &vpu->extmem[fw_type].pa,
+                                              GFP_KERNEL);
+       if (!vpu->extmem[fw_type].va) {
+               dev_err(dev, "Failed to allocate the extended program memory\n");
+               return PTR_ERR(vpu->extmem[fw_type].va);
+       }
+
+       /* Disable extend0. Enable extend1 */
+       vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0);
+       vpu_cfg_writel(vpu, (vpu->extmem[fw_type].pa & 0xFFFFF000) + offset_4gb,
+                      vpu_ext_mem1);
+
+       dev_info(dev, "%s extend memory phy=0x%llx virt=0x%p\n",
+                fw_type ? "Data" : "Program",
+                (unsigned long long)vpu->extmem[fw_type].pa,
+                vpu->extmem[fw_type].va);
+
+       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) {
check rcv_obj->id >= 0
+               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;
+       u32 vpu_to_host;
+       int ret;
+
+       /*
+        * Clock should have been enabled already.
+        * Enable again in case vpu_ipi_send times out
+        * and has disabled the clock.
+        */
+       ret = clk_enable(vpu->clk);
+       if (ret) {
+               dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret);
+               return IRQ_NONE;
+       }
+       vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST);
+       if (vpu_to_host & VPU_IPC_INT) {
+               vpu_ipi_handler(vpu);
+       } else {
+               dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host);
+               if (vpu->wdt.wq)
Remove. mtk_vpu_probe makes sure wdt.wq is not NULL.
quoted hunk
+                       queue_work(vpu->wdt.wq, &vpu->wdt.ws);
+       }
+
+       /* 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) {
+               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;
+       }
+
+       /* VPU watchdog */
+       vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt");
+       if (!vpu->wdt.wq) {
+               dev_err(dev, "initialize wdt workqueue failed\n");
+               return -ENOMEM;
+       }
+       INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func);
+
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(dev, "enable vpu clock failed\n");
+               goto workqueue_destroy;
+       }
+
+       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;
+       }
+
+       /* 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);
+
+       vpu->enable_4GB = !!(max_pfn > (0xffffffffUL >> PAGE_SHIFT));
+       dev_dbg(dev, "4GB mode %u\n", vpu->enable_4GB);
+
+       if (vpu->enable_4GB) {
+               ret = of_reserved_mem_device_init(dev);
+               if (ret)
+                       dev_info(dev, "init reserved memory failed\n");
+                       /* continue to use dynamic allocation if failed */
+       }
+
+       ret = vpu_alloc_ext_mem(vpu, D_FW);
+       if (ret) {
+               dev_err(dev, "Allocate DM failed\n");
+               goto remove_debugfs;
+       }
+
+       ret = vpu_alloc_ext_mem(vpu, P_FW);
+       if (ret) {
+               dev_err(dev, "Allocate PM failed\n");
+               goto free_d_mem;
+       }
+
+       init_waitqueue_head(&vpu->run.wq);
+       init_waitqueue_head(&vpu->ack_wq);
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "get IRQ resource failed.\n");
+               ret = -ENXIO;
+               goto free_p_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_p_mem;
+       }
+
+       vpu_clock_disable(vpu);
+       dev_dbg(dev, "initialization completed\n");
+
+       return 0;
+
+free_p_mem:
+       vpu_free_ext_mem(vpu, P_FW);
+free_d_mem:
+       vpu_free_ext_mem(vpu, D_FW);
+remove_debugfs:
+       of_reserved_mem_device_release(dev);
+#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);
+workqueue_destroy:
+       destroy_workqueue(vpu->wdt.wq);
+
+       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
+       if (vpu->wdt.wq) {
+               flush_workqueue(vpu->wdt.wq);
+               destroy_workqueue(vpu->wdt.wq);
+       }
+       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..d9c3cde
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h
@@ -0,0 +1,167 @@
+/*
+* 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,
+};
+
+/**
+ * enum rst_id - reset id to register reset function for VPU watchdog timeout
+ *
+ * @VPU_RST_DEC: decoder reset id
+ * @VPU_RST_ENC: encoder reset id
+ * @VPU_RST_MDP: MDP (Media Data Path) reset id
+ * @VPU_RST_MAX: maximum reset id
+ */
+enum rst_id {
+       VPU_RST_ENC,
+       VPU_RST_DEC,
+       VPU_RST_MDP,
+       VPU_RST_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_wdt_reg_handler - register a VPU watchdog handler
+ *
+ * @pdev:               VPU platform device
+ * @vpu_wdt_reset_func:        the callback reset function
+ * @private_data:       the private data for reset function
+ * @rst_id:            reset id
+ *
+ * Register a handler performing own tasks when vpu reset by watchdog
+ *
+ * Return: Return 0 if the handler is added successfully,
+ * otherwise it is failed.
+ *
+ **/
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+                       void vpu_wdt_reset_func(void *),
+                       void *priv, enum rst_id id);
+
+/**
+ * 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);
+#endif /* _MTK_VPU_H */
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Re: [PATCH v4 2/8] [media] VPU: mediatek: support Mediatek VPU

From: andrew-ct chen <andrew-ct.chen@mediatek.com>
Date: 2016-02-16 09:36:40

On Mon, 2016-02-15 at 21:59 +0800, Wu-Cheng Li (???) wrote:
On Thu, Feb 4, 2016 at 7:34 PM, Tiffany Lin [off-list ref] wrote:
quoted
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           |    9 +
 drivers/media/platform/Makefile          |    2 +
 drivers/media/platform/mtk-vpu/Makefile  |    1 +
 drivers/media/platform/mtk-vpu/mtk_vpu.c |  994 ++++++++++++++++++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h |  167 +++++
 5 files changed, 1173 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..ba812d6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -148,6 +148,15 @@ 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 && ARCH_MEDIATEK
+       ---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..f54fd89
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -0,0 +1,994 @@
+/*
+* 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/bootmem.h>
+#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/of_reserved_mem.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
+ * @pa:                the physical memory address of VPU extended memory
This should be device memory address? If yes, s/physical/device/ and
s/pa/dma_addr/.
It's really a physical memory since vpu is not attached to IOMMU.It
should be s/dma_addr_t/phys_addr_t for the data type.Thanks.
quoted
+ *
+ */
+struct vpu_mem {
+       void *va;
+       dma_addr_t pa;
+};
+
+/**
+ * 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_wdt_handler - VPU watchdog reset handler
+ *
+ * @reset_func:        reset handler
+ * @priv:      private data
+ */
+struct vpu_wdt_handler {
+       void (*reset_func)(void *);
+       void *priv;
+};
+
+/**
+ * struct vpu_wdt - VPU watchdog workqueue
+ *
+ * @handler:   VPU watchdog reset handler
+ * @ws:                workstruct for VPU watchdog
+ * @wq:                workqueue for VPU watchdog
+ */
+struct vpu_wdt {
+       struct vpu_wdt_handler handler[VPU_RST_MAX];
+       struct work_struct ws;
+       struct workqueue_struct *wq;
+};
+
+/**
+ * struct vpu_run - VPU initialization status
+ *
+ * @signaled:          the signal of vpu initialization completed
+ * @fw_ver:            VPU firmware version
+ * @enc_capability:    encoder capability
We should document the meaning of the bit used. Since no bit is used
by the codec driver, document this is not used for now and the value
is reserved for future use.
This is not used for now. I will document this in next version. Thanks.
quoted
+ * @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 {
+       s32 id;
+       u32 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
+ * @enable_4GB:                VPU 4GB mode 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
s/ipi_ack_signaled/ipi_id_ack/. Move to after |ack_wq| to be
consistent with the order of variable declaration.
I will change this in next version. Thanks.
quoted
+ * @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_wdt wdt;
+       struct vpu_ipi_desc ipi_desc[IPI_MAX];
+       struct share_obj *recv_buf;
+       struct share_obj *send_buf;
+       struct device *dev;
+       struct clk *clk;
+       bool enable_4GB;
+       struct mutex vpu_mutex; /* for protecting vpu data data structure */
Remove the comment here. It should be documented in function comment above
It will get message "CHECK" from checkpatch script if removing this
comment.
.
quoted
+       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))
Checking wdt_refcnt and doing vpu_cfg_writel should be done
atomically. For example, if vpu_clock_enable is called after
atomic_dec_and_test(&vpu->wdt_refcnt) and before vpu_cfg_writel(...),
the state of watchdog could be wrong. Adding a spinlock to protect
wdt_refcnt and vpu_cfg_writel together. Then wdt_refcnt doesn't need
to be atomic_t.
I would use mutex to protect this since this only be called in process
context. I will change this in next version. Thanks.
quoted
+               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))
Do we need to enable the watchdog other than vpu_ipi_send? The only
place that AP waits for VPU is in vpu_ipi_send. Right? If yes, can we
just enable and disable the watchdog in vpu_ipi_send?
Watchdog also should be enabled during driver initialization and
firmware download.
quoted
+               vpu_cfg_writel(vpu,
+                              vpu_cfg_readl(vpu, VPU_WDT_REG) | (1L << 31),
+                              VPU_WDT_REG);
+
+       atomic_inc(&vpu->wdt_refcnt);
Same above. atomic_read, vpu_cfg_writel, and atomic_inc should be done
atomically.
I will change this in next version. Thanks.
quoted
+
+       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) {
Check if id >= 0 because type of enum is implementation dependent.
I will change this in next version. Thanks.
quoted
+               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");
print id
I will change this in next version. Thanks.
quoted
+       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) {
+               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");
+               ret = -EINVAL;
+               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 (ret == 0) {
+               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;
I don't see why we need to set it to false here. Remove if not needed.
I will remove this in next version. Thanks.
quoted
+       mutex_unlock(&vpu->vpu_mutex);
+clock_disable:
+       vpu_clock_disable(vpu);
+
+       return ret;
+}
+
+static void vpu_wdt_reset_func(struct work_struct *ws)
+{
+       struct vpu_wdt *wdt = container_of(ws, struct vpu_wdt, ws);
+       struct mtk_vpu *vpu = container_of(wdt, struct mtk_vpu, wdt);
+       struct vpu_wdt_handler *handler = wdt->handler;
+       int index, ret;
+
+       dev_info(vpu->dev, "vpu reset\n");
+       mutex_lock(&vpu->vpu_mutex);
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
This is missing mutex_unlock. Move mutex_lock right before
vpu_cfg_writel(vpu, 0x0, VPU_RESET);. So we don't need to unlock when
vpu_clock_enable fails.
I will change this in next version. Thanks.
quoted
+               dev_err(vpu->dev, "[VPU] wdt enables clock failed %d\n", ret);
+               return;
+       }
+       vpu_cfg_writel(vpu, 0x0, VPU_RESET);
+       vpu_clock_disable(vpu);
+       mutex_unlock(&vpu->vpu_mutex);
+
+       for (index = 0; index < VPU_RST_MAX; index++) {
+               if (handler[index].reset_func) {
+                       handler[index].reset_func(handler[index].priv);
+                       dev_dbg(vpu->dev, "wdt handler func %d\n", index);
+               }
+       }
+}
+
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+                       void wdt_reset(void *),
+                       void *priv, enum rst_id id)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       struct vpu_wdt_handler *handler = vpu->wdt.handler;
+
+       if (!vpu) {
+               dev_err(vpu->dev, "vpu device in not ready\n");
+               return -EPROBE_DEFER;
+       }
+
+       if (id < VPU_RST_MAX && wdt_reset != NULL) {
Check id >= 0
I will change this in next version. Thanks.
quoted
+               dev_dbg(vpu->dev, "wdt register id %d\n", id);
+               mutex_lock(&vpu->vpu_mutex);
+               handler[id].reset_func = wdt_reset;
+               handler[id].priv = priv;
+               mutex_unlock(&vpu->vpu_mutex);
+               return 0;
+       }
+
+       dev_err(vpu->dev, "register vpu wdt handler failed\n");
+       return -EINVAL;
+}
+
+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);
+}
+
+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, "mediatek,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 (ret == 0) {
+               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;
+}
+
+int vpu_compare_version(struct platform_device *pdev,
+                       const char *expected_version)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       int cur_major, cur_minor, cur_build, cur_rel, cur_ver_num;
+       int major, minor, build, rel, ver_num;
+       char *cur_version = vpu->run.fw_ver;
+
+       cur_ver_num = sscanf(cur_version, "%d.%d.%d-rc%d",
+                            &cur_major, &cur_minor, &cur_build, &cur_rel);
+       if (cur_ver_num < 3)
+               return -1;
+       ver_num = sscanf(expected_version, "%d.%d.%d-rc%d",
+                        &major, &minor, &build, &rel);
+       if (ver_num < 3)
+               return -1;
+
+       if (cur_major < major)
+               return -1;
+       if (cur_major > major)
+               return 1;
+
+       if (cur_minor < minor)
+               return -1;
+       if (cur_minor > minor)
+               return 1;
+
+       if (cur_build < build)
+               return -1;
+       if (cur_build > build)
+               return 1;
+
+       if (cur_ver_num < ver_num)
+               return -1;
+       if (cur_ver_num > ver_num)
+               return 1;
+
+       if (ver_num > 3) {
+               if (cur_rel < rel)
+                       return -1;
+               if (cur_rel > rel)
+                       return 1;
+       }
+
+       return 0;
+}
+
+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;
+
+       dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va,
+                         vpu->extmem[fw_type].pa);
+}
+
+static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type)
+{
+       struct device *dev = vpu->dev;
+       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;
+       u32 offset_4gb = vpu->enable_4GB ? 0x40000000 : 0;
+
+       vpu->extmem[fw_type].va = dma_alloc_coherent(dev,
+                                              fw_ext_size,
+                                              &vpu->extmem[fw_type].pa,
+                                              GFP_KERNEL);
+       if (!vpu->extmem[fw_type].va) {
+               dev_err(dev, "Failed to allocate the extended program memory\n");
+               return PTR_ERR(vpu->extmem[fw_type].va);
+       }
+
+       /* Disable extend0. Enable extend1 */
+       vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0);
+       vpu_cfg_writel(vpu, (vpu->extmem[fw_type].pa & 0xFFFFF000) + offset_4gb,
+                      vpu_ext_mem1);
+
+       dev_info(dev, "%s extend memory phy=0x%llx virt=0x%p\n",
+                fw_type ? "Data" : "Program",
+                (unsigned long long)vpu->extmem[fw_type].pa,
+                vpu->extmem[fw_type].va);
+
+       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) {
check rcv_obj->id >= 0
The variable type of "rcv_obj->id" is u32. I think we don't need to
check this range.Thanks.
quoted
+               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;
+       u32 vpu_to_host;
+       int ret;
+
+       /*
+        * Clock should have been enabled already.
+        * Enable again in case vpu_ipi_send times out
+        * and has disabled the clock.
+        */
+       ret = clk_enable(vpu->clk);
+       if (ret) {
+               dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret);
+               return IRQ_NONE;
+       }
+       vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST);
+       if (vpu_to_host & VPU_IPC_INT) {
+               vpu_ipi_handler(vpu);
+       } else {
+               dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host);
+               if (vpu->wdt.wq)
Remove. mtk_vpu_probe makes sure wdt.wq is not NULL.
I will change this in next version. Thanks.
quoted
+                       queue_work(vpu->wdt.wq, &vpu->wdt.ws);
+       }
+
+       /* 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) {
+               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;
+       }
+
+       /* VPU watchdog */
+       vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt");
+       if (!vpu->wdt.wq) {
+               dev_err(dev, "initialize wdt workqueue failed\n");
+               return -ENOMEM;
+       }
+       INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func);
+
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(dev, "enable vpu clock failed\n");
+               goto workqueue_destroy;
+       }
+
+       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;
+       }
+
+       /* 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);
+
+       vpu->enable_4GB = !!(max_pfn > (0xffffffffUL >> PAGE_SHIFT));
+       dev_dbg(dev, "4GB mode %u\n", vpu->enable_4GB);
+
+       if (vpu->enable_4GB) {
+               ret = of_reserved_mem_device_init(dev);
+               if (ret)
+                       dev_info(dev, "init reserved memory failed\n");
+                       /* continue to use dynamic allocation if failed */
+       }
+
+       ret = vpu_alloc_ext_mem(vpu, D_FW);
+       if (ret) {
+               dev_err(dev, "Allocate DM failed\n");
+               goto remove_debugfs;
+       }
+
+       ret = vpu_alloc_ext_mem(vpu, P_FW);
+       if (ret) {
+               dev_err(dev, "Allocate PM failed\n");
+               goto free_d_mem;
+       }
+
+       init_waitqueue_head(&vpu->run.wq);
+       init_waitqueue_head(&vpu->ack_wq);
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "get IRQ resource failed.\n");
+               ret = -ENXIO;
+               goto free_p_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_p_mem;
+       }
+
+       vpu_clock_disable(vpu);
+       dev_dbg(dev, "initialization completed\n");
+
+       return 0;
+
+free_p_mem:
+       vpu_free_ext_mem(vpu, P_FW);
+free_d_mem:
+       vpu_free_ext_mem(vpu, D_FW);
+remove_debugfs:
+       of_reserved_mem_device_release(dev);
+#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);
+workqueue_destroy:
+       destroy_workqueue(vpu->wdt.wq);
+
+       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
+       if (vpu->wdt.wq) {
+               flush_workqueue(vpu->wdt.wq);
+               destroy_workqueue(vpu->wdt.wq);
+       }
+       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..d9c3cde
--- /dev/null
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h
@@ -0,0 +1,167 @@
+/*
+* 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,
+};
+
+/**
+ * enum rst_id - reset id to register reset function for VPU watchdog timeout
+ *
+ * @VPU_RST_DEC: decoder reset id
+ * @VPU_RST_ENC: encoder reset id
+ * @VPU_RST_MDP: MDP (Media Data Path) reset id
+ * @VPU_RST_MAX: maximum reset id
+ */
+enum rst_id {
+       VPU_RST_ENC,
+       VPU_RST_DEC,
+       VPU_RST_MDP,
+       VPU_RST_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_wdt_reg_handler - register a VPU watchdog handler
+ *
+ * @pdev:               VPU platform device
+ * @vpu_wdt_reset_func:        the callback reset function
+ * @private_data:       the private data for reset function
+ * @rst_id:            reset id
+ *
+ * Register a handler performing own tasks when vpu reset by watchdog
+ *
+ * Return: Return 0 if the handler is added successfully,
+ * otherwise it is failed.
+ *
+ **/
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+                       void vpu_wdt_reset_func(void *),
+                       void *priv, enum rst_id id);
+
+/**
+ * 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);
+#endif /* _MTK_VPU_H */
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Re: [PATCH v4 1/8] dt-bindings: Add a binding for Mediatek Video Processor

From: Rob Herring <robh@kernel.org>
Date: 2016-02-08 18:54:22

On Thu, Feb 04, 2016 at 07:34:56PM +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>
---
 .../devicetree/bindings/media/mediatek-vpu.txt     |   31 ++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/mediatek-vpu.txt
Acked-by: Rob Herring <robh@kernel.org>

Re: [PATCH v4 0/8] Add MT8173 Video Encoder Driver and VPU Driver

From: Hans Verkuil <hidden>
Date: 2016-02-15 12:04:59

Hi Tiffany,

Once again, thanks for your patch series!

I have some more comments below.

On 02/04/2016 12:34 PM, Tiffany Lin wrote:
==============
 Introduction
==============

The purpose of this series is to add the driver for video codec hw embedded in the Mediatek's MT8173 SoCs.
Mediatek Video Codec is able to handle video encoding of in a range of formats.

This patch series 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[1] and MTK SMI[2] have not yet been merged, but we wanted to start discussion about the driver earlier so it could be merged sooner.

[1]https://patchwork.kernel.org/patch/8120921/
[2]https://patchwork.kernel.org/patch/8120941/


==================
 Device interface
==================

In principle the driver bases on v4l2 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).

==============================
 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 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 physical address for VPU 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 v4:
Vcodec Part
1. Remove MTK_ENCODE_PARAM_SKIP_FRAME support
2. Remove MTK_ENCODE_PARAM_FRAME_TYPE and change to use V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME[3]
3. Refine Encoder HW clock source
4. Refine debug log
5. Add watchdog support
6. With patch "media: v4l2-compat-ioctl32: fix missing length copy in put_v4l2_buffer32"[4], v4l2-compliance test passed[5] in v4.4-rc5 

VPU Part
1. These two patches were Acked-by: Rob Herring [off-list ref] in v3
   [PATCH v3 1/8] dt-bindings: Add a binding for Mediatek Video Processor
   [PATCH v3 3/8] arm64: dts: mediatek: Add node for Mediatek Video Processor Unit
   Because we were wrong about how the hardware works, there is no connection between VPU and IOMMU HW
   We remove VPU attaching to IOMMU
2. Support VPU running on 4GB DRAM system
3. Support VPU watchdog reset
4. Refine for coding style 

[3]https://patchwork.linuxtv.org/patch/32670/
[4] https://patchwork.linuxtv.org/patch/32631/
[5]localhost ~ # /usr/bin/v4l2-compliance -d /dev/video1
Driver Info:
        Driver name   : mtk-vcodec-en
        Card type     : platform:mt817
        Bus info      : platform:mt817
Perhaps this should be mt817x?
        Driver version: 4.4.0
        Capabilities  : 0x84204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format

Compliance test for device /dev/video1 (not using libv4l2):

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second video open: OK
        test VIDIOC_QUERYCAP: OK
        test VIDIOC_G/S_PRIORITY: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

        Control ioctls:
                test VIDIOC_QUERYCTRL/MENU: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 12 Private Controls: 0

        Format ioctls:
                test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
                test VIDIOC_G/S_PARM: OK (Not Supported)
                test VIDIOC_G_FBUF: OK (Not Supported)
                test VIDIOC_G_FMT: OK
                test VIDIOC_TRY_FMT: OK
                test VIDIOC_S_FMT: OK
                test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                warn: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(475): VIDIOC_CREATE_BUFS not supported
                warn: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(475): VIDIOC_CREATE_BUFS not supported
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
                test VIDIOC_EXPBUF: OK

Total: 38, Succeeded: 38, Failed: 0, Warnings: 2
Looks great!

Having reviewed the v4 patch series I'd say it is close to being ready (at least
w.r.t. the V4L2 parts). Most of my comments are minor, but fixing the open()
limitation is probably a bigger change on your side.

It should be well worth the effort, though. In my experience limiting the number
of time you can call open() is always a sign of a design flaw and the code improves
if this is handled in the right place (usually start_streaming, which is when you
actually start to use the hardware).

And besides, limiting the number of open()s is out-of-spec.

Regards,

	Hans

Change in v3:
1.Refine code to pass v4l2-compliance test, now it still has 2 issues 2.Refine code according to latest MTK IOMMU patches[1] 3.Remove MFC51 specific CIDs and add MTK specific CIDs for for keyframe and
  skip I-frame
4.Refine code according to review comments

Below is the v1.6 version v4l2-compliance report for the mt8173 encoder driver.
Now there are still 2 test fail in v1.6.
For VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF, we directly use v4l2_m2m_ioctl_* functions, but it still fail. It pass in kernel 3.18 but fail in kernel 4.4.
We will try v1.8 in next version.
VIDIOC_EXPBUF is becuase we support all three memory types VB2_DMABUF, VB2_MMAP and VB2_USERPTR. VIDIOC_EXPBUF only allowed when only VB2_MMAP supported.
localhost ~ # /usr/bin/v4l2-compliance -d /dev/video1 Driver Info:
        Driver name   : mtk-vcodec-en
        Card type     : platform:mt817
        Bus info      : platform:mt817
        Driver version: 4.4.0
        Capabilities  : 0x84204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format

Compliance test for device /dev/video1 (not using libv4l2):

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second video open: OK
        test VIDIOC_QUERYCAP: OK
        test VIDIOC_G/S_PRIORITY: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

        Control ioctls:
                test VIDIOC_QUERYCTRL/MENU: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 11 Private Controls: 2

        Format ioctls:
                test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
                test VIDIOC_G/S_PARM: OK (Not Supported)
                test VIDIOC_G_FBUF: OK (Not Supported)
                test VIDIOC_G_FMT: OK
                test VIDIOC_TRY_FMT: OK
                test VIDIOC_S_FMT: OK
                test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(266): vp->length == 0
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(335): buf.check(Unqueued, i)
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(420): testQueryBuf(node, i, q.g_buffers())
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: FAIL
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(500): q.has_expbuf(node)
                test VIDIOC_EXPBUF: FAIL
Total: 38, Succeeded: 36, Failed: 2, Warnings: 0


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):
  [media] VPU: mediatek: support Mediatek VPU
  arm64: dts: mediatek: Add node for Mediatek Video Processor Unit
  dt-bindings: Add a binding for Mediatek Video Encoder
  [media] vcodec: mediatek: Add Mediatek VP8 Video Encoder Driver
  [media] vcodec: mediatek: Add Mediatek H264 Video Encoder Driver
  arm64: dts: mediatek: Add Video Encoder for MT8173

 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 +
 .../devicetree/bindings/media/mediatek-vpu.txt     |   31 +
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |   62 +
 drivers/media/platform/Kconfig                     |   20 +
 drivers/media/platform/Makefile                    |    4 +
 drivers/media/platform/mtk-vcodec/Makefile         |   11 +
 .../media/platform/mtk-vcodec/h264_enc/Makefile    |    6 +
 .../platform/mtk-vcodec/h264_enc/venc_h264_if.c    |  540 ++++++++
 .../platform/mtk-vcodec/h264_enc/venc_h264_if.h    |  165 +++
 .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.c   |  309 +++++
 .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.h   |   30 +
 drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h |  388 ++++++
 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 1380 ++++++++++++++++++++
 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h |   46 +
 .../media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c |  476 +++++++
 .../media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c  |  132 ++
 .../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    |  106 ++
 drivers/media/platform/mtk-vcodec/venc_drv_if.h    |  175 +++
 drivers/media/platform/mtk-vcodec/venc_ipi_msg.h   |  212 +++
 drivers/media/platform/mtk-vcodec/vp8_enc/Makefile |    6 +
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.c      |  422 ++++++
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.h      |  149 +++
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c     |  240 ++++
 .../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           |  994 ++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h           |  167 +++
 include/uapi/linux/v4l2-controls.h                 |    4 +
 34 files changed, 6573 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
 mode change 100644 => 100755 include/uapi/linux/v4l2-controls.h

Re: [PATCH v4 0/8] Add MT8173 Video Encoder Driver and VPU Driver

From: tiffany lin <tiffany.lin@mediatek.com>
Date: 2016-02-16 06:46:46

Hi Hans,

On Mon, 2016-02-15 at 13:04 +0100, Hans Verkuil wrote:
Hi Tiffany,

Once again, thanks for your patch series!

I have some more comments below.

On 02/04/2016 12:34 PM, Tiffany Lin wrote:
quoted
==============
 Introduction
==============

The purpose of this series is to add the driver for video codec hw embedded in the Mediatek's MT8173 SoCs.
Mediatek Video Codec is able to handle video encoding of in a range of formats.

This patch series 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[1] and MTK SMI[2] have not yet been merged, but we wanted to start discussion about the driver earlier so it could be merged sooner.

[1]https://patchwork.kernel.org/patch/8120921/
[2]https://patchwork.kernel.org/patch/8120941/


==================
 Device interface
==================

In principle the driver bases on v4l2 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).

==============================
 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 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 physical address for VPU 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 v4:
Vcodec Part
1. Remove MTK_ENCODE_PARAM_SKIP_FRAME support
2. Remove MTK_ENCODE_PARAM_FRAME_TYPE and change to use V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME[3]
3. Refine Encoder HW clock source
4. Refine debug log
5. Add watchdog support
6. With patch "media: v4l2-compat-ioctl32: fix missing length copy in put_v4l2_buffer32"[4], v4l2-compliance test passed[5] in v4.4-rc5 

VPU Part
1. These two patches were Acked-by: Rob Herring [off-list ref] in v3
   [PATCH v3 1/8] dt-bindings: Add a binding for Mediatek Video Processor
   [PATCH v3 3/8] arm64: dts: mediatek: Add node for Mediatek Video Processor Unit
   Because we were wrong about how the hardware works, there is no connection between VPU and IOMMU HW
   We remove VPU attaching to IOMMU
2. Support VPU running on 4GB DRAM system
3. Support VPU watchdog reset
4. Refine for coding style 

[3]https://patchwork.linuxtv.org/patch/32670/
[4] https://patchwork.linuxtv.org/patch/32631/
[5]localhost ~ # /usr/bin/v4l2-compliance -d /dev/video1
Driver Info:
        Driver name   : mtk-vcodec-en
        Card type     : platform:mt817
        Bus info      : platform:mt817
Perhaps this should be mt817x?
quoted
        Driver version: 4.4.0
        Capabilities  : 0x84204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format

Compliance test for device /dev/video1 (not using libv4l2):

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second video open: OK
        test VIDIOC_QUERYCAP: OK
        test VIDIOC_G/S_PRIORITY: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

        Control ioctls:
                test VIDIOC_QUERYCTRL/MENU: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 12 Private Controls: 0

        Format ioctls:
                test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
                test VIDIOC_G/S_PARM: OK (Not Supported)
                test VIDIOC_G_FBUF: OK (Not Supported)
                test VIDIOC_G_FMT: OK
                test VIDIOC_TRY_FMT: OK
                test VIDIOC_S_FMT: OK
                test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                warn: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(475): VIDIOC_CREATE_BUFS not supported
                warn: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(475): VIDIOC_CREATE_BUFS not supported
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
                test VIDIOC_EXPBUF: OK

Total: 38, Succeeded: 38, Failed: 0, Warnings: 2
Looks great!

Having reviewed the v4 patch series I'd say it is close to being ready (at least
w.r.t. the V4L2 parts). Most of my comments are minor, but fixing the open()
limitation is probably a bigger change on your side.

It should be well worth the effort, though. In my experience limiting the number
of time you can call open() is always a sign of a design flaw and the code improves
if this is handled in the right place (usually start_streaming, which is when you
actually start to use the hardware).

And besides, limiting the number of open()s is out-of-spec.
You are right. We did not think about this before.
We will remove open() limitation and put limitation control in right
place.
Appreciated your time and suggestions.


Regards,

	Hans
quoted

Change in v3:
1.Refine code to pass v4l2-compliance test, now it still has 2 issues 2.Refine code according to latest MTK IOMMU patches[1] 3.Remove MFC51 specific CIDs and add MTK specific CIDs for for keyframe and
  skip I-frame
4.Refine code according to review comments

Below is the v1.6 version v4l2-compliance report for the mt8173 encoder driver.
Now there are still 2 test fail in v1.6.
For VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF, we directly use v4l2_m2m_ioctl_* functions, but it still fail. It pass in kernel 3.18 but fail in kernel 4.4.
We will try v1.8 in next version.
VIDIOC_EXPBUF is becuase we support all three memory types VB2_DMABUF, VB2_MMAP and VB2_USERPTR. VIDIOC_EXPBUF only allowed when only VB2_MMAP supported.
localhost ~ # /usr/bin/v4l2-compliance -d /dev/video1 Driver Info:
        Driver name   : mtk-vcodec-en
        Card type     : platform:mt817
        Bus info      : platform:mt817
        Driver version: 4.4.0
        Capabilities  : 0x84204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04204000
                Video Memory-to-Memory Multiplanar
                Streaming
                Extended Pix Format

Compliance test for device /dev/video1 (not using libv4l2):

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second video open: OK
        test VIDIOC_QUERYCAP: OK
        test VIDIOC_G/S_PRIORITY: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

        Control ioctls:
                test VIDIOC_QUERYCTRL/MENU: OK
                test VIDIOC_G/S_CTRL: OK
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 11 Private Controls: 2

        Format ioctls:
                test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
                test VIDIOC_G/S_PARM: OK (Not Supported)
                test VIDIOC_G_FBUF: OK (Not Supported)
                test VIDIOC_G_FMT: OK
                test VIDIOC_TRY_FMT: OK
                test VIDIOC_S_FMT: OK
                test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(266): vp->length == 0
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(335): buf.check(Unqueued, i)
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(420): testQueryBuf(node, i, q.g_buffers())
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: FAIL
                fail: ../../../v4l-utils-1.6.0/utils/v4l2-compliance/v4l2-test-buffers.cpp(500): q.has_expbuf(node)
                test VIDIOC_EXPBUF: FAIL
Total: 38, Succeeded: 36, Failed: 2, Warnings: 0


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):
  [media] VPU: mediatek: support Mediatek VPU
  arm64: dts: mediatek: Add node for Mediatek Video Processor Unit
  dt-bindings: Add a binding for Mediatek Video Encoder
  [media] vcodec: mediatek: Add Mediatek VP8 Video Encoder Driver
  [media] vcodec: mediatek: Add Mediatek H264 Video Encoder Driver
  arm64: dts: mediatek: Add Video Encoder for MT8173

 .../devicetree/bindings/media/mediatek-vcodec.txt  |   59 +
 .../devicetree/bindings/media/mediatek-vpu.txt     |   31 +
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |   62 +
 drivers/media/platform/Kconfig                     |   20 +
 drivers/media/platform/Makefile                    |    4 +
 drivers/media/platform/mtk-vcodec/Makefile         |   11 +
 .../media/platform/mtk-vcodec/h264_enc/Makefile    |    6 +
 .../platform/mtk-vcodec/h264_enc/venc_h264_if.c    |  540 ++++++++
 .../platform/mtk-vcodec/h264_enc/venc_h264_if.h    |  165 +++
 .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.c   |  309 +++++
 .../platform/mtk-vcodec/h264_enc/venc_h264_vpu.h   |   30 +
 drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h |  388 ++++++
 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 1380 ++++++++++++++++++++
 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h |   46 +
 .../media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c |  476 +++++++
 .../media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c  |  132 ++
 .../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    |  106 ++
 drivers/media/platform/mtk-vcodec/venc_drv_if.h    |  175 +++
 drivers/media/platform/mtk-vcodec/venc_ipi_msg.h   |  212 +++
 drivers/media/platform/mtk-vcodec/vp8_enc/Makefile |    6 +
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.c      |  422 ++++++
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_if.h      |  149 +++
 .../platform/mtk-vcodec/vp8_enc/venc_vp8_vpu.c     |  240 ++++
 .../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           |  994 ++++++++++++++
 drivers/media/platform/mtk-vpu/mtk_vpu.h           |  167 +++
 include/uapi/linux/v4l2-controls.h                 |    4 +
 34 files changed, 6573 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
 mode change 100644 => 100755 include/uapi/linux/v4l2-controls.h
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help