All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC/PATCH 0/4] Multi Format Codec 5.0 driver for S5PC110 SoC
@ 2010-10-13 11:09 Kamil Debski
  2010-10-13 11:09 ` [PATCH 1/4] MFC: Changes in include/linux/videodev2.h for MFC 5.1 codec Kamil Debski
                   ` (3 more replies)
  0 siblings, 4 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-13 11:09 UTC (permalink / raw)
  To: linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, k.debski, jaeryul.oh, kgene.kim

Hello,

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

The purpose of this RFC is to discuss the driver for a hw video codec
embedded in the new Samusng's SoCs. Multi Format Codec 5.0 is able to
handle video decoding of in a range of formats.

So far no hardware codec was supported in V4L2 and this would be the
first one. I guess there are more similar device that would benefit from
a V4L2 unified interface. I suggest a separate control class for codec
devices - V4L2_CTRL_CLASS_CODEC. 

Internally the driver uses videobuf2 framework and CMA memory allocator.
I am aware that those have not yet been merged, but I wanted to start
discussion about the driver earlier so it could be merged sooner. The
driver posted here is the initial version, so I suppose it will require
more work.

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

In principle the driver bases on the idea of memory-to-memory devices:
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. encoded video frames)
and CAPTURE (for destination buffers, i.e. decoded raw video frames).
The process of decoding video data from stream is a bit more complicated
than typical memory-to-memory processing, that's why the m2m framework
is not directly used (it is too limited for this case). The main reason
for this is the fact that the CAPTURE buffers can be dequeued in a
different order than they queued. The hw block decides which buffer has
been completely processed. This is due to the structure of most
compressed video streams - use of B frames causes that decoding and
display order may be different.

==============================
 Decoding initialization path
==============================

First the OUTPUT queue is initialized. With S_FMT the application
chooses which video format to decode and what size should be the input
buffer. Fourcc values have been defined for different codecs e.g.
V4L2_PIX_FMT_H264 for h264. Then the OUTPUT buffers are requested and
mmaped. The stream header frame is loaded into the first buffer,
queued and streaming is enabled. At this point the hardware is able to
start processing the stream header and afterwards it will have
information about the video dimensions and the size of the buffers with
raw video data.

The next step is setting up the CAPTURE queue and buffers. The width,
height, buffer size and minimum number of buffers can be read with G_FMT
call. The application can request more output buffer if necessary. After
requesting and mmaping buffers the device is ready to decode video
stream.

The stream frames (ES frames) are written to the OUTPUT buffers,
and decoded video frames can be read from the CAPTURE buffers. When no
more source frames are present a single buffer with bytesused set to 0
should be queued. This will inform the driver that processing should be
finished and it can dequeue all video frames that are still left. The
number of such frames is dependent on the stream and its internal
structure (how many frames had to be kept as reference frames for
decoding, etc).

===============
 Usage summary
===============

This is a step by step summary of the video decoding (from user
application point of view, with 2 treads and blocking api):

01. S_FMT(OUTPUT, V4L2_PIX_FMT_H264, ...)
02. REQ_BUFS(OUTPUT, n)
03. for i=1..n MMAP(OUTPUT, i)
04. put stream header to buffer #1
05. QBUF(OUTPUT, #1)
06. STREAM_ON(OUTPUT)
07. G_FMT(CAPTURE)
08. REQ_BUFS(CAPTURE, m)
09. for j=1..m MMAP(CAPTURE, j)
10. for j=1..m QBUF(CAPTURE, #j)
11. STREAM_ON(CAPTURE)

display thread:
12. DQBUF(CAPTURE) -> got decoded video data in buffer #j
13. display buffer #j
14. QBUF(CAPTURE, #j)
15. goto 12

parser thread:
16. put next ES frame to buffer #i
17. QBUF(OUTPUT, #i)
18. DQBUF(OUTPUT) -> get next empty buffer #i
19. goto 16

...

Similar usage sequence can be achieved with single threaded application
and non-blocking api with poll() call.

Branch with MFC, CMA and videobuf2 will be soon available at
http://git.infradead.org/users/kmpark/linux-2.6-samsung/shortlog/refs/heads/mfc
This tree is based on kgene/for-next (Samsung S5P platform 'next' tree).

Please have a look at the code and the idea of how to introduce codec
devices to V4L2. Comments will be very much appreciated.

Best regards,
Kamil Debski

Patch summary:

Kamil Debski (4):
  MFC: Changes in include/linux/videodev2.h for MFC 5.1 codec
  MFC: Add MFC 5.1 driver to plat-s5p
  MFC: Add MFC 5.1 V4L2 driver
  s5pc110: Enable MFC 5.1 on Goni

 arch/arm/mach-s5pv210/Kconfig                |    1 +
 arch/arm/mach-s5pv210/clock.c                |    6 +
 arch/arm/mach-s5pv210/include/mach/map.h     |    4 +
 arch/arm/mach-s5pv210/mach-goni.c            |    1 +
 arch/arm/plat-s5p/Kconfig                    |    5 +
 arch/arm/plat-s5p/Makefile                   |    1 +
 arch/arm/plat-s5p/dev-mfc5.c                 |   37 +
 arch/arm/plat-samsung/include/plat/devs.h    |    2 +
 drivers/media/video/Kconfig                  |    8 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/s5p-mfc/Makefile         |    3 +
 drivers/media/video/s5p-mfc/regs-mfc5.h      |  305 +++++
 drivers/media/video/s5p-mfc/s5p_mfc.c        | 1879 ++++++++++++++++++++++++++
 drivers/media/video/s5p-mfc/s5p_mfc_common.h |  190 +++
 drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h  |  173 +++
 drivers/media/video/s5p-mfc/s5p_mfc_intr.c   |   77 ++
 drivers/media/video/s5p-mfc/s5p_mfc_intr.h   |   26 +
 drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h |   65 +
 drivers/media/video/s5p-mfc/s5p_mfc_memory.h |   32 +
 drivers/media/video/s5p-mfc/s5p_mfc_opr.c    |  800 +++++++++++
 drivers/media/video/s5p-mfc/s5p_mfc_opr.h    |   90 ++
 include/linux/videodev2.h                    |   48 +
 22 files changed, 3754 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/plat-s5p/dev-mfc5.c
 create mode 100644 drivers/media/video/s5p-mfc/Makefile
 create mode 100644 drivers/media/video/s5p-mfc/regs-mfc5.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc.c
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_common.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.c
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_memory.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.c
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.h


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

* [PATCH 1/4] MFC: Changes in include/linux/videodev2.h for MFC 5.1 codec
  2010-10-13 11:09 [RFC/PATCH 0/4] Multi Format Codec 5.0 driver for S5PC110 SoC Kamil Debski
@ 2010-10-13 11:09 ` Kamil Debski
  2010-10-13 11:09 ` [PATCH 2/4] MFC: Add MFC 5.1 driver to plat-s5p Kamil Debski
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-13 11:09 UTC (permalink / raw)
  To: linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, k.debski, jaeryul.oh, kgene.kim

Add fourcc values for compressed video stream formats and
V4L2_CTRL_CLASS_CODEC. Also adds controls used by MFC driver.


Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 include/linux/videodev2.h |   48 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 48 insertions(+), 0 deletions(-)

diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index dd3d93d..2614a62 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -349,6 +349,14 @@ struct v4l2_pix_format {
 /* three non-contiguous planes -- Y, Cb, Cr */
 #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
 
+/* two non contiguous planes -- one Y, one Cr + Cb interleaved  */
+#define V4L2_PIX_FMT_NV12M   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
+/* 12  Y/CbCr 4:2:0 64x32 macroblocks */
+#define V4L2_PIX_FMT_NV12MT  v4l2_fourcc('T', 'M', '1', '2')
+
+/* three non contiguous planes -- Y, Cb, Cr */
+#define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
+
 /* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
 #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B', 'A', '8', '1') /*  8  BGBG.. GRGR.. */
 #define V4L2_PIX_FMT_SGBRG8  v4l2_fourcc('G', 'B', 'R', 'G') /*  8  GBGB.. RGRG.. */
@@ -372,6 +380,21 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_DV       v4l2_fourcc('d', 'v', 's', 'd') /* 1394          */
 #define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4    */
 
+
+#define V4L2_PIX_FMT_H264     v4l2_fourcc('H', '2', '6', '4') /* H264    */
+#define V4L2_PIX_FMT_H263     v4l2_fourcc('H', '2', '6', '3') /* H263    */
+#define V4L2_PIX_FMT_MPEG12   v4l2_fourcc('M', 'P', '1', '2') /* MPEG-1/2  */
+#define V4L2_PIX_FMT_MPEG4    v4l2_fourcc('M', 'P', 'G', '4') /* MPEG-4  */
+#define V4L2_PIX_FMT_DIVX     v4l2_fourcc('D', 'I', 'V', 'X') /* DivX  */
+#define V4L2_PIX_FMT_DIVX3    v4l2_fourcc('D', 'I', 'V', '3') /* DivX 3.11  */
+#define V4L2_PIX_FMT_DIVX4    v4l2_fourcc('D', 'I', 'V', '4') /* DivX 4.12  */
+#define V4L2_PIX_FMT_DIVX500    v4l2_fourcc('D', 'X', '5', '2') /* DivX 5.00 - 5.02  */
+#define V4L2_PIX_FMT_DIVX503    v4l2_fourcc('D', 'X', '5', '3') /* DivX 5.03 - x  */
+#define V4L2_PIX_FMT_XVID     v4l2_fourcc('X', 'V', 'I', 'D') /* Xvid */
+#define V4L2_PIX_FMT_VC1      v4l2_fourcc('V', 'C', '1', 'A') /* VC-1 */
+#define V4L2_PIX_FMT_VC1_RCV      v4l2_fourcc('V', 'C', '1', 'R') /* VC-1 RCV */
+
+
 /*  Vendor-specific formats   */
 #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
 #define V4L2_PIX_FMT_WNVA     v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */
@@ -980,6 +1003,7 @@ struct v4l2_output {
 #define V4L2_OUTPUT_TYPE_ANALOG			2
 #define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY	3
 
+
 /* capabilities flags */
 #define V4L2_OUT_CAP_PRESETS		0x00000001 /* Supports S_DV_PRESET */
 #define V4L2_OUT_CAP_CUSTOM_TIMINGS	0x00000002 /* Supports S_DV_TIMINGS */
@@ -1017,6 +1041,7 @@ struct v4l2_ext_controls {
 #define V4L2_CTRL_CLASS_MPEG 0x00990000	/* MPEG-compression controls */
 #define V4L2_CTRL_CLASS_CAMERA 0x009a0000	/* Camera class controls */
 #define V4L2_CTRL_CLASS_FM_TX 0x009b0000	/* FM Modulator control class */
+#define V4L2_CTRL_CLASS_CODEC 0x009c0000	/* Codec control class */
 
 #define V4L2_CTRL_ID_MASK      	  (0x0fffffff)
 #define V4L2_CTRL_ID2CLASS(id)    ((id) & 0x0fff0000UL)
@@ -1347,6 +1372,29 @@ enum v4l2_mpeg_cx2341x_video_median_filter_type {
 #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP 	(V4L2_CID_MPEG_CX2341X_BASE+10)
 #define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS 	(V4L2_CID_MPEG_CX2341X_BASE+11)
 
+/* For codecs */
+
+#define V4L2_CID_CODEC_BASE 			(V4L2_CTRL_CLASS_CODEC | 0x900)
+#define V4L2_CID_CODEC_CLASS 			(V4L2_CTRL_CLASS_CODEC | 1)
+
+/* For both decoding and encoding */
+
+/* For encoding */
+#define V4L2_CID_CODEC_LOOP_FILTER_H264		(V4L2_CID_CODEC_BASE + 9)
+enum v4l2_cid_codec_loop_filter_h264 {
+	V4L2_CID_CODEC_LOOP_FILTER_H264_ENABLE = 0,
+	V4L2_CID_CODEC_LOOP_FILTER_H264_DISABLE = 1,
+	V4L2_CID_CODEC_LOOP_FILTER_H264_DISABLE_AT_BOUNDARY = 2,
+};
+
+/* For decoding */
+
+#define V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE	(V4L2_CID_CODEC_BASE + 110)
+#define V4L2_CID_CODEC_DISPLAY_DELAY		(V4L2_CID_CODEC_BASE + 137)
+#define V4L2_CID_CODEC_REQ_NUM_BUFS		(V4L2_CID_CODEC_BASE + 140)
+#define V4L2_CID_CODEC_SLICE_INTERFACE		(V4L2_CID_CODEC_BASE + 141)
+#define V4L2_CID_CODEC_PACKED_PB		(V4L2_CID_CODEC_BASE + 142)
+
 /*  Camera class control IDs */
 #define V4L2_CID_CAMERA_CLASS_BASE 	(V4L2_CTRL_CLASS_CAMERA | 0x900)
 #define V4L2_CID_CAMERA_CLASS 		(V4L2_CTRL_CLASS_CAMERA | 1)
-- 
1.6.3.3


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

* [PATCH 2/4] MFC: Add MFC 5.1 driver to plat-s5p
  2010-10-13 11:09 [RFC/PATCH 0/4] Multi Format Codec 5.0 driver for S5PC110 SoC Kamil Debski
  2010-10-13 11:09 ` [PATCH 1/4] MFC: Changes in include/linux/videodev2.h for MFC 5.1 codec Kamil Debski
@ 2010-10-13 11:09 ` Kamil Debski
  2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
  2010-10-13 11:09 ` [PATCH 4/4] s5pc110: Enable MFC 5.1 on Goni Kamil Debski
  3 siblings, 0 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-13 11:09 UTC (permalink / raw)
  To: linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, k.debski, jaeryul.oh, kgene.kim

Add platform support for Multi Format Codec 5.1 is a module available
on S5PC110 and S5PC210 Samsung SoCs. Hardware is capable of handling
a range of video codecs and this driver provides V4L2 interface for
video decoding.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 arch/arm/mach-s5pv210/clock.c             |    6 ++++
 arch/arm/mach-s5pv210/include/mach/map.h  |    4 +++
 arch/arm/plat-s5p/Kconfig                 |    5 ++++
 arch/arm/plat-s5p/Makefile                |    1 +
 arch/arm/plat-s5p/dev-mfc5.c              |   37 +++++++++++++++++++++++++++++
 arch/arm/plat-samsung/include/plat/devs.h |    2 +
 6 files changed, 55 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/plat-s5p/dev-mfc5.c

diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c
index d562670..0717a07 100644
--- a/arch/arm/mach-s5pv210/clock.c
+++ b/arch/arm/mach-s5pv210/clock.c
@@ -294,6 +294,12 @@ static struct clk init_clocks_disable[] = {
 		.enable		= s5pv210_clk_ip0_ctrl,
 		.ctrlbit	= (1 << 26),
 	}, {
+		.name		= "mfc",
+		.id		= -1,
+		.parent		= &clk_pclk_psys.clk,
+		.enable		= s5pv210_clk_ip0_ctrl,
+		.ctrlbit	= (1 << 16),
+	}, {
 		.name		= "otg",
 		.id		= -1,
 		.parent		= &clk_hclk_psys.clk,
diff --git a/arch/arm/mach-s5pv210/include/mach/map.h b/arch/arm/mach-s5pv210/include/mach/map.h
index 586652f..9960d50 100644
--- a/arch/arm/mach-s5pv210/include/mach/map.h
+++ b/arch/arm/mach-s5pv210/include/mach/map.h
@@ -104,6 +104,9 @@
 #define S5PV210_PA_DMC0		(0xF0000000)
 #define S5PV210_PA_DMC1		(0xF1400000)
 
+/* MFC */
+#define S5PV210_PA_MFC		(0xF1700000)
+
 /* compatibiltiy defines. */
 #define S3C_PA_UART		S5PV210_PA_UART
 #define S3C_PA_HSMMC0		S5PV210_PA_HSMMC(0)
@@ -120,6 +123,7 @@
 #define S5P_PA_FIMC0		S5PV210_PA_FIMC0
 #define S5P_PA_FIMC1		S5PV210_PA_FIMC1
 #define S5P_PA_FIMC2		S5PV210_PA_FIMC2
+#define S5P_PA_MFC		S5PV210_PA_MFC
 
 #define SAMSUNG_PA_ADC		S5PV210_PA_ADC
 #define SAMSUNG_PA_CFCON	S5PV210_PA_CFCON
diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig
index 65dbfa8..a1918fc 100644
--- a/arch/arm/plat-s5p/Kconfig
+++ b/arch/arm/plat-s5p/Kconfig
@@ -5,6 +5,11 @@
 #
 # Licensed under GPLv2
 
+config S5P_DEV_MFC
+	bool
+	help
+	  Compile in platform device definitions for MFC 
+	  
 config PLAT_S5P
 	bool
 	depends on (ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310)
diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile
index de65238..d1d1ea9 100644
--- a/arch/arm/plat-s5p/Makefile
+++ b/arch/arm/plat-s5p/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_PM)		+= pm.o
 obj-$(CONFIG_PM)		+= irq-pm.o
 
 # devices
+obj-$(CONFIG_S5P_DEV_MFC)	+= dev-mfc5.o
 
 obj-$(CONFIG_S5P_DEV_FIMC0)	+= dev-fimc0.o
 obj-$(CONFIG_S5P_DEV_FIMC1)	+= dev-fimc1.o
diff --git a/arch/arm/plat-s5p/dev-mfc5.c b/arch/arm/plat-s5p/dev-mfc5.c
new file mode 100644
index 0000000..c06ea97
--- /dev/null
+++ b/arch/arm/plat-s5p/dev-mfc5.c
@@ -0,0 +1,37 @@
+/* Base S3C64XX mfc resource and device definitions */
+
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+
+#include <mach/map.h>
+#include <plat/map-base.h>
+#include <plat/devs.h>
+#include <plat/irqs.h>
+
+/* MFC controller */
+static struct resource s5p_mfc_resource[] = {
+	[0] = {
+		.start  = S5P_PA_MFC,
+		.end    = S5P_PA_MFC + SZ_64K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = IRQ_MFC,
+		.end    = IRQ_MFC,
+		.flags  = IORESOURCE_IRQ,
+	}
+};
+
+struct platform_device s5p_device_mfc5 = {
+	.name          = "s5p-mfc5",
+	.id            = -1,
+	.num_resources = ARRAY_SIZE(s5p_mfc_resource),
+	.resource      = s5p_mfc_resource,
+};
+
+EXPORT_SYMBOL(s5p_device_mfc5);
+
+
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 71bcc0f..39948de 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -118,6 +118,8 @@ extern struct platform_device s5p_device_fimc0;
 extern struct platform_device s5p_device_fimc1;
 extern struct platform_device s5p_device_fimc2;
 
+extern struct platform_device s5p_device_mfc5;
+
 /* s3c2440 specific devices */
 
 #ifdef CONFIG_CPU_S3C2440
-- 
1.6.3.3


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

* [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-13 11:09 [RFC/PATCH 0/4] Multi Format Codec 5.0 driver for S5PC110 SoC Kamil Debski
  2010-10-13 11:09 ` [PATCH 1/4] MFC: Changes in include/linux/videodev2.h for MFC 5.1 codec Kamil Debski
  2010-10-13 11:09 ` [PATCH 2/4] MFC: Add MFC 5.1 driver to plat-s5p Kamil Debski
@ 2010-10-13 11:09 ` Kamil Debski
  2010-10-14  6:25   ` Jaeryul Oh
                     ` (4 more replies)
  2010-10-13 11:09 ` [PATCH 4/4] s5pc110: Enable MFC 5.1 on Goni Kamil Debski
  3 siblings, 5 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-13 11:09 UTC (permalink / raw)
  To: linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, k.debski, jaeryul.oh, kgene.kim

Multi Format Codec 5.1 is a module available on S5PC110 and S5PC210
Samsung SoCs. Hardware is capable of handling a range of video codecs
and this driver provides V4L2 interface for video decoding.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig                  |    8 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/s5p-mfc/Makefile         |    3 +
 drivers/media/video/s5p-mfc/regs-mfc5.h      |  305 +++++
 drivers/media/video/s5p-mfc/s5p_mfc.c        | 1879 ++++++++++++++++++++++++++
 drivers/media/video/s5p-mfc/s5p_mfc_common.h |  190 +++
 drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h  |  173 +++
 drivers/media/video/s5p-mfc/s5p_mfc_intr.c   |   77 ++
 drivers/media/video/s5p-mfc/s5p_mfc_intr.h   |   26 +
 drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h |   65 +
 drivers/media/video/s5p-mfc/s5p_mfc_memory.h |   32 +
 drivers/media/video/s5p-mfc/s5p_mfc_opr.c    |  800 +++++++++++
 drivers/media/video/s5p-mfc/s5p_mfc_opr.h    |   90 ++
 13 files changed, 3649 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/s5p-mfc/Makefile
 create mode 100644 drivers/media/video/s5p-mfc/regs-mfc5.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc.c
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_common.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.c
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_memory.h
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.c
 create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 6d0bd36..1d0b91e 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1047,4 +1047,12 @@ config  VIDEO_SAMSUNG_S5P_FIMC
 	  This is a v4l2 driver for the S5P camera interface
 	  (video postprocessor)
 
+config VIDEO_SAMSUNG_S5P_MFC
+	tristate "Samsung S5P MFC 5.0 Video Codec"
+	depends on VIDEO_V4L2 && CMA
+	select VIDEOBUF2_CMA
+	default n
+	help
+	    MFC 5.0 driver for V4L2.
+
 endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 4146700..117a3cb 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -178,6 +178,7 @@ obj-$(CONFIG_VIDEO_PXA27x)		+= pxa_camera.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
 obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
 
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
 
diff --git a/drivers/media/video/s5p-mfc/Makefile b/drivers/media/video/s5p-mfc/Makefile
new file mode 100644
index 0000000..69b6294
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
+s5p-mfc-y := s5p_mfc.o s5p_mfc_intr.o  s5p_mfc_opr.o
+
diff --git a/drivers/media/video/s5p-mfc/regs-mfc5.h b/drivers/media/video/s5p-mfc/regs-mfc5.h
new file mode 100644
index 0000000..8c628ad
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/regs-mfc5.h
@@ -0,0 +1,305 @@
+/*
+ *
+ * Register definition file for Samsung MFC V4.0 & V5.0 Interface (FIMV) driver
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.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.
+*/
+
+#ifndef _REGS_FIMV_H
+#define _REGS_FIMV_H
+
+#define S5P_FIMV_REG_SIZE	(S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
+#define S5P_FIMV_REG_COUNT	((S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR) / 4)
+
+#define S5P_FIMV_START_ADDR	0x0000
+#define S5P_FIMV_END_ADDR	0xe008
+
+#define S5P_FIMV_SW_RESET	0x0000
+#define S5P_FIMV_RISC_HOST_INT	0x0008
+/* Command from HOST to RISC */
+#define S5P_FIMV_HOST2RISC_CMD	0x0030
+#define S5P_FIMV_HOST2RISC_ARG1	0x0034
+#define S5P_FIMV_HOST2RISC_ARG2	0x0038
+#define S5P_FIMV_HOST2RISC_ARG3	0x003c
+#define S5P_FIMV_HOST2RISC_ARG4	0x0040
+/* Command from RISC to HOST */
+#define S5P_FIMV_RISC2HOST_CMD	0x0044
+#define S5P_FIMV_RISC2HOST_ARG1	0x0048
+#define S5P_FIMV_RISC2HOST_ARG2	0x004c
+#define S5P_FIMV_RISC2HOST_ARG3	0x0050
+#define S5P_FIMV_RISC2HOST_ARG4	0x0054
+
+#define S5P_FIMV_FW_VERSION	0x0058
+#define S5P_FIMV_SYS_MEM_SZ	0x005c
+#define S5P_FIMV_FW_STATUS	0x0080
+/* Memory controller register */
+#define S5P_FIMV_MC_DRAMBASE_ADR_A	0x0508
+#define S5P_FIMV_MC_DRAMBASE_ADR_B	0x050c
+#define S5P_FIMV_MC_STATUS	0x0510
+
+/***** In case of 2 master *****/
+/* Common register */
+#define S5P_FIMV_SYS_MEM_ADR	0x0600 /* firmware buffer */
+#define S5P_FIMV_CPB_BUF_ADR	0x0604 /* stream buffer */
+#define S5P_FIMV_DESC_BUF_ADR	0x0608 /* descriptor buffer */
+/* H264 decoding */
+#define S5P_FIMV_VERT_NB_MV_ADR	0x068c /* vertical neighbor motion vector */
+#define S5P_FIMV_VERT_NB_IP_ADR	0x0690 /* neighbor pixels for intra pred */
+#define S5P_FIMV_H264_LUMA_ADR	0x0700 /* Luma0 ~ Luma18 700 */
+#define S5P_FIMV_H264_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma18 614 */
+#define S5P_FIMV_MV_ADR		0x0780 /* H264 motion vector 660 780 */
+/* H263/MPEG4/MPEG2/VC-1/ decoding */
+#define S5P_FIMV_NB_DCAC_ADR	0x068c /* neighbor AC/DC coeff. buffer */
+#define S5P_FIMV_UP_NB_MV_ADR	0x0690 /* upper neighbor motion vector buffer */
+#define S5P_FIMV_SA_MV_ADR	0x0694 /* subseq. anchor motion vector buffer */
+#define S5P_FIMV_OT_LINE_ADR	0x0698 /* overlap transform line buffer */
+#define S5P_FIMV_BITPLANE3_ADR	0x069C /* bitplane3 addr */
+#define S5P_FIMV_BITPLANE2_ADR	0x06A0 /* bitplane2 addr */
+#define S5P_FIMV_BITPLANE1_ADR	0x06A4 /* bitplane1 addr */
+#define S5P_FIMV_SP_ADR		0x06A8 /* syntax parser addr */
+#define S5P_FIMV_LUMA_ADR	0x0700 /* Luma0 ~ Luma5 */
+#define S5P_FIMV_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma5 */
+/* Encoder register */
+#define S5P_FIMV_ENC_UP_MV_ADR		0x0600 /* upper motion vector addr */
+#define S5P_FIMV_ENC_COZERO_FLAG_ADR	0x0610 /* direct cozero flag addr */
+#define S5P_FIMV_ENC_UP_INTRA_MD_ADR	0x0608 /* upper intra MD addr */
+#define S5P_FIMV_ENC_UP_INTRA_PRED_ADR	0x0740 /* upper intra PRED addr */
+#define S5P_FIMV_ENC_NB_DCAC_ADR	0x0604 /* entropy engine's neighbor
+						inform and AC/DC coeff. */
+
+#define S5P_FIMV_ENC_CUR_LUMA_ADR	0x0718 /* current Luma addr */
+#define S5P_FIMV_ENC_CUR_CHROMA_ADR	0x071C /* current Chroma addr */
+
+#define S5P_FIMV_ENC_REF0_LUMA_ADR	0x061c /* ref0 Luma addr */
+#define S5P_FIMV_ENC_REF0_CHROMA_ADR	0x0700 /* ref0 Chroma addr */
+#define S5P_FIMV_ENC_REF1_LUMA_ADR	0x0620 /* ref1 Luma addr */
+#define S5P_FIMV_ENC_REF1_CHROMA_ADR	0x0704 /* ref1 Chroma addr */
+#define S5P_FIMV_ENC_REF2_LUMA_ADR	0x0710 /* ref2 Luma addr */
+#define S5P_FIMV_ENC_REF2_CHROMA_ADR	0x0708 /* ref2 Chroma addr */
+#define S5P_FIMV_ENC_REF3_LUMA_ADR	0x0714 /* ref3 Luma addr */
+#define S5P_FIMV_ENC_REF3_CHROMA_ADR	0x070c /* ref3 Chroma addr */
+
+/* Codec common register */
+#define S5P_FIMV_ENC_HSIZE_PX		0x0818 /* frame width at encoder */
+#define S5P_FIMV_ENC_VSIZE_PX		0x081c /* frame height at encoder */
+#define S5P_FIMV_ENC_PROFILE		0x0830 /* profile register */
+#define S5P_FIMV_ENC_PIC_STRUCT		0x083c /* picture field/frame flag */
+#define S5P_FIMV_ENC_LF_CTRL		0x0848 /* loop filter control */
+#define S5P_FIMV_ENC_ALPHA_OFF		0x084c /* loop filter alpha offset */
+#define S5P_FIMV_ENC_BETA_OFF		0x0850 /* loop filter beta offset */
+#define S5P_FIMV_MR_BUSIF_CTRL		0x0854 /* hidden, bus interface ctrl */
+#define S5P_FIMV_ENC_PXL_CACHE_CTRL	0x0a00 /* pixel cache control */
+
+/* Channel & stream interface register */
+#define S5P_FIMV_SI_RTN_CHID	0x2000 /* Return CH instance ID register */
+#define S5P_FIMV_SI_CH0_INST_ID	0x2040 /* codec instance ID */
+#define S5P_FIMV_SI_CH1_INST_ID	0x2080 /* codec instance ID */
+/* Decoder */
+#define S5P_FIMV_SI_VRESOL	0x2004 /* vertical resolution of decoder */
+#define S5P_FIMV_SI_HRESOL	0x2008 /* horizontal resolution of decoder */
+#define S5P_FIMV_SI_BUF_NUMBER	0x200c /* number of frames in the decoded pic */
+#define S5P_FIMV_SI_DISPLAY_Y_ADR 0x2010 /* luma address of displayed pic */
+#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma address of displayed pic */
+#define S5P_FIMV_SI_DEC_FRM_SIZE 0x2018 /* the number of frames decoded */
+#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded picture */
+#define S5P_FIMV_SI_FRAME_TYPE	0x2020 /* frame type such as skip/I/P/B */
+
+#define S5P_FIMV_SI_CH0_SB_ST_ADR	0x2044 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH0_SB_FRM_SIZE	0x2048 /* size of stream buf */
+#define S5P_FIMV_SI_CH0_DESC_ADR	0x204c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH0_CPB_SIZE	0x2058 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH0_DESC_SIZE	0x205c /* max size of descriptor buf */
+
+#define S5P_FIMV_SI_CH1_SB_ST_ADR	0x2084 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH1_SB_FRM_SIZE	0x2088 /* size of stream buf */
+#define S5P_FIMV_SI_CH1_DESC_ADR	0x208c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH1_CPB_SIZE	0x2098 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH1_DESC_SIZE	0x209c /* max size of descriptor buf */
+
+#define S5P_FIMV_SI_DIVX311_HRESOL	0x2054 /* horizontal resolution */
+#define S5P_FIMV_SI_DIVX311_VRESOL	0x2050 /* vertical resolution */
+#define S5P_FIMV_CRC_LUMA0	0x2030 /* luma crc data per frame(top field)*/
+#define S5P_FIMV_CRC_CHROMA0	0x2034 /* chroma crc data per frame(top field)*/
+#define S5P_FIMV_CRC_LUMA1	0x2038 /* luma crc data per bottom field */
+#define S5P_FIMV_CRC_CHROMA1	0x203c /* chroma crc data per bottom field */
+
+/* Display status */
+#define S5P_FIMV_DEC_STATUS_DECODING_ONLY		0
+#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY		1
+#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY		2
+#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY		3
+#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK	7
+#define S5P_FIMV_DEC_STATUS_PROGRESSIVE			(0<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE			(1<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK		(1<<3)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO		(0<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR		(1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK		(1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_GENERATED		(1<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED		(0<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_MASK			(1<<5)
+
+/* Decode frame address */
+#define S5P_FIMV_DECODE_Y_ADR			0x2024
+#define S5P_FIMV_DECODE_C_ADR			0x2028
+
+/* Decoded frame tpe */
+#define S5P_FIMV_DECODE_FRAME_TYPE		0x2020
+#define S5P_FIMV_DECODE_FRAME_MASK		7
+
+#define S5P_FIMV_DECODE_FRAME_SKIPPED		0
+#define S5P_FIMV_DECODE_FRAME_I_FRAME		1
+#define S5P_FIMV_DECODE_FRAME_P_FRAME		2
+#define S5P_FIMV_DECODE_FRAME_202_FRAME		3
+#define S5P_FIMV_DECODE_FRAME_OTHER_FRAME	4
+
+/* Sizes of buffers required for decoding */
+#define S5P_FIMV_DEC_NB_IP_SIZE			(32*1024)
+#define S5P_FIMV_DEC_VERT_NB_MV_SIZE		(16*1024)
+#define S5P_FIMV_DEC_NB_DCAC_SIZE		(16*1024)
+#define S5P_FIMV_DEC_UPNB_MV_SIZE		(68*1024)
+#define S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE		(136*1024)
+#define S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE     (32*1024)
+#define S5P_FIMV_DEC_VC1_BITPLANE_SIZE		(2*1024)
+#define S5P_FIMV_DEC_STX_PARSER_SIZE		(68*1024)
+
+
+/* Encoder */
+#define S5P_FIMV_ENC_SI_STRM_SIZE	0x2004 /* stream size */
+#define S5P_FIMV_ENC_SI_PIC_CNT		0x2008 /* picture count */
+#define S5P_FIMV_ENC_SI_WRITE_PTR	0x200c /* write pointer */
+#define S5P_FIMV_ENC_SI_SLICE_TYPE	0x2010 /* slice type(I/P/B/IDR) */
+
+#define S5P_FIMV_ENC_SI_CH0_SB_U_ADR	0x2044 /* addr of upper stream buf */
+#define S5P_FIMV_ENC_SI_CH0_SB_L_ADR	0x2048 /* addr of lower stream buf */
+#define S5P_FIMV_ENC_SI_CH0_SB_SIZE	0x204c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR	0x2050 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH0_CUR_C_ADR	0x2054 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH0_FRAME_QP	0x2058 /* frame QP */
+#define S5P_FIMV_ENC_SI_CH0_SLICE_ARG	0x205c /* slice argument */
+
+#define S5P_FIMV_ENC_SI_CH1_SB_U_ADR	0x2084 /* addr of upper stream buf */
+#define S5P_FIMV_ENC_SI_CH1_SB_L_ADR	0x2088 /* addr of lower stream buf */
+#define S5P_FIMV_ENC_SI_CH1_SB_SIZE	0x208c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH1_CUR_Y_ADR	0x2090 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH1_CUR_C_ADR	0x2094 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH1_FRAME_QP	0x2098 /* frame QP */
+#define S5P_FIMV_ENC_SI_CH1_SLICE_ARG	0x209c /* slice argument */
+
+#define S5P_FIMV_ENC_STR_BF_U_FULL	0xc004 /* upper stream buf full */
+#define S5P_FIMV_ENC_STR_BF_U_EMPTY	0xc008 /* upper stream buf empty */
+#define S5P_FIMV_ENC_STR_BF_L_FULL	0xc00c /* lower stream buf full */
+#define S5P_FIMV_ENC_STR_BF_L_EMPTY	0xc010 /* lower stream buf empty */
+#define S5P_FIMV_ENC_STR_STATUS		0xc018 /* stream buf interrupt status */
+#define S5P_FIMV_ENC_SF_EPB_ON_CTRL	0xc054 /* stream control */
+#define S5P_FIMV_ENC_SF_BUF_CTRL	0xc058 /* buffer control */
+#define S5P_FIMV_ENC_BF_MODE_CTRL	0xc05c /* fifo level control */
+
+#define S5P_FIMV_ENC_PIC_TYPE_CTRL	0xc504 /* pic type level control */
+#define S5P_FIMV_ENC_B_RECON_WRITE_ON	0xc508 /* B frame recon write ctrl */
+#define S5P_FIMV_ENC_MSLICE_CTRL	0xc50c /* multi slice control */
+#define S5P_FIMV_ENC_MSLICE_MB		0xc510 /* MB number in the one slice */
+#define S5P_FIMV_ENC_MSLICE_BYTE	0xc514 /* byte number for one slice */
+#define S5P_FIMV_ENC_CIR_CTRL		0xc518 /* number of intra refresh MB */
+#define S5P_FIMV_ENC_MAP_FOR_CUR	0xc51c /* linear or 64x32 tiled mode */
+#define S5P_FIMV_ENC_PADDING_CTRL	0xc520 /* padding control */
+#define S5P_FIMV_ENC_INT_MASK		0xc528 /* interrupt mask */
+
+#define S5P_FIMV_ENC_RC_CONFIG		0xc5a0 /* RC config */
+#define S5P_FIMV_ENC_RC_FRAME_RATE	0xc5a4 /* frame rate */
+#define S5P_FIMV_ENC_RC_BIT_RATE	0xc5a8 /* bit rate */
+#define S5P_FIMV_ENC_RC_QBOUND		0xc5ac /* max/min QP */
+#define S5P_FIMV_ENC_RC_RPARA		0xc5b0 /* rate control reaction coeff */
+#define S5P_FIMV_ENC_RC_MB_CTRL		0xc5b4 /* MB adaptive scaling */
+
+/* Encoder for H264 */
+#define S5P_FIMV_ENC_ENTRP_MODE		0xd004 /* CAVLC or CABAC */
+#define S5P_FIMV_ENC_H264_ALPHA_OFF	0xd008 /* loop filter alpha offset */
+#define S5P_FIMV_ENC_H264_BETA_OFF	0xd00c /* loop filter beta offset */
+#define S5P_FIMV_ENC_H264_NUM_OF_REF	0xd010 /* number of reference for P/B */
+#define S5P_FIMV_ENC_H264_MDINTER_WGT	0xd01c /* inter weighted parameter */
+#define S5P_FIMV_ENC_H264_MDINTRA_WGT	0xd020 /* intra weighted parameter */
+#define S5P_FIMV_ENC_H264_TRANS_FLAG	0xd034 /* 8x8 transform flag in PPS &
+								high profile */
+/* Encoder for MPEG4 */
+#define S5P_FIMV_ENC_MPEG4_QUART_PXL	0xe008 /* qpel interpolation ctrl */
+
+/* Additional */
+#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL   0x2068 /* DPB Config Control Register */
+#define S5P_FIMV_SI_CH0_RELEASE_BUF     0x2060 /* DPB release buffer register */
+#define S5P_FIMV_SI_CH0_HOST_WR_ADR	0x2064
+
+/* #define S5P_FIMV_RET_VALUE              0xc08 */
+#define S5P_FIMV_ENC_B_RECON_WRITE_ON   0xc508 /* B frame recon write ctrl */
+
+#define S5P_FIMV_ENC_REF_B_LUMA_ADR     0x062c /* ref B Luma addr */
+#define S5P_FIMV_ENC_REF_B_CHROMA_ADR   0x0630 /* ref B Chroma addr */
+
+#define S5P_FIMV_ENCODED_Y_ADDR         0x2014 /* the address of the encoded
+							luminance picture */
+#define S5P_FIMV_ENCODED_C_ADDR         0x2018 /* the address of the encoded
+							chrominance picture*/
+
+/* Codec numbers  */
+#define S5P_FIMV_CODEC_H264_DEC			0
+#define S5P_FIMV_CODEC_VC1_DEC			1
+#define S5P_FIMV_CODEC_MPEG4_DEC		2
+#define S5P_FIMV_CODEC_MPEG2_DEC		3
+#define S5P_FIMV_CODEC_H263_DEC			4
+#define S5P_FIMV_CODEC_VC1RCV_DEC		5
+#define S5P_FIMV_CODEC_DIVX311_DEC		6
+#define S5P_FIMV_CODEC_DIVX412_DEC		7
+#define S5P_FIMV_CODEC_DIVX502_DEC		8
+#define S5P_FIMV_CODEC_DIVX503_DEC		9
+
+#define S5P_FIMV_CODEC_H264_ENC			16
+#define S5P_FIMV_CODEC_MPEG4_ENC		17
+#define S5P_FIMV_CODEC_H263_ENC			18
+
+/* Channel Control Register */
+#define S5P_FIMV_CH_SEQ_HEADER		1
+#define S5P_FIMV_CH_FRAME_START		2
+#define S5P_FIMV_CH_LAST_FRAME		3
+#define S5P_FIMV_CH_INIT_BUFS		4
+#define S5P_FIMV_CH_FRAME_START_REALLOC	5
+
+
+/* Host to RISC command */
+#define S5P_FIMV_H2R_CMD_EMPTY		0
+#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE	1
+#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE	2
+#define S5P_FIMV_H2R_CMD_SYS_INIT	3
+
+#define S5P_FIMV_R2H_CMD_EMPTY			0
+#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET	1
+#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET	2
+#define S5P_FIMV_R2H_CMD_ERROR_RET		3
+#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET		4
+#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET		5
+#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET		6
+#define S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET	7
+#define S5P_FIMV_R2H_CMD_SYS_INIT_RET		8
+#define S5P_FIMV_R2H_CMD_FW_STATUS_RET		9
+#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET	15
+#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET		16
+#define S5P_FIMV_R2H_CMD_DECODE_ERR_RET		32
+
+/* Shared memory registers' offsets */
+
+/* An offset of the start position in the stream when
+ * the start position is not aligned */
+#define S5P_FIMV_SHARED_CROP_INFO_H		0x0020
+#define S5P_FIMV_SHARED_CROP_INFO_V		0x0024
+#define S5P_FIMV_SHARED_START_BYTE_NUM		0x0018
+#define S5P_FIMV_SHARED_LUMA_DPB_SIZE		0x0064
+#define S5P_FIMV_SHARED_CHROMA_DPB_SIZE		0x0068
+#define S5P_FIMV_SHARED_MV_SIZE			0x006C
+#define S5P_FIMV_SHARED_PIC_TIME_TOP		0x0010
+#define S5P_FIMV_SHARED_PIC_TIME_BOTTOM		0x0014
+
+#endif /* _REGS_FIMV_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c
new file mode 100644
index 0000000..f549ab6
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
@@ -0,0 +1,1879 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.0
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/videodev2.h>
+#include <media/videobuf2-cma.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc5.h"
+
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_logmsg.h"
+#include "s5p_mfc_memory.h"
+#include "s5p_mfc_ctrls.h"
+
+#define S5P_MFC_NAME	"s5p-mfc5"
+
+/* Offset base used to differentiate between CAPTURE and OUTPUT
+*  while mmaping */
+#define DST_QUEUE_OFF_BASE      (TASK_SIZE / 2)
+
+struct s5p_mfc_dev *dev;
+static const char *s5p_mem_types[] = {"b", "a"};
+static unsigned long s5p_mem_alignments[] = {8192, 8192};
+
+/* Function prototypes */
+static void s5p_mfc_try_run(void);
+
+/* Helper functions for interrupt processing */
+/* Remove from hw execution round robin */
+static inline void clear_work_bit(struct s5p_mfc_ctx *ctx)
+{
+	spin_lock(&dev->condlock);
+	clear_bit(ctx->num, &dev->ctx_work_bits);
+	spin_unlock(&dev->condlock);
+}
+
+/* Wake up context wait_queue */
+static inline void wake_up_ctx(struct s5p_mfc_ctx *ctx,
+	unsigned int reason, unsigned int err)
+{
+	ctx->int_cond = 1;
+	ctx->int_type = reason;
+	ctx->int_err = err;
+	wake_up_interruptible(&ctx->queue);
+}
+
+/* Wake up device wait_queue */
+static inline void wake_up_dev(unsigned int reason, unsigned int err)
+{
+	dev->int_cond = 1;
+	dev->int_type = reason;
+	dev->int_err = err;
+	wake_up_interruptible(&dev->queue);
+}
+
+void s5p_mfc_error_cleanup_queue(struct list_head *lh, \
+						struct vb2_queue *vq)
+{
+	struct vb2_buffer *b;
+	int i;
+	spin_lock(&dev->irqlock);
+	while (!list_empty(lh)) {
+		b = list_entry(lh->next, struct vb2_buffer, drv_entry);
+		for (i = 0; i < b->num_planes; i++)
+			vb2_set_plane_payload(b, i, 0);
+		spin_unlock(&dev->irqlock);
+		vb2_buffer_done(b, VB2_BUF_STATE_ERROR);
+		spin_lock(&dev->irqlock);
+		list_del(&b->drv_entry);
+	}
+	spin_unlock(&dev->irqlock);
+}
+
+void s5p_mfc_watchdog(unsigned long arg)
+{
+	if (test_bit(0, &dev->hw_lock))
+		atomic_inc(&dev->watchdog_cnt);
+	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
+		/* This means that hw is busy and no interrupts were
+		 * generated by hw for the Nth time of running this
+		 * watchdog timer. This usually means a serious hw
+		 * error. Now it is time to kill all instances and
+		 * reset the MFC. */
+		mfc_err("Time out during waiting for HW.\n");
+		queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
+	}
+	dev->watchdog_timer.expires += msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+	add_timer(&dev->watchdog_timer);
+}
+
+static void s5p_mfc_watchdog_worker(struct work_struct *work)
+{
+	struct s5p_mfc_ctx *ctx;
+	int i, ret;
+	int mutex_locked;
+	mfc_err("Driver timeout error handling.\n");
+	/* Lock the mutex that protects open and release.
+	 * This is necessary as they may load and unload firmware. */
+	mutex_locked = mutex_trylock(dev->mfc_mutex);
+	if (!mutex_locked)
+		mfc_err("Ok. This is not good. Some instance may be " \
+							"closing/opening.\n");
+	clk_disable(dev->clock1);
+	clk_disable(dev->clock2);
+	spin_lock(&dev->irqlock);
+	for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+		ctx = dev->ctx[i];
+		if (ctx) {
+			ctx->state = MFCINST_DEC_ERROR;
+			spin_unlock(&dev->irqlock);
+			s5p_mfc_error_cleanup_queue(&ctx->dst_queue,
+				&ctx->vq_dst);
+			s5p_mfc_error_cleanup_queue(&ctx->src_queue,
+				&ctx->vq_src);
+			spin_lock(&dev->irqlock);
+			clear_work_bit(ctx);
+			wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_DECODE_ERR_RET, 0);
+		}
+	}
+	clear_bit(0, &dev->hw_lock);
+	spin_unlock(&dev->irqlock);
+	/* Double check if there is at least one instance running.
+	 * If no instance is in memory than no firmware should be present */
+	if (atomic_read(&dev->num_inst) > 0) {
+		ret = s5p_mfc_load_firmware(dev);
+		if (ret != 0) {
+			mfc_err("Failed to reload FW. This is bad.\n");
+			if (mutex_locked)
+				mutex_unlock(dev->mfc_mutex);
+			return;
+		}
+		clk_enable(dev->clock1);
+		clk_enable(dev->clock2);
+		ret = s5p_mfc_init_hw(dev);
+		if (ret != 0) {
+			mfc_err("Failed to reinitialize FW. This is bad.\n");
+			if (mutex_locked)
+				mutex_unlock(dev->mfc_mutex);
+			return;
+		}
+	}
+	if (mutex_locked)
+		mutex_unlock(dev->mfc_mutex);
+}
+
+/* Check whether a context should be run on hardware */
+int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+	mfc_debug("s5p_mfc_ctx_ready: src=%d, dst=%d, state=%d\n",
+		  ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
+	/* Context is to parse header */
+	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_GOT_INST)
+		return 1;
+	/* Context is to decode a frame */
+	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_RUNNING &&
+					ctx->dst_queue_cnt >= ctx->dpb_count)
+		return 1;
+	/* Context is to return last frame */
+	if (ctx->state == MFCINST_DEC_FINISHING &&
+	    ctx->dst_queue_cnt >= ctx->dpb_count)
+		return 1;
+	/* Context is to set buffers */
+	if (ctx->src_queue_cnt >= 1 &&
+	    ctx->state == MFCINST_DEC_HEAD_PARSED &&
+	    ctx->capture_state == QUEUE_BUFS_MMAPED)
+		return 1;
+	mfc_debug("s5p_mfc_ctx_ready: ctx is not ready.\n");
+	return 0;
+}
+
+/* Query control */
+static struct v4l2_queryctrl *get_ctrl(int id)
+{
+	int i;
+	for (i = 0; i < NUM_CTRLS; ++i)
+		if (id == s5p_mfc_ctrls[i].id)
+			return &s5p_mfc_ctrls[i];
+	return NULL;
+}
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+	strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->version = KERNEL_VERSION(1, 0, 0);
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+						    | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+/* Enumerate format */
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+	struct s5p_mfc_fmt *fmt;
+	int i, j = 0;
+	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+		if (mplane && formats[i].num_planes == 1)
+			continue;
+		else if (!mplane && formats[i].num_planes > 1)
+			continue;
+		if (out && formats[i].type != MFC_FMT_RAW)
+			continue;
+		else if (!out && formats[i].type != MFC_FMT_DEC)
+			continue;
+
+		if (j == f->index)
+			break;
+		++j;
+	}
+	if (i == ARRAY_SIZE(formats))
+		return -EINVAL;
+	fmt = &formats[i];
+	strlcpy(f->description, fmt->name, sizeof(f->description));
+	f->pixelformat = fmt->fourcc;
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+							struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt(f, true, true);
+}
+
+/* Get format */
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	mfc_debug("vidioc_g_fmt++\n");
+	mfc_debug("f->type = %d ctx->state = %d\n", f->type, ctx->state);
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+	    ctx->state == MFCINST_DEC_GOT_INST) {
+		/* If the MFC is parsing the header,
+		 * so wait until it is finished */
+		s5p_mfc_clean_ctx_int_flags(ctx);
+		s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_SEQ_DONE_RET, \
+									1);
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+	    ctx->state >= MFCINST_DEC_HEAD_PARSED &&
+	    ctx->state < MFCINST_ENC_INIT) {
+		/* This is run on CAPTURE (deocde output) */
+		/* Width and height are set to the dimensions
+		   of the movie, the buffer is bigger and
+		   further processing stages should crop to this
+		   rectangle. */
+		f->fmt.pix_mp.width = ctx->buf_width;
+		f->fmt.pix_mp.height = ctx->buf_height;
+		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+		f->fmt.pix_mp.num_planes = 2;
+		/* Set pixelformat to the format in which MFC
+		   outputs the decoded frame */
+		f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
+		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx->buf_width;
+		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx->luma_size;
+		f->fmt.pix_mp.plane_fmt[1].bytesperline = ctx->buf_width;
+		f->fmt.pix_mp.plane_fmt[1].sizeimage = ctx->chroma_size;
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/* This is run on OUTPUT
+		   The buffer contains compressed image
+		   so width and height have no meaning */
+		f->fmt.pix_mp.width = 1;
+		f->fmt.pix_mp.height = 1;
+		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx->dec_src_buf_size;
+		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx->dec_src_buf_size;
+		f->fmt.pix_mp.pixelformat = ctx->fmt->fourcc;
+		f->fmt.pix_mp.num_planes = ctx->fmt->num_planes;
+	} else {
+		mfc_err("Format could not be read\n");
+		mfc_debug("vidioc_g_fmt-- (error)\n");
+		return -EINVAL;
+	}
+	mfc_debug("vidioc_g_fmt--\n");
+	return 0;
+}
+
+/* Find selected format description */
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f)
+{
+	struct s5p_mfc_fmt *fmt;
+	unsigned int i;
+	for (i = 0; i < NUM_FORMATS; ++i) {
+		fmt = &formats[i];
+		if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
+			break;
+	}
+	if (i == NUM_FORMATS)
+		return NULL;
+	return fmt;
+}
+
+/* Try format */
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_fmt *fmt;
+	mfc_debug("Type is %d\n", f->type);
+	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		mfc_err("Currently only decoding is supported.\n");
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = find_format(f);
+		if (!fmt) {
+			mfc_err("Unsupported format.\n");
+			return -EINVAL;
+		}
+		if (fmt->type != MFC_FMT_DEC) {
+			mfc_err("\n");
+			return -EINVAL;
+		}
+		if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) {
+			mfc_err("Application is required to specify"
+				" input buffer size (via sizeimage)\n");
+			return -EINVAL;
+		}
+		/* As this buffer will contain compressed data, the size is set
+		 * to the maximum size.
+		 * Width and height are left intact as they may be relevant for
+		 * DivX 3.11 decoding. */
+		f->fmt.pix_mp.plane_fmt[0].bytesperline =
+					f->fmt.pix_mp.plane_fmt[0].sizeimage;
+	}
+	return 0;
+}
+
+/* Set format */
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	unsigned long flags;
+	int ret = 0;
+	struct s5p_mfc_fmt *fmt;
+	mfc_debug("vidioc_s_fmt++\n");
+	ret = vidioc_try_fmt(file, priv, f);
+	if (ret)
+		return ret;
+	mutex_lock(&ctx->vq_src.vb_lock);
+	mutex_lock(&ctx->vq_dst.vb_lock);
+	if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+		ret = -EBUSY;
+		goto out;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = find_format(f);
+		if (!fmt || fmt->codec_mode == MFC_FORMATS_NO_CODEC) {
+			mfc_err("Unknown codec.\n");
+			ret = -EINVAL;
+			goto out;
+		}
+		if (fmt->type != MFC_FMT_DEC) {
+			mfc_err("Wrong format selected - you should choose" \
+						" format for decoding.\n");
+			ret = -EINVAL;
+			goto out;
+		}
+		ctx->fmt = fmt;
+		ctx->codec_mode = fmt->codec_mode;
+		mfc_debug("The codec number is: %d\n", ctx->codec_mode);
+		ctx->pix_format = f->fmt.pix_mp.pixelformat;
+		if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_DIVX3) {
+			f->fmt.pix_mp.height = 1;
+			f->fmt.pix_mp.width = 1;
+		} else {
+			ctx->img_height = f->fmt.pix_mp.height;
+			ctx->img_width = f->fmt.pix_mp.width;
+		}
+		mfc_debug("s_fmt w/h: %dx%d, ctx: %dx%d\n", f->fmt.pix_mp.width,
+			f->fmt.pix_mp.height, ctx->img_width, ctx->img_height);
+		ctx->dec_src_buf_size =	f->fmt.pix_mp.plane_fmt[0].sizeimage;
+		f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+		ctx->state = MFCINST_DEC_INIT;
+		ctx->dec_dst_buf_cnt = 0;
+		ctx->capture_state = QUEUE_FREE;
+		ctx->output_state = QUEUE_FREE;
+		s5p_mfc_alloc_instance_buffer(ctx);
+		s5p_mfc_alloc_dec_temp_buffers(ctx);
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+		s5p_mfc_clean_ctx_int_flags(ctx);
+		s5p_mfc_try_run();
+		if (s5p_mfc_wait_for_done_ctx(ctx, \
+				S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
+			/* Error or timeout */
+			mfc_err("Error getting instance from hardware.\n");
+			s5p_mfc_release_instance_buffer(ctx);
+			s5p_mfc_release_dec_buffers(ctx);
+			ret = -EAGAIN;
+			goto out;
+		}
+		mfc_debug("Got instance number: %d\n", ctx->inst_no);
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		mfc_err("Currently only decoding is supported.\n");
+		ret = -EINVAL;
+	}
+out:
+	mutex_unlock(&ctx->vq_dst.vb_lock);
+	mutex_unlock(&ctx->vq_src.vb_lock);
+	mfc_debug("vidioc_s_fmt--\n");
+	return ret;
+}
+
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+					  struct v4l2_requestbuffers *reqbufs)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	int ret = 0;
+	mfc_debug("vidioc_reqbufs++\n");
+	mfc_debug("Memory type: %d\n", reqbufs->memory);
+	if (reqbufs->memory != V4L2_MEMORY_MMAP) {
+		mfc_err("Only V4L2_MEMORY_MAP is supported.\n");
+		return -EINVAL;
+	}
+	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/* Can only request buffers after an instance has been opened.*/
+		if (ctx->state == MFCINST_DEC_GOT_INST) {
+			/* Decoding */
+			if (ctx->output_state != QUEUE_FREE) {
+				mfc_err("Bufs have already been requested.\n");
+				return -EINVAL;
+			}
+			ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+			if (ret) {
+				mfc_err("vb2_reqbufs on output failed.\n");
+				return ret;
+			}
+			mfc_debug("vb2_reqbufs: %d\n", ret);
+			ctx->output_state = QUEUE_BUFS_REQUESTED;
+		}
+	}
+	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		if (ctx->capture_state != QUEUE_FREE) {
+			mfc_err("Bufs have already been requested.\n");
+			return -EINVAL;
+		}
+		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+		if (ret) {
+			mfc_err("vb2_reqbufs on output failed.\n");
+			return ret;
+		}
+		if (reqbufs->count < ctx->dpb_count) {
+			mfc_err("Not enough buffers allocated.\n");
+			reqbufs->count = 0;
+			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+			return -ENOMEM;
+		}
+		ctx->total_dpb_count = reqbufs->count;
+		ret = s5p_mfc_alloc_dec_buffers(ctx);
+		if (ret) {
+			mfc_err("Failed to allocate decoding buffers.\n");
+			reqbufs->count = 0;
+			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+			return -ENOMEM;
+		}
+		ctx->capture_state = QUEUE_BUFS_REQUESTED;
+	}
+	mfc_debug("vidioc_reqbufs--\n");
+	return ret;
+}
+
+/* Query buffer */
+static int vidioc_querybuf(struct file *file, void *priv,
+						   struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	int ret;
+	int i;
+	mfc_debug("vidioc_querybuf++\n");
+	if (buf->memory != V4L2_MEMORY_MMAP) {
+		mfc_err("Only mmaped buffers can be used.\n");
+		return -EINVAL;
+	}
+	mfc_debug("State: %d, buf->type: %d\n", ctx->state, buf->type);
+	if (ctx->state == MFCINST_DEC_GOT_INST &&
+			buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ret = vb2_querybuf(&ctx->vq_src, buf);
+	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
+			buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		ret = vb2_querybuf(&ctx->vq_dst, buf);
+		for (i = 0; i < buf->length; i++)
+			buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
+	} else {
+		mfc_err("vidioc_querybuf called in an inappropriate state.\n");
+		ret = -EINVAL;
+	}
+	mfc_debug("vidioc_querybuf--\n");
+	return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	mfc_debug("vidioc_qbuf++\n");
+	mfc_debug("Enqueued buf: %d (type = %d)\n", buf->index, buf->type);
+	if (ctx->state == MFCINST_DEC_ERROR) {
+		mfc_err("Call on QBUF after unrecoverable error.\n");
+		return -EIO;
+	}
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return vb2_qbuf(&ctx->vq_src, buf);
+	else
+		return vb2_qbuf(&ctx->vq_dst, buf);
+	mfc_debug("vidioc_qbuf--\n");
+	return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	int ret;
+	mfc_debug("vidioc_dqbuf++\n");
+	mfc_debug("Addr: %p %p %p Type: %d\n", &ctx->vq_src, buf, buf->m.planes,
+								  buf->type);
+	if (ctx->state == MFCINST_DEC_ERROR) {
+		mfc_err("Call on DQBUF after unrecoverable error.\n");
+		return -EIO;
+	}
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		ret = vb2_dqbuf(&ctx->vq_src, buf,
+				      file->f_flags & O_NONBLOCK);
+	} else {
+		ret = vb2_dqbuf(&ctx->vq_dst, buf,
+				      file->f_flags & O_NONBLOCK);
+	}
+	mfc_debug("vidioc_dqbuf--\n");
+	return ret;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+			   enum v4l2_buf_type type)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	int ret = -EINVAL;
+	unsigned long flags;
+	mfc_debug("vidioc_streamon++\n");
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		ret = vb2_streamon(&ctx->vq_src, type);
+	else
+		ret = vb2_streamon(&ctx->vq_dst, type);
+	mfc_debug("ctx->src_queue_cnt = %d ctx->state = %d "
+		  "ctx->dst_queue_cnt = %d ctx->dpb_count = %d\n",
+		  ctx->src_queue_cnt, ctx->state, ctx->dst_queue_cnt,
+		  ctx->dpb_count);
+	/* If context is ready then schedule it to run */
+	if (s5p_mfc_ctx_ready(ctx)) {
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+	}
+	s5p_mfc_try_run();
+	mfc_debug("vidioc_streamon--\n");
+	return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+			    enum v4l2_buf_type type)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	int ret;
+	mfc_debug("vidioc_streamoff++\n");
+	ret = -EINVAL;
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		ret = vb2_streamoff(&ctx->vq_src, type);
+	else
+		ret = vb2_streamoff(&ctx->vq_dst, type);
+	mfc_debug("vidioc_streamoff--\n");
+	return ret;
+}
+
+/* Query a ctrl */
+static int vidioc_queryctrl(struct file *file, void *priv,
+			    struct v4l2_queryctrl *qc)
+{
+	struct v4l2_queryctrl *c;
+	c = get_ctrl(qc->id);
+	if (!c)
+		return -EINVAL;
+	*qc = *c;
+	return 0;
+}
+
+/* Get ctrl */
+static int vidioc_g_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	mfc_debug("vidioc_g_ctrl++\n");
+	switch (ctrl->id) {
+	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
+		ctrl->value = ctx->loop_filter_mpeg4;
+		break;
+	case V4L2_CID_CODEC_DISPLAY_DELAY:
+		ctrl->value = ctx->display_delay;
+		break;
+	case V4L2_CID_CODEC_REQ_NUM_BUFS:
+		if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
+		    ctx->state < MFCINST_ENC_INIT) {
+			ctrl->value = ctx->dpb_count;
+		} else if (ctx->state == MFCINST_DEC_INIT) {
+			/* Should wait for the header to be parsed */
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			s5p_mfc_wait_for_done_ctx(ctx, \
+					S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 1);
+			if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
+			    ctx->state < MFCINST_ENC_INIT) {
+				ctrl->value = ctx->dpb_count;
+			} else {
+				v4l2_err(&dev->v4l2_dev,
+						 "Decoding not initialised.\n");
+				return -EINVAL;
+			}
+		} else {
+			v4l2_err(&dev->v4l2_dev,
+						 "Decoding not initialised.\n");
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_CODEC_SLICE_INTERFACE:
+		ctrl->value = ctx->slice_interface;
+		break;
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+	mfc_debug("vidioc_g_ctrl--\n");
+	return 0;
+}
+
+/* Check whether a ctrl value if correct */
+static int check_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control *ctrl)
+{
+	struct v4l2_queryctrl *c;
+	c = get_ctrl(ctrl->id);
+	if (!c)
+		return -EINVAL;
+	if (ctrl->value < c->minimum || ctrl->value > c->maximum
+	    || (c->step != 0 && ctrl->value % c->step != 0)) {
+		v4l2_err(&dev->v4l2_dev, "Invalid control value\n");
+		return -ERANGE;
+	}
+	return 0;
+}
+
+/* Set a ctrl */
+static int vidioc_s_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	int ret = 0;
+	int stream_on;
+	mfc_debug("vidioc_s_ctrl++\n");
+	stream_on = ctx->vq_src.streaming || ctx->vq_dst.streaming;
+	ret = check_ctrl_val(ctx, ctrl);
+	if (ret != 0)
+		return ret;
+	switch (ctrl->id) {
+	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
+		if (stream_on)
+			return -EBUSY;
+		ctx->loop_filter_mpeg4 = ctrl->value;
+		break;
+	case V4L2_CID_CODEC_DISPLAY_DELAY:
+		if (stream_on)
+			return -EBUSY;
+		ctx->display_delay = ctrl->value;
+		break;
+	case V4L2_CID_CODEC_SLICE_INTERFACE:
+		if (stream_on)
+			return -EBUSY;
+		ctx->slice_interface = ctrl->value;
+		break;
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+	mfc_debug("vidioc_s_ctrl--\n");
+	return 0;
+}
+/* Get cropping information */
+static int vidioc_g_crop(struct file *file, void *priv,
+		struct v4l2_crop *cr)
+{
+	struct s5p_mfc_ctx *ctx = priv;
+	u32 left, right, top, bottom;
+	mfc_debug("vidioc_g_crop++\n");
+	if (ctx->state != MFCINST_DEC_HEAD_PARSED &&
+	ctx->state != MFCINST_DEC_RUNNING && ctx->state != MFCINST_DEC_FINISHING
+					&& ctx->state != MFCINST_DEC_FINISHED) {
+			mfc_debug("vidioc_g_crop--(err)\n");
+			return -EINVAL;
+		}
+	if (ctx->fmt->fourcc == V4L2_PIX_FMT_H264) {
+		left = s5p_mfc_get_h_crop(ctx);
+		right = left >> 16;
+		left = left & 0xFFFF;
+		top = s5p_mfc_get_v_crop(ctx);
+		bottom = top >> 16;
+		top = top & 0xFFFF;
+		cr->c.left = left;
+		cr->c.top = top;
+		cr->c.width = ctx->img_width - left - right;
+		cr->c.height = ctx->img_height - top - bottom;
+		mfc_debug("Cropping info [h264]: l=%d t=%d w=%d h=%d (r=%d " \
+				"b=%d fw=%d fh=%d\n", left, top, cr->c.width, \
+				cr->c.height, right, bottom, ctx->buf_width, \
+							ctx->buf_height);
+	} else {
+		cr->c.left = 0;
+		cr->c.top = 0;
+		cr->c.width = ctx->img_width;
+		cr->c.height = ctx->img_height;
+		mfc_debug("Cropping info: w=%d h=%d fw=%d fh=%d\n", cr->c.width,
+				cr->c.height, ctx->buf_width, ctx->buf_height);
+	}
+	mfc_debug("vidioc_g_crop--\n");
+	return 0;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops s5p_mfc_ioctl_ops = {
+	.vidioc_querycap = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+	.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+	.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+	.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+	.vidioc_reqbufs = vidioc_reqbufs,
+	.vidioc_querybuf = vidioc_querybuf,
+	.vidioc_qbuf = vidioc_qbuf,
+	.vidioc_dqbuf = vidioc_dqbuf,
+	.vidioc_streamon = vidioc_streamon,
+	.vidioc_streamoff = vidioc_streamoff,
+	.vidioc_queryctrl = vidioc_queryctrl,
+	.vidioc_g_ctrl = vidioc_g_ctrl,
+	.vidioc_s_ctrl = vidioc_s_ctrl,
+	.vidioc_g_crop = vidioc_g_crop,
+};
+
+/* Negotiate buffers */
+static int s5p_mfc_buf_negotiate(struct vb2_queue *vq, unsigned int *buf_count,
+						unsigned int *plane_count)
+{
+	struct s5p_mfc_ctx *ctx = vq->drv_priv;
+	mfc_debug("s5p_mfc_buf_negotiate++\n");
+	/* Video output for decoding (source)
+	 * this can be set after getting an instance */
+	if (ctx->state == MFCINST_DEC_GOT_INST &&
+	    vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		mfc_debug("setting for VIDEO output\n");
+		/* A single plane is required for input */
+		*plane_count = 1;
+		if (*buf_count < 1)
+			*buf_count = 1;
+		if (*buf_count > MFC_MAX_BUFFERS)
+			*buf_count = MFC_MAX_BUFFERS;
+	/* Video capture for decoding (destination)
+	 * this can be set after the header was parsed */
+	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
+		   vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		mfc_debug("setting for VIDEO capture\n");
+		/* Output plane count is 2 - one for Y and one for CbCr */
+		*plane_count = 2;
+		/* Setup buffer count */
+		if (*buf_count < ctx->dpb_count)
+			*buf_count = ctx->dpb_count;
+		if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
+			*buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
+		if (*buf_count > MFC_MAX_BUFFERS)
+			*buf_count = MFC_MAX_BUFFERS;
+	} else {
+		mfc_err("State seems invalid. State = %d, vq->type = %d\n",
+							ctx->state, vq->type);
+		return -EINVAL;
+	}
+	mfc_debug("%s, buffer count=%d, plane count=%d type=0x%x\n", __func__,
+					*buf_count, *plane_count, vq->type);
+	mfc_debug("s5p_mfc_buf_negotiate--\n");
+	return 0;
+}
+
+/* Setup plane */
+static int s5p_mfc_buf_setup_plane(struct vb2_queue *vq,
+			   unsigned int plane, unsigned long *plane_size)
+{
+	struct s5p_mfc_ctx *ctx = vq->drv_priv;
+	mfc_debug("s5p_mfc_buf_setup_plane++\n");
+	if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
+	    vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		switch (plane) {
+		case 0:
+			/* Plane 0 is for luma (Y) */
+			*plane_size = ctx->luma_size;
+			break;
+		case 1:
+			/* Plane 1 is for chroma (C) */
+			*plane_size = ctx->chroma_size;
+			break;
+		default:
+			mfc_err("%s, invalid plane=%d\n", __func__, plane);
+			return -EINVAL;
+		}
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+		   ctx->state == MFCINST_DEC_GOT_INST) {
+		if (plane != 0) {
+			mfc_err("%s, invalid plane=%d\n", __func__, plane);
+			return -EINVAL;
+		}
+		/* dec_src_buf_size was set in s_fmt */
+		*plane_size = ctx->dec_src_buf_size;
+	} else {
+		mfc_err("Currently only decoding is supported."\
+						" Decoding not initalised.\n");
+		return -EINVAL;
+	}
+	mfc_debug("%s, plane=%d, size=%lu\n", __func__, plane, *plane_size);
+	mfc_debug("s5p_mfc_buf_setup_plane--\n");
+	return 0;
+}
+
+/* Prepare a buffer */
+static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct s5p_mfc_ctx *ctx = vq->drv_priv;
+	unsigned int i;
+	mfc_debug("s5p_mfc_buf_prepare++\n");
+	BUG_ON(NULL == ctx->fmt);
+	if (!ctx->fmt) {
+		mfc_err("Format passed to the function is null.\n");
+		return -EINVAL;
+	}
+	mfc_debug("Addr: %p (%d)\n", (void *)vb2_plane_paddr(vb, 0),
+							vb->v4l2_buf.index);
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
+			mfc_debug("s5p_mfc_buf_prepare--\n");
+			return 0;
+		}
+		for (i = 0; i <= ctx->fmt->num_planes ; i++) {
+			if (vb2_plane_paddr(vb, i) == 0) {
+				mfc_err("Plane mem not allocated.\n");
+				return -EINVAL;
+			}
+		}
+		if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+			vb2_plane_size(vb, 1) < ctx->chroma_size) {
+			mfc_err("Plane buffer (CAPTURE) is too small.\n");
+			return -EINVAL;
+		}
+		mfc_debug("Size: 0=%lu 2=%lu\n", vb2_plane_size(vb, 0),
+							vb2_plane_size(vb, 1));
+		i = vb->v4l2_buf.index;
+		ctx->dec_dst_buf_luma[i] = vb2_plane_paddr(vb, 0);
+		ctx->dec_dst_buf_chroma[i] = vb2_plane_paddr(vb, 1);
+		ctx->dec_dst_buf_cnt++;
+		if (ctx->dec_dst_buf_cnt == ctx->total_dpb_count)
+			ctx->capture_state = QUEUE_BUFS_MMAPED;
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		if (vb2_plane_paddr(vb, 0)  == 0) {
+			mfc_err("Plane memory not allocated.\n");
+			return -EINVAL;
+		}
+		mfc_debug("Plane size: %ld, ctx->dec_src_buf_size: %d\n",
+				vb2_plane_size(vb, 0), ctx->dec_src_buf_size);
+		if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
+			mfc_err("Plane buffer (OUTPUT) is too small.\n");
+			return -EINVAL;
+		}
+	} else {
+		mfc_err("s5p_mfc_buf_prepare: unknown queue type.\n");
+		return -EINVAL;
+	}
+	mfc_debug("s5p_mfc_buf_prepare--\n");
+	return 0;
+}
+
+/* Try running an operation on hardware */
+static void s5p_mfc_try_run()
+{
+	struct vb2_buffer *temp_vb;
+	struct s5p_mfc_ctx *ctx;
+	int new_ctx;
+	unsigned long flags;
+	unsigned int cnt;
+	unsigned int ret;
+	mfc_debug("Try run dev: %p\n", dev);
+	/* Check whether hardware is not running */
+	if (test_and_set_bit(0, &dev->hw_lock) == 0) {
+		/* Choose the context to run */
+		spin_lock_irqsave(&dev->condlock, flags);
+		mfc_debug("Previos context: %d (bit field %08lx)\n",
+					  dev->curr_ctx, dev->ctx_work_bits);
+		new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
+		cnt = 0;
+		while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
+			new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
+			cnt++;
+			if (cnt > MFC_NUM_CONTEXTS) {
+				/* No contexts to run */
+				spin_unlock_irqrestore(&dev->condlock, flags);
+				if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+					mfc_err("Failed to unlock hardware.\n");
+					return;
+				}
+				mfc_debug("No ctx is scheduled to be run.\n");
+				return;
+			}
+		}
+		mfc_debug("New context: %d\n", new_ctx);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+		ctx = dev->ctx[new_ctx];
+		mfc_debug("Seting new context to %p\n", ctx);
+		/* Got context to run in ctx */
+		mfc_debug("ctx->dst_queue_cnt=%d ctx->dpb_count=%d" \
+				" ctx->src_queue_cnt=%d\n", ctx->dst_queue_cnt,\
+					ctx->dpb_count, ctx->src_queue_cnt);
+		mfc_debug("ctx->state=%d\n", ctx->state);
+		/* Last frame has already been sent to MFC
+		 * Now obtaining frames from MFC buffer */
+		if (ctx->state == MFCINST_DEC_FINISHING) {
+			s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
+			dev->curr_ctx = ctx->num;
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			s5p_mfc_decode_one_frame(ctx, 1);
+		} else if (ctx->state == MFCINST_DEC_RUNNING) {
+			/* Frames are being decoded */
+			if (list_empty(&ctx->src_queue)) {
+				if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+					mfc_err("Failed to unlock hardware.\n");
+					return;
+				}
+				mfc_debug("No source buffers.\n");
+				return;
+			}
+			/* Get the next source buffer */
+			temp_vb = list_entry(ctx->src_queue.next, \
+						struct vb2_buffer, drv_entry);
+			mfc_debug("Temp vb: %p\n", temp_vb);
+			mfc_debug("Src Addr: %08lx\n",
+						vb2_plane_paddr(temp_vb, 0));
+			s5p_mfc_set_dec_stream_buffer(ctx, \
+			      vb2_plane_paddr(temp_vb, 0), 0, \
+			      temp_vb->v4l2_planes[0].bytesused);
+			dev->curr_ctx = ctx->num;
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			s5p_mfc_decode_one_frame(ctx,
+				temp_vb->v4l2_planes[0].bytesused == 0);
+		} else if (ctx->state == MFCINST_DEC_INIT) {
+			/* Preparing decoding - getting instance number */
+			mfc_debug("Getting instance number\n");
+			dev->curr_ctx = ctx->num;
+			s5p_mfc_clean_ctx_int_flags(ctx);
+/*			s5p_mfc_set_dec_temp_buffers(ctx);
+ *			Removed per request by Peter, check if MFC works OK */
+			ret = s5p_mfc_open_inst(ctx);
+			if (ret) {
+				mfc_err("Failed to create a new instance.\n");
+				ctx->state = MFCINST_DEC_ERROR;
+			}
+		} else if (ctx->state == MFCINST_DEC_RETURN_INST) {
+			/* Closing decoding instance  */
+			mfc_debug("Returning instance number\n");
+			dev->curr_ctx = ctx->num;
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			ret = s5p_mfc_return_inst_no(ctx);
+			if (ret) {
+				mfc_err("Failed to return an instance.\n");
+				ctx->state = MFCINST_DEC_ERROR;
+			}
+
+		} else if (ctx->state == MFCINST_DEC_GOT_INST) {
+			/* Initializing decoding - parsing header */
+			mfc_debug("Preparing to init decoding.\n");
+			temp_vb = list_entry(ctx->src_queue.next,
+						struct vb2_buffer, drv_entry);
+			s5p_mfc_set_dec_temp_buffers(ctx);
+			mfc_debug("Header size: %d\n", \
+					temp_vb->v4l2_planes[0].bytesused);
+			s5p_mfc_set_dec_stream_buffer(ctx,\
+					vb2_plane_paddr(temp_vb, 0), 0,
+					 temp_vb->v4l2_planes[0].bytesused);
+			dev->curr_ctx = ctx->num;
+			mfc_debug("paddr: %08x\n", \
+				(int)phys_to_virt(vb2_plane_paddr(temp_vb, 0)));
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			s5p_mfc_init_decode(ctx);
+		} else if (ctx->state == MFCINST_DEC_HEAD_PARSED) {
+			/* Header was parsed now starting processing
+			 * First set the output frame buffers
+			 * s5p_mfc_alloc_dec_buffers(ctx); */
+			if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
+				temp_vb = list_entry(ctx->src_queue.next, \
+						struct vb2_buffer, drv_entry);
+				mfc_debug("Header size: %d\n",
+					temp_vb->v4l2_planes[0].bytesused);
+				s5p_mfc_set_dec_stream_buffer(ctx, \
+					vb2_plane_paddr(temp_vb, 0), 0, \
+					temp_vb->v4l2_planes[0].bytesused);
+				dev->curr_ctx = ctx->num;
+				s5p_mfc_clean_ctx_int_flags(ctx);
+				s5p_mfc_set_dec_frame_buffer(ctx, 1);
+			} else {
+				mfc_err("It seems that not all destionation" \
+				    " buffers were mmaped.\nMFC requires that" \
+				    " all destination are mmaped before" \
+				    " starting processing.\n");
+				if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+					mfc_err("Failed to unlock hardware.\n");
+					return;
+				}
+			}
+		} else {
+			/* Free hardware lock */
+			if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+				mfc_err("Failed to unlock hardware.\n");
+				return;
+			}
+		}
+	} else {
+		/* This is perfectly ok, the scheduled ctx should wait */
+		mfc_debug("Couldn't lock HW.\n");
+	}
+}
+
+/* Queue buffer */
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_queue *vq = vb->vb2_queue;
+	struct s5p_mfc_ctx *ctx = vq->drv_priv;
+	unsigned long flags;
+	mfc_debug("s5p_mfc_buf_queue++\n");
+	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		mfc_debug("Src queue: %p\n", &ctx->src_queue);
+		mfc_debug("Adding to src: %p (%08lx)\n", vb,
+							vb2_plane_paddr(vb, 0));
+		list_add_tail(&vb->drv_entry, &ctx->src_queue);
+		ctx->src_queue_cnt++;
+	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		mfc_debug("Dst queue: %p\n", &ctx->dst_queue);
+		mfc_debug("Adding to dst: %p (%lx)\n", vb, \
+						  vb2_plane_paddr(vb, 0));
+		mfc_debug("ADDING Flag before: %lx (%d)\n", ctx->dec_dst_flag, \
+							vb->v4l2_buf.index);
+		/* Mark destination as available for use by MFC */
+		set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
+		mfc_debug("ADDING Flag after: %lx\n", ctx->dec_dst_flag);
+		list_add_tail(&vb->drv_entry, &ctx->dst_queue);
+		ctx->dst_queue_cnt++;
+	} else {
+		mfc_err("Unsupported buffer type (%d)\n", vq->type);
+	}
+	if (s5p_mfc_ctx_ready(ctx)) {
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+	}
+	s5p_mfc_try_run();
+	mfc_debug("s5p_mfc_buf_queue--\n");
+}
+
+/* Videobuf opts */
+static struct vb2_ops s5p_mfc_qops = {
+	.buf_queue = s5p_mfc_buf_queue,
+	.queue_negotiate = s5p_mfc_buf_negotiate,
+	.plane_setup = s5p_mfc_buf_setup_plane,
+	.buf_prepare = s5p_mfc_buf_prepare,
+};
+
+/* Handle frame decoding interrupt */
+static void s5p_mfc_handle_frame_int(struct s5p_mfc_ctx *ctx, \
+					unsigned int reason, unsigned int err)
+{
+	size_t dst_ret_addr;
+	unsigned int dst_frame_status;
+	unsigned int dec_frame_type;
+	struct vb2_buffer *src_buf, *dst_buf;
+	dst_ret_addr = s5p_mfc_get_dspl_y_adr();
+	dst_frame_status = s5p_mfc_get_dspl_status()
+				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
+	mfc_debug("Orig Status: %d\n", s5p_mfc_get_dspl_status());
+	dec_frame_type = s5p_mfc_get_frame_type();
+	mfc_debug("Status: %x, addr Y: %08x\n", dst_frame_status, dst_ret_addr);
+	mfc_debug("Decode_y: %08x, frame type: %d\n", dst_ret_addr, \
+								dec_frame_type);
+	spin_lock(&dev->irqlock);
+	/* All frames remaining in the buffer have been extracted  */
+	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
+		ctx->state = MFCINST_DEC_FINISHED;
+		mfc_debug("Decided to finish\n");
+		ctx->sequence++;
+		while (!list_empty(&ctx->dst_queue)) {
+			dst_buf = list_entry(ctx->dst_queue.next,
+					     struct vb2_buffer, drv_entry);
+			mfc_debug("Cleaning up buffer: %d\n",
+						  dst_buf->v4l2_buf.index);
+			vb2_set_plane_payload(dst_buf, 0, 0);
+			vb2_set_plane_payload(dst_buf, 1, 0);
+			list_del(&dst_buf->drv_entry);
+			ctx->dst_queue_cnt--;
+			dst_buf->v4l2_buf.sequence = (ctx->sequence++);
+			if (s5p_mfc_get_pic_time_top(ctx) ==
+				s5p_mfc_get_pic_time_bottom(ctx))
+				dst_buf->v4l2_buf.field = V4L2_FIELD_NONE;
+			else
+				dst_buf->v4l2_buf.field = V4L2_FIELD_INTERLACED;
+			spin_unlock(&dev->irqlock);
+			mfc_debug("Finishing Flags before: %08ld\n",
+							ctx->dec_dst_flag);
+			ctx->dec_dst_flag &= ~(1 << dst_buf->v4l2_buf.index);
+			mfc_debug("Finishing Flags after: %08ld\n",
+							ctx->dec_dst_flag);
+			vb2_buffer_done(dst_buf, VB2_BUF_STATE_DONE);
+			spin_lock(&dev->irqlock);
+			mfc_debug("Cleaned up buffer: %d\n",
+				  dst_buf->v4l2_buf.index);
+		}
+		mfc_debug("After cleanup\n");
+	}
+
+	/* A frame has been decoded and is in the buffer  */
+	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
+	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
+		ctx->sequence++;
+		/* If frame is same as previous then skip and do not dequeue */
+		if (dec_frame_type !=  S5P_FIMV_DECODE_FRAME_SKIPPED) {
+		/* The MFC returns address of the buffer, now we have to
+		 * check which videobuf does it correspond to */
+		list_for_each_entry(dst_buf, &ctx->dst_queue, drv_entry) {
+			mfc_debug("Listing: %d\n", dst_buf->v4l2_buf.index);
+			/* This is the buffer we're looking for */
+			mfc_debug("paddr: %p mfc: %p\n",
+					(void *)vb2_plane_paddr(dst_buf, 1),
+							(void *)dst_ret_addr);
+			if (vb2_plane_paddr(dst_buf, 0) == dst_ret_addr) {
+				list_del(&dst_buf->drv_entry);
+				ctx->dst_queue_cnt--;
+				mfc_debug("Flag before: %lx (%d)\n",
+							ctx->dec_dst_flag,
+						dst_buf->v4l2_buf.index);
+				dst_buf->v4l2_buf.sequence = ctx->sequence;
+				if (s5p_mfc_get_pic_time_top(ctx) ==
+					s5p_mfc_get_pic_time_bottom(ctx))
+					dst_buf->v4l2_buf.field =
+								V4L2_FIELD_NONE;
+				else
+					dst_buf->v4l2_buf.field =
+							V4L2_FIELD_INTERLACED;
+				mfc_debug("Field: %d\n",
+						dst_buf->v4l2_buf.field);
+				mfc_debug("Freed -> sequence: %d index: %d\n",
+						dst_buf->v4l2_buf.field,
+						dst_buf->v4l2_buf.index);
+				vb2_set_plane_payload(dst_buf, 0,
+								ctx->luma_size);
+				vb2_set_plane_payload(dst_buf, 1,
+							ctx->chroma_size);
+				spin_unlock(&dev->irqlock);
+				clear_bit(dst_buf->v4l2_buf.index,
+							&ctx->dec_dst_flag);
+				if (err) {
+					vb2_buffer_done(dst_buf,
+							VB2_BUF_STATE_ERROR);
+				} else {
+					vb2_buffer_done(dst_buf,
+							VB2_BUF_STATE_DONE);
+				}
+				spin_lock(&dev->irqlock);
+				mfc_debug("Flag after: %lx\n",
+					  ctx->dec_dst_flag);
+				break;
+			}
+		}
+		}
+	} else {
+		mfc_debug("No frame decode.\n");
+	}
+	/* Mark source buffer as complete */
+	if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
+		&& !list_empty(&ctx->src_queue)) {
+		src_buf = list_entry(ctx->src_queue.next, struct vb2_buffer,
+								drv_entry);
+		mfc_debug("Packed PB test. Size: %d, prev off:"\
+			" %ld now con: %d\n", src_buf->v4l2_planes[0].bytesused,
+			ctx->consumed_stream, s5p_mfc_get_consumed_stream());
+		ctx->consumed_stream += s5p_mfc_get_consumed_stream();
+		if (dec_frame_type == S5P_FIMV_DECODE_FRAME_P_FRAME
+						&& ctx->consumed_stream <
+					src_buf->v4l2_planes[0].bytesused) {
+			/* Run MFC again on the same buffer */
+			mfc_debug("Running again the same buffer.\n");
+			s5p_mfc_set_dec_stream_buffer(ctx,
+						vb2_plane_paddr(src_buf, 0),
+							ctx->consumed_stream,
+					src_buf->v4l2_planes[0].bytesused -
+							ctx->consumed_stream);
+			dev->curr_ctx = ctx->num;
+			s5p_mfc_clean_ctx_int_flags(ctx);
+			spin_unlock(&dev->irqlock);
+			s5p_mfc_clear_int_flags();
+			wake_up_ctx(ctx, reason, err);
+			s5p_mfc_decode_one_frame(ctx, 0);
+			return;
+		} else {
+			mfc_debug("MFC needs next buffer..\n");
+			/* Advance to next buffer */
+			ctx->consumed_stream = 0;
+			list_del(&src_buf->drv_entry);
+			ctx->src_queue_cnt--;
+			if (vb2_plane_size(src_buf, 0) == 0) {
+				mfc_debug("Setting ctx->state to FINISH\n");
+				ctx->state = MFCINST_DEC_FINISHING;
+			}
+			spin_unlock(&dev->irqlock);
+			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
+
+		}
+	} else {
+		spin_unlock(&dev->irqlock);
+	}
+	mfc_debug("Assesing whether this context should be run again.\n");
+	if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_DEC_FINISHING)
+				    || ctx->dst_queue_cnt < ctx->dpb_count) {
+		mfc_debug("No need to run again.\n");
+		clear_work_bit(ctx);
+	}
+	mfc_debug("After assesing whether this context should be run again.\n");
+	s5p_mfc_clear_int_flags();
+	wake_up_ctx(ctx, reason, err);
+	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+		BUG();
+	s5p_mfc_try_run();
+}
+
+/* Error handling for interrupt */
+static inline void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
+	unsigned int reason, unsigned int err)
+{
+	mfc_err("Interrupt Error: %08x\n", err);
+	s5p_mfc_clear_int_flags();
+	wake_up_dev(reason, err);
+	/* If no context is available then all necessary
+	 * processing has been done. */
+	if (ctx == 0)
+		return;
+	/* Error recovery is dependent on the state of context */
+	switch (ctx->state) {
+	case MFCINST_DEC_INIT:
+		/* This error had to happen while acquireing instance */
+	case MFCINST_DEC_GOT_INST:
+		/* This error had to happen while parsing the header */
+	case MFCINST_DEC_HEAD_PARSED:
+		/* This error had to happen while setting dst buffers */
+	case MFCINST_DEC_RETURN_INST:
+		/* This error had to happen while releasing instance */
+		clear_work_bit(ctx);
+		wake_up_ctx(ctx, reason, err);
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+		break;
+	case MFCINST_DEC_FINISHING:
+	case MFCINST_DEC_FINISHED:
+	case MFCINST_DEC_RUNNING:
+		/* It is higly probable that an error occured
+		 * while decoding a frame */
+		clear_work_bit(ctx);
+		ctx->state = MFCINST_DEC_ERROR;
+		/* Mark all dst buffers as having an error */
+		s5p_mfc_error_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+		/* Mark all src buffers as having an error */
+		s5p_mfc_error_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+		break;
+	default:
+		mfc_err("Encountered an error interrupt which had not been" \
+								" handled.\n");
+		break;
+	}
+	return;
+}
+
+/* Interrupt processing */
+static irqreturn_t s5p_mfc_irq(int irq, void *priv)
+{
+	struct vb2_buffer *src_buf;
+	struct s5p_mfc_ctx *ctx;
+	unsigned int reason;
+	unsigned int err;
+	mfc_debug("s5p_mfc_irq++\n");
+	/* Reset the timeout watchdog */
+	atomic_set(&dev->watchdog_cnt, 0);
+	ctx = dev->ctx[dev->curr_ctx];
+	/* Get the reason of interrupt and the error code */
+	reason = s5p_mfc_get_int_reason();
+	err = s5p_mfc_get_int_err();
+	mfc_debug("Int reason: %d (error: %08x)\n", reason, err);
+	switch (reason) {
+	case S5P_FIMV_R2H_CMD_DECODE_ERR_RET:
+		/* An error has occured */
+		if (ctx->state == MFCINST_DEC_RUNNING && err >= 145)
+			s5p_mfc_handle_frame_int(ctx, reason, err);
+		else
+			s5p_mfc_handle_error(ctx, reason, err);
+		break;
+	case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
+	case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
+		s5p_mfc_handle_frame_int(ctx, reason, err);
+		break;
+	case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
+		if (ctx->fmt->fourcc != V4L2_PIX_FMT_DIVX3) {
+			ctx->img_width = s5p_mfc_get_img_width();
+			ctx->img_height = s5p_mfc_get_img_height();
+		}
+		ctx->buf_width = ALIGN(ctx->img_width, 128);
+		ctx->buf_height = ALIGN(ctx->img_height, 32);
+		mfc_debug("SEQ Done: ctx:%dx%d, buf:%dx%d\n", ctx->img_width,
+			ctx->img_height, ctx->buf_width, ctx->buf_height);
+		ctx->luma_size = ALIGN(ctx->buf_width * ctx->buf_height, 8192);
+		ctx->chroma_size = ALIGN(ctx->buf_width *
+					ALIGN(ctx->img_height / 2, 32), 8192);
+		if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
+			ctx->mv_size = ALIGN(ctx->buf_width *
+					 ALIGN(ctx->buf_height / 4, 32), 8192);
+		else
+			ctx->mv_size = 0;
+		ctx->dpb_count = s5p_mfc_get_dpb_count();
+		ctx->state = MFCINST_DEC_HEAD_PARSED;
+		s5p_mfc_clear_int_flags();
+		clear_work_bit(ctx);
+		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+			BUG();
+		s5p_mfc_try_run();
+		wake_up_ctx(ctx, reason, err);
+		break;
+	case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
+		ctx->inst_no = s5p_mfc_get_inst_no();
+		ctx->state = MFCINST_DEC_GOT_INST;
+		clear_work_bit(ctx);
+		wake_up_interruptible(&ctx->queue);
+		goto irq_cleanup_hw;
+		break;
+	case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
+		clear_work_bit(ctx);
+		ctx->state = MFCINST_FREE;
+		wake_up(&ctx->queue);
+		goto irq_cleanup_hw;
+		break;
+	case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
+	case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
+		if (ctx)
+			clear_work_bit(ctx);
+		s5p_mfc_clear_int_flags();
+		wake_up_dev(reason, err);
+		clear_bit(0, &dev->hw_lock);
+		break;
+	case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
+		s5p_mfc_clear_int_flags();
+		ctx->int_type = reason;
+		ctx->int_err = err;
+		ctx->int_cond = 1;
+		spin_lock(&dev->condlock);
+		clear_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock(&dev->condlock);
+		if (err == 0) {
+			ctx->state = MFCINST_DEC_RUNNING;
+			spin_lock(&dev->irqlock);
+			src_buf =
+			    list_entry(ctx->src_queue.next,
+				       struct vb2_buffer, drv_entry);
+			list_del(&src_buf->drv_entry);
+			ctx->src_queue_cnt--;
+			spin_unlock(&dev->irqlock);
+			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
+			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+				BUG();
+			wake_up_interruptible(&ctx->queue);
+			s5p_mfc_try_run();
+		} else {
+			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+				BUG();
+			wake_up_interruptible(&ctx->queue);
+		}
+		break;
+	default:
+		mfc_debug("Unknown int reason.\n");
+		s5p_mfc_clear_int_flags();
+	}
+	mfc_debug("s5p_mfc_irq--\n");
+	return IRQ_HANDLED;
+irq_cleanup_hw:
+	s5p_mfc_clear_int_flags();
+	ctx->int_type = reason;
+	ctx->int_err = err;
+	ctx->int_cond = 1;
+	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+		mfc_err("Failed to unlock hw.\n");
+	s5p_mfc_try_run();
+	mfc_debug("s5p_mfc_irq-- (via irq_cleanup_hw)\n");
+	return IRQ_HANDLED;
+}
+
+/* Open an MFC node */
+static int s5p_mfc_open(struct file *file)
+{
+	struct s5p_mfc_ctx *ctx = NULL;
+	unsigned long flags;
+	int ret = 0;
+	mfc_debug("s5p_mfc_open++\n");
+	mutex_lock(dev->mfc_mutex);
+	atomic_inc(&dev->num_inst);	/* It is guarded by mfc_mutex */
+	/* Allocate memory for context */
+	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+	if (!ctx) {
+		mfc_err("Not enough memory.\n");
+		ret = -ENOMEM;
+		goto out_open;
+	}
+	file->private_data = ctx;
+	ctx->dev = dev;
+	INIT_LIST_HEAD(&ctx->src_queue);
+	INIT_LIST_HEAD(&ctx->dst_queue);
+	ctx->src_queue_cnt = 0;
+	ctx->dst_queue_cnt = 0;
+	/* Get context number */
+	ctx->num = 0;
+	while (dev->ctx[ctx->num]) {
+		ctx->num++;
+		if (ctx->num >= MFC_NUM_CONTEXTS) {
+			mfc_err("Too many open contexts.\n");
+			ret = -EAGAIN;
+			goto out_open;
+		}
+	}
+	/* Mark context as idle */
+	spin_lock_irqsave(&dev->condlock, flags);
+	clear_bit(ctx->num, &dev->ctx_work_bits);
+	spin_unlock_irqrestore(&dev->condlock, flags);
+	dev->ctx[ctx->num] = ctx;
+	/* Default format */
+	ctx->fmt = &formats[0];
+	/* Load firmware if this is the first instance */
+	if (atomic_read(&dev->num_inst) == 1) {
+		/* Load the FW */
+		ret = s5p_mfc_alloc_firmware(dev);
+		if (ret != 0)
+			goto out_open_2a;
+		ret = s5p_mfc_load_firmware(dev);
+		if (ret != 0)
+			goto out_open_2;
+		mfc_debug("Enabling clocks.\n");
+		clk_enable(dev->clock1);
+		clk_enable(dev->clock2);
+		/* Init the FW */
+		ret = s5p_mfc_init_hw(dev);
+		if (ret != 0)
+			goto out_open_3;
+	}
+	/* Init videobuf2 queue for CAPTURE */
+	ret = vb2_queue_init(&ctx->vq_dst, &s5p_mfc_qops,
+			       dev->alloc_ctx[0], &dev->irqlock,
+			       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, ctx);
+	if (ret) {
+		mfc_err("Failed to initialize videobuf2 queue (capture)\n");
+		goto out_open_3;
+	}
+	/* Init videobuf2 queue for OUTPUT */
+	ret = vb2_queue_init(&ctx->vq_src, &s5p_mfc_qops,
+			       dev->alloc_ctx[1], &dev->irqlock,
+			       V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, ctx);
+	if (ret) {
+		mfc_err("Failed to initialize videobuf2 queue (output)\n");
+		goto out_open_3;
+	}
+	vb2_set_alloc_ctx(&ctx->vq_dst, dev->alloc_ctx[1], 1);
+	init_waitqueue_head(&ctx->queue);
+	mutex_unlock(dev->mfc_mutex);
+	mfc_debug("s5p_mfc_open--\n");
+	return ret;
+	/* Deinit when failure occured */
+out_open_3:
+	if (atomic_read(&dev->num_inst) == 1) {
+		clk_disable(dev->clock1);
+		clk_disable(dev->clock2);
+		s5p_mfc_release_firmware();
+	}
+out_open_2:
+	s5p_mfc_release_firmware();
+out_open_2a:
+	dev->ctx[ctx->num] = 0;
+	kfree(ctx);
+out_open:
+	atomic_dec(&dev->num_inst);
+	mutex_unlock(dev->mfc_mutex);
+	mfc_debug("s5p_mfc_open-- with error\n");
+	return ret;
+}
+
+/* Release MFC context */
+static int s5p_mfc_release(struct file *file)
+{
+	struct s5p_mfc_ctx *ctx = file->private_data;
+	unsigned long flags;
+	mfc_debug("s5p_mfc_release++\n");
+	mutex_lock(dev->mfc_mutex);
+	/* Stop all the processing */
+	vb2_queue_release(&ctx->vq_src);
+	vb2_queue_release(&ctx->vq_dst);
+	/* Mark context as idle */
+	clear_bit(ctx->num, &dev->ctx_work_bits);
+	/* If instance was initialised then
+	 * return instance and free reosurces */
+	if (ctx->state < MFCINST_ENC_INIT && ctx->state >= MFCINST_DEC_INIT) {
+		ctx->state = MFCINST_DEC_RETURN_INST;
+		spin_lock_irqsave(&dev->condlock, flags);
+		set_bit(ctx->num, &dev->ctx_work_bits);
+		spin_unlock_irqrestore(&dev->condlock, flags);
+		s5p_mfc_clean_ctx_int_flags(ctx);
+		s5p_mfc_try_run();
+		/* Wait until instance is returned or timeout occured */
+		if (s5p_mfc_wait_for_done_ctx
+		    (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
+			mfc_err("Error returning instance.\n");
+		}
+		/* Free resources */
+		s5p_mfc_release_dec_buffers(ctx);
+		s5p_mfc_release_instance_buffer(ctx);
+		s5p_mfc_release_dec_temp_buffers(ctx);
+	}
+	/* hardware locking scheme */
+	if (dev->curr_ctx == ctx->num)
+		clear_bit(0, &dev->hw_lock);
+	atomic_dec(&dev->num_inst);
+	if (atomic_read(&dev->num_inst) == 0) {
+		/* Actually this is also protected by mfc_mutex */
+		s5p_mfc_deinit_hw(dev);
+		mfc_debug("Disabling clocks...\n");
+		clk_disable(dev->clock1);
+		clk_disable(dev->clock2);
+		s5p_mfc_release_firmware();
+	}
+	dev->ctx[ctx->num] = 0;
+	kfree(ctx);
+	mutex_unlock(dev->mfc_mutex);
+	mfc_debug("s5p_mfc_release--\n");
+	return 0;
+}
+
+/* Poll */
+static unsigned int s5p_mfc_poll(struct file *file,
+				 struct poll_table_struct *wait)
+{
+	struct s5p_mfc_ctx *ctx = file->private_data;
+	return vb2_poll(&ctx->vq_dst, file, wait);
+}
+
+/* Mmap */
+static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct s5p_mfc_ctx *ctx = file->private_data;
+	int ret;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	mfc_debug("s5p_mfc_mmap++\n");
+	if (offset < DST_QUEUE_OFF_BASE) {
+		mfc_debug("mmaping source.\n");
+		ret = vb2_mmap(&ctx->vq_src, vma);
+	} else {		/* capture */
+		mfc_debug("mmaping destination.\n");
+		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+		ret = vb2_mmap(&ctx->vq_dst, vma);
+	}
+	mfc_debug("s5p_mfc_mmap--\n");
+	return ret;
+}
+
+/* v4l2 ops */
+static const struct v4l2_file_operations s5p_mfc_fops = {
+	.owner = THIS_MODULE,
+	.open = s5p_mfc_open,
+	.release = s5p_mfc_release,
+	.poll = s5p_mfc_poll,
+	.ioctl = video_ioctl2,
+	.mmap = s5p_mfc_mmap,
+};
+
+/* videodec structure */
+static struct video_device s5p_mfc_videodev = {
+	.name = S5P_MFC_NAME,
+	.fops = &s5p_mfc_fops,
+	.ioctl_ops = &s5p_mfc_ioctl_ops,
+	.minor = -1,
+	.release = video_device_release,
+};
+
+/* MFC probe function */
+static int s5p_mfc_probe(struct platform_device *pdev)
+{
+	struct video_device *vfd;
+	struct resource *res;
+	int ret = -ENOENT;
+	size_t size;
+	mfc_debug("s5p_mfc_probe++\n");
+	dev = kzalloc(sizeof *dev, GFP_KERNEL);
+	if (!dev) {
+		dev_err(&pdev->dev, "Not enough memoty for MFC device.\n");
+		return -ENOMEM;
+	}
+	mfc_debug("Ok, allocated memory for device structure.\n");
+	spin_lock_init(&dev->irqlock);
+	spin_lock_init(&dev->condlock);
+	mfc_debug("Initialised spin lock\n");
+	dev->plat_dev = pdev;
+	if (!dev->plat_dev) {
+		dev_err(&pdev->dev, "No platform data specified\n");
+		ret = -ENODEV;
+		goto free_dev;
+	}
+	mfc_debug("Getting clocks\n");
+	dev->clock1 = clk_get(&pdev->dev, "sclk_mfc");
+	mfc_debug("Got clock1\n");
+	dev->clock2 = clk_get(&pdev->dev, "mfc");
+	mfc_debug("Got clock2\n");
+	if (IS_ERR(dev->clock1) || IS_ERR(dev->clock2)) {
+		dev_err(&pdev->dev, "failed to get mfc clock source\n");
+		goto free_clk;
+	}
+	mfc_debug("No \"error\" in clock\n");
+	mfc_debug("After setting clock rate\n");
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get memory region resource.\n");
+		ret = -ENOENT;
+		goto probe_out1;
+	}
+	size = (res->end - res->start) + 1;
+	dev->mfc_mem = request_mem_region(res->start, size, pdev->name);
+	if (dev->mfc_mem == NULL) {
+		dev_err(&pdev->dev, "failed to get memory region.\n");
+		ret = -ENOENT;
+		goto probe_out2;
+	}
+	dev->base_virt_addr = ioremap(dev->mfc_mem->start,
+			      dev->mfc_mem->end - dev->mfc_mem->start + 1);
+	if (dev->base_virt_addr == NULL) {
+		dev_err(&pdev->dev, "failed to ioremap address region.\n");
+		ret = -ENOENT;
+		goto probe_out3;
+	}
+	dev->regs_base = dev->base_virt_addr;
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get irq resource.\n");
+		ret = -ENOENT;
+		goto probe_out4;
+	}
+	dev->irq = res->start;
+	ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
+									dev);
+
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
+		goto probe_out5;
+	}
+	dev->mfc_mutex = kmalloc(sizeof(struct mutex), GFP_KERNEL);
+	if (dev->mfc_mutex == NULL) {
+		dev_err(&pdev->dev, "Memory allocation failed\n");
+		ret = -ENOMEM;
+		goto probe_out6;
+	}
+	mutex_init(dev->mfc_mutex);
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		goto probe_out7;
+	atomic_set(&dev->num_inst, 0);
+	init_waitqueue_head(&dev->queue);
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto unreg_dev;
+	}
+	*vfd = s5p_mfc_videodev;
+	video_set_drvdata(vfd, dev);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", s5p_mfc_videodev.name);
+	dev->vfd = vfd;
+
+	platform_set_drvdata(pdev, dev);
+	dev->hw_lock = 0;
+	dev->watchdog_workqueue = create_singlethread_workqueue("s5p-mfc");
+	INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
+	atomic_set(&dev->watchdog_cnt, 0);
+	init_timer(&dev->watchdog_timer);
+	dev->watchdog_timer.data = 0;
+	dev->watchdog_timer.function = s5p_mfc_watchdog;
+	dev->watchdog_timer.expires = jiffies +
+					msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+	add_timer(&dev->watchdog_timer);
+
+	dev->alloc_ctx = vb2_cma_init_multi(&pdev->dev, 2, s5p_mem_types,
+							s5p_mem_alignments);
+	if (IS_ERR(dev->alloc_ctx)) {
+		mfc_err("Couldn't prepare allocator context.\n");
+		ret = PTR_ERR(dev->alloc_ctx);
+		goto alloc_ctx_fail;
+	}
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		video_device_release(vfd);
+		goto rel_vdev;
+	}
+	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
+								vfd->num);
+	mfc_debug("s5p_mfc_probe--\n");
+	return 0;
+
+/* Deinit MFC if probe had failed */
+rel_vdev:
+	vb2_cma_cleanup_multi(dev->alloc_ctx);
+alloc_ctx_fail:
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+probe_out7:
+	if (dev->mfc_mutex) {
+		mutex_destroy(dev->mfc_mutex);
+		kfree(dev->mfc_mutex);
+	}
+probe_out6:
+	free_irq(dev->irq, dev);
+probe_out5:
+probe_out4:
+	iounmap(dev->base_virt_addr);
+	dev->base_virt_addr = NULL;
+probe_out3:
+	release_resource(dev->mfc_mem);
+	kfree(dev->mfc_mem);
+probe_out2:
+probe_out1:
+	clk_put(dev->clock1);
+	clk_put(dev->clock2);
+free_clk:
+
+free_dev:
+	kfree(dev);
+	mfc_debug("s5p_mfc_probe-- with error\n");
+	return ret;
+}
+
+/* Remove the driver */
+static int s5p_mfc_remove(struct platform_device *pdev)
+{
+	mfc_debug("s5p_mfc_remove++\n");
+	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
+	del_timer_sync(&dev->watchdog_timer);
+	flush_workqueue(dev->watchdog_workqueue);
+	destroy_workqueue(dev->watchdog_workqueue);
+	video_unregister_device(dev->vfd);
+	v4l2_device_unregister(&dev->v4l2_dev);
+	vb2_cma_cleanup_multi(dev->alloc_ctx);
+	if (dev->mfc_mutex) {
+		mutex_destroy(dev->mfc_mutex);
+		kfree(dev->mfc_mutex);
+	}
+	mfc_debug("Will now deinit HW\n");
+	s5p_mfc_deinit_hw(dev);
+	free_irq(dev->irq, dev);
+	iounmap(dev->base_virt_addr);
+	if (dev->mfc_mem != NULL) {
+		release_resource(dev->mfc_mem);
+		kfree(dev->mfc_mem);
+		dev->mfc_mem = NULL;
+	}
+	clk_put(dev->clock1);
+	clk_put(dev->clock2);
+	kfree(dev);
+	mfc_debug("s5p_mfc_remove--\n");
+	return 0;
+}
+
+static int s5p_mfc_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int s5p_mfc_resume(struct device *dev)
+{
+	return 0;
+}
+
+/* Power management */
+static const struct dev_pm_ops s5p_mfc_pm_ops = {
+	.suspend = s5p_mfc_suspend,
+	.resume = s5p_mfc_resume,
+};
+
+static struct platform_driver s5p_mfc_pdrv = {
+	.probe = s5p_mfc_probe,
+	.remove = __devexit_p(s5p_mfc_remove),
+	.driver = {
+		   .name = S5P_MFC_NAME,
+		   .owner = THIS_MODULE,
+		   .pm = &s5p_mfc_pm_ops},
+};
+
+static char banner[] __initdata =
+			"S5P MFC V4L2 Driver, (c) 2010 Samsung Electronics\n";
+
+static int __init s5p_mfc_init(void)
+{
+	mfc_info("%s", banner);
+	if (platform_driver_register(&s5p_mfc_pdrv) != 0) {
+		printk(KERN_ERR "platform device registration failed..\n");
+		return -1;
+	}
+	mfc_info("After init...\n");
+	return 0;
+}
+
+static void __devexit s5p_mfc_exit(void)
+{
+	platform_driver_unregister(&s5p_mfc_pdrv);
+}
+
+module_init(s5p_mfc_init);
+module_exit(s5p_mfc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
new file mode 100644
index 0000000..4f18937
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
@@ -0,0 +1,190 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.0
+ *
+ * This file contains definitions of enums and structs used by the codec
+ * driver.
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef S5P_MFC_COMMON_H_
+#define S5P_MFC_COMMON_H_
+
+#include "regs-mfc5.h"
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#include <media/videobuf2-core.h>
+
+#define MFC_MAX_EXTRA_DPB       5
+#define MFC_MAX_BUFFERS		32
+#define MFC_FRAME_PLANES	2
+
+#define MFC_NUM_CONTEXTS	4
+/* Interrupt timeout */
+#define MFC_INT_TIMEOUT		2000
+/* Busy wait timeout */
+#define MFC_BW_TIMEOUT		500
+/* Watchdog interval */
+#define MFC_WATCHDOG_INTERVAL   1000
+/* After how many executions watchdog should assume lock up */
+#define MFC_WATCHDOG_CNT        10
+
+
+/**
+ * enum s5p_mfc_inst_state - The state of an MFC instance.
+ */
+enum s5p_mfc_inst_state {
+	MFCINST_FREE = 0,
+	MFCINST_DEC_INIT = 100,
+	MFCINST_DEC_GOT_INST,
+	MFCINST_DEC_HEAD_PARSED,
+	MFCINST_DEC_BUFS_SET,
+	MFCINST_DEC_RUNNING,
+	MFCINST_DEC_FINISHING,
+	MFCINST_DEC_FINISHED,
+	MFCINST_DEC_RETURN_INST,
+	MFCINST_DEC_ERROR,
+	MFCINST_ENC_INIT = 200,
+};
+
+/**
+ * enum s5p_mfc_queue_state - The state of buffer queue.
+ */
+enum s5p_mfc_queue_state {
+	QUEUE_FREE = 0,
+	QUEUE_BUFS_REQUESTED,
+	QUEUE_BUFS_QUERIED,
+	QUEUE_BUFS_MMAPED,
+};
+
+struct s5p_mfc_ctx;
+
+/**
+ * struct s5p_mfc_dev - The struct containing driver internal parameters.
+ */
+struct s5p_mfc_dev {
+	struct v4l2_device v4l2_dev;
+	struct video_device *vfd;
+	struct platform_device *plat_dev;
+
+	atomic_t num_inst;
+	spinlock_t irqlock;
+	spinlock_t condlock;
+
+	void __iomem *regs_base;
+	int irq;
+
+	struct resource *mfc_mem;
+	void __iomem *base_virt_addr;
+
+	struct mutex *mfc_mutex;
+
+	int int_cond;
+	int int_type;
+	unsigned int int_err;
+	wait_queue_head_t queue;
+
+	size_t port_a;
+	size_t port_b;
+
+	unsigned long hw_lock;
+
+	struct clk *clock1;
+	struct clk *clock2;
+
+	struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
+	int curr_ctx;
+	unsigned long ctx_work_bits;
+
+	atomic_t watchdog_cnt;
+	struct timer_list watchdog_timer;
+	struct workqueue_struct *watchdog_workqueue;
+	struct work_struct watchdog_work;
+
+	struct vb2_alloc_ctx **alloc_ctx;
+};
+
+/**
+ * struct s5p_mfc_ctx - This struct contains the instance context
+ */
+struct s5p_mfc_ctx {
+	struct s5p_mfc_dev *dev;
+	int num;
+
+	int int_cond;
+	int int_type;
+	unsigned int int_err;
+	wait_queue_head_t queue;
+
+	struct s5p_mfc_fmt *fmt;
+
+	struct vb2_queue vq_src;
+	struct vb2_queue vq_dst;
+
+	struct list_head src_queue;
+	struct list_head dst_queue;
+
+	unsigned int src_queue_cnt;
+	unsigned int dst_queue_cnt;
+
+	enum s5p_mfc_inst_state state;
+	int inst_no;
+
+	/* Decoder parameters */
+	int img_width;
+	int img_height;
+	int buf_width;
+	int buf_height;
+	int dpb_count;
+	int total_dpb_count;
+
+	int luma_size;
+	int chroma_size;
+	int mv_size;
+
+	unsigned long consumed_stream;
+	int slice_interface;
+
+	/* Buffers */
+	size_t port_a;
+	size_t port_a_size;
+	size_t port_b;
+	size_t port_b_size;
+
+
+	enum s5p_mfc_queue_state capture_state;
+	enum s5p_mfc_queue_state output_state;
+
+	size_t dec_dst_buf_luma[MFC_MAX_BUFFERS];
+	size_t dec_dst_buf_chroma[MFC_MAX_BUFFERS];
+
+	int dec_dst_buf_cnt;
+	unsigned int sequence;
+	unsigned long dec_dst_flag;
+	size_t dec_src_buf_size;
+
+	/* Control values */
+	int codec_mode;
+	__u32 pix_format;
+	int loop_filter_mpeg4;
+	int display_delay;
+
+	/* Buffers */
+	size_t instance_phys;
+	size_t instance_size;
+	size_t desc_phys;
+	size_t shared_phys;
+	void *shared_virt;
+
+};
+
+#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
new file mode 100644
index 0000000..222ed71
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
@@ -0,0 +1,173 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.0
+ *
+ * This file contains description of formats used by MFC and cotrols
+ * used by the driver.
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef S5P_MFC_CTRLS_H_
+#define S5P_MFC_CTRLS_H_
+
+#include <media/v4l2-ioctl.h>
+#include "regs-mfc5.h"
+
+#define MFC_FMT_DEC	0
+#define MFC_FMT_ENC	1
+#define MFC_FMT_RAW	2
+
+struct s5p_mfc_fmt {
+	char *name;
+	u32 fourcc;
+	u32 codec_mode;
+	u32 type;
+	u32 num_planes;
+};
+
+#define MFC_FORMATS_NO_CODEC -1
+
+static struct s5p_mfc_fmt formats[] = {
+	{
+	.name = "4:2:0 2 Planes 64x32 Tiles",
+	.fourcc = V4L2_PIX_FMT_NV12MT,
+	.codec_mode = MFC_FORMATS_NO_CODEC,
+	.type = MFC_FMT_RAW,
+	.num_planes = 2,
+	 },
+	{
+	.name = "4:2:0 2 Planes",
+	.fourcc = V4L2_PIX_FMT_NV12,
+	.codec_mode = MFC_FORMATS_NO_CODEC,
+	.type = MFC_FMT_RAW,
+	.num_planes = 2,
+	},
+	{
+	.name = "H264 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_H264,
+	.codec_mode = S5P_FIMV_CODEC_H264_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "H263 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_H263,
+	.codec_mode = S5P_FIMV_CODEC_H263_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "MPEG1/MPEG2 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_MPEG12,
+	.codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "MPEG4 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_MPEG4,
+	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "DivX Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_DIVX,
+	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "DivX 3.11 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_DIVX3,
+	.codec_mode = S5P_FIMV_CODEC_DIVX311_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "DivX 4.12 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_DIVX4,
+	.codec_mode = S5P_FIMV_CODEC_DIVX412_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "DivX 5.00-5.02 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_DIVX500,
+	.codec_mode = S5P_FIMV_CODEC_DIVX502_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "DivX 5.03 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_DIVX503,
+	.codec_mode = S5P_FIMV_CODEC_DIVX503_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "XviD Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_XVID,
+	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "VC1 Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_VC1,
+	.codec_mode = S5P_FIMV_CODEC_VC1_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+	{
+	.name = "VC1 RCV Encoded Stream",
+	.fourcc = V4L2_PIX_FMT_VC1_RCV,
+	.codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
+	.type = MFC_FMT_DEC,
+	.num_planes = 1,
+	},
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct v4l2_queryctrl s5p_mfc_ctrls[] = {
+/* For decoding */
+	{
+	.id = V4L2_CID_CODEC_DISPLAY_DELAY,
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.name = "",
+	.minimum = 0,
+	.maximum = 16383,
+	.step = 1,
+	.default_value = 0,
+	},
+	{
+	.id = V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE,
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.name = "Mpeg4 Loop Filter Enable",
+	.minimum = 0,
+	.maximum = 1,
+	.step = 1,
+	.default_value = 0,
+	},
+	{
+	.id = V4L2_CID_CODEC_SLICE_INTERFACE,
+	.type = V4L2_CTRL_TYPE_BOOLEAN,
+	.name = "Slice Interface Enable",
+	.minimum = 0,
+	.maximum = 1,
+	.step = 1,
+	.default_value = 0,
+	},
+};
+
+#define NUM_CTRLS ARRAY_SIZE(s5p_mfc_ctrls)
+
+#endif /* S5P_MFC_CTRLS_H_ */
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
new file mode 100644
index 0000000..543f3fb
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
@@ -0,0 +1,77 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
+ *
+ * C file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains functions used to wait for command completion.
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include "regs-mfc5.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_logmsg.h"
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
+{
+	if (wait_event_interruptible_timeout(dev->queue,
+		(dev->int_cond && (dev->int_type == command
+		|| dev->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
+		msecs_to_jiffies(MFC_INT_TIMEOUT)) == 0) {
+		mfc_err("Interrupt (%d dev) timed out.\n", dev->int_type);
+		return 1;
+	}
+	mfc_debug("Finished waiting (dev->queue, %d).\n", dev->int_type);
+	if (dev->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
+		return 1;
+	return 0;
+}
+
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
+{
+	dev->int_cond = 0;
+	dev->int_type = 0;
+	dev->int_err = 0;
+}
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+				    int command, int interrupt)
+{
+	int ret;
+	if (interrupt) {
+		ret = wait_event_interruptible_timeout(ctx->queue,
+				(ctx->int_cond && (ctx->int_type == command
+			|| ctx->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
+					msecs_to_jiffies(MFC_INT_TIMEOUT));
+	} else {
+		ret = wait_event_timeout(ctx->queue,
+				(ctx->int_cond && (ctx->int_type == command
+			|| ctx->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
+					msecs_to_jiffies(MFC_INT_TIMEOUT));
+	}
+	if (ret == 0) {
+		mfc_err("Interrupt (%d ctx) timed out.\n", ctx->int_type);
+		return 1;
+	}
+	mfc_debug("Finished waiting (ctx->queue, %d).\n", ctx->int_type);
+	if (ctx->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
+		return 1;
+	return 0;
+}
+
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
+{
+	ctx->int_cond = 0;
+	ctx->int_type = 0;
+	ctx->int_err = 0;
+}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
new file mode 100644
index 0000000..8c531b6
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
@@ -0,0 +1,26 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * It contains waiting functions declarations.
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#ifndef _S5P_MFC_INTR_H_
+#define _S5P_MFC_INTR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+						int command, int interrupt);
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
+
+#endif /* _S5P_MFC_INTR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
new file mode 100644
index 0000000..90fa84c
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
@@ -0,0 +1,65 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_logmsg.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains debug macros
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_LOGMSG_H_
+#define S5P_MFC_LOGMSG_H_
+
+/* Debug macros */
+#define MFC_DEBUG(fmt, ...)					\
+	do {							\
+		printk(KERN_ERR					\
+			"%s: " fmt, __func__, ##__VA_ARGS__);	\
+	} while (0)
+
+#define MFC_ERROR(fmt, ...)					\
+	do {							\
+		printk(KERN_ERR					\
+			"%s: " fmt, __func__, ##__VA_ARGS__);	\
+	} while (0)
+
+#define MFC_NOTICE(fmt, ...)					\
+	do {							\
+		printk(KERN_NOTICE				\
+			fmt, ##__VA_ARGS__);			\
+	} while (0)
+
+#define MFC_INFO(fmt, ...)					\
+	do {							\
+		printk(KERN_INFO				\
+			fmt, ##__VA_ARGS__);			\
+	} while (0)
+
+#define MFC_WARN(fmt, ...)					\
+	do {							\
+		printk(KERN_WARNING				\
+			fmt, ##__VA_ARGS__);			\
+	} while (0)
+
+/* Uncomment the line below do enable debug messages */
+/* #define CONFIG_VIDEO_MFC50_DEBUG */
+
+#ifdef CONFIG_VIDEO_MFC50_DEBUG
+#define mfc_debug(fmt, ...)		MFC_DEBUG(fmt, ##__VA_ARGS__)
+#warning "DEBUG in MFC is switched on!"
+#else
+#define mfc_debug(fmt, ...)
+#endif /* CONFIG_VIDEO_MFC50_DEBUG */
+
+#define mfc_err(fmt, ...)		MFC_ERROR(fmt, ##__VA_ARGS__)
+#define mfc_notice(fmt, ...)		MFC_NOTICE(fmt, ##__VA_ARGS__)
+#define mfc_info(fmt, ...)		MFC_INFO(fmt, ##__VA_ARGS__)
+#define mfc_warn(fmt, ...)		MFC_WARN(fmt, ##__VA_ARGS__)
+
+#endif /* S5P_MFC_LOGMSG_H_ */
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_memory.h b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
new file mode 100644
index 0000000..a7e4009
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
@@ -0,0 +1,32 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_memory.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * Contains memory related defines.
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_MEMORY_H_
+#define S5P_MFC_MEMORY_H_
+
+#include "s5p_mfc_common.h"
+
+#define FIRMWARE_CODE_SIZE		0x60000	/* 384KB */
+#define MFC_H264_INSTANCE_BUF_SIZE	0x96000	/* 600KB per H264 instance */
+#define MFC_INSTANCE_BUF_SIZE		0x2800	/* 10KB per instance */
+#define DESC_BUF_SIZE			0x20000	/* 128KB for DESC buffer */
+#define SHARED_BUF_SIZE			0x01000	/* 4KB for shared buffer */
+#define CPB_BUF_SIZE			0x400000/* 4MB fr decoder */
+
+/* Define names for CMA memory kinds used by MFC */
+#define MFC_CMA_BANK1		"a"
+#define MFC_CMA_BANK2		"b"
+#define MFC_CMA_FW		"f"
+
+#endif /* S5P_MFC_MEMORY_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
new file mode 100644
index 0000000..b6e2334
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
@@ -0,0 +1,800 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
+ *
+ * Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains hw related functions.
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include "regs-mfc5.h"
+
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_logmsg.h"
+#include "s5p_mfc_memory.h"
+
+#include "s5p_mfc_intr.h"
+
+#include <linux/firmware.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/cma.h>
+
+static size_t s5p_mfc_phys_bitproc_buff;
+static unsigned char *s5p_mfc_virt_bitproc_buff;
+
+/* #define S5P_MFC_DEBUG_REGWRITE  */
+#ifdef S5P_MFC_DEBUG_REGWRITE
+#undef writel
+#define writel(v, r) do { \
+	printk(KERN_ERR "MFCWRITE(%p): %08x\n", r, (unsigned int)v); \
+	__raw_writel(v, r); } while (0)
+#endif /* S5P_MFC_DEBUG_REGWRITE */
+
+#define READL(offset)		readl(dev->regs_base + (offset))
+#define WRITEL(data, offset)	writel((data), dev->regs_base + (offset))
+#define OFFSETA(x)		(((x) - dev->port_a) >> 11)
+#define OFFSETB(x)		(((x) - dev->port_b) >> 11)
+
+/* Reset the device */
+static int s5p_mfc_cmd_reset(struct s5p_mfc_dev *dev)
+{
+	unsigned int mc_status;
+	unsigned long timeout;
+	mfc_debug("s5p_mfc_cmd_reset++\n");
+	/* Stop procedure */
+	WRITEL(0x3f7, S5P_FIMV_SW_RESET);	/*  reset VI */
+	WRITEL(0x3f6, S5P_FIMV_SW_RESET);	/*  reset RISC */
+	WRITEL(0x3e2, S5P_FIMV_SW_RESET);	/*  All reset except for MC */
+	mdelay(10);
+	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+	/* Check MC status */
+	do {
+		if (time_after(jiffies, timeout)) {
+			mfc_err("Timeout while resetting MFC.\n");
+			return -EIO;
+		}
+		mc_status = READL(S5P_FIMV_MC_STATUS);
+	} while (mc_status & 0x3);
+	WRITEL(0x0, S5P_FIMV_SW_RESET);
+	WRITEL(0x3fe, S5P_FIMV_SW_RESET);
+	mfc_debug("s5p_mfc_cmd_reset--\n");
+	return 0;
+}
+
+/* Send a command to the MFC */
+static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev,
+				struct s5p_mfc_ctx *mfc_ctx, int cmd, int arg)
+{
+	int cur_cmd;
+	unsigned long timeout;
+	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+	/* wait until host to risc command register becomes 'H2R_CMD_EMPTY' */
+	do {
+		if (time_after(jiffies, timeout)) {
+			mfc_err("Timeout while waiting for hardware.\n");
+			return -EIO;
+		}
+		cur_cmd = READL(S5P_FIMV_HOST2RISC_CMD);
+	} while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
+	WRITEL(arg, S5P_FIMV_HOST2RISC_ARG1);
+	if (cmd == S5P_FIMV_H2R_CMD_OPEN_INSTANCE) {
+		/* No CRC calculation (slow!) */
+		WRITEL(0, S5P_FIMV_HOST2RISC_ARG2);
+		/* Physical addr of the instance buffer */
+		WRITEL(OFFSETA(mfc_ctx->instance_phys),
+		       S5P_FIMV_HOST2RISC_ARG3);
+		/* Size of the instance buffer */
+		WRITEL(mfc_ctx->instance_size, S5P_FIMV_HOST2RISC_ARG4);
+	}
+	/* Issue the command */
+	WRITEL(cmd, S5P_FIMV_HOST2RISC_CMD);
+	return 0;
+}
+/*
+static void s5p_mfc_cmd_sleep()
+{
+	WRITEL(-1, S5P_FIMV_CH_ID);
+	WRITEL(MFC_SLEEP, S5P_FIMV_COMMAND_TYPE);
+}
+*/
+
+/*
+static void s5p_mfc_cmd_wakeup()
+{
+	WRITEL(-1, S5P_FIMV_CH_ID);
+	WRITEL(MFC_WAKEUP, S5P_FIMV_COMMAND_TYPE);
+	mdelay(100);
+}
+*/
+
+/* Allocate temporary buffers for decoding */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
+{
+	void *desc_virt;
+	mfc_debug("s5p_mfc_alloc_dec_temp_buffers++\n");
+	mfc_ctx->desc_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
+					MFC_CMA_BANK1, DESC_BUF_SIZE, 2048);
+	if (IS_ERR_VALUE(mfc_ctx->desc_phys)) {
+		mfc_ctx->desc_phys = 0;
+		mfc_err("Allocating DESC buffer failed.\n");
+		return -ENOMEM;
+	}
+	desc_virt = ioremap_nocache(mfc_ctx->desc_phys, DESC_BUF_SIZE);
+	if (desc_virt == NULL) {
+		cma_free(mfc_ctx->desc_phys);
+		mfc_ctx->desc_phys = 0;
+		mfc_err("Remapping DESC buffer failed.\n");
+		return -ENOMEM;
+	}
+	/* Zero content of the allocated memory, in future this might be done
+	 * by cma_alloc */
+	memset(desc_virt, 0, DESC_BUF_SIZE);
+	iounmap(desc_virt);
+	mfc_debug("s5p_mfc_alloc_dec_temp_buffers--\n");
+	return 0;
+}
+
+/* Release temproary buffers for decoding */
+void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
+{
+	if (mfc_ctx->desc_phys) {
+		cma_free(mfc_ctx->desc_phys);
+		mfc_ctx->desc_phys = 0;
+	}
+}
+
+/* Allocate decoding buffers */
+int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
+{
+	unsigned int luma_size, chroma_size, mv_size;
+	mfc_debug("s5p_mfc_alloc_dec_buffers++\n");
+	luma_size = mfc_ctx->luma_size;
+	chroma_size = mfc_ctx->chroma_size;
+	mv_size = mfc_ctx->mv_size;
+	mfc_debug("Luma size: %d Chroma size: %d MV size: %d\n",
+		  luma_size, chroma_size, mv_size);
+	/* Codecs have different memory requirements */
+	switch (mfc_ctx->codec_mode) {
+	case S5P_FIMV_CODEC_H264_DEC:
+		mfc_ctx->port_a_size =
+		    ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
+			     S5P_FIMV_DEC_VERT_NB_MV_SIZE, 8192);
+		mfc_ctx->port_b_size =
+		    mfc_ctx->total_dpb_count * ALIGN(mv_size, 8192) * 2;
+		break;
+	case S5P_FIMV_CODEC_MPEG4_DEC:
+	case S5P_FIMV_CODEC_DIVX412_DEC:
+	case S5P_FIMV_CODEC_DIVX311_DEC:
+	case S5P_FIMV_CODEC_DIVX502_DEC:
+	case S5P_FIMV_CODEC_DIVX503_DEC:
+		mfc_ctx->port_a_size =
+		    ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
+			     S5P_FIMV_DEC_UPNB_MV_SIZE +
+			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+			     S5P_FIMV_DEC_STX_PARSER_SIZE +
+			     S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE, 8192);
+		mfc_ctx->port_b_size = 0;
+		break;
+
+	case S5P_FIMV_CODEC_VC1RCV_DEC:
+	case S5P_FIMV_CODEC_VC1_DEC:
+		mfc_ctx->port_a_size =
+		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+			     S5P_FIMV_DEC_UPNB_MV_SIZE +
+			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+			     S5P_FIMV_DEC_NB_DCAC_SIZE +
+			     3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE, 8192);
+		mfc_ctx->port_b_size = 0;
+		break;
+
+	case S5P_FIMV_CODEC_MPEG2_DEC:
+		mfc_ctx->port_a_size = 0;
+		mfc_ctx->port_b_size = 0;
+		break;
+	case S5P_FIMV_CODEC_H263_DEC:
+		mfc_ctx->port_a_size =
+		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+			     S5P_FIMV_DEC_UPNB_MV_SIZE +
+			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+			     S5P_FIMV_DEC_NB_DCAC_SIZE, 8192);
+		mfc_ctx->port_b_size = 0;
+		break;
+	default:
+		break;
+	}
+
+	/* Allocate only if memory from bank 1 is necessary */
+	if (mfc_ctx->port_a_size > 0) {
+		mfc_ctx->port_a = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
+				MFC_CMA_BANK1, mfc_ctx->port_a_size, 2048);
+		if (IS_ERR_VALUE(mfc_ctx->port_a)) {
+			mfc_ctx->port_a = 0;
+			printk(KERN_ERR
+			       "Buf alloc for decoding failed (port A).\n");
+			return -ENOMEM;
+		}
+	}
+
+	/* Allocate only if memory from bank 2 is necessary */
+	if (mfc_ctx->port_b_size > 0) {
+		mfc_ctx->port_b = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
+				MFC_CMA_BANK2, mfc_ctx->port_b_size, 2048);
+		if (IS_ERR_VALUE(mfc_ctx->port_b)) {
+			mfc_ctx->port_b = 0;
+			mfc_err("Buf alloc for decoding failed (port B).\n");
+			return -ENOMEM;
+		}
+	}
+	mfc_debug("s5p_mfc_alloc_dec_buffers--\n");
+
+	return 0;
+}
+
+/* Release buffers allocated for decoding */
+void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
+{
+	if (mfc_ctx->port_a) {
+		cma_free(mfc_ctx->port_a);
+		mfc_ctx->port_a = 0;
+		mfc_ctx->port_a_size = 0;
+	}
+	if (mfc_ctx->port_b) {
+		cma_free(mfc_ctx->port_b);
+		mfc_ctx->port_b = 0;
+		mfc_ctx->port_b_size = 0;
+	}
+}
+
+/* Allocate memory for instance data buffer */
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
+{
+	void *instance_virt;
+	mfc_debug("s5p_mfc_alloc_instance_buffer++\n");
+	/* According to EVT1 docu, H264 requires 600KB
+	 * and all other need onlu 10KB */
+	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
+		mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+		mfc_ctx->instance_size = MFC_H264_INSTANCE_BUF_SIZE;
+	else
+		mfc_ctx->instance_size = MFC_INSTANCE_BUF_SIZE;
+	mfc_ctx->instance_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
+				MFC_CMA_BANK1, mfc_ctx->instance_size, 2048);
+	if (IS_ERR_VALUE(mfc_ctx->instance_phys)) {
+		mfc_ctx->instance_phys = 0;
+		mfc_err("Allocating instance buffer failed.\n");
+		return -ENOMEM;
+	}
+	instance_virt = ioremap_nocache(mfc_ctx->instance_phys,
+		mfc_ctx->instance_size);
+	if (instance_virt == NULL) {
+		cma_free(mfc_ctx->instance_phys);
+		mfc_ctx->instance_phys = 0;
+		mfc_err("Remapping instance buffer failed.\n");
+		return -ENOMEM;
+	}
+	/* Zero content of the allocated memory, in future this might be done
+	 * by cma_alloc */
+	memset(instance_virt, 0, mfc_ctx->instance_size);
+	iounmap(instance_virt);
+	mfc_ctx->shared_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
+					MFC_CMA_BANK1, SHARED_BUF_SIZE, 2048);
+	if (IS_ERR_VALUE(mfc_ctx->shared_phys)) {
+		mfc_ctx->shared_phys = 0;
+		mfc_err("Allocating shared buffer failed\n");
+		cma_free(mfc_ctx->instance_phys);
+		return -ENOMEM;
+	}
+	mfc_ctx->shared_virt = ioremap_nocache(mfc_ctx->shared_phys,
+							       SHARED_BUF_SIZE);
+	if (!mfc_ctx->shared_virt) {
+		cma_free(mfc_ctx->instance_phys);
+		cma_free(mfc_ctx->shared_phys);
+		mfc_ctx->shared_phys = 0;
+		mfc_ctx->instance_phys = 0;
+		return -ENOMEM;
+	}
+	/* Zero content of the allocated memory, in future this might be done
+	 * by cma_alloc */
+	memset((void *)mfc_ctx->shared_virt, 0, SHARED_BUF_SIZE);
+	mfc_debug("s5p_mfc_alloc_instance_buffer--\n");
+	return 0;
+}
+
+/* Release instance buffer */
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
+{
+	mfc_debug("s5p_mfc_release_instance_buffer++\n");
+	if (mfc_ctx->instance_phys) {
+		cma_free(mfc_ctx->instance_phys);
+		mfc_ctx->instance_phys = 0;
+	}
+	if (mfc_ctx->shared_virt) {
+		iounmap(mfc_ctx->shared_virt);
+		mfc_ctx->shared_virt = 0;
+	}
+	if (mfc_ctx->shared_phys) {
+		cma_free(mfc_ctx->shared_phys);
+		mfc_ctx->shared_phys = 0;
+	}
+	mfc_debug("s5p_mfc_release_instance_buffer--\n");
+}
+
+/* Set registers for decoding temporary buffers */
+void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
+{
+	struct s5p_mfc_dev *dev = mfc_ctx->dev;
+	WRITEL(OFFSETA(mfc_ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
+	WRITEL(CPB_BUF_SIZE, S5P_FIMV_SI_CH0_CPB_SIZE);
+	WRITEL(DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
+	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
+	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
+}
+
+/* Set registers for decoding stream buffer */
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int buf_addr,
+		  unsigned int start_num_byte, unsigned int buf_size)
+{
+	struct s5p_mfc_dev *dev = mfc_ctx->dev;
+	mfc_debug("inst_no : %d, buf_addr : 0x%08x, buf_size : 0x%08x (%d)\n",
+		  mfc_ctx->inst_no, buf_addr, buf_size, buf_size);
+	if (buf_addr & (2048 - 1)) {
+		mfc_err("Source stream buffer is not aligned correctly.\n");
+		return -EINVAL;
+	}
+	WRITEL(OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
+	WRITEL(buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
+	mfc_debug("Shared_virt: %p (start offset: %d)\n", mfc_ctx->shared_virt,
+		  start_num_byte);
+	writel(start_num_byte, mfc_ctx->shared_virt \
+					 + S5P_FIMV_SHARED_START_BYTE_NUM);
+	return 0;
+}
+
+/* Set decoding frame buffer */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int do_int)
+{
+	unsigned int frame_size, i;
+	unsigned int frame_size_ch, frame_size_mv;
+	struct s5p_mfc_dev *dev = mfc_ctx->dev;
+	unsigned int dpb;
+	size_t buf_addr1, buf_addr2;
+	int buf_size1, buf_size2;
+	buf_addr1 = mfc_ctx->port_a;
+	buf_size1 = mfc_ctx->port_a_size;
+	buf_addr2 = mfc_ctx->port_b;
+	buf_size2 = mfc_ctx->port_b_size;
+	mfc_debug("Buf1: %p (%d) Buf2: %p (%d)\n", (void *)buf_addr1, buf_size1,
+		  (void *)buf_addr2, buf_size2);
+	/* Enable generation of extra info */
+/*	*(shared_mem_vir_addr + 0x0038) = 63; */
+	mfc_debug("Total DPB COUNT: %d\n", mfc_ctx->total_dpb_count);
+	mfc_debug("Setting display delay to %d\n", mfc_ctx->display_delay);
+	dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & 0xFFFF0000;
+	WRITEL(mfc_ctx->total_dpb_count | dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+	s5p_mfc_set_dec_temp_buffers(mfc_ctx);
+	switch (mfc_ctx->codec_mode) {
+	case S5P_FIMV_CODEC_H264_DEC:
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_IP_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
+		break;
+	case S5P_FIMV_CODEC_MPEG4_DEC:
+	case S5P_FIMV_CODEC_DIVX311_DEC:
+	case S5P_FIMV_CODEC_DIVX412_DEC:
+	case S5P_FIMV_CODEC_DIVX502_DEC:
+	case S5P_FIMV_CODEC_DIVX503_DEC:
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SP_ADR);
+		buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
+		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		break;
+	case S5P_FIMV_CODEC_H263_DEC:
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
+		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+		break;
+	case S5P_FIMV_CODEC_VC1_DEC:
+	case S5P_FIMV_CODEC_VC1RCV_DEC:
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
+		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
+		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
+		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE3_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE2_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE1_ADR);
+		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+		break;
+	case S5P_FIMV_CODEC_MPEG2_DEC:
+		break;
+	default:
+		mfc_err("Unknown codec set for decoding (%x).\n",
+			mfc_ctx->codec_mode);
+		return -EINVAL;
+		break;
+	}
+	frame_size = mfc_ctx->luma_size;
+	frame_size_ch = mfc_ctx->chroma_size;
+	frame_size_mv = mfc_ctx->mv_size;
+	mfc_debug("Frame size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
+		  frame_size_mv);
+	for (i = 0; i < mfc_ctx->total_dpb_count; i++) {
+		/* Port B */
+		mfc_debug("Luma %d: %x\n", i, mfc_ctx->dec_dst_buf_luma[i]);
+		WRITEL(OFFSETB(mfc_ctx->dec_dst_buf_luma[i]),
+		       S5P_FIMV_LUMA_ADR + i * 4);
+		mfc_debug("\tChroma %d: %x\n", i, \
+						mfc_ctx->dec_dst_buf_chroma[i]);
+		WRITEL(OFFSETA(mfc_ctx->dec_dst_buf_chroma[i]),
+		       S5P_FIMV_CHROMA_ADR + i * 4);
+		if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+			mfc_debug("\tBuf2: %x, buf_size2: %d\n", buf_addr2,
+				  buf_size2);
+			WRITEL(OFFSETB(buf_addr2), S5P_FIMV_MV_ADR + i * 4);
+			buf_addr2 += ALIGN(frame_size_mv, 8192);
+			buf_size2 -= ALIGN(frame_size_mv, 8192);
+		}
+	}
+	mfc_debug("Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
+	mfc_debug("Buf 1/2 size after: %d/%d (num frames %d)\n", buf_size1,
+		  buf_size2, mfc_ctx->total_dpb_count);
+	if (buf_size1 < 0 || buf_size2 < 0) {
+		mfc_debug("Not enough memory has been allocated.\n");
+		return -1337;
+	}
+	writel(frame_size, mfc_ctx->shared_virt \
+					+ S5P_FIMV_SHARED_LUMA_DPB_SIZE);
+	writel(frame_size_ch, mfc_ctx->shared_virt \
+					+ S5P_FIMV_SHARED_CHROMA_DPB_SIZE);
+	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+		writel(frame_size_mv, mfc_ctx->shared_virt \
+						+ S5P_FIMV_SHARED_MV_SIZE);
+	}
+	if (do_int) {
+		mfc_debug("Doing int.\n");
+		WRITEL(((S5P_FIMV_CH_INIT_BUFS << 16) & 0x70000) | \
+				(mfc_ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+
+	}
+	mfc_debug("After setting buffers.\n");
+	return 0;
+}
+
+/* Allocate firmware */
+int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
+{
+	int err;
+	struct cma_info mem_info_f, mem_info_a, mem_info_b;
+	mfc_debug("s5p_mfc_alloc_firmware++\n");
+	if (s5p_mfc_phys_bitproc_buff) {
+		mfc_err("Attempting to allocate firmware when it seems that" \
+			"it is already loaded.\n");
+		return -ENOMEM;
+	}
+	/* Get memory region information and check if it is correct */
+	err = cma_info(&mem_info_f, dev->v4l2_dev.dev, MFC_CMA_FW);
+	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
+		"f", mem_info_f.lower_bound, mem_info_f.upper_bound,
+		mem_info_f.total_size);
+	if (err) {
+		mfc_err("Couldn't get memory information from CMA.\n");
+		return -EINVAL;
+	}
+	err = cma_info(&mem_info_a, dev->v4l2_dev.dev, MFC_CMA_BANK1);
+	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
+		"a", mem_info_a.lower_bound, mem_info_a.upper_bound,
+		mem_info_a.total_size);
+	if (err) {
+		mfc_err("Couldn't get memory information from CMA.\n");
+		return -EINVAL;
+	}
+	err = cma_info(&mem_info_b, dev->v4l2_dev.dev, MFC_CMA_BANK2);
+	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
+		"b", mem_info_b.lower_bound, mem_info_b.upper_bound,
+		mem_info_b.total_size);
+	if (err) {
+		mfc_err("Couldn't get memory information from CMA.\n");
+		return -EINVAL;
+	}
+	if (mem_info_f.upper_bound > mem_info_a.lower_bound) {
+			mfc_err("Firmware has to be allocated before" \
+			" memory for buffers (bank A).\n");
+		return -EINVAL;
+	}
+	mfc_debug("Allocating memory for firmware.\n");
+	s5p_mfc_phys_bitproc_buff = cma_alloc(dev->v4l2_dev.dev, MFC_CMA_FW,
+						FIRMWARE_CODE_SIZE, 128 * 1024);
+	mfc_debug("Phys addr from CMA: %08x\n", s5p_mfc_phys_bitproc_buff);
+	if (IS_ERR_VALUE(s5p_mfc_phys_bitproc_buff)) {
+		s5p_mfc_phys_bitproc_buff = 0;
+		printk(KERN_ERR "Allocating bitprocessor buffer failed\n");
+		return -ENOMEM;
+	}
+	if (s5p_mfc_phys_bitproc_buff & 0x0001FFFF) {
+		mfc_err("The base memory is not aligned to 128KB.\n");
+		cma_free(s5p_mfc_phys_bitproc_buff);
+		return -EIO;
+	}
+	dev->port_a = s5p_mfc_phys_bitproc_buff;
+	dev->port_b = mem_info_b.lower_bound;
+	mfc_debug("Port A: %08x Port B: %08x (FW: %08x size: %08x)\n",
+			dev->port_a, dev->port_b, s5p_mfc_phys_bitproc_buff,
+							FIRMWARE_CODE_SIZE);
+	s5p_mfc_virt_bitproc_buff = ioremap_nocache(s5p_mfc_phys_bitproc_buff,
+							FIRMWARE_CODE_SIZE);
+	mfc_debug("Virtual address for FW: %08lx\n",
+				(long unsigned int)s5p_mfc_virt_bitproc_buff);
+	if (!s5p_mfc_virt_bitproc_buff) {
+		mfc_err("Bitprocessor memory ioremap failed\n");
+		cma_free(s5p_mfc_phys_bitproc_buff);
+		s5p_mfc_phys_bitproc_buff = 0;
+		return -EIO;;
+	}
+	return 0;
+}
+
+/* Load firmware to MFC */
+int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
+{
+	struct firmware *fw_blob;
+	int err;
+	/* Firmare has to be present as a separate file or compiled
+	 * into kernel. */
+	mfc_debug("s5p_mfc_load_firmware++\n");
+	mfc_debug("Requesting fw\n");
+	err = request_firmware((const struct firmware **)&fw_blob,
+				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
+	mfc_debug("Ret of request_firmware: %d Size: %d\n", err, fw_blob->size);
+	if (err != 0) {
+		mfc_err("Firmware is not present in the /lib/firmware directory"
+						" nor compiled in kernel.\n");
+		return -EINVAL;
+	}
+	if (fw_blob->size > FIRMWARE_CODE_SIZE) {
+		mfc_err("MFC firmware is too big to be loaded.\n");
+		release_firmware(fw_blob);
+		return -ENOMEM;
+	}
+	if (s5p_mfc_phys_bitproc_buff == 0 || s5p_mfc_phys_bitproc_buff == 0) {
+		mfc_err("MFC firmware is not allocated or was not mapped "\
+								"correctly.\n");
+		release_firmware(fw_blob);
+		return -EINVAL;
+	}
+	memcpy(s5p_mfc_virt_bitproc_buff, fw_blob->data, fw_blob->size);
+/*	If byteswap is needed then use the following instead of memcpy */
+/*	for (i = 0; i < fw_blob->size; i += 4) {
+		s5p_mfc_virt_bitproc_buff[i + 0] = fw_blob->data[i + 3];
+		s5p_mfc_virt_bitproc_buff[i + 1] = fw_blob->data[i + 2];
+		s5p_mfc_virt_bitproc_buff[i + 2] = fw_blob->data[i + 1];
+		s5p_mfc_virt_bitproc_buff[i + 3] = fw_blob->data[i + 0];
+	}*/
+	release_firmware(fw_blob);
+	mfc_debug("s5p_mfc_load_firmware--\n");
+	return 0;
+}
+
+/* Release firmware memory */
+int s5p_mfc_release_firmware()
+{
+	/* Before calling this function one has to make sure
+	 * that MFC is no longer processing */
+	if (!s5p_mfc_phys_bitproc_buff)
+		return -EINVAL;
+	mfc_debug("s5p_mfc_virt_bitproc_buff: %p\n", s5p_mfc_virt_bitproc_buff);
+	iounmap(s5p_mfc_virt_bitproc_buff);
+	s5p_mfc_virt_bitproc_buff = NULL;
+	cma_free(s5p_mfc_phys_bitproc_buff);
+	s5p_mfc_phys_bitproc_buff = 0;
+	return 0;
+}
+
+/* Initialize hardware */
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
+{
+	int fw_buf_size;
+	unsigned int fw_version;
+	int ret;
+	mfc_debug("s5p_mfc_init_hw++\n");
+	mfc_debug("Device pointer: %p\n", dev);
+	if (!s5p_mfc_phys_bitproc_buff)
+		return -EINVAL;
+	/* 0. MFC reset */
+	mfc_debug("MFC reset...\n");
+	ret = s5p_mfc_cmd_reset(dev);
+	if (ret) {
+		mfc_err("Failed to reset MFC - timeout.\n");
+		return ret;
+	}
+	mfc_debug("Done MFC reset...\n");
+	/* 1. Set DRAM base Addr */
+	WRITEL(dev->port_a, S5P_FIMV_MC_DRAMBASE_ADR_A); /* channelA, port0 */
+	WRITEL(dev->port_b, S5P_FIMV_MC_DRAMBASE_ADR_B); /* channelB, port1 */
+	mfc_debug("Port A: %08x, Port B: %08x\n", dev->port_a, dev->port_b);
+	/* 2. Initialize registers of stream I/F for decoder */
+	WRITEL(0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
+	WRITEL(0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
+	WRITEL(0, S5P_FIMV_RISC2HOST_CMD);
+	WRITEL(0, S5P_FIMV_HOST2RISC_CMD);
+	/* 3. Release reset signal to the RISC.  */
+	WRITEL(0x3ff, S5P_FIMV_SW_RESET);
+	mfc_debug("Will now wait for completion of firmware transfer.\n");
+	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
+		mfc_err("Failed to load firmware.\n");
+		s5p_mfc_clean_dev_int_flags(dev);
+		return -EIO;
+	}
+	s5p_mfc_clean_dev_int_flags(dev);
+	/* 4. Initialize firmware */
+	fw_buf_size = FIRMWARE_CODE_SIZE;
+	mfc_debug("Writing a command\n");
+	ret = s5p_mfc_cmd_host2risc(dev, 0, S5P_FIMV_H2R_CMD_SYS_INIT,
+								fw_buf_size);
+	if (ret) {
+		mfc_err("Failed to send command to MFC - timeout.\n");
+		return ret;
+	}
+	mfc_debug("Ok, now will write a command to init the system\n");
+	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
+		mfc_err("Failed to load firmware\n");
+		return -EIO;
+	}
+	dev->int_cond = 0;
+	if (dev->int_err != 0 || dev->int_type !=
+						S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
+		/* Failure. */
+		mfc_err("Failed to init firmware - error: %d int: %d.\n",
+			dev->int_err, dev->int_type);
+		return -EIO;
+	}
+	fw_version = READL(S5P_FIMV_FW_VERSION);
+	mfc_info("MFC FW version : %02xyy, %02xmm, %02xdd\n",
+			 (fw_version >> 16) & 0xff, (fw_version >> 8) & 0xff,
+							 (fw_version) & 0xff);
+	mfc_debug("FW_PHY_BUFFER : 0x%08x\n",
+		  READL(S5P_FIMV_MC_DRAMBASE_ADR_A));
+	mfc_debug("DPB_LUMA_BUFFER : 0x%08x\n",
+		  READL(S5P_FIMV_MC_DRAMBASE_ADR_B));
+	mfc_debug("s5p_mfc_init_hw--\n");
+	return 0;
+}
+
+/* Open a new instance and get its number */
+int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx)
+{
+	int ret;
+	mfc_debug("s5p_mfc_get_inst_no++\n");
+	mfc_debug("Requested codec mode: %d\n", mfc_ctx->codec_mode);
+	ret = s5p_mfc_cmd_host2risc(mfc_ctx->dev, mfc_ctx, \
+			S5P_FIMV_H2R_CMD_OPEN_INSTANCE, mfc_ctx->codec_mode);
+	mfc_debug("s5p_mfc_get_inst_no--\n");
+	return ret;
+}
+
+/* Close instance */
+int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx)
+{
+	int ret = 0;
+	struct s5p_mfc_dev *dev = mfc_ctx->dev;
+	mfc_debug("s5p_mfc_return_inst_no++\n");
+	if (mfc_ctx->state != MFCINST_FREE) {
+		ret = s5p_mfc_cmd_host2risc(dev, mfc_ctx,
+			S5P_FIMV_H2R_CMD_CLOSE_INSTANCE, mfc_ctx->inst_no);
+	} else {
+		ret = -EINVAL;
+	}
+	mfc_debug("s5p_mfc_return_inst_no--\n");
+	return ret;
+}
+
+/* Initialize decoding */
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx)
+{
+	struct s5p_mfc_dev *dev = mfc_ctx->dev;
+	mfc_debug("++\n");
+
+	mfc_debug("InstNo: %d/%d\n", mfc_ctx->inst_no, S5P_FIMV_CH_SEQ_HEADER);
+
+	mfc_debug("BUFs: %08x %08x %08x %08x %08x\n",
+		  READL(S5P_FIMV_SI_CH0_DESC_ADR),
+		  READL(S5P_FIMV_SI_CH0_CPB_SIZE),
+		  READL(S5P_FIMV_SI_CH0_DESC_SIZE),
+		  READL(S5P_FIMV_SI_CH0_SB_ST_ADR),
+		  READL(S5P_FIMV_SI_CH0_SB_FRM_SIZE));
+	/* Setup loop filter, for decoding this is only valid for MPEG4 */
+	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC) {
+		mfc_debug("Setting loop filter to: %d\n", \
+						mfc_ctx->loop_filter_mpeg4);
+		WRITEL(mfc_ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
+	} else {
+		WRITEL(0, S5P_FIMV_ENC_LF_CTRL);
+	}
+	WRITEL(((mfc_ctx->slice_interface & 1)<<31) |
+				((mfc_ctx->display_delay > 0 ? 1 : 0) << 30) |
+					((mfc_ctx->display_delay & 0xFF) << 16),
+						S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_DIVX311_DEC) {
+		mfc_debug("Setting DivX 3.11 resolution to %dx%d\n",
+			mfc_ctx->img_width, mfc_ctx->img_height);
+		WRITEL(mfc_ctx->img_width, S5P_FIMV_SI_DIVX311_HRESOL);
+		WRITEL(mfc_ctx->img_height, S5P_FIMV_SI_DIVX311_VRESOL);
+	}
+	WRITEL(((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) | (mfc_ctx->inst_no),
+						       S5P_FIMV_SI_CH0_INST_ID);
+	return 0;
+}
+
+/* Decode a single frame */
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int last_frame)
+{
+	struct s5p_mfc_dev *dev = mfc_ctx->dev;
+	mfc_debug("Setting flags to %08lx (free: %d WTF: %d)\n",
+				mfc_ctx->dec_dst_flag, mfc_ctx->dst_queue_cnt,
+						mfc_ctx->dec_dst_buf_cnt);
+	WRITEL(mfc_ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
+	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
+	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
+	/* Issue different commands to instance basing on whether it
+	 * is the last frame or not. */
+	if (!last_frame)
+		WRITEL((S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
+				(mfc_ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+	else
+		WRITEL((S5P_FIMV_CH_LAST_FRAME << 16 & 0x70000) |
+				(mfc_ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+	mfc_debug("Decoding a usual frame.\n");
+	return 0;
+}
+
+/* Deinitialize hardware */
+void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
+{
+	s5p_mfc_cmd_reset(dev);
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
new file mode 100644
index 0000000..162509f
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
@@ -0,0 +1,90 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * Contains declarations of hw related functions.
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_OPR_H_
+#define S5P_MFC_OPR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_release_firmware(void);
+int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx);
+void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev);
+int s5p_mfc_set_sleep(struct s5p_mfc_ctx *mfc_ctx);
+int s5p_mfc_set_wakeup(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int do_int);
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int buf_addr,
+						  unsigned int start_num_byte,
+						  unsigned int buf_size);
+
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int last_frame);
+
+/* Instance handling */
+int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx);
+int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Memory allocation */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
+void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
+void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
+void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Getting parameters from MFC */
+#define s5p_mfc_get_h_crop(ctx)		readl((ctx)->shared_virt + \
+						S5P_FIMV_SHARED_CROP_INFO_H)
+#define s5p_mfc_get_v_crop(ctx)		readl((ctx)->shared_virt + \
+						S5P_FIMV_SHARED_CROP_INFO_V)
+#define s5p_mfc_get_dspl_y_adr()	(readl(dev->regs_base + \
+					S5P_FIMV_SI_DISPLAY_Y_ADR) << 11)
+#define s5p_mfc_get_dspl_status()	readl(dev->regs_base + \
+						S5P_FIMV_SI_DISPLAY_STATUS)
+#define s5p_mfc_get_frame_type()	(readl(dev->regs_base + \
+						S5P_FIMV_DECODE_FRAME_TYPE) \
+					& S5P_FIMV_DECODE_FRAME_MASK)
+#define s5p_mfc_get_consumed_stream()	readl(dev->regs_base + \
+						S5P_FIMV_SI_DEC_FRM_SIZE)
+#define s5p_mfc_get_int_reason()	(readl(dev->regs_base + \
+					S5P_FIMV_RISC2HOST_CMD) & 0x1FFFF)
+#define s5p_mfc_get_int_err()		readl(dev->regs_base + \
+						S5P_FIMV_RISC2HOST_ARG2)
+#define s5p_mfc_get_img_width()		readl(dev->regs_base + \
+						S5P_FIMV_SI_HRESOL)
+#define s5p_mfc_get_img_height()	readl(dev->regs_base + \
+						S5P_FIMV_SI_VRESOL)
+#define s5p_mfc_get_dpb_count()		readl(dev->regs_base + \
+						S5P_FIMV_SI_BUF_NUMBER)
+#define s5p_mfc_get_inst_no()		readl(dev->regs_base + \
+						S5P_FIMV_RISC2HOST_ARG1)
+#define s5p_mfc_get_pic_time_top(ctx)	readl((ctx)->shared_virt + \
+						S5P_FIMV_SHARED_PIC_TIME_TOP)
+#define s5p_mfc_get_pic_time_bottom(ctx) readl((ctx)->shared_virt + \
+						S5P_FIMV_SHARED_PIC_TIME_BOTTOM)
+
+/* Interrupt handling routines */
+#define s5p_mfc_clear_int_flags()				\
+do {								\
+	writel(0, dev->regs_base + S5P_FIMV_RISC_HOST_INT);	\
+	writel(0, dev->regs_base + S5P_FIMV_RISC2HOST_CMD);	\
+	writel(0xffff, dev->regs_base + S5P_FIMV_SI_RTN_CHID);	\
+} while (0)
+#endif /* S5P_MFC_OPR_H_ */
-- 
1.6.3.3


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

* [PATCH 4/4] s5pc110: Enable MFC 5.1 on Goni
  2010-10-13 11:09 [RFC/PATCH 0/4] Multi Format Codec 5.0 driver for S5PC110 SoC Kamil Debski
                   ` (2 preceding siblings ...)
  2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
@ 2010-10-13 11:09 ` Kamil Debski
  3 siblings, 0 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-13 11:09 UTC (permalink / raw)
  To: linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, k.debski, jaeryul.oh, kgene.kim

Multi Format Codec 5.1 is a module available on S5PC110 and S5PC210
Samsung SoCs. Hardware is capable of handling a range of video codecs.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 arch/arm/mach-s5pv210/Kconfig     |    1 +
 arch/arm/mach-s5pv210/mach-goni.c |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s5pv210/Kconfig b/arch/arm/mach-s5pv210/Kconfig
index 862f239..b5e3a39 100644
--- a/arch/arm/mach-s5pv210/Kconfig
+++ b/arch/arm/mach-s5pv210/Kconfig
@@ -85,6 +85,7 @@ config MACH_GONI
 	select S3C_DEV_I2C1
 	select S3C_DEV_I2C2
 	select S3C_DEV_USB_HSOTG
+	select S5P_DEV_MFC
 	select S5P_DEV_ONENAND
 	select SAMSUNG_DEV_KEYPAD
 	select S5PV210_SETUP_FB_24BPP
diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c
index 3602d16..cc5cdad 100644
--- a/arch/arm/mach-s5pv210/mach-goni.c
+++ b/arch/arm/mach-s5pv210/mach-goni.c
@@ -648,6 +648,7 @@ static struct platform_device *goni_devices[] __initdata = {
 	&goni_i2c_gpio_pmic,
 	&mmc2_fixed_voltage,
 	&goni_device_gpiokeys,
+	&s5p_device_mfc5,
 	&s5p_device_fimc0,
 	&s5p_device_fimc1,
 	&s5p_device_fimc2,
-- 
1.6.3.3


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
@ 2010-10-14  6:25   ` Jaeryul Oh
  2010-10-14 12:56     ` Kamil Debski
  2010-10-15  4:06   ` Jaeryul Oh
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 18+ messages in thread
From: Jaeryul Oh @ 2010-10-14  6:25 UTC (permalink / raw)
  To: 'Kamil Debski', linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Hi, Kamil & Dear members
I have some first comments about this code. 
and Generally, Making patch set separately will be more helpful to everyone.

k.debski@samsung.com wrote:
> Multi Format Codec 5.1 is a module available on S5PC110 and S5PC210
> Samsung SoCs. Hardware is capable of handling a range of video codecs
> and this driver provides V4L2 interface for video decoding.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig                  |    8 +
>  drivers/media/video/Makefile                 |    1 +
>  drivers/media/video/s5p-mfc/Makefile         |    3 +
>  drivers/media/video/s5p-mfc/regs-mfc5.h      |  305 +++++
>  drivers/media/video/s5p-mfc/s5p_mfc.c        | 1879
> ++++++++++++++++++++++++++
>  drivers/media/video/s5p-mfc/s5p_mfc_common.h |  190 +++
>  drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h  |  173 +++
>  drivers/media/video/s5p-mfc/s5p_mfc_intr.c   |   77 ++
>  drivers/media/video/s5p-mfc/s5p_mfc_intr.h   |   26 +
>  drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h |   65 +
>  drivers/media/video/s5p-mfc/s5p_mfc_memory.h |   32 +
>  drivers/media/video/s5p-mfc/s5p_mfc_opr.c    |  800 +++++++++++
>  drivers/media/video/s5p-mfc/s5p_mfc_opr.h    |   90 ++
>  13 files changed, 3649 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/s5p-mfc/Makefile
>  create mode 100644 drivers/media/video/s5p-mfc/regs-mfc5.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_common.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_memory.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 6d0bd36..1d0b91e 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -1047,4 +1047,12 @@ config  VIDEO_SAMSUNG_S5P_FIMC
>  	  This is a v4l2 driver for the S5P camera interface
>  	  (video postprocessor)
> 
> +config VIDEO_SAMSUNG_S5P_MFC
> +	tristate "Samsung S5P MFC 5.0 Video Codec"
> +	depends on VIDEO_V4L2 && CMA
> +	select VIDEOBUF2_CMA
> +	default n
> +	help
> +	    MFC 5.0 driver for V4L2.
> +
>  endif # V4L_MEM2MEM_DRIVERS

What about unifying MFC version as a MFC 5.1, because we are using MFC HW
ver.(MFC 5.1.x)
in the C110/C210 chip.

> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 4146700..117a3cb 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -178,6 +178,7 @@ obj-$(CONFIG_VIDEO_PXA27x)		+=
pxa_camera.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
> +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
> 
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
> 
> diff --git a/drivers/media/video/s5p-mfc/Makefile
> b/drivers/media/video/s5p-mfc/Makefile
> new file mode 100644
> index 0000000..69b6294
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
> +s5p-mfc-y := s5p_mfc.o s5p_mfc_intr.o  s5p_mfc_opr.o
> +
> diff --git a/drivers/media/video/s5p-mfc/regs-mfc5.h
> b/drivers/media/video/s5p-mfc/regs-mfc5.h
> new file mode 100644
> index 0000000..8c628ad
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/regs-mfc5.h
> @@ -0,0 +1,305 @@
> +/*
> + *
> + * Register definition file for Samsung MFC V4.0 & V5.0 Interface (FIMV)
> driver
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> +*/

Incorrect comment in the header, MFC V4.0 is not included

> +
> +#ifndef _REGS_FIMV_H
> +#define _REGS_FIMV_H
> +
> +#define S5P_FIMV_REG_SIZE	(S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
> +#define S5P_FIMV_REG_COUNT	((S5P_FIMV_END_ADDR -
> S5P_FIMV_START_ADDR) / 4)
> +
> +#define S5P_FIMV_START_ADDR	0x0000
> +#define S5P_FIMV_END_ADDR	0xe008
> +
> +#define S5P_FIMV_SW_RESET	0x0000
> +#define S5P_FIMV_RISC_HOST_INT	0x0008
> +/* Command from HOST to RISC */
> +#define S5P_FIMV_HOST2RISC_CMD	0x0030
> +#define S5P_FIMV_HOST2RISC_ARG1	0x0034
> +#define S5P_FIMV_HOST2RISC_ARG2	0x0038
> +#define S5P_FIMV_HOST2RISC_ARG3	0x003c
> +#define S5P_FIMV_HOST2RISC_ARG4	0x0040
> +/* Command from RISC to HOST */
> +#define S5P_FIMV_RISC2HOST_CMD	0x0044
> +#define S5P_FIMV_RISC2HOST_ARG1	0x0048
> +#define S5P_FIMV_RISC2HOST_ARG2	0x004c
> +#define S5P_FIMV_RISC2HOST_ARG3	0x0050
> +#define S5P_FIMV_RISC2HOST_ARG4	0x0054
> +
> +#define S5P_FIMV_FW_VERSION	0x0058
> +#define S5P_FIMV_SYS_MEM_SZ	0x005c
> +#define S5P_FIMV_FW_STATUS	0x0080
> +/* Memory controller register */
> +#define S5P_FIMV_MC_DRAMBASE_ADR_A	0x0508
> +#define S5P_FIMV_MC_DRAMBASE_ADR_B	0x050c
> +#define S5P_FIMV_MC_STATUS	0x0510
> +
> +/***** In case of 2 master *****/

This comment(In case of 2 master) is meaningless. it was used at the
beginning
step of development.

> +/* Common register */
> +#define S5P_FIMV_SYS_MEM_ADR	0x0600 /* firmware buffer */
> +#define S5P_FIMV_CPB_BUF_ADR	0x0604 /* stream buffer */
> +#define S5P_FIMV_DESC_BUF_ADR	0x0608 /* descriptor buffer */
> +/* H264 decoding */
> +#define S5P_FIMV_VERT_NB_MV_ADR	0x068c /* vertical neighbor motion
> vector */
> +#define S5P_FIMV_VERT_NB_IP_ADR	0x0690 /* neighbor pixels for intra
pred
> */
> +#define S5P_FIMV_H264_LUMA_ADR	0x0700 /* Luma0 ~ Luma18 700 */
> +#define S5P_FIMV_H264_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma18 614
> */
> +#define S5P_FIMV_MV_ADR		0x0780 /* H264 motion vector 660
780 */
> +/* H263/MPEG4/MPEG2/VC-1/ decoding */
> +#define S5P_FIMV_NB_DCAC_ADR	0x068c /* neighbor AC/DC coeff. buffer
> */
> +#define S5P_FIMV_UP_NB_MV_ADR	0x0690 /* upper neighbor motion
vector
> buffer */
> +#define S5P_FIMV_SA_MV_ADR	0x0694 /* subseq. anchor motion vector
> buffer */
> +#define S5P_FIMV_OT_LINE_ADR	0x0698 /* overlap transform line buffer
> */
> +#define S5P_FIMV_BITPLANE3_ADR	0x069C /* bitplane3 addr */
> +#define S5P_FIMV_BITPLANE2_ADR	0x06A0 /* bitplane2 addr */
> +#define S5P_FIMV_BITPLANE1_ADR	0x06A4 /* bitplane1 addr */
> +#define S5P_FIMV_SP_ADR		0x06A8 /* syntax parser addr */
> +#define S5P_FIMV_LUMA_ADR	0x0700 /* Luma0 ~ Luma5 */
> +#define S5P_FIMV_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma5 */
> +/* Encoder register */
> +#define S5P_FIMV_ENC_UP_MV_ADR		0x0600 /* upper motion
vector addr
> */
> +#define S5P_FIMV_ENC_COZERO_FLAG_ADR	0x0610 /* direct cozero flag addr
> */
> +#define S5P_FIMV_ENC_UP_INTRA_MD_ADR	0x0608 /* upper intra MD addr */
> +#define S5P_FIMV_ENC_UP_INTRA_PRED_ADR	0x0740 /* upper intra PRED
addr */
> +#define S5P_FIMV_ENC_NB_DCAC_ADR	0x0604 /* entropy engine's
> neighbor
> +						inform and AC/DC coeff. */
> +
> +#define S5P_FIMV_ENC_CUR_LUMA_ADR	0x0718 /* current Luma addr */
> +#define S5P_FIMV_ENC_CUR_CHROMA_ADR	0x071C /* current Chroma addr */
> +
> +#define S5P_FIMV_ENC_REF0_LUMA_ADR	0x061c /* ref0 Luma addr */
> +#define S5P_FIMV_ENC_REF0_CHROMA_ADR	0x0700 /* ref0 Chroma addr */
> +#define S5P_FIMV_ENC_REF1_LUMA_ADR	0x0620 /* ref1 Luma addr */
> +#define S5P_FIMV_ENC_REF1_CHROMA_ADR	0x0704 /* ref1 Chroma addr */
> +#define S5P_FIMV_ENC_REF2_LUMA_ADR	0x0710 /* ref2 Luma addr */
> +#define S5P_FIMV_ENC_REF2_CHROMA_ADR	0x0708 /* ref2 Chroma addr */
> +#define S5P_FIMV_ENC_REF3_LUMA_ADR	0x0714 /* ref3 Luma addr */
> +#define S5P_FIMV_ENC_REF3_CHROMA_ADR	0x070c /* ref3 Chroma addr */
> +
> +/* Codec common register */
> +#define S5P_FIMV_ENC_HSIZE_PX		0x0818 /* frame width at
encoder
> */
> +#define S5P_FIMV_ENC_VSIZE_PX		0x081c /* frame height at
encoder
> */
> +#define S5P_FIMV_ENC_PROFILE		0x0830 /* profile register */
> +#define S5P_FIMV_ENC_PIC_STRUCT		0x083c /* picture
field/frame flag
> */
> +#define S5P_FIMV_ENC_LF_CTRL		0x0848 /* loop filter control */
> +#define S5P_FIMV_ENC_ALPHA_OFF		0x084c /* loop filter alpha
offset
> */
> +#define S5P_FIMV_ENC_BETA_OFF		0x0850 /* loop filter beta
offset
> */
> +#define S5P_FIMV_MR_BUSIF_CTRL		0x0854 /* hidden, bus
interface
> ctrl */
> +#define S5P_FIMV_ENC_PXL_CACHE_CTRL	0x0a00 /* pixel cache control */
> +
> +/* Channel & stream interface register */
> +#define S5P_FIMV_SI_RTN_CHID	0x2000 /* Return CH instance ID register
> */
> +#define S5P_FIMV_SI_CH0_INST_ID	0x2040 /* codec instance ID */
> +#define S5P_FIMV_SI_CH1_INST_ID	0x2080 /* codec instance ID */
> +/* Decoder */
> +#define S5P_FIMV_SI_VRESOL	0x2004 /* vertical resolution of decoder
> */
> +#define S5P_FIMV_SI_HRESOL	0x2008 /* horizontal resolution of
> decoder */
> +#define S5P_FIMV_SI_BUF_NUMBER	0x200c /* number of frames in the
> decoded pic */
> +#define S5P_FIMV_SI_DISPLAY_Y_ADR 0x2010 /* luma address of displayed pic
> */
> +#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma address of displayed
> pic */
> +#define S5P_FIMV_SI_DEC_FRM_SIZE 0x2018 /* the number of frames decoded
> */

S5P_FIMV_SI_DEC_FRM_SIZE does actually means 
"consumed number of bytes to decode a frame"

> +#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded picture */
> +#define S5P_FIMV_SI_FRAME_TYPE	0x2020 /* frame type such as
skip/I/P/B
> */
> +
> +#define S5P_FIMV_SI_CH0_SB_ST_ADR	0x2044 /* start addr of stream buf
> */
> +#define S5P_FIMV_SI_CH0_SB_FRM_SIZE	0x2048 /* size of stream buf */
> +#define S5P_FIMV_SI_CH0_DESC_ADR	0x204c /* addr of descriptor buf
> */
> +#define S5P_FIMV_SI_CH0_CPB_SIZE	0x2058 /* max size of coded pic.
> buf */
> +#define S5P_FIMV_SI_CH0_DESC_SIZE	0x205c /* max size of descriptor
> buf */
> +
> +#define S5P_FIMV_SI_CH1_SB_ST_ADR	0x2084 /* start addr of stream buf
> */
> +#define S5P_FIMV_SI_CH1_SB_FRM_SIZE	0x2088 /* size of stream buf */
> +#define S5P_FIMV_SI_CH1_DESC_ADR	0x208c /* addr of descriptor buf
> */
> +#define S5P_FIMV_SI_CH1_CPB_SIZE	0x2098 /* max size of coded pic.
> buf */
> +#define S5P_FIMV_SI_CH1_DESC_SIZE	0x209c /* max size of descriptor
> buf */
> +
> +#define S5P_FIMV_SI_DIVX311_HRESOL	0x2054 /* horizontal resolution */
> +#define S5P_FIMV_SI_DIVX311_VRESOL	0x2050 /* vertical resolution */
> +#define S5P_FIMV_CRC_LUMA0	0x2030 /* luma crc data per frame(top
> field)*/
> +#define S5P_FIMV_CRC_CHROMA0	0x2034 /* chroma crc data per frame(top
> field)*/
> +#define S5P_FIMV_CRC_LUMA1	0x2038 /* luma crc data per bottom field
> */
> +#define S5P_FIMV_CRC_CHROMA1	0x203c /* chroma crc data per bottom
> field */
> +
> +/* Display status */
> +#define S5P_FIMV_DEC_STATUS_DECODING_ONLY		0
> +#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY		1
> +#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY		2
> +#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY		3
> +#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK	7
> +#define S5P_FIMV_DEC_STATUS_PROGRESSIVE			(0<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE			(1<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK		(1<<3)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO		(0<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR		(1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK		(1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_GENERATED		(1<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED		(0<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_MASK			(1<<5)
> +
> +/* Decode frame address */
> +#define S5P_FIMV_DECODE_Y_ADR			0x2024
> +#define S5P_FIMV_DECODE_C_ADR			0x2028
> +
> +/* Decoded frame tpe */
> +#define S5P_FIMV_DECODE_FRAME_TYPE		0x2020
> +#define S5P_FIMV_DECODE_FRAME_MASK		7
> +
> +#define S5P_FIMV_DECODE_FRAME_SKIPPED		0
> +#define S5P_FIMV_DECODE_FRAME_I_FRAME		1
> +#define S5P_FIMV_DECODE_FRAME_P_FRAME		2
> +#define S5P_FIMV_DECODE_FRAME_202_FRAME		3
> +#define S5P_FIMV_DECODE_FRAME_OTHER_FRAME	4
> +
> +/* Sizes of buffers required for decoding */
> +#define S5P_FIMV_DEC_NB_IP_SIZE			(32*1024)
> +#define S5P_FIMV_DEC_VERT_NB_MV_SIZE		(16*1024)
> +#define S5P_FIMV_DEC_NB_DCAC_SIZE		(16*1024)
> +#define S5P_FIMV_DEC_UPNB_MV_SIZE		(68*1024)
> +#define S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE		(136*1024)
> +#define S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE     (32*1024)
> +#define S5P_FIMV_DEC_VC1_BITPLANE_SIZE		(2*1024)
> +#define S5P_FIMV_DEC_STX_PARSER_SIZE		(68*1024)
> +
> +
> +/* Encoder */
> +#define S5P_FIMV_ENC_SI_STRM_SIZE	0x2004 /* stream size */
> +#define S5P_FIMV_ENC_SI_PIC_CNT		0x2008 /* picture count */
> +#define S5P_FIMV_ENC_SI_WRITE_PTR	0x200c /* write pointer */
> +#define S5P_FIMV_ENC_SI_SLICE_TYPE	0x2010 /* slice type(I/P/B/IDR) */
> +
> +#define S5P_FIMV_ENC_SI_CH0_SB_U_ADR	0x2044 /* addr of upper stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH0_SB_L_ADR	0x2048 /* addr of lower stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH0_SB_SIZE	0x204c /* size of stream buf */
> +#define S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR	0x2050 /* current Luma addr
*/
> +#define S5P_FIMV_ENC_SI_CH0_CUR_C_ADR	0x2054 /* current Chroma
addr */
> +#define S5P_FIMV_ENC_SI_CH0_FRAME_QP	0x2058 /* frame QP */
> +#define S5P_FIMV_ENC_SI_CH0_SLICE_ARG	0x205c /* slice argument */
> +
> +#define S5P_FIMV_ENC_SI_CH1_SB_U_ADR	0x2084 /* addr of upper stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH1_SB_L_ADR	0x2088 /* addr of lower stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH1_SB_SIZE	0x208c /* size of stream buf */
> +#define S5P_FIMV_ENC_SI_CH1_CUR_Y_ADR	0x2090 /* current Luma addr
*/
> +#define S5P_FIMV_ENC_SI_CH1_CUR_C_ADR	0x2094 /* current Chroma
addr */
> +#define S5P_FIMV_ENC_SI_CH1_FRAME_QP	0x2098 /* frame QP */
> +#define S5P_FIMV_ENC_SI_CH1_SLICE_ARG	0x209c /* slice argument */
> +
> +#define S5P_FIMV_ENC_STR_BF_U_FULL	0xc004 /* upper stream buf full */
> +#define S5P_FIMV_ENC_STR_BF_U_EMPTY	0xc008 /* upper stream buf empty
> */
> +#define S5P_FIMV_ENC_STR_BF_L_FULL	0xc00c /* lower stream buf full */
> +#define S5P_FIMV_ENC_STR_BF_L_EMPTY	0xc010 /* lower stream buf empty
> */
> +#define S5P_FIMV_ENC_STR_STATUS		0xc018 /* stream buf
interrupt
> status */
> +#define S5P_FIMV_ENC_SF_EPB_ON_CTRL	0xc054 /* stream control */
> +#define S5P_FIMV_ENC_SF_BUF_CTRL	0xc058 /* buffer control */
> +#define S5P_FIMV_ENC_BF_MODE_CTRL	0xc05c /* fifo level control */
> +
> +#define S5P_FIMV_ENC_PIC_TYPE_CTRL	0xc504 /* pic type level control
> */
> +#define S5P_FIMV_ENC_B_RECON_WRITE_ON	0xc508 /* B frame recon
write ctrl
> */
> +#define S5P_FIMV_ENC_MSLICE_CTRL	0xc50c /* multi slice control */
> +#define S5P_FIMV_ENC_MSLICE_MB		0xc510 /* MB number in the
one
> slice */
> +#define S5P_FIMV_ENC_MSLICE_BYTE	0xc514 /* byte number for one
> slice */
> +#define S5P_FIMV_ENC_CIR_CTRL		0xc518 /* number of intra
refresh
> MB */
> +#define S5P_FIMV_ENC_MAP_FOR_CUR	0xc51c /* linear or 64x32 tiled
> mode */
> +#define S5P_FIMV_ENC_PADDING_CTRL	0xc520 /* padding control */
> +#define S5P_FIMV_ENC_INT_MASK		0xc528 /* interrupt mask */
> +
> +#define S5P_FIMV_ENC_RC_CONFIG		0xc5a0 /* RC config */
> +#define S5P_FIMV_ENC_RC_FRAME_RATE	0xc5a4 /* frame rate */
> +#define S5P_FIMV_ENC_RC_BIT_RATE	0xc5a8 /* bit rate */
> +#define S5P_FIMV_ENC_RC_QBOUND		0xc5ac /* max/min QP */
> +#define S5P_FIMV_ENC_RC_RPARA		0xc5b0 /* rate control
reaction
> coeff */
> +#define S5P_FIMV_ENC_RC_MB_CTRL		0xc5b4 /* MB adaptive
scaling */
> +
> +/* Encoder for H264 */
> +#define S5P_FIMV_ENC_ENTRP_MODE		0xd004 /* CAVLC or CABAC */
> +#define S5P_FIMV_ENC_H264_ALPHA_OFF	0xd008 /* loop filter alpha offset
> */
> +#define S5P_FIMV_ENC_H264_BETA_OFF	0xd00c /* loop filter beta offset
> */
> +#define S5P_FIMV_ENC_H264_NUM_OF_REF	0xd010 /* number of reference for
> P/B */
> +#define S5P_FIMV_ENC_H264_MDINTER_WGT	0xd01c /* inter weighted
parameter
> */
> +#define S5P_FIMV_ENC_H264_MDINTRA_WGT	0xd020 /* intra weighted
parameter
> */
> +#define S5P_FIMV_ENC_H264_TRANS_FLAG	0xd034 /* 8x8 transform flag in
> PPS &
> +								high
profile */
> +/* Encoder for MPEG4 */
> +#define S5P_FIMV_ENC_MPEG4_QUART_PXL	0xe008 /* qpel interpolation ctrl
> */
> +
> +/* Additional */
> +#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL   0x2068 /* DPB Config Control
> Register */
> +#define S5P_FIMV_SI_CH0_RELEASE_BUF     0x2060 /* DPB release buffer
> register */
> +#define S5P_FIMV_SI_CH0_HOST_WR_ADR	0x2064

S5P_FIMV_SI_CH0_HOST_WR_ADR means 'address of shared memory'
if comments is needed

> +
> +/* #define S5P_FIMV_RET_VALUE              0xc08 */

S5P_FIMV_RET_VALUE reg. is no more used

> +#define S5P_FIMV_ENC_B_RECON_WRITE_ON   0xc508 /* B frame recon write
> ctrl */
> +
> +#define S5P_FIMV_ENC_REF_B_LUMA_ADR     0x062c /* ref B Luma addr */
> +#define S5P_FIMV_ENC_REF_B_CHROMA_ADR   0x0630 /* ref B Chroma addr */
> +
> +#define S5P_FIMV_ENCODED_Y_ADDR         0x2014 /* the address of the
> encoded
> +							luminance picture */
> +#define S5P_FIMV_ENCODED_C_ADDR         0x2018 /* the address of the
> encoded
> +							chrominance
picture*/
> +
> +/* Codec numbers  */
> +#define S5P_FIMV_CODEC_H264_DEC			0
> +#define S5P_FIMV_CODEC_VC1_DEC			1
> +#define S5P_FIMV_CODEC_MPEG4_DEC		2
> +#define S5P_FIMV_CODEC_MPEG2_DEC		3
> +#define S5P_FIMV_CODEC_H263_DEC			4
> +#define S5P_FIMV_CODEC_VC1RCV_DEC		5
> +#define S5P_FIMV_CODEC_DIVX311_DEC		6
> +#define S5P_FIMV_CODEC_DIVX412_DEC		7
> +#define S5P_FIMV_CODEC_DIVX502_DEC		8
> +#define S5P_FIMV_CODEC_DIVX503_DEC		9
> +
> +#define S5P_FIMV_CODEC_H264_ENC			16
> +#define S5P_FIMV_CODEC_MPEG4_ENC		17
> +#define S5P_FIMV_CODEC_H263_ENC			18
> +
> +/* Channel Control Register */
> +#define S5P_FIMV_CH_SEQ_HEADER		1
> +#define S5P_FIMV_CH_FRAME_START		2
> +#define S5P_FIMV_CH_LAST_FRAME		3
> +#define S5P_FIMV_CH_INIT_BUFS		4
> +#define S5P_FIMV_CH_FRAME_START_REALLOC	5
> +
> +
> +/* Host to RISC command */
> +#define S5P_FIMV_H2R_CMD_EMPTY		0
> +#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE	1
> +#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE	2
> +#define S5P_FIMV_H2R_CMD_SYS_INIT	3
> +
> +#define S5P_FIMV_R2H_CMD_EMPTY			0
> +#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET	1
> +#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET	2
> +#define S5P_FIMV_R2H_CMD_ERROR_RET		3
> +#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET		4
> +#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET		5
> +#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET		6
> +#define S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET	7
> +#define S5P_FIMV_R2H_CMD_SYS_INIT_RET		8
> +#define S5P_FIMV_R2H_CMD_FW_STATUS_RET		9
> +#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET	15
> +#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET		16
> +#define S5P_FIMV_R2H_CMD_DECODE_ERR_RET		32
> +
> +/* Shared memory registers' offsets */
> +
> +/* An offset of the start position in the stream when
> + * the start position is not aligned */
> +#define S5P_FIMV_SHARED_CROP_INFO_H		0x0020
> +#define S5P_FIMV_SHARED_CROP_INFO_V		0x0024
> +#define S5P_FIMV_SHARED_START_BYTE_NUM		0x0018
> +#define S5P_FIMV_SHARED_LUMA_DPB_SIZE		0x0064
> +#define S5P_FIMV_SHARED_CHROMA_DPB_SIZE		0x0068
> +#define S5P_FIMV_SHARED_MV_SIZE			0x006C
> +#define S5P_FIMV_SHARED_PIC_TIME_TOP		0x0010
> +#define S5P_FIMV_SHARED_PIC_TIME_BOTTOM		0x0014
> +
> +#endif /* _REGS_FIMV_H */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c
> b/drivers/media/video/s5p-mfc/s5p_mfc.c
> new file mode 100644
> index 0000000..f549ab6
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
> @@ -0,0 +1,1879 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#include <linux/io.h>
> +#include <linux/sched.h>
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/version.h>
> +#include <linux/workqueue.h>
> +#include <linux/videodev2.h>
> +#include <media/videobuf2-cma.h>
> +#include <media/videobuf2-core.h>
> +#include "regs-mfc5.h"
> +
> +#include "s5p_mfc_opr.h"
> +#include "s5p_mfc_intr.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_memory.h"
> +#include "s5p_mfc_ctrls.h"
> +
> +#define S5P_MFC_NAME	"s5p-mfc5"
> +
> +/* Offset base used to differentiate between CAPTURE and OUTPUT
> +*  while mmaping */
> +#define DST_QUEUE_OFF_BASE      (TASK_SIZE / 2)
> +
> +struct s5p_mfc_dev *dev;
> +static const char *s5p_mem_types[] = {"b", "a"};
> +static unsigned long s5p_mem_alignments[] = {8192, 8192};
> +
> +/* Function prototypes */
> +static void s5p_mfc_try_run(void);
> +
> +/* Helper functions for interrupt processing */
> +/* Remove from hw execution round robin */
> +static inline void clear_work_bit(struct s5p_mfc_ctx *ctx)
> +{
> +	spin_lock(&dev->condlock);
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	spin_unlock(&dev->condlock);
> +}
> +
> +/* Wake up context wait_queue */
> +static inline void wake_up_ctx(struct s5p_mfc_ctx *ctx,
> +	unsigned int reason, unsigned int err)
> +{
> +	ctx->int_cond = 1;
> +	ctx->int_type = reason;
> +	ctx->int_err = err;
> +	wake_up_interruptible(&ctx->queue);
> +}
> +
> +/* Wake up device wait_queue */
> +static inline void wake_up_dev(unsigned int reason, unsigned int err)
> +{
> +	dev->int_cond = 1;
> +	dev->int_type = reason;
> +	dev->int_err = err;
> +	wake_up_interruptible(&dev->queue);
> +}
> +
> +void s5p_mfc_error_cleanup_queue(struct list_head *lh, \
> +						struct vb2_queue *vq)
> +{
> +	struct vb2_buffer *b;
> +	int i;
> +	spin_lock(&dev->irqlock);
> +	while (!list_empty(lh)) {
> +		b = list_entry(lh->next, struct vb2_buffer, drv_entry);
> +		for (i = 0; i < b->num_planes; i++)
> +			vb2_set_plane_payload(b, i, 0);
> +		spin_unlock(&dev->irqlock);
> +		vb2_buffer_done(b, VB2_BUF_STATE_ERROR);
> +		spin_lock(&dev->irqlock);
> +		list_del(&b->drv_entry);
> +	}
> +	spin_unlock(&dev->irqlock);
> +}

How about adding new line b/w local var defintions & running code thru 
whole function for readability.

> +
> +void s5p_mfc_watchdog(unsigned long arg)
> +{
> +	if (test_bit(0, &dev->hw_lock))
> +		atomic_inc(&dev->watchdog_cnt);
> +	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
> +		/* This means that hw is busy and no interrupts were
> +		 * generated by hw for the Nth time of running this
> +		 * watchdog timer. This usually means a serious hw
> +		 * error. Now it is time to kill all instances and
> +		 * reset the MFC. */
> +		mfc_err("Time out during waiting for HW.\n");
> +		queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
> +	}
> +	dev->watchdog_timer.expires +=
> msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
> +	add_timer(&dev->watchdog_timer);
> +}
> +
> +static void s5p_mfc_watchdog_worker(struct work_struct *work)
> +{
> +	struct s5p_mfc_ctx *ctx;
> +	int i, ret;
> +	int mutex_locked;
> +	mfc_err("Driver timeout error handling.\n");
> +	/* Lock the mutex that protects open and release.
> +	 * This is necessary as they may load and unload firmware. */
> +	mutex_locked = mutex_trylock(dev->mfc_mutex);
> +	if (!mutex_locked)
> +		mfc_err("Ok. This is not good. Some instance may be " \
>
+							"closing/opening.\n"
);
> +	clk_disable(dev->clock1);
> +	clk_disable(dev->clock2);
> +	spin_lock(&dev->irqlock);
> +	for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
> +		ctx = dev->ctx[i];
> +		if (ctx) {
> +			ctx->state = MFCINST_DEC_ERROR;
> +			spin_unlock(&dev->irqlock);
> +			s5p_mfc_error_cleanup_queue(&ctx->dst_queue,
> +				&ctx->vq_dst);
> +			s5p_mfc_error_cleanup_queue(&ctx->src_queue,
> +				&ctx->vq_src);
> +			spin_lock(&dev->irqlock);
> +			clear_work_bit(ctx);
> +			wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_DECODE_ERR_RET,
0);
> +		}
> +	}
> +	clear_bit(0, &dev->hw_lock);
> +	spin_unlock(&dev->irqlock);
> +	/* Double check if there is at least one instance running.
> +	 * If no instance is in memory than no firmware should be present
> */
> +	if (atomic_read(&dev->num_inst) > 0) {
> +		ret = s5p_mfc_load_firmware(dev);
> +		if (ret != 0) {
> +			mfc_err("Failed to reload FW. This is bad.\n");
> +			if (mutex_locked)
> +				mutex_unlock(dev->mfc_mutex);
> +			return;
> +		}
> +		clk_enable(dev->clock1);
> +		clk_enable(dev->clock2);
> +		ret = s5p_mfc_init_hw(dev);
> +		if (ret != 0) {
> +			mfc_err("Failed to reinitialize FW. This is
bad.\n");
> +			if (mutex_locked)
> +				mutex_unlock(dev->mfc_mutex);
> +			return;
> +		}
> +	}
> +	if (mutex_locked)
> +		mutex_unlock(dev->mfc_mutex);
> +}

Does MFC_NUM_CONTEXTS means max num of contexts ? what about
MFC_MAX_CONTEXT_NUM ?
and this number is absolutely dependent on what max size used, 
what kinds of codec used, etc so, you had better use configuration param.
for example>
   #ifdef CONFIG_VIDEO_MFC_MAX_INSTANCE
   #define MFC_MAX_CONTEXT_NUM (CONFIG_VIDEO_MFC_MAX_INSTANCE)
   #endif

> +
> +/* Check whether a context should be run on hardware */
> +int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
> +{
> +	mfc_debug("s5p_mfc_ctx_ready: src=%d, dst=%d, state=%d\n",
> +		  ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
> +	/* Context is to parse header */
> +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_GOT_INST)
> +		return 1;
> +	/* Context is to decode a frame */
> +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_RUNNING &&
> +					ctx->dst_queue_cnt >= ctx-
>dpb_count)
> +		return 1;
> +	/* Context is to return last frame */
> +	if (ctx->state == MFCINST_DEC_FINISHING &&
> +	    ctx->dst_queue_cnt >= ctx->dpb_count)
> +		return 1;
> +	/* Context is to set buffers */
> +	if (ctx->src_queue_cnt >= 1 &&
> +	    ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +	    ctx->capture_state == QUEUE_BUFS_MMAPED)
> +		return 1;
> +	mfc_debug("s5p_mfc_ctx_ready: ctx is not ready.\n");
> +	return 0;
> +}
> +
> +/* Query control */
> +static struct v4l2_queryctrl *get_ctrl(int id)
> +{
> +	int i;
> +	for (i = 0; i < NUM_CTRLS; ++i)
> +		if (id == s5p_mfc_ctrls[i].id)
> +			return &s5p_mfc_ctrls[i];
> +	return NULL;
> +}
> +
> +/* Query capabilities of the device */
> +static int vidioc_querycap(struct file *file, void *priv,
> +			   struct v4l2_capability *cap)
> +{
> +	strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
> +	strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
> +	cap->bus_info[0] = 0;
> +	cap->version = KERNEL_VERSION(1, 0, 0);
> +	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
> +						    | V4L2_CAP_STREAMING;
> +	return 0;
> +}
> +
> +/* Enumerate format */
> +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	int i, j = 0;
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (mplane && formats[i].num_planes == 1)
> +			continue;
> +		else if (!mplane && formats[i].num_planes > 1)
> +			continue;
> +		if (out && formats[i].type != MFC_FMT_RAW)
> +			continue;
> +		else if (!out && formats[i].type != MFC_FMT_DEC)
> +			continue;
> +
> +		if (j == f->index)
> +			break;
> +		++j;
> +	}
> +	if (i == ARRAY_SIZE(formats))
> +		return -EINVAL;
> +	fmt = &formats[i];
> +	strlcpy(f->description, fmt->name, sizeof(f->description));
> +	f->pixelformat = fmt->fourcc;
> +	return 0;
> +}
> +
> +static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, false, false);
> +}
> +
> +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, true, false);
> +}
> +
> +static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, false, true);
> +}
> +
> +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, true, true);
> +}
> +
> +/* Get format */
> +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format
> *f)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_g_fmt++\n");
> +	mfc_debug("f->type = %d ctx->state = %d\n", f->type, ctx->state);
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	    ctx->state == MFCINST_DEC_GOT_INST) {
> +		/* If the MFC is parsing the header,
> +		 * so wait until it is finished */
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_wait_for_done_ctx(ctx,
S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
> \
> +									1);
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	    ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +	    ctx->state < MFCINST_ENC_INIT) {
> +		/* This is run on CAPTURE (deocde output) */
> +		/* Width and height are set to the dimensions
> +		   of the movie, the buffer is bigger and
> +		   further processing stages should crop to this
> +		   rectangle. */
> +		f->fmt.pix_mp.width = ctx->buf_width;
> +		f->fmt.pix_mp.height = ctx->buf_height;
> +		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> +		f->fmt.pix_mp.num_planes = 2;
> +		/* Set pixelformat to the format in which MFC
> +		   outputs the decoded frame */
> +		f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx->buf_width;
> +		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx->luma_size;
> +		f->fmt.pix_mp.plane_fmt[1].bytesperline = ctx->buf_width;
> +		f->fmt.pix_mp.plane_fmt[1].sizeimage = ctx->chroma_size;
> +	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		/* This is run on OUTPUT
> +		   The buffer contains compressed image
> +		   so width and height have no meaning */
> +		f->fmt.pix_mp.width = 1;
> +		f->fmt.pix_mp.height = 1;
> +		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx-
> >dec_src_buf_size;
> +		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx-
>dec_src_buf_size;
> +		f->fmt.pix_mp.pixelformat = ctx->fmt->fourcc;
> +		f->fmt.pix_mp.num_planes = ctx->fmt->num_planes;
> +	} else {
> +		mfc_err("Format could not be read\n");
> +		mfc_debug("vidioc_g_fmt-- (error)\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_g_fmt--\n");
> +	return 0;
> +}
> +
> +/* Find selected format description */
> +static struct s5p_mfc_fmt *find_format(struct v4l2_format *f)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	unsigned int i;
> +	for (i = 0; i < NUM_FORMATS; ++i) {
> +		fmt = &formats[i];
> +		if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
> +			break;
> +	}
> +	if (i == NUM_FORMATS)
> +		return NULL;
> +	return fmt;
> +}
> +
> +/* Try format */
> +static int vidioc_try_fmt(struct file *file, void *priv, struct
> v4l2_format *f)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	mfc_debug("Type is %d\n", f->type);
> +	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_err("Currently only decoding is supported.\n");
> +		return -EINVAL;
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		fmt = find_format(f);
> +		if (!fmt) {
> +			mfc_err("Unsupported format.\n");
> +			return -EINVAL;
> +		}
> +		if (fmt->type != MFC_FMT_DEC) {
> +			mfc_err("\n");
> +			return -EINVAL;
> +		}
> +		if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) {
> +			mfc_err("Application is required to specify"
> +				" input buffer size (via sizeimage)\n");
> +			return -EINVAL;
> +		}
> +		/* As this buffer will contain compressed data, the size is
> set
> +		 * to the maximum size.
> +		 * Width and height are left intact as they may be relevant
> for
> +		 * DivX 3.11 decoding. */
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline =
> +					f-
>fmt.pix_mp.plane_fmt[0].sizeimage;
> +	}
> +	return 0;
> +}
> +
> +/* Set format */
> +static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format
> *f)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	unsigned long flags;
> +	int ret = 0;
> +	struct s5p_mfc_fmt *fmt;
> +	mfc_debug("vidioc_s_fmt++\n");
> +	ret = vidioc_try_fmt(file, priv, f);
> +	if (ret)
> +		return ret;
> +	mutex_lock(&ctx->vq_src.vb_lock);
> +	mutex_lock(&ctx->vq_dst.vb_lock);
> +	if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
> +		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		fmt = find_format(f);
> +		if (!fmt || fmt->codec_mode == MFC_FORMATS_NO_CODEC) {
> +			mfc_err("Unknown codec.\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		if (fmt->type != MFC_FMT_DEC) {
> +			mfc_err("Wrong format selected - you should choose"
\
> +						" format for decoding.\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		ctx->fmt = fmt;
> +		ctx->codec_mode = fmt->codec_mode;
> +		mfc_debug("The codec number is: %d\n", ctx->codec_mode);
> +		ctx->pix_format = f->fmt.pix_mp.pixelformat;
> +		if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_DIVX3) {
> +			f->fmt.pix_mp.height = 1;
> +			f->fmt.pix_mp.width = 1;
> +		} else {
> +			ctx->img_height = f->fmt.pix_mp.height;
> +			ctx->img_width = f->fmt.pix_mp.width;
> +		}
> +		mfc_debug("s_fmt w/h: %dx%d, ctx: %dx%d\n", f-
> >fmt.pix_mp.width,
> +			f->fmt.pix_mp.height, ctx->img_width, ctx-
>img_height);
> +		ctx->dec_src_buf_size =	f-
> >fmt.pix_mp.plane_fmt[0].sizeimage;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
> +		ctx->state = MFCINST_DEC_INIT;
> +		ctx->dec_dst_buf_cnt = 0;
> +		ctx->capture_state = QUEUE_FREE;
> +		ctx->output_state = QUEUE_FREE;
> +		s5p_mfc_alloc_instance_buffer(ctx);
> +		s5p_mfc_alloc_dec_temp_buffers(ctx);
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_try_run();
> +		if (s5p_mfc_wait_for_done_ctx(ctx, \
> +				S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
> +			/* Error or timeout */
> +			mfc_err("Error getting instance from hardware.\n");
> +			s5p_mfc_release_instance_buffer(ctx);
> +			s5p_mfc_release_dec_buffers(ctx);
> +			ret = -EAGAIN;
> +			goto out;
> +		}
> +		mfc_debug("Got instance number: %d\n", ctx->inst_no);
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_err("Currently only decoding is supported.\n");
> +		ret = -EINVAL;
> +	}
> +out:
> +	mutex_unlock(&ctx->vq_dst.vb_lock);
> +	mutex_unlock(&ctx->vq_src.vb_lock);
> +	mfc_debug("vidioc_s_fmt--\n");
> +	return ret;
> +}
> +
> +/* Reqeust buffers */
> +static int vidioc_reqbufs(struct file *file, void *priv,
> +					  struct v4l2_requestbuffers
*reqbufs)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = 0;
> +	mfc_debug("vidioc_reqbufs++\n");
> +	mfc_debug("Memory type: %d\n", reqbufs->memory);
> +	if (reqbufs->memory != V4L2_MEMORY_MMAP) {
> +		mfc_err("Only V4L2_MEMORY_MAP is supported.\n");
> +		return -EINVAL;
> +	}
> +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		/* Can only request buffers after an instance has been
> opened.*/
> +		if (ctx->state == MFCINST_DEC_GOT_INST) {
> +			/* Decoding */
> +			if (ctx->output_state != QUEUE_FREE) {
> +				mfc_err("Bufs have already been
requested.\n");
> +				return -EINVAL;
> +			}
> +			ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
> +			if (ret) {
> +				mfc_err("vb2_reqbufs on output failed.\n");
> +				return ret;
> +			}
> +			mfc_debug("vb2_reqbufs: %d\n", ret);
> +			ctx->output_state = QUEUE_BUFS_REQUESTED;
> +		}
> +	}
> +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		if (ctx->capture_state != QUEUE_FREE) {
> +			mfc_err("Bufs have already been requested.\n");
> +			return -EINVAL;
> +		}
> +		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +		if (ret) {
> +			mfc_err("vb2_reqbufs on output failed.\n");

error message should be mfc_err("vb2_reqbufs on capture failed.\n");

> +			return ret;
> +		}
> +		if (reqbufs->count < ctx->dpb_count) {
> +			mfc_err("Not enough buffers allocated.\n");
> +			reqbufs->count = 0;
> +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +			return -ENOMEM;
> +		}
> +		ctx->total_dpb_count = reqbufs->count;

if 'ctx->total_dpb_count = reqbufs->count;' is here in code, Hmm....
let's suppose that reqbufs->count = 16 & ctx->dpb_count = 10,
in that case, ctx->total_dpb_count is 16, 
but *buf_count(in the s5p_mfc_buf_negotiate()) is 15
so, it is not matched & it affect __vb2_queue_alloc(xx, num_buffers, xx)
i think, is is due to location of 'the ctx->total_dpb_count = reqbufs-
>count;'
and No warning message for exceeding MFC_MAX_EXTRA_DPB
what do you think about that ?

> +		ret = s5p_mfc_alloc_dec_buffers(ctx);
> +		if (ret) {
> +			mfc_err("Failed to allocate decoding buffers.\n");
> +			reqbufs->count = 0;
> +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +			return -ENOMEM;
> +		}
> +		ctx->capture_state = QUEUE_BUFS_REQUESTED;
> +	}
> +	mfc_debug("vidioc_reqbufs--\n");
> +	return ret;
> +}
> +
> +/* Query buffer */
> +static int vidioc_querybuf(struct file *file, void *priv,
> +						   struct v4l2_buffer *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	int i;
> +	mfc_debug("vidioc_querybuf++\n");
> +	if (buf->memory != V4L2_MEMORY_MMAP) {
> +		mfc_err("Only mmaped buffers can be used.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("State: %d, buf->type: %d\n", ctx->state, buf->type);
> +	if (ctx->state == MFCINST_DEC_GOT_INST &&
> +			buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		ret = vb2_querybuf(&ctx->vq_src, buf);
> +	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +			buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		ret = vb2_querybuf(&ctx->vq_dst, buf);
> +		for (i = 0; i < buf->length; i++)
> +			buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
> +	} else {
> +		mfc_err("vidioc_querybuf called in an inappropriate
> state.\n");
> +		ret = -EINVAL;
> +	}
> +	mfc_debug("vidioc_querybuf--\n");
> +	return ret;
> +}
> +
> +/* Queue a buffer */
> +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer
> *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_qbuf++\n");
> +	mfc_debug("Enqueued buf: %d (type = %d)\n", buf->index, buf->type);
> +	if (ctx->state == MFCINST_DEC_ERROR) {
> +		mfc_err("Call on QBUF after unrecoverable error.\n");
> +		return -EIO;
> +	}
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		return vb2_qbuf(&ctx->vq_src, buf);
> +	else
> +		return vb2_qbuf(&ctx->vq_dst, buf);
> +	mfc_debug("vidioc_qbuf--\n");
> +	return -EINVAL;
> +}
> +
> +/* Dequeue a buffer */
> +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer
> *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	mfc_debug("vidioc_dqbuf++\n");
> +	mfc_debug("Addr: %p %p %p Type: %d\n", &ctx->vq_src, buf, buf-
> >m.planes,
> +								  buf-
>type);
> +	if (ctx->state == MFCINST_DEC_ERROR) {
> +		mfc_err("Call on DQBUF after unrecoverable error.\n");
> +		return -EIO;
> +	}
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		ret = vb2_dqbuf(&ctx->vq_src, buf,
> +				      file->f_flags & O_NONBLOCK);
> +	} else {
> +		ret = vb2_dqbuf(&ctx->vq_dst, buf,
> +				      file->f_flags & O_NONBLOCK);
> +	}
> +	mfc_debug("vidioc_dqbuf--\n");
> +	return ret;
> +}
> +
> +/* Stream on */
> +static int vidioc_streamon(struct file *file, void *priv,
> +			   enum v4l2_buf_type type)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = -EINVAL;
> +	unsigned long flags;
> +	mfc_debug("vidioc_streamon++\n");
> +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		ret = vb2_streamon(&ctx->vq_src, type);
> +	else
> +		ret = vb2_streamon(&ctx->vq_dst, type);
> +	mfc_debug("ctx->src_queue_cnt = %d ctx->state = %d "
> +		  "ctx->dst_queue_cnt = %d ctx->dpb_count = %d\n",
> +		  ctx->src_queue_cnt, ctx->state, ctx->dst_queue_cnt,
> +		  ctx->dpb_count);
> +	/* If context is ready then schedule it to run */
> +	if (s5p_mfc_ctx_ready(ctx)) {
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +	}
> +	s5p_mfc_try_run();
> +	mfc_debug("vidioc_streamon--\n");
> +	return ret;
> +}
> +
> +/* Stream off, which equals to a pause */
> +static int vidioc_streamoff(struct file *file, void *priv,
> +			    enum v4l2_buf_type type)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	mfc_debug("vidioc_streamoff++\n");
> +	ret = -EINVAL;
> +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		ret = vb2_streamoff(&ctx->vq_src, type);
> +	else
> +		ret = vb2_streamoff(&ctx->vq_dst, type);
> +	mfc_debug("vidioc_streamoff--\n");
> +	return ret;
> +}
> +
> +/* Query a ctrl */
> +static int vidioc_queryctrl(struct file *file, void *priv,
> +			    struct v4l2_queryctrl *qc)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(qc->id);
> +	if (!c)
> +		return -EINVAL;
> +	*qc = *c;
> +	return 0;
> +}
> +
> +/* Get ctrl */
> +static int vidioc_g_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_g_ctrl++\n");
> +	switch (ctrl->id) {
> +	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
> +		ctrl->value = ctx->loop_filter_mpeg4;
> +		break;
> +	case V4L2_CID_CODEC_DISPLAY_DELAY:
> +		ctrl->value = ctx->display_delay;
> +		break;
> +	case V4L2_CID_CODEC_REQ_NUM_BUFS:
> +		if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +		    ctx->state < MFCINST_ENC_INIT) {
> +			ctrl->value = ctx->dpb_count;
> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> +			/* Should wait for the header to be parsed */
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_wait_for_done_ctx(ctx, \
> +					S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 1);
> +			if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +			    ctx->state < MFCINST_ENC_INIT) {
> +				ctrl->value = ctx->dpb_count;
> +			} else {
> +				v4l2_err(&dev->v4l2_dev,
> +						 "Decoding not
initialised.\n");
> +				return -EINVAL;
> +			}
> +		} else {
> +			v4l2_err(&dev->v4l2_dev,
> +						 "Decoding not
initialised.\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_CODEC_SLICE_INTERFACE:
> +		ctrl->value = ctx->slice_interface;
> +		break;
> +	default:
> +		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_g_ctrl--\n");
> +	return 0;
> +}
> +
> +/* Check whether a ctrl value if correct */
> +static int check_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control
> *ctrl)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(ctrl->id);
> +	if (!c)
> +		return -EINVAL;
> +	if (ctrl->value < c->minimum || ctrl->value > c->maximum
> +	    || (c->step != 0 && ctrl->value % c->step != 0)) {
> +		v4l2_err(&dev->v4l2_dev, "Invalid control value\n");
> +		return -ERANGE;
> +	}
> +	return 0;
> +}
> +
> +/* Set a ctrl */
> +static int vidioc_s_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = 0;
> +	int stream_on;
> +	mfc_debug("vidioc_s_ctrl++\n");
> +	stream_on = ctx->vq_src.streaming || ctx->vq_dst.streaming;
> +	ret = check_ctrl_val(ctx, ctrl);
> +	if (ret != 0)
> +		return ret;
> +	switch (ctrl->id) {
> +	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->loop_filter_mpeg4 = ctrl->value;
> +		break;
> +	case V4L2_CID_CODEC_DISPLAY_DELAY:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->display_delay = ctrl->value;
> +		break;
> +	case V4L2_CID_CODEC_SLICE_INTERFACE:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->slice_interface = ctrl->value;
> +		break;
> +	default:
> +		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_s_ctrl--\n");
> +	return 0;
> +}
> +/* Get cropping information */
> +static int vidioc_g_crop(struct file *file, void *priv,
> +		struct v4l2_crop *cr)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	u32 left, right, top, bottom;
> +	mfc_debug("vidioc_g_crop++\n");
> +	if (ctx->state != MFCINST_DEC_HEAD_PARSED &&
> +	ctx->state != MFCINST_DEC_RUNNING && ctx->state !=
> MFCINST_DEC_FINISHING
> +					&& ctx->state !=
MFCINST_DEC_FINISHED) {
> +			mfc_debug("vidioc_g_crop--(err)\n");
> +			return -EINVAL;
> +		}
> +	if (ctx->fmt->fourcc == V4L2_PIX_FMT_H264) {
> +		left = s5p_mfc_get_h_crop(ctx);
> +		right = left >> 16;
> +		left = left & 0xFFFF;
> +		top = s5p_mfc_get_v_crop(ctx);
> +		bottom = top >> 16;
> +		top = top & 0xFFFF;
> +		cr->c.left = left;
> +		cr->c.top = top;
> +		cr->c.width = ctx->img_width - left - right;
> +		cr->c.height = ctx->img_height - top - bottom;
> +		mfc_debug("Cropping info [h264]: l=%d t=%d w=%d h=%d (r=%d "
> \
> +				"b=%d fw=%d fh=%d\n", left, top, cr-
>c.width, \
> +				cr->c.height, right, bottom, ctx-
>buf_width, \
> +							ctx->buf_height);
> +	} else {
> +		cr->c.left = 0;
> +		cr->c.top = 0;
> +		cr->c.width = ctx->img_width;
> +		cr->c.height = ctx->img_height;
> +		mfc_debug("Cropping info: w=%d h=%d fw=%d fh=%d\n", cr-
> >c.width,
> +				cr->c.height, ctx->buf_width, ctx-
>buf_height);
> +	}
> +	mfc_debug("vidioc_g_crop--\n");
> +	return 0;
> +}
> +
> +/* v4l2_ioctl_ops */
> +static const struct v4l2_ioctl_ops s5p_mfc_ioctl_ops = {
> +	.vidioc_querycap = vidioc_querycap,
> +	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
> +	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
> +	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
> +	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
> +	.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
> +	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
> +	.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
> +	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
> +	.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
> +	.vidioc_reqbufs = vidioc_reqbufs,
> +	.vidioc_querybuf = vidioc_querybuf,
> +	.vidioc_qbuf = vidioc_qbuf,
> +	.vidioc_dqbuf = vidioc_dqbuf,
> +	.vidioc_streamon = vidioc_streamon,
> +	.vidioc_streamoff = vidioc_streamoff,
> +	.vidioc_queryctrl = vidioc_queryctrl,
> +	.vidioc_g_ctrl = vidioc_g_ctrl,
> +	.vidioc_s_ctrl = vidioc_s_ctrl,
> +	.vidioc_g_crop = vidioc_g_crop,
> +};
> +
> +/* Negotiate buffers */
> +static int s5p_mfc_buf_negotiate(struct vb2_queue *vq, unsigned int
> *buf_count,
> +						unsigned int *plane_count)
> +{
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	mfc_debug("s5p_mfc_buf_negotiate++\n");
> +	/* Video output for decoding (source)
> +	 * this can be set after getting an instance */
> +	if (ctx->state == MFCINST_DEC_GOT_INST &&
> +	    vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_debug("setting for VIDEO output\n");
> +		/* A single plane is required for input */
> +		*plane_count = 1;
> +		if (*buf_count < 1)
> +			*buf_count = 1;
> +		if (*buf_count > MFC_MAX_BUFFERS)
> +			*buf_count = MFC_MAX_BUFFERS;
> +	/* Video capture for decoding (destination)
> +	 * this can be set after the header was parsed */
> +	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +		   vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_debug("setting for VIDEO capture\n");
> +		/* Output plane count is 2 - one for Y and one for CbCr */
> +		*plane_count = 2;
> +		/* Setup buffer count */
> +		if (*buf_count < ctx->dpb_count)
> +			*buf_count = ctx->dpb_count;
> +		if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
> +			*buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
> +		if (*buf_count > MFC_MAX_BUFFERS)
> +			*buf_count = MFC_MAX_BUFFERS;
> +	} else {
> +		mfc_err("State seems invalid. State = %d, vq->type = %d\n",
> +							ctx->state, vq-
>type);
> +		return -EINVAL;
> +	}
> +	mfc_debug("%s, buffer count=%d, plane count=%d type=0x%x\n",
> __func__,
> +					*buf_count, *plane_count, vq->type);
> +	mfc_debug("s5p_mfc_buf_negotiate--\n");
> +	return 0;
> +}
> +
> +/* Setup plane */
> +static int s5p_mfc_buf_setup_plane(struct vb2_queue *vq,
> +			   unsigned int plane, unsigned long *plane_size)
> +{
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	mfc_debug("s5p_mfc_buf_setup_plane++\n");
> +	if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +	    vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		switch (plane) {
> +		case 0:
> +			/* Plane 0 is for luma (Y) */
> +			*plane_size = ctx->luma_size;
> +			break;
> +		case 1:
> +			/* Plane 1 is for chroma (C) */
> +			*plane_size = ctx->chroma_size;
> +			break;
> +		default:
> +			mfc_err("%s, invalid plane=%d\n", __func__, plane);
> +			return -EINVAL;
> +		}
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
> +		   ctx->state == MFCINST_DEC_GOT_INST) {
> +		if (plane != 0) {
> +			mfc_err("%s, invalid plane=%d\n", __func__, plane);
> +			return -EINVAL;
> +		}
> +		/* dec_src_buf_size was set in s_fmt */
> +		*plane_size = ctx->dec_src_buf_size;
> +	} else {
> +		mfc_err("Currently only decoding is supported."\
> +						" Decoding not
initalised.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("%s, plane=%d, size=%lu\n", __func__, plane, *plane_size);
> +	mfc_debug("s5p_mfc_buf_setup_plane--\n");
> +	return 0;
> +}
> +
> +/* Prepare a buffer */
> +static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	unsigned int i;
> +	mfc_debug("s5p_mfc_buf_prepare++\n");
> +	BUG_ON(NULL == ctx->fmt);
> +	if (!ctx->fmt) {
> +		mfc_err("Format passed to the function is null.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("Addr: %p (%d)\n", (void *)vb2_plane_paddr(vb, 0),
> +							vb->v4l2_buf.index);
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
> +			mfc_debug("s5p_mfc_buf_prepare--\n");
> +			return 0;
> +		}
> +		for (i = 0; i <= ctx->fmt->num_planes ; i++) {
> +			if (vb2_plane_paddr(vb, i) == 0) {
> +				mfc_err("Plane mem not allocated.\n");
> +				return -EINVAL;
> +			}
> +		}
> +		if (vb2_plane_size(vb, 0) < ctx->luma_size ||
> +			vb2_plane_size(vb, 1) < ctx->chroma_size) {
> +			mfc_err("Plane buffer (CAPTURE) is too small.\n");
> +			return -EINVAL;
> +		}
> +		mfc_debug("Size: 0=%lu 2=%lu\n", vb2_plane_size(vb, 0),
> +							vb2_plane_size(vb,
1));
> +		i = vb->v4l2_buf.index;
> +		ctx->dec_dst_buf_luma[i] = vb2_plane_paddr(vb, 0);
> +		ctx->dec_dst_buf_chroma[i] = vb2_plane_paddr(vb, 1);
> +		ctx->dec_dst_buf_cnt++;
> +		if (ctx->dec_dst_buf_cnt == ctx->total_dpb_count)
> +			ctx->capture_state = QUEUE_BUFS_MMAPED;
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		if (vb2_plane_paddr(vb, 0)  == 0) {
> +			mfc_err("Plane memory not allocated.\n");
> +			return -EINVAL;
> +		}
> +		mfc_debug("Plane size: %ld, ctx->dec_src_buf_size: %d\n",
> +				vb2_plane_size(vb, 0), ctx-
>dec_src_buf_size);
> +		if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
> +			mfc_err("Plane buffer (OUTPUT) is too small.\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		mfc_err("s5p_mfc_buf_prepare: unknown queue type.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("s5p_mfc_buf_prepare--\n");
> +	return 0;
> +}
> +
> +/* Try running an operation on hardware */
> +static void s5p_mfc_try_run()
> +{
> +	struct vb2_buffer *temp_vb;
> +	struct s5p_mfc_ctx *ctx;
> +	int new_ctx;
> +	unsigned long flags;
> +	unsigned int cnt;
> +	unsigned int ret;
> +	mfc_debug("Try run dev: %p\n", dev);
> +	/* Check whether hardware is not running */
> +	if (test_and_set_bit(0, &dev->hw_lock) == 0) {
> +		/* Choose the context to run */
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		mfc_debug("Previos context: %d (bit field %08lx)\n",
> +					  dev->curr_ctx, dev-
>ctx_work_bits);
> +		new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
> +		cnt = 0;
> +		while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
> +			new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
> +			cnt++;
> +			if (cnt > MFC_NUM_CONTEXTS) {
> +				/* No contexts to run */
> +				spin_unlock_irqrestore(&dev->condlock,
flags);
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No ctx is scheduled to be
run.\n");
> +				return;
> +			}
> +		}
> +		mfc_debug("New context: %d\n", new_ctx);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		ctx = dev->ctx[new_ctx];
> +		mfc_debug("Seting new context to %p\n", ctx);
> +		/* Got context to run in ctx */
> +		mfc_debug("ctx->dst_queue_cnt=%d ctx->dpb_count=%d" \
> +				" ctx->src_queue_cnt=%d\n", ctx-
> >dst_queue_cnt,\
> +					ctx->dpb_count, ctx->src_queue_cnt);
> +		mfc_debug("ctx->state=%d\n", ctx->state);
> +		/* Last frame has already been sent to MFC
> +		 * Now obtaining frames from MFC buffer */
> +		if (ctx->state == MFCINST_DEC_FINISHING) {
> +			s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx, 1);
> +		} else if (ctx->state == MFCINST_DEC_RUNNING) {
> +			/* Frames are being decoded */
> +			if (list_empty(&ctx->src_queue)) {
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No source buffers.\n");
> +				return;
> +			}
> +			/* Get the next source buffer */
> +			temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +			mfc_debug("Temp vb: %p\n", temp_vb);
> +			mfc_debug("Src Addr: %08lx\n",
> +						vb2_plane_paddr(temp_vb,
0));
> +			s5p_mfc_set_dec_stream_buffer(ctx, \
> +			      vb2_plane_paddr(temp_vb, 0), 0, \
> +			      temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx,
> +				temp_vb->v4l2_planes[0].bytesused == 0);
> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> +			/* Preparing decoding - getting instance number */
> +			mfc_debug("Getting instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +/*			s5p_mfc_set_dec_temp_buffers(ctx);
> + *			Removed per request by Peter, check if MFC works OK
*/
> +			ret = s5p_mfc_open_inst(ctx);
> +			if (ret) {
> +				mfc_err("Failed to create a new
instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +		} else if (ctx->state == MFCINST_DEC_RETURN_INST) {
> +			/* Closing decoding instance  */
> +			mfc_debug("Returning instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			ret = s5p_mfc_return_inst_no(ctx);
> +			if (ret) {
> +				mfc_err("Failed to return an instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +
> +		} else if (ctx->state == MFCINST_DEC_GOT_INST) {
> +			/* Initializing decoding - parsing header */
> +			mfc_debug("Preparing to init decoding.\n");
> +			temp_vb = list_entry(ctx->src_queue.next,
> +						struct vb2_buffer,
drv_entry);
> +			s5p_mfc_set_dec_temp_buffers(ctx);
> +			mfc_debug("Header size: %d\n", \
> +					temp_vb->v4l2_planes[0].bytesused);
> +			s5p_mfc_set_dec_stream_buffer(ctx,\
> +					vb2_plane_paddr(temp_vb, 0), 0,
> +					 temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			mfc_debug("paddr: %08x\n", \
> +				(int)phys_to_virt(vb2_plane_paddr(temp_vb,
0)));
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_init_decode(ctx);
> +		} else if (ctx->state == MFCINST_DEC_HEAD_PARSED) {
> +			/* Header was parsed now starting processing
> +			 * First set the output frame buffers
> +			 * s5p_mfc_alloc_dec_buffers(ctx); */
> +			if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
> +				temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +				mfc_debug("Header size: %d\n",
> +					temp_vb->v4l2_planes[0].bytesused);
> +				s5p_mfc_set_dec_stream_buffer(ctx, \
> +					vb2_plane_paddr(temp_vb, 0), 0, \
> +					temp_vb->v4l2_planes[0].bytesused);
> +				dev->curr_ctx = ctx->num;
> +				s5p_mfc_clean_ctx_int_flags(ctx);
> +				s5p_mfc_set_dec_frame_buffer(ctx, 1);
> +			} else {
> +				mfc_err("It seems that not all
destionation" \
> +				    " buffers were mmaped.\nMFC requires
that" \
> +				    " all destination are mmaped before" \
> +				    " starting processing.\n");
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +			}
> +		} else {
> +			/* Free hardware lock */
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
> +				mfc_err("Failed to unlock hardware.\n");
> +				return;
> +			}
> +		}
> +	} else {
> +		/* This is perfectly ok, the scheduled ctx should wait */
> +		mfc_debug("Couldn't lock HW.\n");
> +	}
> +}
> +
> +/* Queue buffer */
> +static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	unsigned long flags;
> +	mfc_debug("s5p_mfc_buf_queue++\n");
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_debug("Src queue: %p\n", &ctx->src_queue);
> +		mfc_debug("Adding to src: %p (%08lx)\n", vb,
> +							vb2_plane_paddr(vb,
0));
> +		list_add_tail(&vb->drv_entry, &ctx->src_queue);
> +		ctx->src_queue_cnt++;
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_debug("Dst queue: %p\n", &ctx->dst_queue);
> +		mfc_debug("Adding to dst: %p (%lx)\n", vb, \
> +						  vb2_plane_paddr(vb, 0));
> +		mfc_debug("ADDING Flag before: %lx (%d)\n", ctx-
> >dec_dst_flag, \
> +							vb->v4l2_buf.index);
> +		/* Mark destination as available for use by MFC */
> +		set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
> +		mfc_debug("ADDING Flag after: %lx\n", ctx->dec_dst_flag);
> +		list_add_tail(&vb->drv_entry, &ctx->dst_queue);
> +		ctx->dst_queue_cnt++;
> +	} else {
> +		mfc_err("Unsupported buffer type (%d)\n", vq->type);
> +	}
> +	if (s5p_mfc_ctx_ready(ctx)) {
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +	}
> +	s5p_mfc_try_run();
> +	mfc_debug("s5p_mfc_buf_queue--\n");
> +}
> +
> +/* Videobuf opts */
> +static struct vb2_ops s5p_mfc_qops = {
> +	.buf_queue = s5p_mfc_buf_queue,
> +	.queue_negotiate = s5p_mfc_buf_negotiate,
> +	.plane_setup = s5p_mfc_buf_setup_plane,
> +	.buf_prepare = s5p_mfc_buf_prepare,
> +};
> +
> +/* Handle frame decoding interrupt */
> +static void s5p_mfc_handle_frame_int(struct s5p_mfc_ctx *ctx, \
> +					unsigned int reason, unsigned int
err)
> +{
> +	size_t dst_ret_addr;
> +	unsigned int dst_frame_status;
> +	unsigned int dec_frame_type;
> +	struct vb2_buffer *src_buf, *dst_buf;
> +	dst_ret_addr = s5p_mfc_get_dspl_y_adr();
> +	dst_frame_status = s5p_mfc_get_dspl_status()
> +				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
> +	mfc_debug("Orig Status: %d\n", s5p_mfc_get_dspl_status());
> +	dec_frame_type = s5p_mfc_get_frame_type();
> +	mfc_debug("Status: %x, addr Y: %08x\n", dst_frame_status,
> dst_ret_addr);
> +	mfc_debug("Decode_y: %08x, frame type: %d\n", dst_ret_addr, \
>
+								dec_frame_ty
pe);
> +	spin_lock(&dev->irqlock);
> +	/* All frames remaining in the buffer have been extracted  */
> +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
> +		ctx->state = MFCINST_DEC_FINISHED;
> +		mfc_debug("Decided to finish\n");
> +		ctx->sequence++;
> +		while (!list_empty(&ctx->dst_queue)) {
> +			dst_buf = list_entry(ctx->dst_queue.next,
> +					     struct vb2_buffer, drv_entry);
> +			mfc_debug("Cleaning up buffer: %d\n",
> +						  dst_buf->v4l2_buf.index);
> +			vb2_set_plane_payload(dst_buf, 0, 0);
> +			vb2_set_plane_payload(dst_buf, 1, 0);
> +			list_del(&dst_buf->drv_entry);
> +			ctx->dst_queue_cnt--;
> +			dst_buf->v4l2_buf.sequence = (ctx->sequence++);
> +			if (s5p_mfc_get_pic_time_top(ctx) ==
> +				s5p_mfc_get_pic_time_bottom(ctx))
> +				dst_buf->v4l2_buf.field = V4L2_FIELD_NONE;
> +			else
> +				dst_buf->v4l2_buf.field =
V4L2_FIELD_INTERLACED;
> +			spin_unlock(&dev->irqlock);
> +			mfc_debug("Finishing Flags before: %08ld\n",
> +							ctx->dec_dst_flag);
> +			ctx->dec_dst_flag &= ~(1 << dst_buf-
>v4l2_buf.index);
> +			mfc_debug("Finishing Flags after: %08ld\n",
> +							ctx->dec_dst_flag);
> +			vb2_buffer_done(dst_buf, VB2_BUF_STATE_DONE);
> +			spin_lock(&dev->irqlock);
> +			mfc_debug("Cleaned up buffer: %d\n",
> +				  dst_buf->v4l2_buf.index);
> +		}
> +		mfc_debug("After cleanup\n");
> +	}
> +
> +	/* A frame has been decoded and is in the buffer  */
> +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
> +	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
> +		ctx->sequence++;
> +		/* If frame is same as previous then skip and do not dequeue
> */
> +		if (dec_frame_type !=  S5P_FIMV_DECODE_FRAME_SKIPPED) {
> +		/* The MFC returns address of the buffer, now we have to
> +		 * check which videobuf does it correspond to */
> +		list_for_each_entry(dst_buf, &ctx->dst_queue, drv_entry) {
> +			mfc_debug("Listing: %d\n", dst_buf->v4l2_buf.index);
> +			/* This is the buffer we're looking for */
> +			mfc_debug("paddr: %p mfc: %p\n",
> +					(void *)vb2_plane_paddr(dst_buf, 1),
> +							(void
*)dst_ret_addr);

what is the purpose of this debug message ?
mfc_debug("paddr: %p mfc: %p\n",(void *)vb2_plane_paddr(dst_buf, 1),
                                                                (void
*)dst_ret_addr); 
do you want to compare Y address?
it should be like this, vb2_plane_paddr(dst_buf, 0) with dst_ret_addr.
and dst_y_ret_addr is better than dst_ret_addr, 
making debug msg clear is important 'cause codec debugging is not easy.

> +			if (vb2_plane_paddr(dst_buf, 0) == dst_ret_addr) {
> +				list_del(&dst_buf->drv_entry);
> +				ctx->dst_queue_cnt--;
> +				mfc_debug("Flag before: %lx (%d)\n",
> +							ctx->dec_dst_flag,
> +						dst_buf->v4l2_buf.index);
> +				dst_buf->v4l2_buf.sequence = ctx->sequence;
> +				if (s5p_mfc_get_pic_time_top(ctx) ==
> +					s5p_mfc_get_pic_time_bottom(ctx))
> +					dst_buf->v4l2_buf.field =
>
+								V4L2_FIELD_N
ONE;
> +				else
> +					dst_buf->v4l2_buf.field =
>
+							V4L2_FIELD_INTERLACE
D;
> +				mfc_debug("Field: %d\n",
> +						dst_buf->v4l2_buf.field);
> +				mfc_debug("Freed -> sequence: %d index:
%d\n",
> +						dst_buf->v4l2_buf.field,
> +						dst_buf->v4l2_buf.index);
> +				vb2_set_plane_payload(dst_buf, 0,
> +								ctx-
>luma_size);
> +				vb2_set_plane_payload(dst_buf, 1,
> +							ctx->chroma_size);
> +				spin_unlock(&dev->irqlock);
> +				clear_bit(dst_buf->v4l2_buf.index,
> +							&ctx->dec_dst_flag);
> +				if (err) {
> +					vb2_buffer_done(dst_buf,
>
+							VB2_BUF_STATE_ERROR)
;
> +				} else {
> +					vb2_buffer_done(dst_buf,
> +							VB2_BUF_STATE_DONE);
> +				}
> +				spin_lock(&dev->irqlock);
> +				mfc_debug("Flag after: %lx\n",
> +					  ctx->dec_dst_flag);
> +				break;
> +			}
> +		}
> +		}
> +	} else {
> +		mfc_debug("No frame decode.\n");
> +	}
> +	/* Mark source buffer as complete */
> +	if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
> +		&& !list_empty(&ctx->src_queue)) {
> +		src_buf = list_entry(ctx->src_queue.next, struct vb2_buffer,
> +								drv_entry);
> +		mfc_debug("Packed PB test. Size: %d, prev off:"\
> +			" %ld now con: %d\n", src_buf-
> >v4l2_planes[0].bytesused,
> +			ctx->consumed_stream,
s5p_mfc_get_consumed_stream());
> +		ctx->consumed_stream += s5p_mfc_get_consumed_stream();
> +		if (dec_frame_type == S5P_FIMV_DECODE_FRAME_P_FRAME
> +						&& ctx->consumed_stream <
> +					src_buf->v4l2_planes[0].bytesused) {
> +			/* Run MFC again on the same buffer */
> +			mfc_debug("Running again the same buffer.\n");
> +			s5p_mfc_set_dec_stream_buffer(ctx,
> +						vb2_plane_paddr(src_buf, 0),
> +							ctx-
>consumed_stream,
> +					src_buf->v4l2_planes[0].bytesused -
> +							ctx-
>consumed_stream);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			spin_unlock(&dev->irqlock);
> +			s5p_mfc_clear_int_flags();
> +			wake_up_ctx(ctx, reason, err);
> +			s5p_mfc_decode_one_frame(ctx, 0);
> +			return;
> +		} else {
> +			mfc_debug("MFC needs next buffer..\n");
> +			/* Advance to next buffer */
> +			ctx->consumed_stream = 0;
> +			list_del(&src_buf->drv_entry);
> +			ctx->src_queue_cnt--;
> +			if (vb2_plane_size(src_buf, 0) == 0) {
> +				mfc_debug("Setting ctx->state to FINISH\n");
> +				ctx->state = MFCINST_DEC_FINISHING;
> +			}
> +			spin_unlock(&dev->irqlock);
> +			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
> +
> +		}
> +	} else {
> +		spin_unlock(&dev->irqlock);
> +	}
> +	mfc_debug("Assesing whether this context should be run again.\n");
> +	if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_DEC_FINISHING)
> +				    || ctx->dst_queue_cnt < ctx->dpb_count)
{
> +		mfc_debug("No need to run again.\n");
> +		clear_work_bit(ctx);
> +	}
> +	mfc_debug("After assesing whether this context should be run
> again.\n");
> +	s5p_mfc_clear_int_flags();
> +	wake_up_ctx(ctx, reason, err);
> +	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +		BUG();
> +	s5p_mfc_try_run();
> +}
> +
> +/* Error handling for interrupt */
> +static inline void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
> +	unsigned int reason, unsigned int err)
> +{
> +	mfc_err("Interrupt Error: %08x\n", err);
> +	s5p_mfc_clear_int_flags();
> +	wake_up_dev(reason, err);
> +	/* If no context is available then all necessary
> +	 * processing has been done. */
> +	if (ctx == 0)
> +		return;
> +	/* Error recovery is dependent on the state of context */
> +	switch (ctx->state) {
> +	case MFCINST_DEC_INIT:
> +		/* This error had to happen while acquireing instance */
> +	case MFCINST_DEC_GOT_INST:
> +		/* This error had to happen while parsing the header */
> +	case MFCINST_DEC_HEAD_PARSED:
> +		/* This error had to happen while setting dst buffers */
> +	case MFCINST_DEC_RETURN_INST:
> +		/* This error had to happen while releasing instance */
> +		clear_work_bit(ctx);
> +		wake_up_ctx(ctx, reason, err);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		break;
> +	case MFCINST_DEC_FINISHING:
> +	case MFCINST_DEC_FINISHED:
> +	case MFCINST_DEC_RUNNING:
> +		/* It is higly probable that an error occured
> +		 * while decoding a frame */
> +		clear_work_bit(ctx);
> +		ctx->state = MFCINST_DEC_ERROR;
> +		/* Mark all dst buffers as having an error */
> +		s5p_mfc_error_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
> +		/* Mark all src buffers as having an error */
> +		s5p_mfc_error_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		break;
> +	default:
> +		mfc_err("Encountered an error interrupt which had not been"
> \
> +								"
handled.\n");
> +		break;
> +	}
> +	return;
> +}
> +
> +/* Interrupt processing */
> +static irqreturn_t s5p_mfc_irq(int irq, void *priv)
> +{
> +	struct vb2_buffer *src_buf;
> +	struct s5p_mfc_ctx *ctx;
> +	unsigned int reason;
> +	unsigned int err;
> +	mfc_debug("s5p_mfc_irq++\n");
> +	/* Reset the timeout watchdog */
> +	atomic_set(&dev->watchdog_cnt, 0);
> +	ctx = dev->ctx[dev->curr_ctx];
> +	/* Get the reason of interrupt and the error code */
> +	reason = s5p_mfc_get_int_reason();
> +	err = s5p_mfc_get_int_err();
> +	mfc_debug("Int reason: %d (error: %08x)\n", reason, err);
> +	switch (reason) {
> +	case S5P_FIMV_R2H_CMD_DECODE_ERR_RET:
> +		/* An error has occured */
> +		if (ctx->state == MFCINST_DEC_RUNNING && err >= 145)

What about using MACRO instead of using 145(no)

> +			s5p_mfc_handle_frame_int(ctx, reason, err);
> +		else
> +			s5p_mfc_handle_error(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
> +	case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
> +		s5p_mfc_handle_frame_int(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
> +		if (ctx->fmt->fourcc != V4L2_PIX_FMT_DIVX3) {
> +			ctx->img_width = s5p_mfc_get_img_width();
> +			ctx->img_height = s5p_mfc_get_img_height();
> +		}
> +		ctx->buf_width = ALIGN(ctx->img_width, 128);
> +		ctx->buf_height = ALIGN(ctx->img_height, 32);
> +		mfc_debug("SEQ Done: ctx:%dx%d, buf:%dx%d\n", ctx-
>img_width,
> +			ctx->img_height, ctx->buf_width, ctx->buf_height);
> +		ctx->luma_size = ALIGN(ctx->buf_width * ctx->buf_height,
> 8192);
> +		ctx->chroma_size = ALIGN(ctx->buf_width *
> +					ALIGN(ctx->img_height / 2, 32),
8192);
> +		if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
> +			ctx->mv_size = ALIGN(ctx->buf_width *
> +					 ALIGN(ctx->buf_height / 4, 32),
8192);
> +		else
> +			ctx->mv_size = 0;
> +		ctx->dpb_count = s5p_mfc_get_dpb_count();
> +		ctx->state = MFCINST_DEC_HEAD_PARSED;
> +		s5p_mfc_clear_int_flags();
> +		clear_work_bit(ctx);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		s5p_mfc_try_run();
> +		wake_up_ctx(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
> +		ctx->inst_no = s5p_mfc_get_inst_no();
> +		ctx->state = MFCINST_DEC_GOT_INST;
> +		clear_work_bit(ctx);
> +		wake_up_interruptible(&ctx->queue);
> +		goto irq_cleanup_hw;
> +		break;
> +	case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
> +		clear_work_bit(ctx);
> +		ctx->state = MFCINST_FREE;
> +		wake_up(&ctx->queue);
> +		goto irq_cleanup_hw;
> +		break;
> +	case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
> +	case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
> +		if (ctx)
> +			clear_work_bit(ctx);
> +		s5p_mfc_clear_int_flags();
> +		wake_up_dev(reason, err);
> +		clear_bit(0, &dev->hw_lock);
> +		break;
> +	case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
> +		s5p_mfc_clear_int_flags();
> +		ctx->int_type = reason;
> +		ctx->int_err = err;
> +		ctx->int_cond = 1;
> +		spin_lock(&dev->condlock);
> +		clear_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock(&dev->condlock);
> +		if (err == 0) {
> +			ctx->state = MFCINST_DEC_RUNNING;
> +			spin_lock(&dev->irqlock);
> +			src_buf =
> +			    list_entry(ctx->src_queue.next,
> +				       struct vb2_buffer, drv_entry);
> +			list_del(&src_buf->drv_entry);
> +			ctx->src_queue_cnt--;
> +			spin_unlock(&dev->irqlock);
> +			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +				BUG();
> +			wake_up_interruptible(&ctx->queue);
> +			s5p_mfc_try_run();
> +		} else {
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +				BUG();
> +			wake_up_interruptible(&ctx->queue);
> +		}
> +		break;
> +	default:
> +		mfc_debug("Unknown int reason.\n");
> +		s5p_mfc_clear_int_flags();
> +	}
> +	mfc_debug("s5p_mfc_irq--\n");
> +	return IRQ_HANDLED;
> +irq_cleanup_hw:
> +	s5p_mfc_clear_int_flags();
> +	ctx->int_type = reason;
> +	ctx->int_err = err;
> +	ctx->int_cond = 1;
> +	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +		mfc_err("Failed to unlock hw.\n");
> +	s5p_mfc_try_run();
> +	mfc_debug("s5p_mfc_irq-- (via irq_cleanup_hw)\n");
> +	return IRQ_HANDLED;
> +}
> +
> +/* Open an MFC node */
> +static int s5p_mfc_open(struct file *file)
> +{
> +	struct s5p_mfc_ctx *ctx = NULL;
> +	unsigned long flags;
> +	int ret = 0;
> +	mfc_debug("s5p_mfc_open++\n");
> +	mutex_lock(dev->mfc_mutex);
> +	atomic_inc(&dev->num_inst);	/* It is guarded by mfc_mutex */
> +	/* Allocate memory for context */
> +	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +	if (!ctx) {
> +		mfc_err("Not enough memory.\n");
> +		ret = -ENOMEM;
> +		goto out_open;
> +	}
> +	file->private_data = ctx;
> +	ctx->dev = dev;
> +	INIT_LIST_HEAD(&ctx->src_queue);
> +	INIT_LIST_HEAD(&ctx->dst_queue);
> +	ctx->src_queue_cnt = 0;
> +	ctx->dst_queue_cnt = 0;
> +	/* Get context number */
> +	ctx->num = 0;
> +	while (dev->ctx[ctx->num]) {
> +		ctx->num++;
> +		if (ctx->num >= MFC_NUM_CONTEXTS) {
> +			mfc_err("Too many open contexts.\n");
> +			ret = -EAGAIN;
> +			goto out_open;
> +		}
> +	}
> +	/* Mark context as idle */
> +	spin_lock_irqsave(&dev->condlock, flags);
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	spin_unlock_irqrestore(&dev->condlock, flags);
> +	dev->ctx[ctx->num] = ctx;
> +	/* Default format */
> +	ctx->fmt = &formats[0];
> +	/* Load firmware if this is the first instance */
> +	if (atomic_read(&dev->num_inst) == 1) {
> +		/* Load the FW */
> +		ret = s5p_mfc_alloc_firmware(dev);
> +		if (ret != 0)
> +			goto out_open_2a;
> +		ret = s5p_mfc_load_firmware(dev);
> +		if (ret != 0)
> +			goto out_open_2;
> +		mfc_debug("Enabling clocks.\n");
> +		clk_enable(dev->clock1);
> +		clk_enable(dev->clock2);
> +		/* Init the FW */
> +		ret = s5p_mfc_init_hw(dev);
> +		if (ret != 0)
> +			goto out_open_3;
> +	}
> +	/* Init videobuf2 queue for CAPTURE */
> +	ret = vb2_queue_init(&ctx->vq_dst, &s5p_mfc_qops,
> +			       dev->alloc_ctx[0], &dev->irqlock,
> +			       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, ctx);
> +	if (ret) {
> +		mfc_err("Failed to initialize videobuf2 queue (capture)\n");
> +		goto out_open_3;
> +	}
> +	/* Init videobuf2 queue for OUTPUT */
> +	ret = vb2_queue_init(&ctx->vq_src, &s5p_mfc_qops,
> +			       dev->alloc_ctx[1], &dev->irqlock,
> +			       V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, ctx);
> +	if (ret) {
> +		mfc_err("Failed to initialize videobuf2 queue (output)\n");
> +		goto out_open_3;
> +	}
> +	vb2_set_alloc_ctx(&ctx->vq_dst, dev->alloc_ctx[1], 1);
> +	init_waitqueue_head(&ctx->queue);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_open--\n");
> +	return ret;
> +	/* Deinit when failure occured */
> +out_open_3:
> +	if (atomic_read(&dev->num_inst) == 1) {
> +		clk_disable(dev->clock1);
> +		clk_disable(dev->clock2);
> +		s5p_mfc_release_firmware();
> +	}
> +out_open_2:
> +	s5p_mfc_release_firmware();
> +out_open_2a:
> +	dev->ctx[ctx->num] = 0;
> +	kfree(ctx);
> +out_open:
> +	atomic_dec(&dev->num_inst);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_open-- with error\n");
> +	return ret;
> +}
> +
> +/* Release MFC context */
> +static int s5p_mfc_release(struct file *file)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	unsigned long flags;
> +	mfc_debug("s5p_mfc_release++\n");
> +	mutex_lock(dev->mfc_mutex);
> +	/* Stop all the processing */
> +	vb2_queue_release(&ctx->vq_src);
> +	vb2_queue_release(&ctx->vq_dst);
> +	/* Mark context as idle */
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	/* If instance was initialised then
> +	 * return instance and free reosurces */
> +	if (ctx->state < MFCINST_ENC_INIT && ctx->state >= MFCINST_DEC_INIT)
> {
> +		ctx->state = MFCINST_DEC_RETURN_INST;
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_try_run();
> +		/* Wait until instance is returned or timeout occured */
> +		if (s5p_mfc_wait_for_done_ctx
> +		    (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
> +			mfc_err("Error returning instance.\n");
> +		}
> +		/* Free resources */
> +		s5p_mfc_release_dec_buffers(ctx);
> +		s5p_mfc_release_instance_buffer(ctx);
> +		s5p_mfc_release_dec_temp_buffers(ctx);
> +	}
> +	/* hardware locking scheme */
> +	if (dev->curr_ctx == ctx->num)
> +		clear_bit(0, &dev->hw_lock);
> +	atomic_dec(&dev->num_inst);
> +	if (atomic_read(&dev->num_inst) == 0) {
> +		/* Actually this is also protected by mfc_mutex */
> +		s5p_mfc_deinit_hw(dev);
> +		mfc_debug("Disabling clocks...\n");
> +		clk_disable(dev->clock1);
> +		clk_disable(dev->clock2);
> +		s5p_mfc_release_firmware();
> +	}
> +	dev->ctx[ctx->num] = 0;
> +	kfree(ctx);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_release--\n");
> +	return 0;
> +}
> +
> +/* Poll */
> +static unsigned int s5p_mfc_poll(struct file *file,
> +				 struct poll_table_struct *wait)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	return vb2_poll(&ctx->vq_dst, file, wait);
> +}
> +
> +/* Mmap */
> +static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	int ret;
> +	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> +	mfc_debug("s5p_mfc_mmap++\n");
> +	if (offset < DST_QUEUE_OFF_BASE) {
> +		mfc_debug("mmaping source.\n");
> +		ret = vb2_mmap(&ctx->vq_src, vma);
> +	} else {		/* capture */
> +		mfc_debug("mmaping destination.\n");
> +		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
> +		ret = vb2_mmap(&ctx->vq_dst, vma);
> +	}
> +	mfc_debug("s5p_mfc_mmap--\n");
> +	return ret;
> +}
> +
> +/* v4l2 ops */
> +static const struct v4l2_file_operations s5p_mfc_fops = {
> +	.owner = THIS_MODULE,
> +	.open = s5p_mfc_open,
> +	.release = s5p_mfc_release,
> +	.poll = s5p_mfc_poll,
> +	.ioctl = video_ioctl2,
> +	.mmap = s5p_mfc_mmap,
> +};
> +
> +/* videodec structure */
> +static struct video_device s5p_mfc_videodev = {
> +	.name = S5P_MFC_NAME,
> +	.fops = &s5p_mfc_fops,
> +	.ioctl_ops = &s5p_mfc_ioctl_ops,
> +	.minor = -1,
> +	.release = video_device_release,
> +};
> +
> +/* MFC probe function */
> +static int s5p_mfc_probe(struct platform_device *pdev)
> +{
> +	struct video_device *vfd;
> +	struct resource *res;
> +	int ret = -ENOENT;
> +	size_t size;
> +	mfc_debug("s5p_mfc_probe++\n");
> +	dev = kzalloc(sizeof *dev, GFP_KERNEL);
> +	if (!dev) {
> +		dev_err(&pdev->dev, "Not enough memoty for MFC device.\n");

incorrect spelling (memoty)

> +		return -ENOMEM;
> +	}
> +	mfc_debug("Ok, allocated memory for device structure.\n");
> +	spin_lock_init(&dev->irqlock);
> +	spin_lock_init(&dev->condlock);
> +	mfc_debug("Initialised spin lock\n");
> +	dev->plat_dev = pdev;
> +	if (!dev->plat_dev) {
> +		dev_err(&pdev->dev, "No platform data specified\n");
> +		ret = -ENODEV;
> +		goto free_dev;
> +	}
> +	mfc_debug("Getting clocks\n");
> +	dev->clock1 = clk_get(&pdev->dev, "sclk_mfc");
> +	mfc_debug("Got clock1\n");
> +	dev->clock2 = clk_get(&pdev->dev, "mfc");
> +	mfc_debug("Got clock2\n");
> +	if (IS_ERR(dev->clock1) || IS_ERR(dev->clock2)) {
> +		dev_err(&pdev->dev, "failed to get mfc clock source\n");
> +		goto free_clk;
> +	}
> +	mfc_debug("No \"error\" in clock\n");
> +	mfc_debug("After setting clock rate\n");
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get memory region
> resource.\n");
> +		ret = -ENOENT;
> +		goto probe_out1;
> +	}
> +	size = (res->end - res->start) + 1;
> +	dev->mfc_mem = request_mem_region(res->start, size, pdev->name);
> +	if (dev->mfc_mem == NULL) {
> +		dev_err(&pdev->dev, "failed to get memory region.\n");
> +		ret = -ENOENT;
> +		goto probe_out2;
> +	}
> +	dev->base_virt_addr = ioremap(dev->mfc_mem->start,
> +			      dev->mfc_mem->end - dev->mfc_mem->start + 1);
> +	if (dev->base_virt_addr == NULL) {
> +		dev_err(&pdev->dev, "failed to ioremap address region.\n");
> +		ret = -ENOENT;
> +		goto probe_out3;
> +	}
> +	dev->regs_base = dev->base_virt_addr;
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get irq resource.\n");
> +		ret = -ENOENT;
> +		goto probe_out4;
> +	}
> +	dev->irq = res->start;
> +	ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
>
+									dev)
;
> +
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
> +		goto probe_out5;
> +	}
> +	dev->mfc_mutex = kmalloc(sizeof(struct mutex), GFP_KERNEL);
> +	if (dev->mfc_mutex == NULL) {
> +		dev_err(&pdev->dev, "Memory allocation failed\n");
> +		ret = -ENOMEM;
> +		goto probe_out6;
> +	}
> +	mutex_init(dev->mfc_mutex);
> +	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> +	if (ret)
> +		goto probe_out7;
> +	atomic_set(&dev->num_inst, 0);
> +	init_waitqueue_head(&dev->queue);
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to allocate video
> device\n");
> +		ret = -ENOMEM;
> +		goto unreg_dev;
> +	}
> +	*vfd = s5p_mfc_videodev;
> +	video_set_drvdata(vfd, dev);
> +	snprintf(vfd->name, sizeof(vfd->name), "%s", s5p_mfc_videodev.name);
> +	dev->vfd = vfd;
> +
> +	platform_set_drvdata(pdev, dev);
> +	dev->hw_lock = 0;
> +	dev->watchdog_workqueue = create_singlethread_workqueue("s5p-mfc");
> +	INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
> +	atomic_set(&dev->watchdog_cnt, 0);
> +	init_timer(&dev->watchdog_timer);
> +	dev->watchdog_timer.data = 0;
> +	dev->watchdog_timer.function = s5p_mfc_watchdog;
> +	dev->watchdog_timer.expires = jiffies +
>
+					msecs_to_jiffies(MFC_WATCHDOG_INTERV
AL);
> +	add_timer(&dev->watchdog_timer);
> +
> +	dev->alloc_ctx = vb2_cma_init_multi(&pdev->dev, 2, s5p_mem_types,
> +							s5p_mem_alignments);
> +	if (IS_ERR(dev->alloc_ctx)) {
> +		mfc_err("Couldn't prepare allocator context.\n");
> +		ret = PTR_ERR(dev->alloc_ctx);
> +		goto alloc_ctx_fail;
> +	}
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> +	if (ret) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to register video
> device\n");
> +		video_device_release(vfd);
> +		goto rel_vdev;
> +	}
> +	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
> +								vfd->num);
> +	mfc_debug("s5p_mfc_probe--\n");
> +	return 0;
> +
> +/* Deinit MFC if probe had failed */
> +rel_vdev:
> +	vb2_cma_cleanup_multi(dev->alloc_ctx);
> +alloc_ctx_fail:
> +unreg_dev:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +
> +probe_out7:
> +	if (dev->mfc_mutex) {
> +		mutex_destroy(dev->mfc_mutex);
> +		kfree(dev->mfc_mutex);
> +	}
> +probe_out6:
> +	free_irq(dev->irq, dev);
> +probe_out5:
> +probe_out4:
> +	iounmap(dev->base_virt_addr);
> +	dev->base_virt_addr = NULL;
> +probe_out3:
> +	release_resource(dev->mfc_mem);
> +	kfree(dev->mfc_mem);
> +probe_out2:
> +probe_out1:
> +	clk_put(dev->clock1);
> +	clk_put(dev->clock2);
> +free_clk:
> +
> +free_dev:
> +	kfree(dev);
> +	mfc_debug("s5p_mfc_probe-- with error\n");
> +	return ret;
> +}
> +
> +/* Remove the driver */
> +static int s5p_mfc_remove(struct platform_device *pdev)
> +{
> +	mfc_debug("s5p_mfc_remove++\n");
> +	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
> +	del_timer_sync(&dev->watchdog_timer);
> +	flush_workqueue(dev->watchdog_workqueue);
> +	destroy_workqueue(dev->watchdog_workqueue);
> +	video_unregister_device(dev->vfd);
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +	vb2_cma_cleanup_multi(dev->alloc_ctx);
> +	if (dev->mfc_mutex) {
> +		mutex_destroy(dev->mfc_mutex);
> +		kfree(dev->mfc_mutex);
> +	}
> +	mfc_debug("Will now deinit HW\n");
> +	s5p_mfc_deinit_hw(dev);
> +	free_irq(dev->irq, dev);
> +	iounmap(dev->base_virt_addr);
> +	if (dev->mfc_mem != NULL) {
> +		release_resource(dev->mfc_mem);
> +		kfree(dev->mfc_mem);
> +		dev->mfc_mem = NULL;
> +	}
> +	clk_put(dev->clock1);
> +	clk_put(dev->clock2);
> +	kfree(dev);
> +	mfc_debug("s5p_mfc_remove--\n");
> +	return 0;
> +}
> +
> +static int s5p_mfc_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_mfc_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +/* Power management */
> +static const struct dev_pm_ops s5p_mfc_pm_ops = {
> +	.suspend = s5p_mfc_suspend,
> +	.resume = s5p_mfc_resume,
> +};
> +
> +static struct platform_driver s5p_mfc_pdrv = {
> +	.probe = s5p_mfc_probe,
> +	.remove = __devexit_p(s5p_mfc_remove),
> +	.driver = {
> +		   .name = S5P_MFC_NAME,
> +		   .owner = THIS_MODULE,
> +		   .pm = &s5p_mfc_pm_ops},
> +};
> +
> +static char banner[] __initdata =
> +			"S5P MFC V4L2 Driver, (c) 2010 Samsung
Electronics\n";
> +
> +static int __init s5p_mfc_init(void)
> +{
> +	mfc_info("%s", banner);
> +	if (platform_driver_register(&s5p_mfc_pdrv) != 0) {
> +		printk(KERN_ERR "platform device registration failed..\n");
> +		return -1;
> +	}
> +	mfc_info("After init...\n");
> +	return 0;
> +}
> +
> +static void __devexit s5p_mfc_exit(void)
> +{
> +	platform_driver_unregister(&s5p_mfc_pdrv);
> +}
> +
> +module_init(s5p_mfc_init);
> +module_exit(s5p_mfc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> new file mode 100644
> index 0000000..4f18937
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> @@ -0,0 +1,190 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * This file contains definitions of enums and structs used by the codec
> + * driver.
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#ifndef S5P_MFC_COMMON_H_
> +#define S5P_MFC_COMMON_H_
> +
> +#include "regs-mfc5.h"
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +
> +#include <media/videobuf2-core.h>
> +
> +#define MFC_MAX_EXTRA_DPB       5
> +#define MFC_MAX_BUFFERS		32
> +#define MFC_FRAME_PLANES	2
> +
> +#define MFC_NUM_CONTEXTS	4
> +/* Interrupt timeout */
> +#define MFC_INT_TIMEOUT		2000
> +/* Busy wait timeout */
> +#define MFC_BW_TIMEOUT		500
> +/* Watchdog interval */
> +#define MFC_WATCHDOG_INTERVAL   1000
> +/* After how many executions watchdog should assume lock up */
> +#define MFC_WATCHDOG_CNT        10
> +
> +
> +/**
> + * enum s5p_mfc_inst_state - The state of an MFC instance.
> + */
> +enum s5p_mfc_inst_state {
> +	MFCINST_FREE = 0,
> +	MFCINST_DEC_INIT = 100,
> +	MFCINST_DEC_GOT_INST,
> +	MFCINST_DEC_HEAD_PARSED,
> +	MFCINST_DEC_BUFS_SET,
> +	MFCINST_DEC_RUNNING,
> +	MFCINST_DEC_FINISHING,
> +	MFCINST_DEC_FINISHED,
> +	MFCINST_DEC_RETURN_INST,
> +	MFCINST_DEC_ERROR,
> +	MFCINST_ENC_INIT = 200,
> +};
> +
> +/**
> + * enum s5p_mfc_queue_state - The state of buffer queue.
> + */
> +enum s5p_mfc_queue_state {
> +	QUEUE_FREE = 0,
> +	QUEUE_BUFS_REQUESTED,
> +	QUEUE_BUFS_QUERIED,
> +	QUEUE_BUFS_MMAPED,
> +};
> +
> +struct s5p_mfc_ctx;
> +
> +/**
> + * struct s5p_mfc_dev - The struct containing driver internal parameters.
> + */
> +struct s5p_mfc_dev {
> +	struct v4l2_device v4l2_dev;
> +	struct video_device *vfd;
> +	struct platform_device *plat_dev;
> +
> +	atomic_t num_inst;
> +	spinlock_t irqlock;
> +	spinlock_t condlock;
> +
> +	void __iomem *regs_base;
> +	int irq;
> +
> +	struct resource *mfc_mem;
> +	void __iomem *base_virt_addr;
> +
> +	struct mutex *mfc_mutex;
> +
> +	int int_cond;
> +	int int_type;
> +	unsigned int int_err;
> +	wait_queue_head_t queue;
> +
> +	size_t port_a;
> +	size_t port_b;
> +
> +	unsigned long hw_lock;
> +
> +	struct clk *clock1;
> +	struct clk *clock2;
> +
> +	struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
> +	int curr_ctx;
> +	unsigned long ctx_work_bits;
> +
> +	atomic_t watchdog_cnt;
> +	struct timer_list watchdog_timer;
> +	struct workqueue_struct *watchdog_workqueue;
> +	struct work_struct watchdog_work;
> +
> +	struct vb2_alloc_ctx **alloc_ctx;
> +};
> +
> +/**
> + * struct s5p_mfc_ctx - This struct contains the instance context
> + */
> +struct s5p_mfc_ctx {
> +	struct s5p_mfc_dev *dev;
> +	int num;
> +
> +	int int_cond;
> +	int int_type;
> +	unsigned int int_err;
> +	wait_queue_head_t queue;
> +
> +	struct s5p_mfc_fmt *fmt;
> +
> +	struct vb2_queue vq_src;
> +	struct vb2_queue vq_dst;
> +
> +	struct list_head src_queue;
> +	struct list_head dst_queue;
> +
> +	unsigned int src_queue_cnt;
> +	unsigned int dst_queue_cnt;
> +
> +	enum s5p_mfc_inst_state state;
> +	int inst_no;
> +
> +	/* Decoder parameters */
> +	int img_width;
> +	int img_height;
> +	int buf_width;
> +	int buf_height;
> +	int dpb_count;
> +	int total_dpb_count;
> +
> +	int luma_size;
> +	int chroma_size;
> +	int mv_size;
> +
> +	unsigned long consumed_stream;
> +	int slice_interface;
> +
> +	/* Buffers */
> +	size_t port_a;
> +	size_t port_a_size;
> +	size_t port_b;
> +	size_t port_b_size;
> +
> +
> +	enum s5p_mfc_queue_state capture_state;
> +	enum s5p_mfc_queue_state output_state;
> +
> +	size_t dec_dst_buf_luma[MFC_MAX_BUFFERS];
> +	size_t dec_dst_buf_chroma[MFC_MAX_BUFFERS];
> +
> +	int dec_dst_buf_cnt;
> +	unsigned int sequence;
> +	unsigned long dec_dst_flag;
> +	size_t dec_src_buf_size;
> +
> +	/* Control values */
> +	int codec_mode;
> +	__u32 pix_format;
> +	int loop_filter_mpeg4;
> +	int display_delay;
> +
> +	/* Buffers */
> +	size_t instance_phys;
> +	size_t instance_size;
> +	size_t desc_phys;
> +	size_t shared_phys;
> +	void *shared_virt;
> +
> +};
> +
> +#endif /* S5P_MFC_COMMON_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> new file mode 100644
> index 0000000..222ed71
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> @@ -0,0 +1,173 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * This file contains description of formats used by MFC and cotrols
> + * used by the driver.
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#ifndef S5P_MFC_CTRLS_H_
> +#define S5P_MFC_CTRLS_H_
> +
> +#include <media/v4l2-ioctl.h>
> +#include "regs-mfc5.h"
> +
> +#define MFC_FMT_DEC	0
> +#define MFC_FMT_ENC	1
> +#define MFC_FMT_RAW	2
> +
> +struct s5p_mfc_fmt {
> +	char *name;
> +	u32 fourcc;
> +	u32 codec_mode;
> +	u32 type;
> +	u32 num_planes;
> +};
> +
> +#define MFC_FORMATS_NO_CODEC -1
> +
> +static struct s5p_mfc_fmt formats[] = {
> +	{
> +	.name = "4:2:0 2 Planes 64x32 Tiles",
> +	.fourcc = V4L2_PIX_FMT_NV12MT,
> +	.codec_mode = MFC_FORMATS_NO_CODEC,
> +	.type = MFC_FMT_RAW,
> +	.num_planes = 2,
> +	 },
> +	{
> +	.name = "4:2:0 2 Planes",
> +	.fourcc = V4L2_PIX_FMT_NV12,
> +	.codec_mode = MFC_FORMATS_NO_CODEC,
> +	.type = MFC_FMT_RAW,
> +	.num_planes = 2,
> +	},
> +	{
> +	.name = "H264 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_H264,
> +	.codec_mode = S5P_FIMV_CODEC_H264_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "H263 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_H263,
> +	.codec_mode = S5P_FIMV_CODEC_H263_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "MPEG1/MPEG2 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_MPEG12,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "MPEG4 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_MPEG4,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 3.11 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX3,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX311_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 4.12 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX4,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX412_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 5.00-5.02 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX500,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX502_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 5.03 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX503,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX503_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "XviD Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_XVID,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "VC1 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_VC1,
> +	.codec_mode = S5P_FIMV_CODEC_VC1_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "VC1 RCV Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_VC1_RCV,
> +	.codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +};
> +
> +#define NUM_FORMATS ARRAY_SIZE(formats)
> +
> +static struct v4l2_queryctrl s5p_mfc_ctrls[] = {
> +/* For decoding */
> +	{
> +	.id = V4L2_CID_CODEC_DISPLAY_DELAY,
> +	.type = V4L2_CTRL_TYPE_INTEGER,
> +	.name = "",
> +	.minimum = 0,
> +	.maximum = 16383,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +	{
> +	.id = V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE,
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.name = "Mpeg4 Loop Filter Enable",
> +	.minimum = 0,
> +	.maximum = 1,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +	{
> +	.id = V4L2_CID_CODEC_SLICE_INTERFACE,
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.name = "Slice Interface Enable",
> +	.minimum = 0,
> +	.maximum = 1,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +};
> +
> +#define NUM_CTRLS ARRAY_SIZE(s5p_mfc_ctrls)
> +
> +#endif /* S5P_MFC_CTRLS_H_ */
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> new file mode 100644
> index 0000000..543f3fb
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> @@ -0,0 +1,77 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
> + *
> + * C file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains functions used to wait for command completion.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/wait.h>
> +#include <linux/sched.h>
> +#include <linux/io.h>
> +#include "regs-mfc5.h"
> +#include "s5p_mfc_intr.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
> +{
> +	if (wait_event_interruptible_timeout(dev->queue,
> +		(dev->int_cond && (dev->int_type == command
> +		|| dev->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +		msecs_to_jiffies(MFC_INT_TIMEOUT)) == 0) {
> +		mfc_err("Interrupt (%d dev) timed out.\n", dev->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (dev->queue, %d).\n", dev->int_type);
> +	if (dev->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}
> +
> +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
> +{
> +	dev->int_cond = 0;
> +	dev->int_type = 0;
> +	dev->int_err = 0;
> +}
> +
> +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> +				    int command, int interrupt)
> +{
> +	int ret;
> +	if (interrupt) {
> +		ret = wait_event_interruptible_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	} else {
> +		ret = wait_event_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	}
> +	if (ret == 0) {
> +		mfc_err("Interrupt (%d ctx) timed out.\n", ctx->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (ctx->queue, %d).\n", ctx->int_type);
> +	if (ctx->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}
> +
> +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
> +{
> +	ctx->int_cond = 0;
> +	ctx->int_type = 0;
> +	ctx->int_err = 0;
> +}
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> new file mode 100644
> index 0000000..8c531b6
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> @@ -0,0 +1,26 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * It contains waiting functions declarations.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef _S5P_MFC_INTR_H_
> +#define _S5P_MFC_INTR_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> +						int command, int interrupt);
> +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
> +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
> +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
> +
> +#endif /* _S5P_MFC_INTR_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> new file mode 100644
> index 0000000..90fa84c
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> @@ -0,0 +1,65 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_logmsg.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains debug macros
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_LOGMSG_H_
> +#define S5P_MFC_LOGMSG_H_
> +
> +/* Debug macros */
> +#define MFC_DEBUG(fmt, ...)					\
> +	do {							\
> +		printk(KERN_ERR					\
> +			"%s: " fmt, __func__, ##__VA_ARGS__);	\
> +	} while (0)
> +
> +#define MFC_ERROR(fmt, ...)					\
> +	do {							\
> +		printk(KERN_ERR					\
> +			"%s: " fmt, __func__, ##__VA_ARGS__);	\
> +	} while (0)
> +
> +#define MFC_NOTICE(fmt, ...)					\
> +	do {							\
> +		printk(KERN_NOTICE				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +#define MFC_INFO(fmt, ...)					\
> +	do {							\
> +		printk(KERN_INFO				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +#define MFC_WARN(fmt, ...)					\
> +	do {							\
> +		printk(KERN_WARNING				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +/* Uncomment the line below do enable debug messages */
> +/* #define CONFIG_VIDEO_MFC50_DEBUG */

why don't you make configuration(CONFIG_VIDEO_MFC51_DEBUG) 
in the Kconfig.

> +
> +#ifdef CONFIG_VIDEO_MFC50_DEBUG
> +#define mfc_debug(fmt, ...)		MFC_DEBUG(fmt, ##__VA_ARGS__)
> +#warning "DEBUG in MFC is switched on!"
> +#else
> +#define mfc_debug(fmt, ...)
> +#endif /* CONFIG_VIDEO_MFC50_DEBUG */
> +
> +#define mfc_err(fmt, ...)		MFC_ERROR(fmt, ##__VA_ARGS__)
> +#define mfc_notice(fmt, ...)		MFC_NOTICE(fmt, ##__VA_ARGS__)
> +#define mfc_info(fmt, ...)		MFC_INFO(fmt, ##__VA_ARGS__)
> +#define mfc_warn(fmt, ...)		MFC_WARN(fmt, ##__VA_ARGS__)
> +
> +#endif /* S5P_MFC_LOGMSG_H_ */
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> new file mode 100644
> index 0000000..a7e4009
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> @@ -0,0 +1,32 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_memory.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * Contains memory related defines.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_MEMORY_H_
> +#define S5P_MFC_MEMORY_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +#define FIRMWARE_CODE_SIZE		0x60000	/* 384KB */
> +#define MFC_H264_INSTANCE_BUF_SIZE	0x96000	/* 600KB per H264
> instance */
> +#define MFC_INSTANCE_BUF_SIZE		0x2800	/* 10KB per
instance */
> +#define DESC_BUF_SIZE			0x20000	/* 128KB for DESC
> buffer */
> +#define SHARED_BUF_SIZE			0x01000	/* 4KB for shared
> buffer */
> +#define CPB_BUF_SIZE			0x400000/* 4MB fr decoder */
> +
> +/* Define names for CMA memory kinds used by MFC */
> +#define MFC_CMA_BANK1		"a"
> +#define MFC_CMA_BANK2		"b"
> +#define MFC_CMA_FW		"f"
> +
> +#endif /* S5P_MFC_MEMORY_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> new file mode 100644
> index 0000000..b6e2334
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> @@ -0,0 +1,800 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
> + *
> + * Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains hw related functions.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/mm.h>
> +#include <linux/io.h>
> +#include <linux/jiffies.h>
> +#include "regs-mfc5.h"
> +
> +#include "s5p_mfc_opr.h"
> +#include "s5p_mfc_common.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_memory.h"
> +
> +#include "s5p_mfc_intr.h"
> +
> +#include <linux/firmware.h>
> +#include <linux/err.h>
> +#include <linux/sched.h>
> +#include <linux/cma.h>
> +
> +static size_t s5p_mfc_phys_bitproc_buff;
> +static unsigned char *s5p_mfc_virt_bitproc_buff;
> +
> +/* #define S5P_MFC_DEBUG_REGWRITE  */
> +#ifdef S5P_MFC_DEBUG_REGWRITE
> +#undef writel
> +#define writel(v, r) do { \
> +	printk(KERN_ERR "MFCWRITE(%p): %08x\n", r, (unsigned int)v); \
> +	__raw_writel(v, r); } while (0)
> +#endif /* S5P_MFC_DEBUG_REGWRITE */
> +
> +#define READL(offset)		readl(dev->regs_base + (offset))
> +#define WRITEL(data, offset)	writel((data), dev->regs_base + (offset))
> +#define OFFSETA(x)		(((x) - dev->port_a) >> 11)
> +#define OFFSETB(x)		(((x) - dev->port_b) >> 11)
> +
> +/* Reset the device */
> +static int s5p_mfc_cmd_reset(struct s5p_mfc_dev *dev)
> +{
> +	unsigned int mc_status;
> +	unsigned long timeout;
> +	mfc_debug("s5p_mfc_cmd_reset++\n");
> +	/* Stop procedure */
> +	WRITEL(0x3f7, S5P_FIMV_SW_RESET);	/*  reset VI */
> +	WRITEL(0x3f6, S5P_FIMV_SW_RESET);	/*  reset RISC */
> +	WRITEL(0x3e2, S5P_FIMV_SW_RESET);	/*  All reset except for MC
> */
> +	mdelay(10);
> +	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
> +	/* Check MC status */
> +	do {
> +		if (time_after(jiffies, timeout)) {
> +			mfc_err("Timeout while resetting MFC.\n");
> +			return -EIO;
> +		}
> +		mc_status = READL(S5P_FIMV_MC_STATUS);
> +	} while (mc_status & 0x3);
> +	WRITEL(0x0, S5P_FIMV_SW_RESET);
> +	WRITEL(0x3fe, S5P_FIMV_SW_RESET);
> +	mfc_debug("s5p_mfc_cmd_reset--\n");
> +	return 0;
> +}
> +
> +/* Send a command to the MFC */
> +static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev,
> +				struct s5p_mfc_ctx *mfc_ctx, int cmd, int
arg)
> +{
> +	int cur_cmd;
> +	unsigned long timeout;
> +	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
> +	/* wait until host to risc command register becomes 'H2R_CMD_EMPTY'
> */
> +	do {
> +		if (time_after(jiffies, timeout)) {
> +			mfc_err("Timeout while waiting for hardware.\n");
> +			return -EIO;
> +		}
> +		cur_cmd = READL(S5P_FIMV_HOST2RISC_CMD);
> +	} while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
> +	WRITEL(arg, S5P_FIMV_HOST2RISC_ARG1);
> +	if (cmd == S5P_FIMV_H2R_CMD_OPEN_INSTANCE) {
> +		/* No CRC calculation (slow!) */
> +		WRITEL(0, S5P_FIMV_HOST2RISC_ARG2);
> +		/* Physical addr of the instance buffer */
> +		WRITEL(OFFSETA(mfc_ctx->instance_phys),
> +		       S5P_FIMV_HOST2RISC_ARG3);
> +		/* Size of the instance buffer */
> +		WRITEL(mfc_ctx->instance_size, S5P_FIMV_HOST2RISC_ARG4);
> +	}
> +	/* Issue the command */
> +	WRITEL(cmd, S5P_FIMV_HOST2RISC_CMD);
> +	return 0;
> +}
> +/*
> +static void s5p_mfc_cmd_sleep()
> +{
> +	WRITEL(-1, S5P_FIMV_CH_ID);
> +	WRITEL(MFC_SLEEP, S5P_FIMV_COMMAND_TYPE);
> +}
> +*/
> +
> +/*
> +static void s5p_mfc_cmd_wakeup()
> +{
> +	WRITEL(-1, S5P_FIMV_CH_ID);
> +	WRITEL(MFC_WAKEUP, S5P_FIMV_COMMAND_TYPE);
> +	mdelay(100);
> +}
> +*/
> +
> +/* Allocate temporary buffers for decoding */
> +int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	void *desc_virt;
> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers++\n");
> +	mfc_ctx->desc_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +					MFC_CMA_BANK1, DESC_BUF_SIZE, 2048);
> +	if (IS_ERR_VALUE(mfc_ctx->desc_phys)) {
> +		mfc_ctx->desc_phys = 0;
> +		mfc_err("Allocating DESC buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	desc_virt = ioremap_nocache(mfc_ctx->desc_phys, DESC_BUF_SIZE);
> +	if (desc_virt == NULL) {
> +		cma_free(mfc_ctx->desc_phys);
> +		mfc_ctx->desc_phys = 0;
> +		mfc_err("Remapping DESC buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset(desc_virt, 0, DESC_BUF_SIZE);
> +	iounmap(desc_virt);
> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers--\n");
> +	return 0;
> +}
> +
> +/* Release temproary buffers for decoding */
> +void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	if (mfc_ctx->desc_phys) {
> +		cma_free(mfc_ctx->desc_phys);
> +		mfc_ctx->desc_phys = 0;
> +	}
> +}
> +
> +/* Allocate decoding buffers */
> +int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	unsigned int luma_size, chroma_size, mv_size;
> +	mfc_debug("s5p_mfc_alloc_dec_buffers++\n");
> +	luma_size = mfc_ctx->luma_size;
> +	chroma_size = mfc_ctx->chroma_size;
> +	mv_size = mfc_ctx->mv_size;
> +	mfc_debug("Luma size: %d Chroma size: %d MV size: %d\n",
> +		  luma_size, chroma_size, mv_size);
> +	/* Codecs have different memory requirements */
> +	switch (mfc_ctx->codec_mode) {
> +	case S5P_FIMV_CODEC_H264_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
> +			     S5P_FIMV_DEC_VERT_NB_MV_SIZE, 8192);
> +		mfc_ctx->port_b_size =
> +		    mfc_ctx->total_dpb_count * ALIGN(mv_size, 8192) * 2;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG4_DEC:
> +	case S5P_FIMV_CODEC_DIVX412_DEC:
> +	case S5P_FIMV_CODEC_DIVX311_DEC:
> +	case S5P_FIMV_CODEC_DIVX502_DEC:
> +	case S5P_FIMV_CODEC_DIVX503_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_STX_PARSER_SIZE +
> +			     S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +
> +	case S5P_FIMV_CODEC_VC1RCV_DEC:
> +	case S5P_FIMV_CODEC_VC1_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_NB_DCAC_SIZE +
> +			     3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +
> +	case S5P_FIMV_CODEC_MPEG2_DEC:
> +		mfc_ctx->port_a_size = 0;
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +	case S5P_FIMV_CODEC_H263_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_NB_DCAC_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* Allocate only if memory from bank 1 is necessary */
> +	if (mfc_ctx->port_a_size > 0) {
> +		mfc_ctx->port_a = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +				MFC_CMA_BANK1, mfc_ctx->port_a_size, 2048);
> +		if (IS_ERR_VALUE(mfc_ctx->port_a)) {
> +			mfc_ctx->port_a = 0;
> +			printk(KERN_ERR
> +			       "Buf alloc for decoding failed (port A).\n");
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	/* Allocate only if memory from bank 2 is necessary */
> +	if (mfc_ctx->port_b_size > 0) {
> +		mfc_ctx->port_b = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +				MFC_CMA_BANK2, mfc_ctx->port_b_size, 2048);
> +		if (IS_ERR_VALUE(mfc_ctx->port_b)) {
> +			mfc_ctx->port_b = 0;
> +			mfc_err("Buf alloc for decoding failed (port
B).\n");
> +			return -ENOMEM;
> +		}
> +	}
> +	mfc_debug("s5p_mfc_alloc_dec_buffers--\n");
> +
> +	return 0;
> +}
> +
> +/* Release buffers allocated for decoding */
> +void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	if (mfc_ctx->port_a) {
> +		cma_free(mfc_ctx->port_a);
> +		mfc_ctx->port_a = 0;
> +		mfc_ctx->port_a_size = 0;
> +	}
> +	if (mfc_ctx->port_b) {
> +		cma_free(mfc_ctx->port_b);
> +		mfc_ctx->port_b = 0;
> +		mfc_ctx->port_b_size = 0;
> +	}
> +}
> +
> +/* Allocate memory for instance data buffer */
> +int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	void *instance_virt;
> +	mfc_debug("s5p_mfc_alloc_instance_buffer++\n");
> +	/* According to EVT1 docu, H264 requires 600KB
> +	 * and all other need onlu 10KB */
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
> +		mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
> +		mfc_ctx->instance_size = MFC_H264_INSTANCE_BUF_SIZE;
> +	else
> +		mfc_ctx->instance_size = MFC_INSTANCE_BUF_SIZE;
> +	mfc_ctx->instance_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
> +				MFC_CMA_BANK1, mfc_ctx->instance_size,
2048);
> +	if (IS_ERR_VALUE(mfc_ctx->instance_phys)) {
> +		mfc_ctx->instance_phys = 0;
> +		mfc_err("Allocating instance buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	instance_virt = ioremap_nocache(mfc_ctx->instance_phys,
> +		mfc_ctx->instance_size);
> +	if (instance_virt == NULL) {
> +		cma_free(mfc_ctx->instance_phys);
> +		mfc_ctx->instance_phys = 0;
> +		mfc_err("Remapping instance buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset(instance_virt, 0, mfc_ctx->instance_size);
> +	iounmap(instance_virt);
> +	mfc_ctx->shared_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
> +					MFC_CMA_BANK1, SHARED_BUF_SIZE,
2048);
> +	if (IS_ERR_VALUE(mfc_ctx->shared_phys)) {
> +		mfc_ctx->shared_phys = 0;
> +		mfc_err("Allocating shared buffer failed\n");
> +		cma_free(mfc_ctx->instance_phys);
> +		return -ENOMEM;
> +	}
> +	mfc_ctx->shared_virt = ioremap_nocache(mfc_ctx->shared_phys,
> +							
SHARED_BUF_SIZE);
> +	if (!mfc_ctx->shared_virt) {
> +		cma_free(mfc_ctx->instance_phys);
> +		cma_free(mfc_ctx->shared_phys);
> +		mfc_ctx->shared_phys = 0;
> +		mfc_ctx->instance_phys = 0;
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset((void *)mfc_ctx->shared_virt, 0, SHARED_BUF_SIZE);
> +	mfc_debug("s5p_mfc_alloc_instance_buffer--\n");
> +	return 0;
> +}
> +
> +/* Release instance buffer */
> +void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	mfc_debug("s5p_mfc_release_instance_buffer++\n");
> +	if (mfc_ctx->instance_phys) {
> +		cma_free(mfc_ctx->instance_phys);
> +		mfc_ctx->instance_phys = 0;
> +	}
> +	if (mfc_ctx->shared_virt) {
> +		iounmap(mfc_ctx->shared_virt);
> +		mfc_ctx->shared_virt = 0;
> +	}
> +	if (mfc_ctx->shared_phys) {
> +		cma_free(mfc_ctx->shared_phys);
> +		mfc_ctx->shared_phys = 0;
> +	}
> +	mfc_debug("s5p_mfc_release_instance_buffer--\n");
> +}
> +
> +/* Set registers for decoding temporary buffers */
> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	WRITEL(OFFSETA(mfc_ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
> +	WRITEL(CPB_BUF_SIZE, S5P_FIMV_SI_CH0_CPB_SIZE);
> +	WRITEL(DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
> +}
> +
> +/* Set registers for decoding stream buffer */
> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
> buf_addr,
> +		  unsigned int start_num_byte, unsigned int buf_size)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("inst_no : %d, buf_addr : 0x%08x, buf_size : 0x%08x
> (%d)\n",
> +		  mfc_ctx->inst_no, buf_addr, buf_size, buf_size);
> +	if (buf_addr & (2048 - 1)) {
> +		mfc_err("Source stream buffer is not aligned correctly.\n");
> +		return -EINVAL;
> +	}
> +	WRITEL(OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
> +	WRITEL(buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
> +	mfc_debug("Shared_virt: %p (start offset: %d)\n", mfc_ctx-
> >shared_virt,
> +		  start_num_byte);
> +	writel(start_num_byte, mfc_ctx->shared_virt \
> +					 + S5P_FIMV_SHARED_START_BYTE_NUM);
> +	return 0;
> +}
> +
> +/* Set decoding frame buffer */
> +int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int do_int)
> +{
> +	unsigned int frame_size, i;
> +	unsigned int frame_size_ch, frame_size_mv;
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	unsigned int dpb;
> +	size_t buf_addr1, buf_addr2;
> +	int buf_size1, buf_size2;
> +	buf_addr1 = mfc_ctx->port_a;
> +	buf_size1 = mfc_ctx->port_a_size;
> +	buf_addr2 = mfc_ctx->port_b;
> +	buf_size2 = mfc_ctx->port_b_size;
> +	mfc_debug("Buf1: %p (%d) Buf2: %p (%d)\n", (void *)buf_addr1,
> buf_size1,
> +		  (void *)buf_addr2, buf_size2);
> +	/* Enable generation of extra info */
> +/*	*(shared_mem_vir_addr + 0x0038) = 63; */
> +	mfc_debug("Total DPB COUNT: %d\n", mfc_ctx->total_dpb_count);
> +	mfc_debug("Setting display delay to %d\n", mfc_ctx->display_delay);
> +	dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & 0xFFFF0000;
> +	WRITEL(mfc_ctx->total_dpb_count | dpb,
> S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
> +	s5p_mfc_set_dec_temp_buffers(mfc_ctx);
> +	switch (mfc_ctx->codec_mode) {
> +	case S5P_FIMV_CODEC_H264_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_IP_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG4_DEC:
> +	case S5P_FIMV_CODEC_DIVX311_DEC:
> +	case S5P_FIMV_CODEC_DIVX412_DEC:
> +	case S5P_FIMV_CODEC_DIVX502_DEC:
> +	case S5P_FIMV_CODEC_DIVX503_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SP_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_H263_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_VC1_DEC:
> +	case S5P_FIMV_CODEC_VC1RCV_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE3_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE2_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE1_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG2_DEC:
> +		break;
> +	default:
> +		mfc_err("Unknown codec set for decoding (%x).\n",
> +			mfc_ctx->codec_mode);
> +		return -EINVAL;
> +		break;
> +	}
> +	frame_size = mfc_ctx->luma_size;
> +	frame_size_ch = mfc_ctx->chroma_size;
> +	frame_size_mv = mfc_ctx->mv_size;
> +	mfc_debug("Frame size: %d ch: %d mv: %d\n", frame_size,
> frame_size_ch,
> +		  frame_size_mv);
> +	for (i = 0; i < mfc_ctx->total_dpb_count; i++) {
> +		/* Port B */
> +		mfc_debug("Luma %d: %x\n", i, mfc_ctx->dec_dst_buf_luma[i]);
> +		WRITEL(OFFSETB(mfc_ctx->dec_dst_buf_luma[i]),
> +		       S5P_FIMV_LUMA_ADR + i * 4);
> +		mfc_debug("\tChroma %d: %x\n", i, \
> +						mfc_ctx-
>dec_dst_buf_chroma[i]);
> +		WRITEL(OFFSETA(mfc_ctx->dec_dst_buf_chroma[i]),
> +		       S5P_FIMV_CHROMA_ADR + i * 4);
> +		if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
> +			mfc_debug("\tBuf2: %x, buf_size2: %d\n", buf_addr2,
> +				  buf_size2);
> +			WRITEL(OFFSETB(buf_addr2), S5P_FIMV_MV_ADR + i * 4);
> +			buf_addr2 += ALIGN(frame_size_mv, 8192);
> +			buf_size2 -= ALIGN(frame_size_mv, 8192);
> +		}
> +	}
> +	mfc_debug("Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
> +	mfc_debug("Buf 1/2 size after: %d/%d (num frames %d)\n", buf_size1,
> +		  buf_size2, mfc_ctx->total_dpb_count);
> +	if (buf_size1 < 0 || buf_size2 < 0) {
> +		mfc_debug("Not enough memory has been allocated.\n");
> +		return -1337;
> +	}
> +	writel(frame_size, mfc_ctx->shared_virt \
> +					+ S5P_FIMV_SHARED_LUMA_DPB_SIZE);
> +	writel(frame_size_ch, mfc_ctx->shared_virt \
> +					+ S5P_FIMV_SHARED_CHROMA_DPB_SIZE);
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
> +		writel(frame_size_mv, mfc_ctx->shared_virt \
> +						+ S5P_FIMV_SHARED_MV_SIZE);
> +	}
> +	if (do_int) {
> +		mfc_debug("Doing int.\n");
> +		WRITEL(((S5P_FIMV_CH_INIT_BUFS << 16) & 0x70000) | \
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +
> +	}
> +	mfc_debug("After setting buffers.\n");
> +	return 0;
> +}
> +
> +/* Allocate firmware */
> +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
> +{
> +	int err;
> +	struct cma_info mem_info_f, mem_info_a, mem_info_b;
> +	mfc_debug("s5p_mfc_alloc_firmware++\n");
> +	if (s5p_mfc_phys_bitproc_buff) {
> +		mfc_err("Attempting to allocate firmware when it seems that"
> \
> +			"it is already loaded.\n");
> +		return -ENOMEM;
> +	}
> +	/* Get memory region information and check if it is correct */
> +	err = cma_info(&mem_info_f, dev->v4l2_dev.dev, MFC_CMA_FW);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"f", mem_info_f.lower_bound, mem_info_f.upper_bound,
> +		mem_info_f.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	err = cma_info(&mem_info_a, dev->v4l2_dev.dev, MFC_CMA_BANK1);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"a", mem_info_a.lower_bound, mem_info_a.upper_bound,
> +		mem_info_a.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	err = cma_info(&mem_info_b, dev->v4l2_dev.dev, MFC_CMA_BANK2);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"b", mem_info_b.lower_bound, mem_info_b.upper_bound,
> +		mem_info_b.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	if (mem_info_f.upper_bound > mem_info_a.lower_bound) {
> +			mfc_err("Firmware has to be allocated before" \
> +			" memory for buffers (bank A).\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("Allocating memory for firmware.\n");
> +	s5p_mfc_phys_bitproc_buff = cma_alloc(dev->v4l2_dev.dev, MFC_CMA_FW,
> +						FIRMWARE_CODE_SIZE, 128 *
1024);
> +	mfc_debug("Phys addr from CMA: %08x\n", s5p_mfc_phys_bitproc_buff);
> +	if (IS_ERR_VALUE(s5p_mfc_phys_bitproc_buff)) {
> +		s5p_mfc_phys_bitproc_buff = 0;
> +		printk(KERN_ERR "Allocating bitprocessor buffer failed\n");
> +		return -ENOMEM;
> +	}
> +	if (s5p_mfc_phys_bitproc_buff & 0x0001FFFF) {
> +		mfc_err("The base memory is not aligned to 128KB.\n");
> +		cma_free(s5p_mfc_phys_bitproc_buff);
> +		return -EIO;
> +	}
> +	dev->port_a = s5p_mfc_phys_bitproc_buff;
> +	dev->port_b = mem_info_b.lower_bound;
> +	mfc_debug("Port A: %08x Port B: %08x (FW: %08x size: %08x)\n",
> +			dev->port_a, dev->port_b, s5p_mfc_phys_bitproc_buff,
> +							FIRMWARE_CODE_SIZE);
> +	s5p_mfc_virt_bitproc_buff =
> ioremap_nocache(s5p_mfc_phys_bitproc_buff,
> +							FIRMWARE_CODE_SIZE);
> +	mfc_debug("Virtual address for FW: %08lx\n",
> +				(long unsigned
int)s5p_mfc_virt_bitproc_buff);
> +	if (!s5p_mfc_virt_bitproc_buff) {
> +		mfc_err("Bitprocessor memory ioremap failed\n");
> +		cma_free(s5p_mfc_phys_bitproc_buff);
> +		s5p_mfc_phys_bitproc_buff = 0;
> +		return -EIO;;
> +	}
> +	return 0;
> +}
> +
> +/* Load firmware to MFC */
> +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
> +{
> +	struct firmware *fw_blob;
> +	int err;
> +	/* Firmare has to be present as a separate file or compiled
> +	 * into kernel. */
> +	mfc_debug("s5p_mfc_load_firmware++\n");
> +	mfc_debug("Requesting fw\n");
> +	err = request_firmware((const struct firmware **)&fw_blob,
> +				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
> +	mfc_debug("Ret of request_firmware: %d Size: %d\n", err, fw_blob-
> >size);
> +	if (err != 0) {
> +		mfc_err("Firmware is not present in the /lib/firmware
> directory"
> +						" nor compiled in
kernel.\n");
> +		return -EINVAL;
> +	}
> +	if (fw_blob->size > FIRMWARE_CODE_SIZE) {
> +		mfc_err("MFC firmware is too big to be loaded.\n");
> +		release_firmware(fw_blob);
> +		return -ENOMEM;
> +	}
> +	if (s5p_mfc_phys_bitproc_buff == 0 || s5p_mfc_phys_bitproc_buff ==
> 0) {
> +		mfc_err("MFC firmware is not allocated or was not mapped "\
>
+								"correctly.\
n");
> +		release_firmware(fw_blob);
> +		return -EINVAL;
> +	}
> +	memcpy(s5p_mfc_virt_bitproc_buff, fw_blob->data, fw_blob->size);
> +/*	If byteswap is needed then use the following instead of memcpy */
> +/*	for (i = 0; i < fw_blob->size; i += 4) {
> +		s5p_mfc_virt_bitproc_buff[i + 0] = fw_blob->data[i + 3];
> +		s5p_mfc_virt_bitproc_buff[i + 1] = fw_blob->data[i + 2];
> +		s5p_mfc_virt_bitproc_buff[i + 2] = fw_blob->data[i + 1];
> +		s5p_mfc_virt_bitproc_buff[i + 3] = fw_blob->data[i + 0];
> +	}*/
> +	release_firmware(fw_blob);
> +	mfc_debug("s5p_mfc_load_firmware--\n");
> +	return 0;
> +}
> +
> +/* Release firmware memory */
> +int s5p_mfc_release_firmware()
> +{
> +	/* Before calling this function one has to make sure
> +	 * that MFC is no longer processing */
> +	if (!s5p_mfc_phys_bitproc_buff)
> +		return -EINVAL;
> +	mfc_debug("s5p_mfc_virt_bitproc_buff: %p\n",
> s5p_mfc_virt_bitproc_buff);
> +	iounmap(s5p_mfc_virt_bitproc_buff);
> +	s5p_mfc_virt_bitproc_buff = NULL;
> +	cma_free(s5p_mfc_phys_bitproc_buff);
> +	s5p_mfc_phys_bitproc_buff = 0;
> +	return 0;
> +}
> +
> +/* Initialize hardware */
> +int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
> +{
> +	int fw_buf_size;
> +	unsigned int fw_version;
> +	int ret;
> +	mfc_debug("s5p_mfc_init_hw++\n");
> +	mfc_debug("Device pointer: %p\n", dev);
> +	if (!s5p_mfc_phys_bitproc_buff)
> +		return -EINVAL;
> +	/* 0. MFC reset */
> +	mfc_debug("MFC reset...\n");
> +	ret = s5p_mfc_cmd_reset(dev);
> +	if (ret) {
> +		mfc_err("Failed to reset MFC - timeout.\n");
> +		return ret;
> +	}
> +	mfc_debug("Done MFC reset...\n");
> +	/* 1. Set DRAM base Addr */
> +	WRITEL(dev->port_a, S5P_FIMV_MC_DRAMBASE_ADR_A); /* channelA, port0
> */
> +	WRITEL(dev->port_b, S5P_FIMV_MC_DRAMBASE_ADR_B); /* channelB, port1
> */
> +	mfc_debug("Port A: %08x, Port B: %08x\n", dev->port_a, dev->port_b);
> +	/* 2. Initialize registers of stream I/F for decoder */
> +	WRITEL(0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
> +	WRITEL(0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
> +	WRITEL(0, S5P_FIMV_RISC2HOST_CMD);
> +	WRITEL(0, S5P_FIMV_HOST2RISC_CMD);
> +	/* 3. Release reset signal to the RISC.  */
> +	WRITEL(0x3ff, S5P_FIMV_SW_RESET);
> +	mfc_debug("Will now wait for completion of firmware transfer.\n");
> +	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET))
> {
> +		mfc_err("Failed to load firmware.\n");
> +		s5p_mfc_clean_dev_int_flags(dev);
> +		return -EIO;
> +	}
> +	s5p_mfc_clean_dev_int_flags(dev);
> +	/* 4. Initialize firmware */
> +	fw_buf_size = FIRMWARE_CODE_SIZE;
> +	mfc_debug("Writing a command\n");
> +	ret = s5p_mfc_cmd_host2risc(dev, 0, S5P_FIMV_H2R_CMD_SYS_INIT,
>
+								fw_buf_size)
;
> +	if (ret) {
> +		mfc_err("Failed to send command to MFC - timeout.\n");
> +		return ret;
> +	}
> +	mfc_debug("Ok, now will write a command to init the system\n");
> +	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET))
> {
> +		mfc_err("Failed to load firmware\n");
> +		return -EIO;
> +	}
> +	dev->int_cond = 0;
> +	if (dev->int_err != 0 || dev->int_type !=
>
+						S5P_FIMV_R2H_CMD_SYS_INIT_RE
T) {
> +		/* Failure. */
> +		mfc_err("Failed to init firmware - error: %d int: %d.\n",
> +			dev->int_err, dev->int_type);
> +		return -EIO;
> +	}
> +	fw_version = READL(S5P_FIMV_FW_VERSION);
> +	mfc_info("MFC FW version : %02xyy, %02xmm, %02xdd\n",
> +			 (fw_version >> 16) & 0xff, (fw_version >> 8) &
0xff,
> +							 (fw_version) &
0xff);
> +	mfc_debug("FW_PHY_BUFFER : 0x%08x\n",
> +		  READL(S5P_FIMV_MC_DRAMBASE_ADR_A));
> +	mfc_debug("DPB_LUMA_BUFFER : 0x%08x\n",
> +		  READL(S5P_FIMV_MC_DRAMBASE_ADR_B));
> +	mfc_debug("s5p_mfc_init_hw--\n");
> +	return 0;
> +}
> +
> +/* Open a new instance and get its number */
> +int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	int ret;
> +	mfc_debug("s5p_mfc_get_inst_no++\n");
> +	mfc_debug("Requested codec mode: %d\n", mfc_ctx->codec_mode);
> +	ret = s5p_mfc_cmd_host2risc(mfc_ctx->dev, mfc_ctx, \
> +			S5P_FIMV_H2R_CMD_OPEN_INSTANCE, mfc_ctx-
>codec_mode);
> +	mfc_debug("s5p_mfc_get_inst_no--\n");
> +	return ret;
> +}
> +
> +/* Close instance */
> +int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	int ret = 0;
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("s5p_mfc_return_inst_no++\n");
> +	if (mfc_ctx->state != MFCINST_FREE) {
> +		ret = s5p_mfc_cmd_host2risc(dev, mfc_ctx,
> +			S5P_FIMV_H2R_CMD_CLOSE_INSTANCE, mfc_ctx->inst_no);
> +	} else {
> +		ret = -EINVAL;
> +	}
> +	mfc_debug("s5p_mfc_return_inst_no--\n");
> +	return ret;
> +}
> +
> +/* Initialize decoding */
> +int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("++\n");
> +
> +	mfc_debug("InstNo: %d/%d\n", mfc_ctx->inst_no,
> S5P_FIMV_CH_SEQ_HEADER);
> +
> +	mfc_debug("BUFs: %08x %08x %08x %08x %08x\n",
> +		  READL(S5P_FIMV_SI_CH0_DESC_ADR),
> +		  READL(S5P_FIMV_SI_CH0_CPB_SIZE),
> +		  READL(S5P_FIMV_SI_CH0_DESC_SIZE),
> +		  READL(S5P_FIMV_SI_CH0_SB_ST_ADR),
> +		  READL(S5P_FIMV_SI_CH0_SB_FRM_SIZE));
> +	/* Setup loop filter, for decoding this is only valid for MPEG4 */
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC) {
> +		mfc_debug("Setting loop filter to: %d\n", \
> +						mfc_ctx->loop_filter_mpeg4);
> +		WRITEL(mfc_ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
> +	} else {
> +		WRITEL(0, S5P_FIMV_ENC_LF_CTRL);
> +	}
> +	WRITEL(((mfc_ctx->slice_interface & 1)<<31) |
> +				((mfc_ctx->display_delay > 0 ? 1 : 0) <<
30) |
> +					((mfc_ctx->display_delay & 0xFF) <<
16),
>
+						S5P_FIMV_SI_CH0_DPB_CONF_CTR
L);
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_DIVX311_DEC) {
> +		mfc_debug("Setting DivX 3.11 resolution to %dx%d\n",
> +			mfc_ctx->img_width, mfc_ctx->img_height);
> +		WRITEL(mfc_ctx->img_width, S5P_FIMV_SI_DIVX311_HRESOL);
> +		WRITEL(mfc_ctx->img_height, S5P_FIMV_SI_DIVX311_VRESOL);
> +	}
> +	WRITEL(((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) | (mfc_ctx-
> >inst_no),
> +						
S5P_FIMV_SI_CH0_INST_ID);
> +	return 0;
> +}
> +
> +/* Decode a single frame */
> +int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int last_frame)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("Setting flags to %08lx (free: %d WTF: %d)\n",
> +				mfc_ctx->dec_dst_flag, mfc_ctx-
>dst_queue_cnt,
> +						mfc_ctx->dec_dst_buf_cnt);
> +	WRITEL(mfc_ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
> +	/* Issue different commands to instance basing on whether it
> +	 * is the last frame or not. */
> +	if (!last_frame)
> +		WRITEL((S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +	else
> +		WRITEL((S5P_FIMV_CH_LAST_FRAME << 16 & 0x70000) |
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +	mfc_debug("Decoding a usual frame.\n");
> +	return 0;
> +}
> +
> +/* Deinitialize hardware */
> +void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
> +{
> +	s5p_mfc_cmd_reset(dev);
> +}
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> new file mode 100644
> index 0000000..162509f
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> @@ -0,0 +1,90 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * Contains declarations of hw related functions.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_OPR_H_
> +#define S5P_MFC_OPR_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_release_firmware(void);
> +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev);
> +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev);
> +int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
> +
> +int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev);
> +int s5p_mfc_set_sleep(struct s5p_mfc_ctx *mfc_ctx);
> +int s5p_mfc_set_wakeup(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int
do_int);
> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
> buf_addr,
> +						  unsigned int
start_num_byte,
> +						  unsigned int buf_size);
> +
> +int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int
last_frame);
> +
> +/* Instance handling */
> +int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx);
> +int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx);
> +
> +/* Memory allocation */
> +int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
> +
> +/* Getting parameters from MFC */
> +#define s5p_mfc_get_h_crop(ctx)		readl((ctx)->shared_virt + \
> +						S5P_FIMV_SHARED_CROP_INFO_H)
> +#define s5p_mfc_get_v_crop(ctx)		readl((ctx)->shared_virt + \
> +						S5P_FIMV_SHARED_CROP_INFO_V)
> +#define s5p_mfc_get_dspl_y_adr()	(readl(dev->regs_base + \
> +					S5P_FIMV_SI_DISPLAY_Y_ADR) << 11)
> +#define s5p_mfc_get_dspl_status()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_DISPLAY_STATUS)
> +#define s5p_mfc_get_frame_type()	(readl(dev->regs_base + \
> +						S5P_FIMV_DECODE_FRAME_TYPE)
\
> +					& S5P_FIMV_DECODE_FRAME_MASK)
> +#define s5p_mfc_get_consumed_stream()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_DEC_FRM_SIZE)
> +#define s5p_mfc_get_int_reason()	(readl(dev->regs_base + \
> +					S5P_FIMV_RISC2HOST_CMD) & 0x1FFFF)
> +#define s5p_mfc_get_int_err()		readl(dev->regs_base + \
> +						S5P_FIMV_RISC2HOST_ARG2)
> +#define s5p_mfc_get_img_width()		readl(dev->regs_base + \
> +						S5P_FIMV_SI_HRESOL)
> +#define s5p_mfc_get_img_height()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_VRESOL)
> +#define s5p_mfc_get_dpb_count()		readl(dev->regs_base + \
> +						S5P_FIMV_SI_BUF_NUMBER)
> +#define s5p_mfc_get_inst_no()		readl(dev->regs_base + \
> +						S5P_FIMV_RISC2HOST_ARG1)
> +#define s5p_mfc_get_pic_time_top(ctx)	readl((ctx)->shared_virt + \
>
+						S5P_FIMV_SHARED_PIC_TIME_TOP
)
> +#define s5p_mfc_get_pic_time_bottom(ctx) readl((ctx)->shared_virt + \
>
+						S5P_FIMV_SHARED_PIC_TIME_BOT
TOM)
> +
> +/* Interrupt handling routines */
> +#define s5p_mfc_clear_int_flags()				\
> +do {								\
> +	writel(0, dev->regs_base + S5P_FIMV_RISC_HOST_INT);	\
> +	writel(0, dev->regs_base + S5P_FIMV_RISC2HOST_CMD);	\
> +	writel(0xffff, dev->regs_base + S5P_FIMV_SI_RTN_CHID);	\
> +} while (0)
> +#endif /* S5P_MFC_OPR_H_ */
> --
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-14  6:25   ` Jaeryul Oh
@ 2010-10-14 12:56     ` Kamil Debski
  2010-10-15  4:27       ` Jaeryul Oh
  0 siblings, 1 reply; 18+ messages in thread
From: Kamil Debski @ 2010-10-14 12:56 UTC (permalink / raw)
  To: jaeryul.oh, linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Hi, Peter.

> I have some first comments about this code.
> and Generally, Making patch set separately will be more helpful to
> everyone.

Thank you for your comments.
 
> k.debski@samsung.com wrote:
> > Multi Format Codec 5.1 is a module available on S5PC110 and S5PC210
> > Samsung SoCs. Hardware is capable of handling a range of video codecs
> > and this driver provides V4L2 interface for video decoding.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

[...]

> >
> > diff --git a/drivers/media/video/Kconfig
> b/drivers/media/video/Kconfig
> > index 6d0bd36..1d0b91e 100644
> > --- a/drivers/media/video/Kconfig
> > +++ b/drivers/media/video/Kconfig
> > @@ -1047,4 +1047,12 @@ config  VIDEO_SAMSUNG_S5P_FIMC
> >  	  This is a v4l2 driver for the S5P camera interface
> >  	  (video postprocessor)
> >
> > +config VIDEO_SAMSUNG_S5P_MFC
> > +	tristate "Samsung S5P MFC 5.0 Video Codec"
> > +	depends on VIDEO_V4L2 && CMA
> > +	select VIDEOBUF2_CMA
> > +	default n
> > +	help
> > +	    MFC 5.0 driver for V4L2.
> > +
> >  endif # V4L_MEM2MEM_DRIVERS
> 
> What about unifying MFC version as a MFC 5.1, because we are using MFC
> HW
> ver.(MFC 5.1.x)
> in the C110/C210 chip.

Right, will fix this.

[...]

> > diff --git a/drivers/media/video/s5p-mfc/regs-mfc5.h
> > b/drivers/media/video/s5p-mfc/regs-mfc5.h
> > new file mode 100644
> > index 0000000..8c628ad
> > --- /dev/null
> > +++ b/drivers/media/video/s5p-mfc/regs-mfc5.h
> > @@ -0,0 +1,305 @@
> > +/*
> > + *
> > + * Register definition file for Samsung MFC V4.0 & V5.0 Interface
> (FIMV)
> > driver
> > + *
> > + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> > + * http://www.samsung.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.
> > +*/
> 
> Incorrect comment in the header, MFC V4.0 is not included
> 

Right again. 

> > +
> > +#ifndef _REGS_FIMV_H
> > +#define _REGS_FIMV_H
> > +
> > +#define S5P_FIMV_REG_SIZE	(S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
> > +#define S5P_FIMV_REG_COUNT	((S5P_FIMV_END_ADDR -
> > S5P_FIMV_START_ADDR) / 4)
> > +
> > +#define S5P_FIMV_START_ADDR	0x0000
> > +#define S5P_FIMV_END_ADDR	0xe008
> > +
> > +#define S5P_FIMV_SW_RESET	0x0000
> > +#define S5P_FIMV_RISC_HOST_INT	0x0008
> > +/* Command from HOST to RISC */
> > +#define S5P_FIMV_HOST2RISC_CMD	0x0030
> > +#define S5P_FIMV_HOST2RISC_ARG1	0x0034
> > +#define S5P_FIMV_HOST2RISC_ARG2	0x0038
> > +#define S5P_FIMV_HOST2RISC_ARG3	0x003c
> > +#define S5P_FIMV_HOST2RISC_ARG4	0x0040
> > +/* Command from RISC to HOST */
> > +#define S5P_FIMV_RISC2HOST_CMD	0x0044
> > +#define S5P_FIMV_RISC2HOST_ARG1	0x0048
> > +#define S5P_FIMV_RISC2HOST_ARG2	0x004c
> > +#define S5P_FIMV_RISC2HOST_ARG3	0x0050
> > +#define S5P_FIMV_RISC2HOST_ARG4	0x0054
> > +
> > +#define S5P_FIMV_FW_VERSION	0x0058
> > +#define S5P_FIMV_SYS_MEM_SZ	0x005c
> > +#define S5P_FIMV_FW_STATUS	0x0080
> > +/* Memory controller register */
> > +#define S5P_FIMV_MC_DRAMBASE_ADR_A	0x0508
> > +#define S5P_FIMV_MC_DRAMBASE_ADR_B	0x050c
> > +#define S5P_FIMV_MC_STATUS	0x0510
> > +
> > +/***** In case of 2 master *****/
> 
> This comment(In case of 2 master) is meaningless. it was used at the
> beginning
> step of development.

Will fix this too.
 
> > +/* Common register */
> > +#define S5P_FIMV_SYS_MEM_ADR	0x0600 /* firmware buffer */
> > +#define S5P_FIMV_CPB_BUF_ADR	0x0604 /* stream buffer */
> > +#define S5P_FIMV_DESC_BUF_ADR	0x0608 /* descriptor buffer */
> > +/* H264 decoding */
> > +#define S5P_FIMV_VERT_NB_MV_ADR	0x068c /* vertical neighbor motion
> > vector */
> > +#define S5P_FIMV_VERT_NB_IP_ADR	0x0690 /* neighbor pixels for intra

[...]

> displayed pic
> > */
> > +#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma address of
> displayed
> > pic */
> > +#define S5P_FIMV_SI_DEC_FRM_SIZE 0x2018 /* the number of frames
> decoded
> > */
 
> S5P_FIMV_SI_DEC_FRM_SIZE does actually means
> "consumed number of bytes to decode a frame"

Ok, I've changed the name of the define to reflect the purpose of the
register.

> > +#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded
> picture */
> > +#define S5P_FIMV_SI_FRAME_TYPE	0x2020 /* frame type such as
> skip/I/P/B
> > */
> > +
> > +#define S5P_FIMV_SI_CH0_SB_ST_ADR	0x2044 /* start addr of
> stream buf
> > */
> > +#define S5P_FIMV_SI_CH0_SB_FRM_SIZE	0x2048 /* size of stream buf
> */
> > +#define S5P_FIMV_SI_CH0_DESC_ADR	0x204c /* addr of descriptor buf
> > */
> > +/* Encoder for MPEG4 */
> > +#define S5P_FIMV_ENC_MPEG4_QUART_PXL	0xe008 /* qpel interpolation
> ctrl
> > */
> > +
> > +/* Additional */
> > +#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL   0x2068 /* DPB Config Control
> > Register */
> > +#define S5P_FIMV_SI_CH0_RELEASE_BUF     0x2060 /* DPB release buffer
> > register */
> > +#define S5P_FIMV_SI_CH0_HOST_WR_ADR	0x2064
> 
> S5P_FIMV_SI_CH0_HOST_WR_ADR means 'address of shared memory'
> if comments is needed

Added the comment.
 
[...]

> > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c
> > b/drivers/media/video/s5p-mfc/s5p_mfc.c
> > new file mode 100644
> > index 0000000..f549ab6
> > --- /dev/null
> > +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c

[...]

> > +
> > +void s5p_mfc_error_cleanup_queue(struct list_head *lh, \
> > +						struct vb2_queue *vq)
> > +{
> > +	struct vb2_buffer *b;
> > +	int i;
> > +	spin_lock(&dev->irqlock);
> > +	while (!list_empty(lh)) {
> > +		b = list_entry(lh->next, struct vb2_buffer, drv_entry);
> > +		for (i = 0; i < b->num_planes; i++)
> > +			vb2_set_plane_payload(b, i, 0);
> > +		spin_unlock(&dev->irqlock);
> > +		vb2_buffer_done(b, VB2_BUF_STATE_ERROR);
> > +		spin_lock(&dev->irqlock);
> > +		list_del(&b->drv_entry);
> > +	}
> > +	spin_unlock(&dev->irqlock);
> > +}
> 
> How about adding new line b/w local var defintions & running code thru
> whole function for readability.

Ok, can do that.

> 
> > +
> > +void s5p_mfc_watchdog(unsigned long arg)
> > +{
> > +	if (test_bit(0, &dev->hw_lock))
> > +		atomic_inc(&dev->watchdog_cnt);
> > +	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
> > +		/* This means that hw is busy and no interrupts were
> > +		 * generated by hw for the Nth time of running this
> > +		 * watchdog timer. This usually means a serious hw
> > +		 * error. Now it is time to kill all instances and
> > +		 * reset the MFC. */
> > +		mfc_err("Time out during waiting for HW.\n");
> > +		queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
> > +	}
> > +	dev->watchdog_timer.expires +=
> > msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
> > +	add_timer(&dev->watchdog_timer);
> > +}
> > +

[...]

> > +			if (mutex_locked)
> > +				mutex_unlock(dev->mfc_mutex);
> > +			return;
> > +		}
> > +	}
> > +	if (mutex_locked)
> > +		mutex_unlock(dev->mfc_mutex);
> > +}
> 
> Does MFC_NUM_CONTEXTS means max num of contexts ? what about
> MFC_MAX_CONTEXT_NUM ?
> and this number is absolutely dependent on what max size used,
> what kinds of codec used, etc so, you had better use configuration
> param.
> for example>
>    #ifdef CONFIG_VIDEO_MFC_MAX_INSTANCE
>    #define MFC_MAX_CONTEXT_NUM (CONFIG_VIDEO_MFC_MAX_INSTANCE)
>    #endif

I don't know if this is necessary. MFC_NUM_CONTEXTS can be fixed at
the maximum number allowed by MFC hw: 16. I highly doubt someone
will open that many contexts. Increasing this number will not
significantly increase storage space used by MFC if no contexts are
used. It will only change size of one pointer array
( struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS]; ).

> 
> > +
> > +/* Check whether a context should be run on hardware */
> > +int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
> > +{
> > +	mfc_debug("s5p_mfc_ctx_ready: src=%d, dst=%d, state=%d\n",
> > +		  ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
> > +	/* Context is to parse header */
> > +	if (ctx->src_queue_cnt >= 1 && ctx->state ==
> MFCINST_DEC_GOT_INST)
> > +		return 1;
> > +	/* Context is to decode a frame */
> > +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_RUNNING
> &&
> > +					ctx->dst_queue_cnt >= ctx-
> >dpb_count)
> > +		return 1;
> > +	/* Context is to return last frame */
> > +	if (ctx->state == MFCINST_DEC_FINISHING &&
> > +	    ctx->dst_queue_cnt >= ctx->dpb_count)
> > +		return 1;
> > +	/* Context is to set buffers */
> > +	if (ctx->src_queue_cnt >= 1 &&
> > +	    ctx->state == MFCINST_DEC_HEAD_PARSED &&
> > +	    ctx->capture_state == QUEUE_BUFS_MMAPED)
> > +		return 1;
> > +	mfc_debug("s5p_mfc_ctx_ready: ctx is not ready.\n");
> > +	return 0;
> > +}
> > +

[...]

> > +/* Reqeust buffers */
> > +static int vidioc_reqbufs(struct file *file, void *priv,
> > +					  struct v4l2_requestbuffers
> *reqbufs)
> > +{
> > +	struct s5p_mfc_ctx *ctx = priv;
> > +	int ret = 0;
> > +	mfc_debug("vidioc_reqbufs++\n");
> > +	mfc_debug("Memory type: %d\n", reqbufs->memory);
> > +	if (reqbufs->memory != V4L2_MEMORY_MMAP) {
> > +		mfc_err("Only V4L2_MEMORY_MAP is supported.\n");
> > +		return -EINVAL;
> > +	}
> > +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> > +		/* Can only request buffers after an instance has been
> > opened.*/
> > +		if (ctx->state == MFCINST_DEC_GOT_INST) {
> > +			/* Decoding */
> > +			if (ctx->output_state != QUEUE_FREE) {
> > +				mfc_err("Bufs have already been
> requested.\n");
> > +				return -EINVAL;
> > +			}
> > +			ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
> > +			if (ret) {
> > +				mfc_err("vb2_reqbufs on output failed.\n");
> > +				return ret;
> > +			}
> > +			mfc_debug("vb2_reqbufs: %d\n", ret);
> > +			ctx->output_state = QUEUE_BUFS_REQUESTED;
> > +		}
> > +	}
> > +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> > +		if (ctx->capture_state != QUEUE_FREE) {
> > +			mfc_err("Bufs have already been requested.\n");
> > +			return -EINVAL;
> > +		}
> > +		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> > +		if (ret) {
> > +			mfc_err("vb2_reqbufs on output failed.\n");
> 
> error message should be mfc_err("vb2_reqbufs on capture failed.\n");

Right.

> 
> > +			return ret;
> > +		}
> > +		if (reqbufs->count < ctx->dpb_count) {
> > +			mfc_err("Not enough buffers allocated.\n");
> > +			reqbufs->count = 0;
> > +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> > +			return -ENOMEM;
> > +		}
> > +		ctx->total_dpb_count = reqbufs->count;
> 
> if 'ctx->total_dpb_count = reqbufs->count;' is here in code, Hmm....
> let's suppose that reqbufs->count = 16 & ctx->dpb_count = 10,
> in that case, ctx->total_dpb_count is 16,
> but *buf_count(in the s5p_mfc_buf_negotiate()) is 15
> so, it is not matched & it affect __vb2_queue_alloc(xx, num_buffers,
> xx)
> i think, is is due to location of 'the ctx->total_dpb_count = reqbufs-
> >count;'
> and No warning message for exceeding MFC_MAX_EXTRA_DPB
> what do you think about that ?

The number of buffers requested by the user is stored in reqbufs->count.
Then vb2_reqbufs() is called, which calls the s5p_mfc_buf_negotiate()
callback function. It may change the number of buffers to allocate.
Then vb2_reqbufs() tries to allocate the number of buffers returned in
*buf_count, but it may allocate less if it runs out of memory. If so it
will adjust reqbufs->count accordingly.

Now the driver has to check if reqbufs->count is less than the minimum 
number of buffers required by MFC. If so it has to free the allocated 
buffers and fail with -ENOMEM, as further processing is impossible.
The number of allocated buffers can be smaller than *buf_count set in 
s5p_mfc_buf_negotiate() yet still enough for MFC to work. So I think
this place to set ctx->total_dpb_count is good.

In the scenario you have given as an example the s5p_mfc_buf_negotiate()
would change the value given by the user application from 16 to 15 and 
this would be the number that vb2_reqbufs tries to allocate. If there 
is little memory then it could allocate only 14 buffers, 15 if there is
enough memory. Then ctx->total_dpb_count would be set to reqbufs->count
after call to vb2_reqbufs (which is 14 or 15 depending on the available
memory).

> > +		ret = s5p_mfc_alloc_dec_buffers(ctx);
> > +		if (ret) {
> > +			mfc_err("Failed to allocate decoding buffers.\n");
> > +			reqbufs->count = 0;
> > +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> > +			return -ENOMEM;
> > +		}
> > +		ctx->capture_state = QUEUE_BUFS_REQUESTED;
> > +	}
> > +	mfc_debug("vidioc_reqbufs--\n");
> > +	return ret;
> > +}
> > +

[...]

> > +/* Handle frame decoding interrupt */
> > +static void s5p_mfc_handle_frame_int(struct s5p_mfc_ctx *ctx, \
> > +					unsigned int reason, unsigned int
> err)
> > +{

[...]

> > +	/* A frame has been decoded and is in the buffer  */
> > +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
> > +	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
> > +		ctx->sequence++;
> > +		/* If frame is same as previous then skip and do not
> dequeue
> > */
> > +		if (dec_frame_type !=  S5P_FIMV_DECODE_FRAME_SKIPPED) {
> > +		/* The MFC returns address of the buffer, now we have to
> > +		 * check which videobuf does it correspond to */
> > +		list_for_each_entry(dst_buf, &ctx->dst_queue, drv_entry) {
> > +			mfc_debug("Listing: %d\n", dst_buf->v4l2_buf.index);
> > +			/* This is the buffer we're looking for */
> > +			mfc_debug("paddr: %p mfc: %p\n",
> > +					(void *)vb2_plane_paddr(dst_buf, 1),
> > +							(void
> *)dst_ret_addr);
> 
> what is the purpose of this debug message ?
> mfc_debug("paddr: %p mfc: %p\n",(void *)vb2_plane_paddr(dst_buf, 1),
>                                                                 (void
> *)dst_ret_addr);
> do you want to compare Y address?
> it should be like this, vb2_plane_paddr(dst_buf, 0) with dst_ret_addr.
> and dst_y_ret_addr is better than dst_ret_addr,
> making debug msg clear is important 'cause codec debugging is not easy.

You're right that this debug is unclear and displays the wrong value.
Changing dst_ret_addr name to dst_ret_addr_y seems very reasonable to
make the code more clear. I should remove many debug messages before
posting the final version. They were very useful during development,
but they may clutter the code too much.
 
> > +			if (vb2_plane_paddr(dst_buf, 0) == dst_ret_addr) {
> > +				list_del(&dst_buf->drv_entry);
> > +				ctx->dst_queue_cnt--;
> > +				mfc_debug("Flag before: %lx (%d)\n",
> > +							ctx->dec_dst_flag,
> > +						dst_buf->v4l2_buf.index);
> > +				dst_buf->v4l2_buf.sequence = ctx->sequence;


[...]

> > +/* Interrupt processing */
> > +static irqreturn_t s5p_mfc_irq(int irq, void *priv)
> > +{
> > +	struct vb2_buffer *src_buf;
> > +	struct s5p_mfc_ctx *ctx;
> > +	unsigned int reason;
> > +	unsigned int err;
> > +	mfc_debug("s5p_mfc_irq++\n");
> > +	/* Reset the timeout watchdog */
> > +	atomic_set(&dev->watchdog_cnt, 0);
> > +	ctx = dev->ctx[dev->curr_ctx];
> > +	/* Get the reason of interrupt and the error code */
> > +	reason = s5p_mfc_get_int_reason();
> > +	err = s5p_mfc_get_int_err();
> > +	mfc_debug("Int reason: %d (error: %08x)\n", reason, err);
> > +	switch (reason) {
> > +	case S5P_FIMV_R2H_CMD_DECODE_ERR_RET:
> > +		/* An error has occured */
> > +		if (ctx->state == MFCINST_DEC_RUNNING && err >= 145)
> 
> What about using MACRO instead of using 145(no)

Yes, will definitely change this.

[...]

> > +/* MFC probe function */
> > +static int s5p_mfc_probe(struct platform_device *pdev)
> > +{
> > +	struct video_device *vfd;
> > +	struct resource *res;
> > +	int ret = -ENOENT;
> > +	size_t size;
> > +	mfc_debug("s5p_mfc_probe++\n");
> > +	dev = kzalloc(sizeof *dev, GFP_KERNEL);
> > +	if (!dev) {
> > +		dev_err(&pdev->dev, "Not enough memoty for MFC device.\n");
> 
> incorrect spelling (memoty)

Yes, thanks.

[...]

> > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> > b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> > new file mode 100644
> > index 0000000..90fa84c
> > --- /dev/null
> > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h

[...]

> > +/* Uncomment the line below do enable debug messages */
> > +/* #define CONFIG_VIDEO_MFC50_DEBUG */
> 
> why don't you make configuration(CONFIG_VIDEO_MFC51_DEBUG)
> in the Kconfig.

I am planning to do this as a module parameter, so it could
be set when loading the module.

Best regards,
Kamil Debski
 


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
  2010-10-14  6:25   ` Jaeryul Oh
@ 2010-10-15  4:06   ` Jaeryul Oh
  2010-10-20 14:25     ` Kamil Debski
  2010-10-18  8:30   ` Jaeryul Oh
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 18+ messages in thread
From: Jaeryul Oh @ 2010-10-15  4:06 UTC (permalink / raw)
  To: 'Kamil Debski', linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Hi, Kamil
This is second feedback about the HW op related code. 
(s5p_mfc_opr.c & s5p_mfc.c)

k.debski@samsung.com wrote:
> Multi Format Codec 5.1 is a module available on S5PC110 and S5PC210
> Samsung SoCs. Hardware is capable of handling a range of video codecs
> and this driver provides V4L2 interface for video decoding.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig                  |    8 +
>  drivers/media/video/Makefile                 |    1 +
>  drivers/media/video/s5p-mfc/Makefile         |    3 +
>  drivers/media/video/s5p-mfc/regs-mfc5.h      |  305 +++++
>  drivers/media/video/s5p-mfc/s5p_mfc.c        | 1879
> ++++++++++++++++++++++++++
>  drivers/media/video/s5p-mfc/s5p_mfc_common.h |  190 +++
>  drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h  |  173 +++
>  drivers/media/video/s5p-mfc/s5p_mfc_intr.c   |   77 ++
>  drivers/media/video/s5p-mfc/s5p_mfc_intr.h   |   26 +
>  drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h |   65 +
>  drivers/media/video/s5p-mfc/s5p_mfc_memory.h |   32 +
>  drivers/media/video/s5p-mfc/s5p_mfc_opr.c    |  800 +++++++++++
>  drivers/media/video/s5p-mfc/s5p_mfc_opr.h    |   90 ++
>  13 files changed, 3649 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/s5p-mfc/Makefile
>  create mode 100644 drivers/media/video/s5p-mfc/regs-mfc5.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_common.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_memory.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 6d0bd36..1d0b91e 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -1047,4 +1047,12 @@ config  VIDEO_SAMSUNG_S5P_FIMC
>  	  This is a v4l2 driver for the S5P camera interface
>  	  (video postprocessor)
> 
> +config VIDEO_SAMSUNG_S5P_MFC
> +	tristate "Samsung S5P MFC 5.0 Video Codec"
> +	depends on VIDEO_V4L2 && CMA
> +	select VIDEOBUF2_CMA
> +	default n
> +	help
> +	    MFC 5.0 driver for V4L2.
> +
>  endif # V4L_MEM2MEM_DRIVERS
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 4146700..117a3cb 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -178,6 +178,7 @@ obj-$(CONFIG_VIDEO_PXA27x)		+=
pxa_camera.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
> +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
> 
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
> 
> diff --git a/drivers/media/video/s5p-mfc/Makefile
> b/drivers/media/video/s5p-mfc/Makefile
> new file mode 100644
> index 0000000..69b6294
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
> +s5p-mfc-y := s5p_mfc.o s5p_mfc_intr.o  s5p_mfc_opr.o
> +
> diff --git a/drivers/media/video/s5p-mfc/regs-mfc5.h
> b/drivers/media/video/s5p-mfc/regs-mfc5.h
> new file mode 100644
> index 0000000..8c628ad
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/regs-mfc5.h
> @@ -0,0 +1,305 @@
> +/*
> + *
> + * Register definition file for Samsung MFC V4.0 & V5.0 Interface (FIMV)
> driver
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> +*/
> +
> +#ifndef _REGS_FIMV_H
> +#define _REGS_FIMV_H
> +
> +#define S5P_FIMV_REG_SIZE	(S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
> +#define S5P_FIMV_REG_COUNT	((S5P_FIMV_END_ADDR -
> S5P_FIMV_START_ADDR) / 4)
> +
> +#define S5P_FIMV_START_ADDR	0x0000
> +#define S5P_FIMV_END_ADDR	0xe008
> +
> +#define S5P_FIMV_SW_RESET	0x0000
> +#define S5P_FIMV_RISC_HOST_INT	0x0008
> +/* Command from HOST to RISC */
> +#define S5P_FIMV_HOST2RISC_CMD	0x0030
> +#define S5P_FIMV_HOST2RISC_ARG1	0x0034
> +#define S5P_FIMV_HOST2RISC_ARG2	0x0038
> +#define S5P_FIMV_HOST2RISC_ARG3	0x003c
> +#define S5P_FIMV_HOST2RISC_ARG4	0x0040
> +/* Command from RISC to HOST */
> +#define S5P_FIMV_RISC2HOST_CMD	0x0044
> +#define S5P_FIMV_RISC2HOST_ARG1	0x0048
> +#define S5P_FIMV_RISC2HOST_ARG2	0x004c
> +#define S5P_FIMV_RISC2HOST_ARG3	0x0050
> +#define S5P_FIMV_RISC2HOST_ARG4	0x0054
> +
> +#define S5P_FIMV_FW_VERSION	0x0058
> +#define S5P_FIMV_SYS_MEM_SZ	0x005c
> +#define S5P_FIMV_FW_STATUS	0x0080
> +/* Memory controller register */
> +#define S5P_FIMV_MC_DRAMBASE_ADR_A	0x0508
> +#define S5P_FIMV_MC_DRAMBASE_ADR_B	0x050c
> +#define S5P_FIMV_MC_STATUS	0x0510
> +
> +/***** In case of 2 master *****/
> +/* Common register */
> +#define S5P_FIMV_SYS_MEM_ADR	0x0600 /* firmware buffer */
> +#define S5P_FIMV_CPB_BUF_ADR	0x0604 /* stream buffer */
> +#define S5P_FIMV_DESC_BUF_ADR	0x0608 /* descriptor buffer */
> +/* H264 decoding */
> +#define S5P_FIMV_VERT_NB_MV_ADR	0x068c /* vertical neighbor motion
> vector */
> +#define S5P_FIMV_VERT_NB_IP_ADR	0x0690 /* neighbor pixels for intra
pred
> */
> +#define S5P_FIMV_H264_LUMA_ADR	0x0700 /* Luma0 ~ Luma18 700 */
> +#define S5P_FIMV_H264_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma18 614
> */
> +#define S5P_FIMV_MV_ADR		0x0780 /* H264 motion vector 660
780 */
> +/* H263/MPEG4/MPEG2/VC-1/ decoding */
> +#define S5P_FIMV_NB_DCAC_ADR	0x068c /* neighbor AC/DC coeff. buffer
> */
> +#define S5P_FIMV_UP_NB_MV_ADR	0x0690 /* upper neighbor motion
vector
> buffer */
> +#define S5P_FIMV_SA_MV_ADR	0x0694 /* subseq. anchor motion vector
> buffer */
> +#define S5P_FIMV_OT_LINE_ADR	0x0698 /* overlap transform line buffer
> */
> +#define S5P_FIMV_BITPLANE3_ADR	0x069C /* bitplane3 addr */
> +#define S5P_FIMV_BITPLANE2_ADR	0x06A0 /* bitplane2 addr */
> +#define S5P_FIMV_BITPLANE1_ADR	0x06A4 /* bitplane1 addr */
> +#define S5P_FIMV_SP_ADR		0x06A8 /* syntax parser addr */
> +#define S5P_FIMV_LUMA_ADR	0x0700 /* Luma0 ~ Luma5 */
> +#define S5P_FIMV_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma5 */
> +/* Encoder register */
> +#define S5P_FIMV_ENC_UP_MV_ADR		0x0600 /* upper motion
vector addr
> */
> +#define S5P_FIMV_ENC_COZERO_FLAG_ADR	0x0610 /* direct cozero flag addr
> */
> +#define S5P_FIMV_ENC_UP_INTRA_MD_ADR	0x0608 /* upper intra MD addr */
> +#define S5P_FIMV_ENC_UP_INTRA_PRED_ADR	0x0740 /* upper intra PRED
addr */
> +#define S5P_FIMV_ENC_NB_DCAC_ADR	0x0604 /* entropy engine's
> neighbor
> +						inform and AC/DC coeff. */
> +
> +#define S5P_FIMV_ENC_CUR_LUMA_ADR	0x0718 /* current Luma addr */
> +#define S5P_FIMV_ENC_CUR_CHROMA_ADR	0x071C /* current Chroma addr */
> +
> +#define S5P_FIMV_ENC_REF0_LUMA_ADR	0x061c /* ref0 Luma addr */
> +#define S5P_FIMV_ENC_REF0_CHROMA_ADR	0x0700 /* ref0 Chroma addr */
> +#define S5P_FIMV_ENC_REF1_LUMA_ADR	0x0620 /* ref1 Luma addr */
> +#define S5P_FIMV_ENC_REF1_CHROMA_ADR	0x0704 /* ref1 Chroma addr */
> +#define S5P_FIMV_ENC_REF2_LUMA_ADR	0x0710 /* ref2 Luma addr */
> +#define S5P_FIMV_ENC_REF2_CHROMA_ADR	0x0708 /* ref2 Chroma addr */
> +#define S5P_FIMV_ENC_REF3_LUMA_ADR	0x0714 /* ref3 Luma addr */
> +#define S5P_FIMV_ENC_REF3_CHROMA_ADR	0x070c /* ref3 Chroma addr */
> +
> +/* Codec common register */
> +#define S5P_FIMV_ENC_HSIZE_PX		0x0818 /* frame width at
encoder
> */
> +#define S5P_FIMV_ENC_VSIZE_PX		0x081c /* frame height at
encoder
> */
> +#define S5P_FIMV_ENC_PROFILE		0x0830 /* profile register */
> +#define S5P_FIMV_ENC_PIC_STRUCT		0x083c /* picture
field/frame flag
> */
> +#define S5P_FIMV_ENC_LF_CTRL		0x0848 /* loop filter control */
> +#define S5P_FIMV_ENC_ALPHA_OFF		0x084c /* loop filter alpha
offset
> */
> +#define S5P_FIMV_ENC_BETA_OFF		0x0850 /* loop filter beta
offset
> */
> +#define S5P_FIMV_MR_BUSIF_CTRL		0x0854 /* hidden, bus
interface
> ctrl */
> +#define S5P_FIMV_ENC_PXL_CACHE_CTRL	0x0a00 /* pixel cache control */
> +
> +/* Channel & stream interface register */
> +#define S5P_FIMV_SI_RTN_CHID	0x2000 /* Return CH instance ID register
> */
> +#define S5P_FIMV_SI_CH0_INST_ID	0x2040 /* codec instance ID */
> +#define S5P_FIMV_SI_CH1_INST_ID	0x2080 /* codec instance ID */
> +/* Decoder */
> +#define S5P_FIMV_SI_VRESOL	0x2004 /* vertical resolution of decoder
> */
> +#define S5P_FIMV_SI_HRESOL	0x2008 /* horizontal resolution of
> decoder */
> +#define S5P_FIMV_SI_BUF_NUMBER	0x200c /* number of frames in the
> decoded pic */
> +#define S5P_FIMV_SI_DISPLAY_Y_ADR 0x2010 /* luma address of displayed pic
> */
> +#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma address of displayed
> pic */
> +#define S5P_FIMV_SI_DEC_FRM_SIZE 0x2018 /* the number of frames decoded
> */
> +#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded picture */
> +#define S5P_FIMV_SI_FRAME_TYPE	0x2020 /* frame type such as
skip/I/P/B
> */
> +
> +#define S5P_FIMV_SI_CH0_SB_ST_ADR	0x2044 /* start addr of stream buf
> */
> +#define S5P_FIMV_SI_CH0_SB_FRM_SIZE	0x2048 /* size of stream buf */
> +#define S5P_FIMV_SI_CH0_DESC_ADR	0x204c /* addr of descriptor buf
> */
> +#define S5P_FIMV_SI_CH0_CPB_SIZE	0x2058 /* max size of coded pic.
> buf */
> +#define S5P_FIMV_SI_CH0_DESC_SIZE	0x205c /* max size of descriptor
> buf */
> +
> +#define S5P_FIMV_SI_CH1_SB_ST_ADR	0x2084 /* start addr of stream buf
> */
> +#define S5P_FIMV_SI_CH1_SB_FRM_SIZE	0x2088 /* size of stream buf */
> +#define S5P_FIMV_SI_CH1_DESC_ADR	0x208c /* addr of descriptor buf
> */
> +#define S5P_FIMV_SI_CH1_CPB_SIZE	0x2098 /* max size of coded pic.
> buf */
> +#define S5P_FIMV_SI_CH1_DESC_SIZE	0x209c /* max size of descriptor
> buf */
> +
> +#define S5P_FIMV_SI_DIVX311_HRESOL	0x2054 /* horizontal resolution */
> +#define S5P_FIMV_SI_DIVX311_VRESOL	0x2050 /* vertical resolution */
> +#define S5P_FIMV_CRC_LUMA0	0x2030 /* luma crc data per frame(top
> field)*/
> +#define S5P_FIMV_CRC_CHROMA0	0x2034 /* chroma crc data per frame(top
> field)*/
> +#define S5P_FIMV_CRC_LUMA1	0x2038 /* luma crc data per bottom field
> */
> +#define S5P_FIMV_CRC_CHROMA1	0x203c /* chroma crc data per bottom
> field */
> +
> +/* Display status */
> +#define S5P_FIMV_DEC_STATUS_DECODING_ONLY		0
> +#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY		1
> +#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY		2
> +#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY		3
> +#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK	7
> +#define S5P_FIMV_DEC_STATUS_PROGRESSIVE			(0<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE			(1<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK		(1<<3)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO		(0<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR		(1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK		(1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_GENERATED		(1<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED		(0<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_MASK			(1<<5)
> +
> +/* Decode frame address */
> +#define S5P_FIMV_DECODE_Y_ADR			0x2024
> +#define S5P_FIMV_DECODE_C_ADR			0x2028
> +
> +/* Decoded frame tpe */
> +#define S5P_FIMV_DECODE_FRAME_TYPE		0x2020
> +#define S5P_FIMV_DECODE_FRAME_MASK		7
> +
> +#define S5P_FIMV_DECODE_FRAME_SKIPPED		0
> +#define S5P_FIMV_DECODE_FRAME_I_FRAME		1
> +#define S5P_FIMV_DECODE_FRAME_P_FRAME		2
> +#define S5P_FIMV_DECODE_FRAME_202_FRAME		3
> +#define S5P_FIMV_DECODE_FRAME_OTHER_FRAME	4
> +
> +/* Sizes of buffers required for decoding */
> +#define S5P_FIMV_DEC_NB_IP_SIZE			(32*1024)
> +#define S5P_FIMV_DEC_VERT_NB_MV_SIZE		(16*1024)
> +#define S5P_FIMV_DEC_NB_DCAC_SIZE		(16*1024)
> +#define S5P_FIMV_DEC_UPNB_MV_SIZE		(68*1024)
> +#define S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE		(136*1024)
> +#define S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE     (32*1024)
> +#define S5P_FIMV_DEC_VC1_BITPLANE_SIZE		(2*1024)
> +#define S5P_FIMV_DEC_STX_PARSER_SIZE		(68*1024)
> +
> +
> +/* Encoder */
> +#define S5P_FIMV_ENC_SI_STRM_SIZE	0x2004 /* stream size */
> +#define S5P_FIMV_ENC_SI_PIC_CNT		0x2008 /* picture count */
> +#define S5P_FIMV_ENC_SI_WRITE_PTR	0x200c /* write pointer */
> +#define S5P_FIMV_ENC_SI_SLICE_TYPE	0x2010 /* slice type(I/P/B/IDR) */
> +
> +#define S5P_FIMV_ENC_SI_CH0_SB_U_ADR	0x2044 /* addr of upper stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH0_SB_L_ADR	0x2048 /* addr of lower stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH0_SB_SIZE	0x204c /* size of stream buf */
> +#define S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR	0x2050 /* current Luma addr
*/
> +#define S5P_FIMV_ENC_SI_CH0_CUR_C_ADR	0x2054 /* current Chroma
addr */
> +#define S5P_FIMV_ENC_SI_CH0_FRAME_QP	0x2058 /* frame QP */
> +#define S5P_FIMV_ENC_SI_CH0_SLICE_ARG	0x205c /* slice argument */
> +
> +#define S5P_FIMV_ENC_SI_CH1_SB_U_ADR	0x2084 /* addr of upper stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH1_SB_L_ADR	0x2088 /* addr of lower stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH1_SB_SIZE	0x208c /* size of stream buf */
> +#define S5P_FIMV_ENC_SI_CH1_CUR_Y_ADR	0x2090 /* current Luma addr
*/
> +#define S5P_FIMV_ENC_SI_CH1_CUR_C_ADR	0x2094 /* current Chroma
addr */
> +#define S5P_FIMV_ENC_SI_CH1_FRAME_QP	0x2098 /* frame QP */
> +#define S5P_FIMV_ENC_SI_CH1_SLICE_ARG	0x209c /* slice argument */
> +
> +#define S5P_FIMV_ENC_STR_BF_U_FULL	0xc004 /* upper stream buf full */
> +#define S5P_FIMV_ENC_STR_BF_U_EMPTY	0xc008 /* upper stream buf empty
> */
> +#define S5P_FIMV_ENC_STR_BF_L_FULL	0xc00c /* lower stream buf full */
> +#define S5P_FIMV_ENC_STR_BF_L_EMPTY	0xc010 /* lower stream buf empty
> */
> +#define S5P_FIMV_ENC_STR_STATUS		0xc018 /* stream buf
interrupt
> status */
> +#define S5P_FIMV_ENC_SF_EPB_ON_CTRL	0xc054 /* stream control */
> +#define S5P_FIMV_ENC_SF_BUF_CTRL	0xc058 /* buffer control */
> +#define S5P_FIMV_ENC_BF_MODE_CTRL	0xc05c /* fifo level control */
> +
> +#define S5P_FIMV_ENC_PIC_TYPE_CTRL	0xc504 /* pic type level control
> */
> +#define S5P_FIMV_ENC_B_RECON_WRITE_ON	0xc508 /* B frame recon
write ctrl
> */
> +#define S5P_FIMV_ENC_MSLICE_CTRL	0xc50c /* multi slice control */
> +#define S5P_FIMV_ENC_MSLICE_MB		0xc510 /* MB number in the
one
> slice */
> +#define S5P_FIMV_ENC_MSLICE_BYTE	0xc514 /* byte number for one
> slice */
> +#define S5P_FIMV_ENC_CIR_CTRL		0xc518 /* number of intra
refresh
> MB */
> +#define S5P_FIMV_ENC_MAP_FOR_CUR	0xc51c /* linear or 64x32 tiled
> mode */
> +#define S5P_FIMV_ENC_PADDING_CTRL	0xc520 /* padding control */
> +#define S5P_FIMV_ENC_INT_MASK		0xc528 /* interrupt mask */
> +
> +#define S5P_FIMV_ENC_RC_CONFIG		0xc5a0 /* RC config */
> +#define S5P_FIMV_ENC_RC_FRAME_RATE	0xc5a4 /* frame rate */
> +#define S5P_FIMV_ENC_RC_BIT_RATE	0xc5a8 /* bit rate */
> +#define S5P_FIMV_ENC_RC_QBOUND		0xc5ac /* max/min QP */
> +#define S5P_FIMV_ENC_RC_RPARA		0xc5b0 /* rate control
reaction
> coeff */
> +#define S5P_FIMV_ENC_RC_MB_CTRL		0xc5b4 /* MB adaptive
scaling */
> +
> +/* Encoder for H264 */
> +#define S5P_FIMV_ENC_ENTRP_MODE		0xd004 /* CAVLC or CABAC */
> +#define S5P_FIMV_ENC_H264_ALPHA_OFF	0xd008 /* loop filter alpha offset
> */
> +#define S5P_FIMV_ENC_H264_BETA_OFF	0xd00c /* loop filter beta offset
> */
> +#define S5P_FIMV_ENC_H264_NUM_OF_REF	0xd010 /* number of reference for
> P/B */
> +#define S5P_FIMV_ENC_H264_MDINTER_WGT	0xd01c /* inter weighted
parameter
> */
> +#define S5P_FIMV_ENC_H264_MDINTRA_WGT	0xd020 /* intra weighted
parameter
> */
> +#define S5P_FIMV_ENC_H264_TRANS_FLAG	0xd034 /* 8x8 transform flag in
> PPS &
> +								high
profile */
> +/* Encoder for MPEG4 */
> +#define S5P_FIMV_ENC_MPEG4_QUART_PXL	0xe008 /* qpel interpolation ctrl
> */
> +
> +/* Additional */
> +#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL   0x2068 /* DPB Config Control
> Register */
> +#define S5P_FIMV_SI_CH0_RELEASE_BUF     0x2060 /* DPB release buffer
> register */
> +#define S5P_FIMV_SI_CH0_HOST_WR_ADR	0x2064
> +
> +/* #define S5P_FIMV_RET_VALUE              0xc08 */
> +#define S5P_FIMV_ENC_B_RECON_WRITE_ON   0xc508 /* B frame recon write
> ctrl */
> +
> +#define S5P_FIMV_ENC_REF_B_LUMA_ADR     0x062c /* ref B Luma addr */
> +#define S5P_FIMV_ENC_REF_B_CHROMA_ADR   0x0630 /* ref B Chroma addr */
> +
> +#define S5P_FIMV_ENCODED_Y_ADDR         0x2014 /* the address of the
> encoded
> +							luminance picture */
> +#define S5P_FIMV_ENCODED_C_ADDR         0x2018 /* the address of the
> encoded
> +							chrominance
picture*/
> +
> +/* Codec numbers  */
> +#define S5P_FIMV_CODEC_H264_DEC			0
> +#define S5P_FIMV_CODEC_VC1_DEC			1
> +#define S5P_FIMV_CODEC_MPEG4_DEC		2
> +#define S5P_FIMV_CODEC_MPEG2_DEC		3
> +#define S5P_FIMV_CODEC_H263_DEC			4
> +#define S5P_FIMV_CODEC_VC1RCV_DEC		5
> +#define S5P_FIMV_CODEC_DIVX311_DEC		6
> +#define S5P_FIMV_CODEC_DIVX412_DEC		7
> +#define S5P_FIMV_CODEC_DIVX502_DEC		8
> +#define S5P_FIMV_CODEC_DIVX503_DEC		9
> +
> +#define S5P_FIMV_CODEC_H264_ENC			16
> +#define S5P_FIMV_CODEC_MPEG4_ENC		17
> +#define S5P_FIMV_CODEC_H263_ENC			18
> +
> +/* Channel Control Register */
> +#define S5P_FIMV_CH_SEQ_HEADER		1
> +#define S5P_FIMV_CH_FRAME_START		2
> +#define S5P_FIMV_CH_LAST_FRAME		3
> +#define S5P_FIMV_CH_INIT_BUFS		4
> +#define S5P_FIMV_CH_FRAME_START_REALLOC	5
> +
> +
> +/* Host to RISC command */
> +#define S5P_FIMV_H2R_CMD_EMPTY		0
> +#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE	1
> +#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE	2
> +#define S5P_FIMV_H2R_CMD_SYS_INIT	3
> +
> +#define S5P_FIMV_R2H_CMD_EMPTY			0
> +#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET	1
> +#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET	2
> +#define S5P_FIMV_R2H_CMD_ERROR_RET		3
> +#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET		4
> +#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET		5
> +#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET		6
> +#define S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET	7
> +#define S5P_FIMV_R2H_CMD_SYS_INIT_RET		8
> +#define S5P_FIMV_R2H_CMD_FW_STATUS_RET		9
> +#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET	15
> +#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET		16
> +#define S5P_FIMV_R2H_CMD_DECODE_ERR_RET		32
> +
> +/* Shared memory registers' offsets */
> +
> +/* An offset of the start position in the stream when
> + * the start position is not aligned */
> +#define S5P_FIMV_SHARED_CROP_INFO_H		0x0020
> +#define S5P_FIMV_SHARED_CROP_INFO_V		0x0024
> +#define S5P_FIMV_SHARED_START_BYTE_NUM		0x0018
> +#define S5P_FIMV_SHARED_LUMA_DPB_SIZE		0x0064
> +#define S5P_FIMV_SHARED_CHROMA_DPB_SIZE		0x0068
> +#define S5P_FIMV_SHARED_MV_SIZE			0x006C
> +#define S5P_FIMV_SHARED_PIC_TIME_TOP		0x0010
> +#define S5P_FIMV_SHARED_PIC_TIME_BOTTOM		0x0014
> +
> +#endif /* _REGS_FIMV_H */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c
> b/drivers/media/video/s5p-mfc/s5p_mfc.c
> new file mode 100644
> index 0000000..f549ab6
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
> @@ -0,0 +1,1879 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#include <linux/io.h>
> +#include <linux/sched.h>
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/version.h>
> +#include <linux/workqueue.h>
> +#include <linux/videodev2.h>
> +#include <media/videobuf2-cma.h>
> +#include <media/videobuf2-core.h>
> +#include "regs-mfc5.h"
> +
> +#include "s5p_mfc_opr.h"
> +#include "s5p_mfc_intr.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_memory.h"
> +#include "s5p_mfc_ctrls.h"
> +
> +#define S5P_MFC_NAME	"s5p-mfc5"
> +
> +/* Offset base used to differentiate between CAPTURE and OUTPUT
> +*  while mmaping */
> +#define DST_QUEUE_OFF_BASE      (TASK_SIZE / 2)
> +
> +struct s5p_mfc_dev *dev;
> +static const char *s5p_mem_types[] = {"b", "a"};
> +static unsigned long s5p_mem_alignments[] = {8192, 8192};
> +
> +/* Function prototypes */
> +static void s5p_mfc_try_run(void);
> +
> +/* Helper functions for interrupt processing */
> +/* Remove from hw execution round robin */
> +static inline void clear_work_bit(struct s5p_mfc_ctx *ctx)
> +{
> +	spin_lock(&dev->condlock);
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	spin_unlock(&dev->condlock);
> +}
> +
> +/* Wake up context wait_queue */
> +static inline void wake_up_ctx(struct s5p_mfc_ctx *ctx,
> +	unsigned int reason, unsigned int err)
> +{
> +	ctx->int_cond = 1;
> +	ctx->int_type = reason;
> +	ctx->int_err = err;
> +	wake_up_interruptible(&ctx->queue);
> +}
> +
> +/* Wake up device wait_queue */
> +static inline void wake_up_dev(unsigned int reason, unsigned int err)
> +{
> +	dev->int_cond = 1;
> +	dev->int_type = reason;
> +	dev->int_err = err;
> +	wake_up_interruptible(&dev->queue);
> +}
> +
> +void s5p_mfc_error_cleanup_queue(struct list_head *lh, \
> +						struct vb2_queue *vq)
> +{
> +	struct vb2_buffer *b;
> +	int i;
> +	spin_lock(&dev->irqlock);
> +	while (!list_empty(lh)) {
> +		b = list_entry(lh->next, struct vb2_buffer, drv_entry);
> +		for (i = 0; i < b->num_planes; i++)
> +			vb2_set_plane_payload(b, i, 0);
> +		spin_unlock(&dev->irqlock);
> +		vb2_buffer_done(b, VB2_BUF_STATE_ERROR);
> +		spin_lock(&dev->irqlock);
> +		list_del(&b->drv_entry);
> +	}
> +	spin_unlock(&dev->irqlock);
> +}
> +
> +void s5p_mfc_watchdog(unsigned long arg)
> +{
> +	if (test_bit(0, &dev->hw_lock))
> +		atomic_inc(&dev->watchdog_cnt);
> +	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
> +		/* This means that hw is busy and no interrupts were
> +		 * generated by hw for the Nth time of running this
> +		 * watchdog timer. This usually means a serious hw
> +		 * error. Now it is time to kill all instances and
> +		 * reset the MFC. */
> +		mfc_err("Time out during waiting for HW.\n");
> +		queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
> +	}
> +	dev->watchdog_timer.expires +=
> msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
> +	add_timer(&dev->watchdog_timer);
> +}
> +
> +static void s5p_mfc_watchdog_worker(struct work_struct *work)
> +{
> +	struct s5p_mfc_ctx *ctx;
> +	int i, ret;
> +	int mutex_locked;
> +	mfc_err("Driver timeout error handling.\n");
> +	/* Lock the mutex that protects open and release.
> +	 * This is necessary as they may load and unload firmware. */
> +	mutex_locked = mutex_trylock(dev->mfc_mutex);
> +	if (!mutex_locked)
> +		mfc_err("Ok. This is not good. Some instance may be " \
>
+							"closing/opening.\n"
);
> +	clk_disable(dev->clock1);
> +	clk_disable(dev->clock2);
> +	spin_lock(&dev->irqlock);
> +	for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
> +		ctx = dev->ctx[i];
> +		if (ctx) {
> +			ctx->state = MFCINST_DEC_ERROR;
> +			spin_unlock(&dev->irqlock);
> +			s5p_mfc_error_cleanup_queue(&ctx->dst_queue,
> +				&ctx->vq_dst);
> +			s5p_mfc_error_cleanup_queue(&ctx->src_queue,
> +				&ctx->vq_src);
> +			spin_lock(&dev->irqlock);
> +			clear_work_bit(ctx);
> +			wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_DECODE_ERR_RET,
0);
> +		}
> +	}
> +	clear_bit(0, &dev->hw_lock);
> +	spin_unlock(&dev->irqlock);
> +	/* Double check if there is at least one instance running.
> +	 * If no instance is in memory than no firmware should be present
> */
> +	if (atomic_read(&dev->num_inst) > 0) {
> +		ret = s5p_mfc_load_firmware(dev);
> +		if (ret != 0) {
> +			mfc_err("Failed to reload FW. This is bad.\n");
> +			if (mutex_locked)
> +				mutex_unlock(dev->mfc_mutex);
> +			return;
> +		}
> +		clk_enable(dev->clock1);
> +		clk_enable(dev->clock2);
> +		ret = s5p_mfc_init_hw(dev);
> +		if (ret != 0) {
> +			mfc_err("Failed to reinitialize FW. This is
bad.\n");
> +			if (mutex_locked)
> +				mutex_unlock(dev->mfc_mutex);
> +			return;
> +		}
> +	}
> +	if (mutex_locked)
> +		mutex_unlock(dev->mfc_mutex);
> +}
> +
> +/* Check whether a context should be run on hardware */
> +int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
> +{
> +	mfc_debug("s5p_mfc_ctx_ready: src=%d, dst=%d, state=%d\n",
> +		  ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
> +	/* Context is to parse header */
> +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_GOT_INST)
> +		return 1;
> +	/* Context is to decode a frame */
> +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_RUNNING &&
> +					ctx->dst_queue_cnt >= ctx-
>dpb_count)
> +		return 1;
> +	/* Context is to return last frame */
> +	if (ctx->state == MFCINST_DEC_FINISHING &&
> +	    ctx->dst_queue_cnt >= ctx->dpb_count)
> +		return 1;
> +	/* Context is to set buffers */
> +	if (ctx->src_queue_cnt >= 1 &&
> +	    ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +	    ctx->capture_state == QUEUE_BUFS_MMAPED)
> +		return 1;
> +	mfc_debug("s5p_mfc_ctx_ready: ctx is not ready.\n");
> +	return 0;
> +}
> +
> +/* Query control */
> +static struct v4l2_queryctrl *get_ctrl(int id)
> +{
> +	int i;
> +	for (i = 0; i < NUM_CTRLS; ++i)
> +		if (id == s5p_mfc_ctrls[i].id)
> +			return &s5p_mfc_ctrls[i];
> +	return NULL;
> +}
> +
> +/* Query capabilities of the device */
> +static int vidioc_querycap(struct file *file, void *priv,
> +			   struct v4l2_capability *cap)
> +{
> +	strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
> +	strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
> +	cap->bus_info[0] = 0;
> +	cap->version = KERNEL_VERSION(1, 0, 0);
> +	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
> +						    | V4L2_CAP_STREAMING;
> +	return 0;
> +}
> +
> +/* Enumerate format */
> +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	int i, j = 0;
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (mplane && formats[i].num_planes == 1)
> +			continue;
> +		else if (!mplane && formats[i].num_planes > 1)
> +			continue;
> +		if (out && formats[i].type != MFC_FMT_RAW)
> +			continue;
> +		else if (!out && formats[i].type != MFC_FMT_DEC)
> +			continue;
> +
> +		if (j == f->index)
> +			break;
> +		++j;
> +	}
> +	if (i == ARRAY_SIZE(formats))
> +		return -EINVAL;
> +	fmt = &formats[i];
> +	strlcpy(f->description, fmt->name, sizeof(f->description));
> +	f->pixelformat = fmt->fourcc;
> +	return 0;
> +}
> +
> +static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, false, false);
> +}
> +
> +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, true, false);
> +}
> +
> +static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, false, true);
> +}
> +
> +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, true, true);
> +}
> +
> +/* Get format */
> +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format
> *f)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_g_fmt++\n");
> +	mfc_debug("f->type = %d ctx->state = %d\n", f->type, ctx->state);
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	    ctx->state == MFCINST_DEC_GOT_INST) {
> +		/* If the MFC is parsing the header,
> +		 * so wait until it is finished */
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_wait_for_done_ctx(ctx,
S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
> \
> +									1);
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	    ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +	    ctx->state < MFCINST_ENC_INIT) {
> +		/* This is run on CAPTURE (deocde output) */
> +		/* Width and height are set to the dimensions
> +		   of the movie, the buffer is bigger and
> +		   further processing stages should crop to this
> +		   rectangle. */
> +		f->fmt.pix_mp.width = ctx->buf_width;
> +		f->fmt.pix_mp.height = ctx->buf_height;
> +		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> +		f->fmt.pix_mp.num_planes = 2;
> +		/* Set pixelformat to the format in which MFC
> +		   outputs the decoded frame */
> +		f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx->buf_width;
> +		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx->luma_size;
> +		f->fmt.pix_mp.plane_fmt[1].bytesperline = ctx->buf_width;
> +		f->fmt.pix_mp.plane_fmt[1].sizeimage = ctx->chroma_size;
> +	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		/* This is run on OUTPUT
> +		   The buffer contains compressed image
> +		   so width and height have no meaning */
> +		f->fmt.pix_mp.width = 1;
> +		f->fmt.pix_mp.height = 1;
> +		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx-
> >dec_src_buf_size;
> +		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx-
>dec_src_buf_size;
> +		f->fmt.pix_mp.pixelformat = ctx->fmt->fourcc;
> +		f->fmt.pix_mp.num_planes = ctx->fmt->num_planes;
> +	} else {
> +		mfc_err("Format could not be read\n");
> +		mfc_debug("vidioc_g_fmt-- (error)\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_g_fmt--\n");
> +	return 0;
> +}
> +
> +/* Find selected format description */
> +static struct s5p_mfc_fmt *find_format(struct v4l2_format *f)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	unsigned int i;
> +	for (i = 0; i < NUM_FORMATS; ++i) {
> +		fmt = &formats[i];
> +		if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
> +			break;
> +	}
> +	if (i == NUM_FORMATS)
> +		return NULL;
> +	return fmt;
> +}
> +
> +/* Try format */
> +static int vidioc_try_fmt(struct file *file, void *priv, struct
> v4l2_format *f)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	mfc_debug("Type is %d\n", f->type);
> +	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_err("Currently only decoding is supported.\n");
> +		return -EINVAL;
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		fmt = find_format(f);
> +		if (!fmt) {
> +			mfc_err("Unsupported format.\n");
> +			return -EINVAL;
> +		}
> +		if (fmt->type != MFC_FMT_DEC) {
> +			mfc_err("\n");
> +			return -EINVAL;
> +		}
> +		if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) {
> +			mfc_err("Application is required to specify"
> +				" input buffer size (via sizeimage)\n");
> +			return -EINVAL;
> +		}
> +		/* As this buffer will contain compressed data, the size is
> set
> +		 * to the maximum size.
> +		 * Width and height are left intact as they may be relevant
> for
> +		 * DivX 3.11 decoding. */
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline =
> +					f-
>fmt.pix_mp.plane_fmt[0].sizeimage;
> +	}
> +	return 0;
> +}
> +
> +/* Set format */
> +static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format
> *f)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	unsigned long flags;
> +	int ret = 0;
> +	struct s5p_mfc_fmt *fmt;
> +	mfc_debug("vidioc_s_fmt++\n");
> +	ret = vidioc_try_fmt(file, priv, f);
> +	if (ret)
> +		return ret;
> +	mutex_lock(&ctx->vq_src.vb_lock);
> +	mutex_lock(&ctx->vq_dst.vb_lock);
> +	if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
> +		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		fmt = find_format(f);
> +		if (!fmt || fmt->codec_mode == MFC_FORMATS_NO_CODEC) {
> +			mfc_err("Unknown codec.\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		if (fmt->type != MFC_FMT_DEC) {
> +			mfc_err("Wrong format selected - you should choose"
\
> +						" format for decoding.\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		ctx->fmt = fmt;
> +		ctx->codec_mode = fmt->codec_mode;
> +		mfc_debug("The codec number is: %d\n", ctx->codec_mode);
> +		ctx->pix_format = f->fmt.pix_mp.pixelformat;
> +		if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_DIVX3) {
> +			f->fmt.pix_mp.height = 1;
> +			f->fmt.pix_mp.width = 1;
> +		} else {
> +			ctx->img_height = f->fmt.pix_mp.height;
> +			ctx->img_width = f->fmt.pix_mp.width;
> +		}
> +		mfc_debug("s_fmt w/h: %dx%d, ctx: %dx%d\n", f-
> >fmt.pix_mp.width,
> +			f->fmt.pix_mp.height, ctx->img_width, ctx-
>img_height);
> +		ctx->dec_src_buf_size =	f-
> >fmt.pix_mp.plane_fmt[0].sizeimage;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
> +		ctx->state = MFCINST_DEC_INIT;
> +		ctx->dec_dst_buf_cnt = 0;
> +		ctx->capture_state = QUEUE_FREE;
> +		ctx->output_state = QUEUE_FREE;
> +		s5p_mfc_alloc_instance_buffer(ctx);
> +		s5p_mfc_alloc_dec_temp_buffers(ctx);
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_try_run();
> +		if (s5p_mfc_wait_for_done_ctx(ctx, \
> +				S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
> +			/* Error or timeout */
> +			mfc_err("Error getting instance from hardware.\n");
> +			s5p_mfc_release_instance_buffer(ctx);
> +			s5p_mfc_release_dec_buffers(ctx);
> +			ret = -EAGAIN;
> +			goto out;
> +		}
> +		mfc_debug("Got instance number: %d\n", ctx->inst_no);
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_err("Currently only decoding is supported.\n");
> +		ret = -EINVAL;
> +	}
> +out:
> +	mutex_unlock(&ctx->vq_dst.vb_lock);
> +	mutex_unlock(&ctx->vq_src.vb_lock);
> +	mfc_debug("vidioc_s_fmt--\n");
> +	return ret;
> +}
> +
> +/* Reqeust buffers */
> +static int vidioc_reqbufs(struct file *file, void *priv,
> +					  struct v4l2_requestbuffers
*reqbufs)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = 0;
> +	mfc_debug("vidioc_reqbufs++\n");
> +	mfc_debug("Memory type: %d\n", reqbufs->memory);
> +	if (reqbufs->memory != V4L2_MEMORY_MMAP) {
> +		mfc_err("Only V4L2_MEMORY_MAP is supported.\n");
> +		return -EINVAL;
> +	}
> +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		/* Can only request buffers after an instance has been
> opened.*/
> +		if (ctx->state == MFCINST_DEC_GOT_INST) {
> +			/* Decoding */
> +			if (ctx->output_state != QUEUE_FREE) {
> +				mfc_err("Bufs have already been
requested.\n");
> +				return -EINVAL;
> +			}
> +			ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
> +			if (ret) {
> +				mfc_err("vb2_reqbufs on output failed.\n");
> +				return ret;
> +			}
> +			mfc_debug("vb2_reqbufs: %d\n", ret);
> +			ctx->output_state = QUEUE_BUFS_REQUESTED;
> +		}
> +	}
> +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		if (ctx->capture_state != QUEUE_FREE) {
> +			mfc_err("Bufs have already been requested.\n");
> +			return -EINVAL;
> +		}
> +		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +		if (ret) {
> +			mfc_err("vb2_reqbufs on output failed.\n");
> +			return ret;
> +		}
> +		if (reqbufs->count < ctx->dpb_count) {
> +			mfc_err("Not enough buffers allocated.\n");
> +			reqbufs->count = 0;
> +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +			return -ENOMEM;
> +		}
> +		ctx->total_dpb_count = reqbufs->count;
> +		ret = s5p_mfc_alloc_dec_buffers(ctx);
> +		if (ret) {
> +			mfc_err("Failed to allocate decoding buffers.\n");
> +			reqbufs->count = 0;
> +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +			return -ENOMEM;
> +		}
> +		ctx->capture_state = QUEUE_BUFS_REQUESTED;
> +	}
> +	mfc_debug("vidioc_reqbufs--\n");
> +	return ret;
> +}
> +
> +/* Query buffer */
> +static int vidioc_querybuf(struct file *file, void *priv,
> +						   struct v4l2_buffer *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	int i;
> +	mfc_debug("vidioc_querybuf++\n");
> +	if (buf->memory != V4L2_MEMORY_MMAP) {
> +		mfc_err("Only mmaped buffers can be used.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("State: %d, buf->type: %d\n", ctx->state, buf->type);
> +	if (ctx->state == MFCINST_DEC_GOT_INST &&
> +			buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		ret = vb2_querybuf(&ctx->vq_src, buf);
> +	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +			buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		ret = vb2_querybuf(&ctx->vq_dst, buf);
> +		for (i = 0; i < buf->length; i++)
> +			buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
> +	} else {
> +		mfc_err("vidioc_querybuf called in an inappropriate
> state.\n");
> +		ret = -EINVAL;
> +	}
> +	mfc_debug("vidioc_querybuf--\n");
> +	return ret;
> +}
> +
> +/* Queue a buffer */
> +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer
> *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_qbuf++\n");
> +	mfc_debug("Enqueued buf: %d (type = %d)\n", buf->index, buf->type);
> +	if (ctx->state == MFCINST_DEC_ERROR) {
> +		mfc_err("Call on QBUF after unrecoverable error.\n");
> +		return -EIO;
> +	}
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		return vb2_qbuf(&ctx->vq_src, buf);
> +	else
> +		return vb2_qbuf(&ctx->vq_dst, buf);
> +	mfc_debug("vidioc_qbuf--\n");
> +	return -EINVAL;
> +}
> +
> +/* Dequeue a buffer */
> +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer
> *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	mfc_debug("vidioc_dqbuf++\n");
> +	mfc_debug("Addr: %p %p %p Type: %d\n", &ctx->vq_src, buf, buf-
> >m.planes,
> +								  buf-
>type);
> +	if (ctx->state == MFCINST_DEC_ERROR) {
> +		mfc_err("Call on DQBUF after unrecoverable error.\n");
> +		return -EIO;
> +	}
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		ret = vb2_dqbuf(&ctx->vq_src, buf,
> +				      file->f_flags & O_NONBLOCK);
> +	} else {
> +		ret = vb2_dqbuf(&ctx->vq_dst, buf,
> +				      file->f_flags & O_NONBLOCK);
> +	}
> +	mfc_debug("vidioc_dqbuf--\n");
> +	return ret;
> +}
> +
> +/* Stream on */
> +static int vidioc_streamon(struct file *file, void *priv,
> +			   enum v4l2_buf_type type)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = -EINVAL;
> +	unsigned long flags;
> +	mfc_debug("vidioc_streamon++\n");
> +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		ret = vb2_streamon(&ctx->vq_src, type);
> +	else
> +		ret = vb2_streamon(&ctx->vq_dst, type);
> +	mfc_debug("ctx->src_queue_cnt = %d ctx->state = %d "
> +		  "ctx->dst_queue_cnt = %d ctx->dpb_count = %d\n",
> +		  ctx->src_queue_cnt, ctx->state, ctx->dst_queue_cnt,
> +		  ctx->dpb_count);
> +	/* If context is ready then schedule it to run */
> +	if (s5p_mfc_ctx_ready(ctx)) {
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +	}
> +	s5p_mfc_try_run();
> +	mfc_debug("vidioc_streamon--\n");
> +	return ret;
> +}
> +
> +/* Stream off, which equals to a pause */
> +static int vidioc_streamoff(struct file *file, void *priv,
> +			    enum v4l2_buf_type type)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	mfc_debug("vidioc_streamoff++\n");
> +	ret = -EINVAL;
> +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		ret = vb2_streamoff(&ctx->vq_src, type);
> +	else
> +		ret = vb2_streamoff(&ctx->vq_dst, type);
> +	mfc_debug("vidioc_streamoff--\n");
> +	return ret;
> +}
> +
> +/* Query a ctrl */
> +static int vidioc_queryctrl(struct file *file, void *priv,
> +			    struct v4l2_queryctrl *qc)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(qc->id);
> +	if (!c)
> +		return -EINVAL;
> +	*qc = *c;
> +	return 0;
> +}
> +
> +/* Get ctrl */
> +static int vidioc_g_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_g_ctrl++\n");
> +	switch (ctrl->id) {
> +	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
> +		ctrl->value = ctx->loop_filter_mpeg4;
> +		break;
> +	case V4L2_CID_CODEC_DISPLAY_DELAY:
> +		ctrl->value = ctx->display_delay;
> +		break;
> +	case V4L2_CID_CODEC_REQ_NUM_BUFS:
> +		if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +		    ctx->state < MFCINST_ENC_INIT) {
> +			ctrl->value = ctx->dpb_count;
> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> +			/* Should wait for the header to be parsed */
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_wait_for_done_ctx(ctx, \
> +					S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 1);
> +			if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +			    ctx->state < MFCINST_ENC_INIT) {
> +				ctrl->value = ctx->dpb_count;
> +			} else {
> +				v4l2_err(&dev->v4l2_dev,
> +						 "Decoding not
initialised.\n");
> +				return -EINVAL;
> +			}
> +		} else {
> +			v4l2_err(&dev->v4l2_dev,
> +						 "Decoding not
initialised.\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_CODEC_SLICE_INTERFACE:
> +		ctrl->value = ctx->slice_interface;
> +		break;
> +	default:
> +		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_g_ctrl--\n");
> +	return 0;
> +}
> +
> +/* Check whether a ctrl value if correct */
> +static int check_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control
> *ctrl)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(ctrl->id);
> +	if (!c)
> +		return -EINVAL;
> +	if (ctrl->value < c->minimum || ctrl->value > c->maximum
> +	    || (c->step != 0 && ctrl->value % c->step != 0)) {
> +		v4l2_err(&dev->v4l2_dev, "Invalid control value\n");
> +		return -ERANGE;
> +	}
> +	return 0;
> +}
> +
> +/* Set a ctrl */
> +static int vidioc_s_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = 0;
> +	int stream_on;
> +	mfc_debug("vidioc_s_ctrl++\n");
> +	stream_on = ctx->vq_src.streaming || ctx->vq_dst.streaming;
> +	ret = check_ctrl_val(ctx, ctrl);
> +	if (ret != 0)
> +		return ret;
> +	switch (ctrl->id) {
> +	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->loop_filter_mpeg4 = ctrl->value;
> +		break;
> +	case V4L2_CID_CODEC_DISPLAY_DELAY:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->display_delay = ctrl->value;
> +		break;
> +	case V4L2_CID_CODEC_SLICE_INTERFACE:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->slice_interface = ctrl->value;
> +		break;
> +	default:
> +		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_s_ctrl--\n");
> +	return 0;
> +}
> +/* Get cropping information */
> +static int vidioc_g_crop(struct file *file, void *priv,
> +		struct v4l2_crop *cr)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	u32 left, right, top, bottom;
> +	mfc_debug("vidioc_g_crop++\n");
> +	if (ctx->state != MFCINST_DEC_HEAD_PARSED &&
> +	ctx->state != MFCINST_DEC_RUNNING && ctx->state !=
> MFCINST_DEC_FINISHING
> +					&& ctx->state !=
MFCINST_DEC_FINISHED) {
> +			mfc_debug("vidioc_g_crop--(err)\n");
> +			return -EINVAL;
> +		}
> +	if (ctx->fmt->fourcc == V4L2_PIX_FMT_H264) {
> +		left = s5p_mfc_get_h_crop(ctx);
> +		right = left >> 16;
> +		left = left & 0xFFFF;
> +		top = s5p_mfc_get_v_crop(ctx);
> +		bottom = top >> 16;
> +		top = top & 0xFFFF;
> +		cr->c.left = left;
> +		cr->c.top = top;
> +		cr->c.width = ctx->img_width - left - right;
> +		cr->c.height = ctx->img_height - top - bottom;
> +		mfc_debug("Cropping info [h264]: l=%d t=%d w=%d h=%d (r=%d "
> \
> +				"b=%d fw=%d fh=%d\n", left, top, cr-
>c.width, \
> +				cr->c.height, right, bottom, ctx-
>buf_width, \
> +							ctx->buf_height);
> +	} else {
> +		cr->c.left = 0;
> +		cr->c.top = 0;
> +		cr->c.width = ctx->img_width;
> +		cr->c.height = ctx->img_height;
> +		mfc_debug("Cropping info: w=%d h=%d fw=%d fh=%d\n", cr-
> >c.width,
> +				cr->c.height, ctx->buf_width, ctx-
>buf_height);
> +	}
> +	mfc_debug("vidioc_g_crop--\n");
> +	return 0;
> +}
> +
> +/* v4l2_ioctl_ops */
> +static const struct v4l2_ioctl_ops s5p_mfc_ioctl_ops = {
> +	.vidioc_querycap = vidioc_querycap,
> +	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
> +	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
> +	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
> +	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
> +	.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
> +	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
> +	.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
> +	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
> +	.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
> +	.vidioc_reqbufs = vidioc_reqbufs,
> +	.vidioc_querybuf = vidioc_querybuf,
> +	.vidioc_qbuf = vidioc_qbuf,
> +	.vidioc_dqbuf = vidioc_dqbuf,
> +	.vidioc_streamon = vidioc_streamon,
> +	.vidioc_streamoff = vidioc_streamoff,
> +	.vidioc_queryctrl = vidioc_queryctrl,
> +	.vidioc_g_ctrl = vidioc_g_ctrl,
> +	.vidioc_s_ctrl = vidioc_s_ctrl,
> +	.vidioc_g_crop = vidioc_g_crop,
> +};
> +
> +/* Negotiate buffers */
> +static int s5p_mfc_buf_negotiate(struct vb2_queue *vq, unsigned int
> *buf_count,
> +						unsigned int *plane_count)
> +{
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	mfc_debug("s5p_mfc_buf_negotiate++\n");
> +	/* Video output for decoding (source)
> +	 * this can be set after getting an instance */
> +	if (ctx->state == MFCINST_DEC_GOT_INST &&
> +	    vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_debug("setting for VIDEO output\n");
> +		/* A single plane is required for input */
> +		*plane_count = 1;
> +		if (*buf_count < 1)
> +			*buf_count = 1;
> +		if (*buf_count > MFC_MAX_BUFFERS)
> +			*buf_count = MFC_MAX_BUFFERS;
> +	/* Video capture for decoding (destination)
> +	 * this can be set after the header was parsed */
> +	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +		   vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_debug("setting for VIDEO capture\n");
> +		/* Output plane count is 2 - one for Y and one for CbCr */
> +		*plane_count = 2;
> +		/* Setup buffer count */
> +		if (*buf_count < ctx->dpb_count)
> +			*buf_count = ctx->dpb_count;
> +		if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
> +			*buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
> +		if (*buf_count > MFC_MAX_BUFFERS)
> +			*buf_count = MFC_MAX_BUFFERS;
> +	} else {
> +		mfc_err("State seems invalid. State = %d, vq->type = %d\n",
> +							ctx->state, vq-
>type);
> +		return -EINVAL;
> +	}
> +	mfc_debug("%s, buffer count=%d, plane count=%d type=0x%x\n",
> __func__,
> +					*buf_count, *plane_count, vq->type);
> +	mfc_debug("s5p_mfc_buf_negotiate--\n");
> +	return 0;
> +}
> +
> +/* Setup plane */
> +static int s5p_mfc_buf_setup_plane(struct vb2_queue *vq,
> +			   unsigned int plane, unsigned long *plane_size)
> +{
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	mfc_debug("s5p_mfc_buf_setup_plane++\n");
> +	if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +	    vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		switch (plane) {
> +		case 0:
> +			/* Plane 0 is for luma (Y) */
> +			*plane_size = ctx->luma_size;
> +			break;
> +		case 1:
> +			/* Plane 1 is for chroma (C) */
> +			*plane_size = ctx->chroma_size;
> +			break;
> +		default:
> +			mfc_err("%s, invalid plane=%d\n", __func__, plane);
> +			return -EINVAL;
> +		}
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
> +		   ctx->state == MFCINST_DEC_GOT_INST) {
> +		if (plane != 0) {
> +			mfc_err("%s, invalid plane=%d\n", __func__, plane);
> +			return -EINVAL;
> +		}
> +		/* dec_src_buf_size was set in s_fmt */
> +		*plane_size = ctx->dec_src_buf_size;
> +	} else {
> +		mfc_err("Currently only decoding is supported."\
> +						" Decoding not
initalised.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("%s, plane=%d, size=%lu\n", __func__, plane, *plane_size);
> +	mfc_debug("s5p_mfc_buf_setup_plane--\n");
> +	return 0;
> +}
> +
> +/* Prepare a buffer */
> +static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	unsigned int i;
> +	mfc_debug("s5p_mfc_buf_prepare++\n");
> +	BUG_ON(NULL == ctx->fmt);
> +	if (!ctx->fmt) {
> +		mfc_err("Format passed to the function is null.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("Addr: %p (%d)\n", (void *)vb2_plane_paddr(vb, 0),
> +							vb->v4l2_buf.index);
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
> +			mfc_debug("s5p_mfc_buf_prepare--\n");
> +			return 0;
> +		}
> +		for (i = 0; i <= ctx->fmt->num_planes ; i++) {
> +			if (vb2_plane_paddr(vb, i) == 0) {
> +				mfc_err("Plane mem not allocated.\n");
> +				return -EINVAL;
> +			}
> +		}
> +		if (vb2_plane_size(vb, 0) < ctx->luma_size ||
> +			vb2_plane_size(vb, 1) < ctx->chroma_size) {
> +			mfc_err("Plane buffer (CAPTURE) is too small.\n");
> +			return -EINVAL;
> +		}
> +		mfc_debug("Size: 0=%lu 2=%lu\n", vb2_plane_size(vb, 0),
> +							vb2_plane_size(vb,
1));
> +		i = vb->v4l2_buf.index;
> +		ctx->dec_dst_buf_luma[i] = vb2_plane_paddr(vb, 0);
> +		ctx->dec_dst_buf_chroma[i] = vb2_plane_paddr(vb, 1);
> +		ctx->dec_dst_buf_cnt++;
> +		if (ctx->dec_dst_buf_cnt == ctx->total_dpb_count)
> +			ctx->capture_state = QUEUE_BUFS_MMAPED;
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		if (vb2_plane_paddr(vb, 0)  == 0) {
> +			mfc_err("Plane memory not allocated.\n");
> +			return -EINVAL;
> +		}
> +		mfc_debug("Plane size: %ld, ctx->dec_src_buf_size: %d\n",
> +				vb2_plane_size(vb, 0), ctx-
>dec_src_buf_size);
> +		if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
> +			mfc_err("Plane buffer (OUTPUT) is too small.\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		mfc_err("s5p_mfc_buf_prepare: unknown queue type.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("s5p_mfc_buf_prepare--\n");
> +	return 0;
> +}
> +
> +/* Try running an operation on hardware */
> +static void s5p_mfc_try_run()
> +{
> +	struct vb2_buffer *temp_vb;
> +	struct s5p_mfc_ctx *ctx;
> +	int new_ctx;
> +	unsigned long flags;
> +	unsigned int cnt;
> +	unsigned int ret;
> +	mfc_debug("Try run dev: %p\n", dev);
> +	/* Check whether hardware is not running */
> +	if (test_and_set_bit(0, &dev->hw_lock) == 0) {
> +		/* Choose the context to run */
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		mfc_debug("Previos context: %d (bit field %08lx)\n",
> +					  dev->curr_ctx, dev-
>ctx_work_bits);
> +		new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
> +		cnt = 0;
> +		while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
> +			new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
> +			cnt++;
> +			if (cnt > MFC_NUM_CONTEXTS) {
> +				/* No contexts to run */
> +				spin_unlock_irqrestore(&dev->condlock,
flags);
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No ctx is scheduled to be
run.\n");
> +				return;
> +			}
> +		}
> +		mfc_debug("New context: %d\n", new_ctx);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		ctx = dev->ctx[new_ctx];
> +		mfc_debug("Seting new context to %p\n", ctx);
> +		/* Got context to run in ctx */
> +		mfc_debug("ctx->dst_queue_cnt=%d ctx->dpb_count=%d" \
> +				" ctx->src_queue_cnt=%d\n", ctx-
> >dst_queue_cnt,\
> +					ctx->dpb_count, ctx->src_queue_cnt);
> +		mfc_debug("ctx->state=%d\n", ctx->state);
> +		/* Last frame has already been sent to MFC
> +		 * Now obtaining frames from MFC buffer */
> +		if (ctx->state == MFCINST_DEC_FINISHING) {
> +			s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx, 1);
> +		} else if (ctx->state == MFCINST_DEC_RUNNING) {
> +			/* Frames are being decoded */
> +			if (list_empty(&ctx->src_queue)) {
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No source buffers.\n");
> +				return;
> +			}
> +			/* Get the next source buffer */
> +			temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +			mfc_debug("Temp vb: %p\n", temp_vb);
> +			mfc_debug("Src Addr: %08lx\n",
> +						vb2_plane_paddr(temp_vb,
0));
> +			s5p_mfc_set_dec_stream_buffer(ctx, \
> +			      vb2_plane_paddr(temp_vb, 0), 0, \
> +			      temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx,
> +				temp_vb->v4l2_planes[0].bytesused == 0);
> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> +			/* Preparing decoding - getting instance number */
> +			mfc_debug("Getting instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +/*			s5p_mfc_set_dec_temp_buffers(ctx);
> + *			Removed per request by Peter, check if MFC works OK
*/

Yes, you don't have to set s5p_mfc_set_dec_temp_buffers(ctx)at this point
'cause this is only required before parsing header of the stream except for 
setting shared memory, 
so, I think, separating 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from the 
s5p_mfc_set_dec_temp_buffers() is needed. So I mean
remove  'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from 
s5p_mfc_set_dec_temp_buffers(ctx);, then add 'setting
S5P_FIMV_SI_CH0_HOST_WR_ADR'
in the 3 functions (s5p_mfc_set_dec_frame_buffer(),s5p_mfc_init_decode(),
s5p_mfc_decode_one_frame()) 
I also commented suggested loc. of 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR'


> +			ret = s5p_mfc_open_inst(ctx);
> +			if (ret) {
> +				mfc_err("Failed to create a new
instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +		} else if (ctx->state == MFCINST_DEC_RETURN_INST) {
> +			/* Closing decoding instance  */
> +			mfc_debug("Returning instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			ret = s5p_mfc_return_inst_no(ctx);
> +			if (ret) {
> +				mfc_err("Failed to return an instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +
> +		} else if (ctx->state == MFCINST_DEC_GOT_INST) {
> +			/* Initializing decoding - parsing header */
> +			mfc_debug("Preparing to init decoding.\n");
> +			temp_vb = list_entry(ctx->src_queue.next,
> +						struct vb2_buffer,
drv_entry);
> +			s5p_mfc_set_dec_temp_buffers(ctx);
> +			mfc_debug("Header size: %d\n", \
> +					temp_vb->v4l2_planes[0].bytesused);
> +			s5p_mfc_set_dec_stream_buffer(ctx,\
> +					vb2_plane_paddr(temp_vb, 0), 0,
> +					 temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			mfc_debug("paddr: %08x\n", \
> +				(int)phys_to_virt(vb2_plane_paddr(temp_vb,
0)));
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_init_decode(ctx);
> +		} else if (ctx->state == MFCINST_DEC_HEAD_PARSED) {
> +			/* Header was parsed now starting processing
> +			 * First set the output frame buffers
> +			 * s5p_mfc_alloc_dec_buffers(ctx); */
> +			if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
> +				temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +				mfc_debug("Header size: %d\n",
> +					temp_vb->v4l2_planes[0].bytesused);
> +				s5p_mfc_set_dec_stream_buffer(ctx, \
> +					vb2_plane_paddr(temp_vb, 0), 0, \
> +					temp_vb->v4l2_planes[0].bytesused);
> +				dev->curr_ctx = ctx->num;
> +				s5p_mfc_clean_ctx_int_flags(ctx);
> +				s5p_mfc_set_dec_frame_buffer(ctx, 1);
> +			} else {
> +				mfc_err("It seems that not all
destionation" \
> +				    " buffers were mmaped.\nMFC requires
that" \
> +				    " all destination are mmaped before" \
> +				    " starting processing.\n");
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +			}
> +		} else {
> +			/* Free hardware lock */
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
> +				mfc_err("Failed to unlock hardware.\n");
> +				return;
> +			}
> +		}
> +	} else {
> +		/* This is perfectly ok, the scheduled ctx should wait */
> +		mfc_debug("Couldn't lock HW.\n");
> +	}
> +}
> +
> +/* Queue buffer */
> +static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	unsigned long flags;
> +	mfc_debug("s5p_mfc_buf_queue++\n");
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_debug("Src queue: %p\n", &ctx->src_queue);
> +		mfc_debug("Adding to src: %p (%08lx)\n", vb,
> +							vb2_plane_paddr(vb,
0));
> +		list_add_tail(&vb->drv_entry, &ctx->src_queue);
> +		ctx->src_queue_cnt++;
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_debug("Dst queue: %p\n", &ctx->dst_queue);
> +		mfc_debug("Adding to dst: %p (%lx)\n", vb, \
> +						  vb2_plane_paddr(vb, 0));
> +		mfc_debug("ADDING Flag before: %lx (%d)\n", ctx-
> >dec_dst_flag, \
> +							vb->v4l2_buf.index);
> +		/* Mark destination as available for use by MFC */
> +		set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
> +		mfc_debug("ADDING Flag after: %lx\n", ctx->dec_dst_flag);
> +		list_add_tail(&vb->drv_entry, &ctx->dst_queue);
> +		ctx->dst_queue_cnt++;
> +	} else {
> +		mfc_err("Unsupported buffer type (%d)\n", vq->type);
> +	}
> +	if (s5p_mfc_ctx_ready(ctx)) {
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +	}
> +	s5p_mfc_try_run();
> +	mfc_debug("s5p_mfc_buf_queue--\n");
> +}
> +
> +/* Videobuf opts */
> +static struct vb2_ops s5p_mfc_qops = {
> +	.buf_queue = s5p_mfc_buf_queue,
> +	.queue_negotiate = s5p_mfc_buf_negotiate,
> +	.plane_setup = s5p_mfc_buf_setup_plane,
> +	.buf_prepare = s5p_mfc_buf_prepare,
> +};
> +
> +/* Handle frame decoding interrupt */
> +static void s5p_mfc_handle_frame_int(struct s5p_mfc_ctx *ctx, \
> +					unsigned int reason, unsigned int
err)
> +{
> +	size_t dst_ret_addr;
> +	unsigned int dst_frame_status;
> +	unsigned int dec_frame_type;
> +	struct vb2_buffer *src_buf, *dst_buf;
> +	dst_ret_addr = s5p_mfc_get_dspl_y_adr();
> +	dst_frame_status = s5p_mfc_get_dspl_status()
> +				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
> +	mfc_debug("Orig Status: %d\n", s5p_mfc_get_dspl_status());
> +	dec_frame_type = s5p_mfc_get_frame_type();
> +	mfc_debug("Status: %x, addr Y: %08x\n", dst_frame_status,
> dst_ret_addr);
> +	mfc_debug("Decode_y: %08x, frame type: %d\n", dst_ret_addr, \
>
+								dec_frame_ty
pe);
> +	spin_lock(&dev->irqlock);
> +	/* All frames remaining in the buffer have been extracted  */
> +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
> +		ctx->state = MFCINST_DEC_FINISHED;
> +		mfc_debug("Decided to finish\n");
> +		ctx->sequence++;
> +		while (!list_empty(&ctx->dst_queue)) {
> +			dst_buf = list_entry(ctx->dst_queue.next,
> +					     struct vb2_buffer, drv_entry);
> +			mfc_debug("Cleaning up buffer: %d\n",
> +						  dst_buf->v4l2_buf.index);
> +			vb2_set_plane_payload(dst_buf, 0, 0);
> +			vb2_set_plane_payload(dst_buf, 1, 0);
> +			list_del(&dst_buf->drv_entry);
> +			ctx->dst_queue_cnt--;
> +			dst_buf->v4l2_buf.sequence = (ctx->sequence++);
> +			if (s5p_mfc_get_pic_time_top(ctx) ==
> +				s5p_mfc_get_pic_time_bottom(ctx))
> +				dst_buf->v4l2_buf.field = V4L2_FIELD_NONE;
> +			else
> +				dst_buf->v4l2_buf.field =
V4L2_FIELD_INTERLACED;
> +			spin_unlock(&dev->irqlock);
> +			mfc_debug("Finishing Flags before: %08ld\n",
> +							ctx->dec_dst_flag);
> +			ctx->dec_dst_flag &= ~(1 << dst_buf-
>v4l2_buf.index);
> +			mfc_debug("Finishing Flags after: %08ld\n",
> +							ctx->dec_dst_flag);
> +			vb2_buffer_done(dst_buf, VB2_BUF_STATE_DONE);
> +			spin_lock(&dev->irqlock);
> +			mfc_debug("Cleaned up buffer: %d\n",
> +				  dst_buf->v4l2_buf.index);
> +		}
> +		mfc_debug("After cleanup\n");
> +	}
> +
> +	/* A frame has been decoded and is in the buffer  */
> +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
> +	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
> +		ctx->sequence++;
> +		/* If frame is same as previous then skip and do not dequeue
> */
> +		if (dec_frame_type !=  S5P_FIMV_DECODE_FRAME_SKIPPED) {
> +		/* The MFC returns address of the buffer, now we have to
> +		 * check which videobuf does it correspond to */
> +		list_for_each_entry(dst_buf, &ctx->dst_queue, drv_entry) {
> +			mfc_debug("Listing: %d\n", dst_buf->v4l2_buf.index);
> +			/* This is the buffer we're looking for */
> +			mfc_debug("paddr: %p mfc: %p\n",
> +					(void *)vb2_plane_paddr(dst_buf, 1),
> +							(void
*)dst_ret_addr);
> +			if (vb2_plane_paddr(dst_buf, 0) == dst_ret_addr) {
> +				list_del(&dst_buf->drv_entry);
> +				ctx->dst_queue_cnt--;
> +				mfc_debug("Flag before: %lx (%d)\n",
> +							ctx->dec_dst_flag,
> +						dst_buf->v4l2_buf.index);
> +				dst_buf->v4l2_buf.sequence = ctx->sequence;
> +				if (s5p_mfc_get_pic_time_top(ctx) ==
> +					s5p_mfc_get_pic_time_bottom(ctx))
> +					dst_buf->v4l2_buf.field =
>
+								V4L2_FIELD_N
ONE;
> +				else
> +					dst_buf->v4l2_buf.field =
>
+							V4L2_FIELD_INTERLACE
D;
> +				mfc_debug("Field: %d\n",
> +						dst_buf->v4l2_buf.field);
> +				mfc_debug("Freed -> sequence: %d index:
%d\n",
> +						dst_buf->v4l2_buf.field,
> +						dst_buf->v4l2_buf.index);
> +				vb2_set_plane_payload(dst_buf, 0,
> +								ctx-
>luma_size);
> +				vb2_set_plane_payload(dst_buf, 1,
> +							ctx->chroma_size);
> +				spin_unlock(&dev->irqlock);
> +				clear_bit(dst_buf->v4l2_buf.index,
> +							&ctx->dec_dst_flag);
> +				if (err) {
> +					vb2_buffer_done(dst_buf,
>
+							VB2_BUF_STATE_ERROR)
;
> +				} else {
> +					vb2_buffer_done(dst_buf,
> +							VB2_BUF_STATE_DONE);
> +				}
> +				spin_lock(&dev->irqlock);
> +				mfc_debug("Flag after: %lx\n",
> +					  ctx->dec_dst_flag);
> +				break;
> +			}
> +		}
> +		}
> +	} else {
> +		mfc_debug("No frame decode.\n");
> +	}
> +	/* Mark source buffer as complete */
> +	if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
> +		&& !list_empty(&ctx->src_queue)) {
> +		src_buf = list_entry(ctx->src_queue.next, struct vb2_buffer,
> +								drv_entry);
> +		mfc_debug("Packed PB test. Size: %d, prev off:"\
> +			" %ld now con: %d\n", src_buf-
> >v4l2_planes[0].bytesused,
> +			ctx->consumed_stream,
s5p_mfc_get_consumed_stream());
> +		ctx->consumed_stream += s5p_mfc_get_consumed_stream();
> +		if (dec_frame_type == S5P_FIMV_DECODE_FRAME_P_FRAME
> +						&& ctx->consumed_stream <
> +					src_buf->v4l2_planes[0].bytesused) {
> +			/* Run MFC again on the same buffer */
> +			mfc_debug("Running again the same buffer.\n");
> +			s5p_mfc_set_dec_stream_buffer(ctx,
> +						vb2_plane_paddr(src_buf, 0),
> +							ctx-
>consumed_stream,
> +					src_buf->v4l2_planes[0].bytesused -
> +							ctx-
>consumed_stream);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			spin_unlock(&dev->irqlock);
> +			s5p_mfc_clear_int_flags();
> +			wake_up_ctx(ctx, reason, err);
> +			s5p_mfc_decode_one_frame(ctx, 0);
> +			return;
> +		} else {
> +			mfc_debug("MFC needs next buffer..\n");
> +			/* Advance to next buffer */
> +			ctx->consumed_stream = 0;
> +			list_del(&src_buf->drv_entry);
> +			ctx->src_queue_cnt--;
> +			if (vb2_plane_size(src_buf, 0) == 0) {
> +				mfc_debug("Setting ctx->state to FINISH\n");
> +				ctx->state = MFCINST_DEC_FINISHING;
> +			}
> +			spin_unlock(&dev->irqlock);
> +			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
> +
> +		}
> +	} else {
> +		spin_unlock(&dev->irqlock);
> +	}
> +	mfc_debug("Assesing whether this context should be run again.\n");
> +	if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_DEC_FINISHING)
> +				    || ctx->dst_queue_cnt < ctx->dpb_count)
{
> +		mfc_debug("No need to run again.\n");
> +		clear_work_bit(ctx);
> +	}
> +	mfc_debug("After assesing whether this context should be run
> again.\n");
> +	s5p_mfc_clear_int_flags();
> +	wake_up_ctx(ctx, reason, err);
> +	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +		BUG();
> +	s5p_mfc_try_run();
> +}
> +
> +/* Error handling for interrupt */
> +static inline void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
> +	unsigned int reason, unsigned int err)
> +{
> +	mfc_err("Interrupt Error: %08x\n", err);
> +	s5p_mfc_clear_int_flags();
> +	wake_up_dev(reason, err);
> +	/* If no context is available then all necessary
> +	 * processing has been done. */
> +	if (ctx == 0)
> +		return;
> +	/* Error recovery is dependent on the state of context */
> +	switch (ctx->state) {
> +	case MFCINST_DEC_INIT:
> +		/* This error had to happen while acquireing instance */
> +	case MFCINST_DEC_GOT_INST:
> +		/* This error had to happen while parsing the header */
> +	case MFCINST_DEC_HEAD_PARSED:
> +		/* This error had to happen while setting dst buffers */
> +	case MFCINST_DEC_RETURN_INST:
> +		/* This error had to happen while releasing instance */
> +		clear_work_bit(ctx);
> +		wake_up_ctx(ctx, reason, err);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		break;
> +	case MFCINST_DEC_FINISHING:
> +	case MFCINST_DEC_FINISHED:
> +	case MFCINST_DEC_RUNNING:
> +		/* It is higly probable that an error occured
> +		 * while decoding a frame */
> +		clear_work_bit(ctx);
> +		ctx->state = MFCINST_DEC_ERROR;
> +		/* Mark all dst buffers as having an error */
> +		s5p_mfc_error_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
> +		/* Mark all src buffers as having an error */
> +		s5p_mfc_error_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		break;
> +	default:
> +		mfc_err("Encountered an error interrupt which had not been"
> \
> +								"
handled.\n");
> +		break;
> +	}
> +	return;
> +}
> +
> +/* Interrupt processing */
> +static irqreturn_t s5p_mfc_irq(int irq, void *priv)
> +{
> +	struct vb2_buffer *src_buf;
> +	struct s5p_mfc_ctx *ctx;
> +	unsigned int reason;
> +	unsigned int err;
> +	mfc_debug("s5p_mfc_irq++\n");
> +	/* Reset the timeout watchdog */
> +	atomic_set(&dev->watchdog_cnt, 0);
> +	ctx = dev->ctx[dev->curr_ctx];
> +	/* Get the reason of interrupt and the error code */
> +	reason = s5p_mfc_get_int_reason();
> +	err = s5p_mfc_get_int_err();
> +	mfc_debug("Int reason: %d (error: %08x)\n", reason, err);
> +	switch (reason) {
> +	case S5P_FIMV_R2H_CMD_DECODE_ERR_RET:
> +		/* An error has occured */
> +		if (ctx->state == MFCINST_DEC_RUNNING && err >= 145)
> +			s5p_mfc_handle_frame_int(ctx, reason, err);
> +		else
> +			s5p_mfc_handle_error(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
> +	case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
> +		s5p_mfc_handle_frame_int(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
> +		if (ctx->fmt->fourcc != V4L2_PIX_FMT_DIVX3) {
> +			ctx->img_width = s5p_mfc_get_img_width();
> +			ctx->img_height = s5p_mfc_get_img_height();
> +		}
> +		ctx->buf_width = ALIGN(ctx->img_width, 128);
> +		ctx->buf_height = ALIGN(ctx->img_height, 32);
> +		mfc_debug("SEQ Done: ctx:%dx%d, buf:%dx%d\n", ctx-
>img_width,
> +			ctx->img_height, ctx->buf_width, ctx->buf_height);
> +		ctx->luma_size = ALIGN(ctx->buf_width * ctx->buf_height,
> 8192);
> +		ctx->chroma_size = ALIGN(ctx->buf_width *
> +					ALIGN(ctx->img_height / 2, 32),
8192);
> +		if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
> +			ctx->mv_size = ALIGN(ctx->buf_width *
> +					 ALIGN(ctx->buf_height / 4, 32),
8192);
> +		else
> +			ctx->mv_size = 0;
> +		ctx->dpb_count = s5p_mfc_get_dpb_count();
> +		ctx->state = MFCINST_DEC_HEAD_PARSED;
> +		s5p_mfc_clear_int_flags();
> +		clear_work_bit(ctx);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		s5p_mfc_try_run();
> +		wake_up_ctx(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
> +		ctx->inst_no = s5p_mfc_get_inst_no();
> +		ctx->state = MFCINST_DEC_GOT_INST;
> +		clear_work_bit(ctx);
> +		wake_up_interruptible(&ctx->queue);
> +		goto irq_cleanup_hw;
> +		break;
> +	case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
> +		clear_work_bit(ctx);
> +		ctx->state = MFCINST_FREE;
> +		wake_up(&ctx->queue);
> +		goto irq_cleanup_hw;
> +		break;
> +	case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
> +	case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
> +		if (ctx)
> +			clear_work_bit(ctx);
> +		s5p_mfc_clear_int_flags();
> +		wake_up_dev(reason, err);
> +		clear_bit(0, &dev->hw_lock);
> +		break;
> +	case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
> +		s5p_mfc_clear_int_flags();
> +		ctx->int_type = reason;
> +		ctx->int_err = err;
> +		ctx->int_cond = 1;
> +		spin_lock(&dev->condlock);
> +		clear_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock(&dev->condlock);
> +		if (err == 0) {
> +			ctx->state = MFCINST_DEC_RUNNING;
> +			spin_lock(&dev->irqlock);
> +			src_buf =
> +			    list_entry(ctx->src_queue.next,
> +				       struct vb2_buffer, drv_entry);
> +			list_del(&src_buf->drv_entry);
> +			ctx->src_queue_cnt--;
> +			spin_unlock(&dev->irqlock);
> +			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +				BUG();
> +			wake_up_interruptible(&ctx->queue);
> +			s5p_mfc_try_run();
> +		} else {
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +				BUG();
> +			wake_up_interruptible(&ctx->queue);
> +		}
> +		break;
> +	default:
> +		mfc_debug("Unknown int reason.\n");
> +		s5p_mfc_clear_int_flags();
> +	}
> +	mfc_debug("s5p_mfc_irq--\n");
> +	return IRQ_HANDLED;
> +irq_cleanup_hw:
> +	s5p_mfc_clear_int_flags();
> +	ctx->int_type = reason;
> +	ctx->int_err = err;
> +	ctx->int_cond = 1;
> +	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +		mfc_err("Failed to unlock hw.\n");
> +	s5p_mfc_try_run();
> +	mfc_debug("s5p_mfc_irq-- (via irq_cleanup_hw)\n");
> +	return IRQ_HANDLED;
> +}
> +
> +/* Open an MFC node */
> +static int s5p_mfc_open(struct file *file)
> +{
> +	struct s5p_mfc_ctx *ctx = NULL;
> +	unsigned long flags;
> +	int ret = 0;
> +	mfc_debug("s5p_mfc_open++\n");
> +	mutex_lock(dev->mfc_mutex);
> +	atomic_inc(&dev->num_inst);	/* It is guarded by mfc_mutex */
> +	/* Allocate memory for context */
> +	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +	if (!ctx) {
> +		mfc_err("Not enough memory.\n");
> +		ret = -ENOMEM;
> +		goto out_open;
> +	}
> +	file->private_data = ctx;
> +	ctx->dev = dev;
> +	INIT_LIST_HEAD(&ctx->src_queue);
> +	INIT_LIST_HEAD(&ctx->dst_queue);
> +	ctx->src_queue_cnt = 0;
> +	ctx->dst_queue_cnt = 0;
> +	/* Get context number */
> +	ctx->num = 0;
> +	while (dev->ctx[ctx->num]) {
> +		ctx->num++;
> +		if (ctx->num >= MFC_NUM_CONTEXTS) {
> +			mfc_err("Too many open contexts.\n");
> +			ret = -EAGAIN;
> +			goto out_open;
> +		}
> +	}
> +	/* Mark context as idle */
> +	spin_lock_irqsave(&dev->condlock, flags);
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	spin_unlock_irqrestore(&dev->condlock, flags);
> +	dev->ctx[ctx->num] = ctx;
> +	/* Default format */
> +	ctx->fmt = &formats[0];
> +	/* Load firmware if this is the first instance */
> +	if (atomic_read(&dev->num_inst) == 1) {
> +		/* Load the FW */
> +		ret = s5p_mfc_alloc_firmware(dev);
> +		if (ret != 0)
> +			goto out_open_2a;
> +		ret = s5p_mfc_load_firmware(dev);
> +		if (ret != 0)
> +			goto out_open_2;
> +		mfc_debug("Enabling clocks.\n");
> +		clk_enable(dev->clock1);
> +		clk_enable(dev->clock2);
> +		/* Init the FW */
> +		ret = s5p_mfc_init_hw(dev);
> +		if (ret != 0)
> +			goto out_open_3;
> +	}
> +	/* Init videobuf2 queue for CAPTURE */
> +	ret = vb2_queue_init(&ctx->vq_dst, &s5p_mfc_qops,
> +			       dev->alloc_ctx[0], &dev->irqlock,
> +			       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, ctx);
> +	if (ret) {
> +		mfc_err("Failed to initialize videobuf2 queue (capture)\n");
> +		goto out_open_3;
> +	}
> +	/* Init videobuf2 queue for OUTPUT */
> +	ret = vb2_queue_init(&ctx->vq_src, &s5p_mfc_qops,
> +			       dev->alloc_ctx[1], &dev->irqlock,
> +			       V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, ctx);
> +	if (ret) {
> +		mfc_err("Failed to initialize videobuf2 queue (output)\n");
> +		goto out_open_3;
> +	}

About using dev->irqlock, (spinlock_t *drv_lock) in the vb2_queue_init() is
removed
according to the Marek's patch (from the Hans Verkuil request)
 : http://www.spinics.net/lists/linux-media/msg23902.html
Does it mean that we don't have to use drv_lock in the driver ?
What actually purpose of drv_lock that you used in the MFC driver ?


> +	vb2_set_alloc_ctx(&ctx->vq_dst, dev->alloc_ctx[1], 1);
> +	init_waitqueue_head(&ctx->queue);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_open--\n");
> +	return ret;
> +	/* Deinit when failure occured */
> +out_open_3:
> +	if (atomic_read(&dev->num_inst) == 1) {
> +		clk_disable(dev->clock1);
> +		clk_disable(dev->clock2);
> +		s5p_mfc_release_firmware();
> +	}
> +out_open_2:
> +	s5p_mfc_release_firmware();
> +out_open_2a:
> +	dev->ctx[ctx->num] = 0;
> +	kfree(ctx);
> +out_open:
> +	atomic_dec(&dev->num_inst);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_open-- with error\n");
> +	return ret;
> +}
> +
> +/* Release MFC context */
> +static int s5p_mfc_release(struct file *file)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	unsigned long flags;
> +	mfc_debug("s5p_mfc_release++\n");
> +	mutex_lock(dev->mfc_mutex);
> +	/* Stop all the processing */
> +	vb2_queue_release(&ctx->vq_src);
> +	vb2_queue_release(&ctx->vq_dst);
> +	/* Mark context as idle */
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	/* If instance was initialised then
> +	 * return instance and free reosurces */
> +	if (ctx->state < MFCINST_ENC_INIT && ctx->state >= MFCINST_DEC_INIT)
> {
> +		ctx->state = MFCINST_DEC_RETURN_INST;
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_try_run();
> +		/* Wait until instance is returned or timeout occured */
> +		if (s5p_mfc_wait_for_done_ctx
> +		    (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
> +			mfc_err("Error returning instance.\n");
> +		}
> +		/* Free resources */
> +		s5p_mfc_release_dec_buffers(ctx);
> +		s5p_mfc_release_instance_buffer(ctx);
> +		s5p_mfc_release_dec_temp_buffers(ctx);
> +	}
> +	/* hardware locking scheme */
> +	if (dev->curr_ctx == ctx->num)
> +		clear_bit(0, &dev->hw_lock);
> +	atomic_dec(&dev->num_inst);
> +	if (atomic_read(&dev->num_inst) == 0) {
> +		/* Actually this is also protected by mfc_mutex */
> +		s5p_mfc_deinit_hw(dev);
> +		mfc_debug("Disabling clocks...\n");
> +		clk_disable(dev->clock1);
> +		clk_disable(dev->clock2);
> +		s5p_mfc_release_firmware();
> +	}
> +	dev->ctx[ctx->num] = 0;
> +	kfree(ctx);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_release--\n");
> +	return 0;
> +}
> +
> +/* Poll */
> +static unsigned int s5p_mfc_poll(struct file *file,
> +				 struct poll_table_struct *wait)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	return vb2_poll(&ctx->vq_dst, file, wait);
> +}
> +
> +/* Mmap */
> +static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	int ret;
> +	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> +	mfc_debug("s5p_mfc_mmap++\n");
> +	if (offset < DST_QUEUE_OFF_BASE) {
> +		mfc_debug("mmaping source.\n");
> +		ret = vb2_mmap(&ctx->vq_src, vma);
> +	} else {		/* capture */
> +		mfc_debug("mmaping destination.\n");
> +		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
> +		ret = vb2_mmap(&ctx->vq_dst, vma);
> +	}
> +	mfc_debug("s5p_mfc_mmap--\n");
> +	return ret;
> +}
> +
> +/* v4l2 ops */
> +static const struct v4l2_file_operations s5p_mfc_fops = {
> +	.owner = THIS_MODULE,
> +	.open = s5p_mfc_open,
> +	.release = s5p_mfc_release,
> +	.poll = s5p_mfc_poll,
> +	.ioctl = video_ioctl2,
> +	.mmap = s5p_mfc_mmap,
> +};
> +
> +/* videodec structure */
> +static struct video_device s5p_mfc_videodev = {
> +	.name = S5P_MFC_NAME,
> +	.fops = &s5p_mfc_fops,
> +	.ioctl_ops = &s5p_mfc_ioctl_ops,
> +	.minor = -1,
> +	.release = video_device_release,
> +};
> +
> +/* MFC probe function */
> +static int s5p_mfc_probe(struct platform_device *pdev)
> +{
> +	struct video_device *vfd;
> +	struct resource *res;
> +	int ret = -ENOENT;
> +	size_t size;
> +	mfc_debug("s5p_mfc_probe++\n");
> +	dev = kzalloc(sizeof *dev, GFP_KERNEL);
> +	if (!dev) {
> +		dev_err(&pdev->dev, "Not enough memoty for MFC device.\n");
> +		return -ENOMEM;
> +	}
> +	mfc_debug("Ok, allocated memory for device structure.\n");
> +	spin_lock_init(&dev->irqlock);
> +	spin_lock_init(&dev->condlock);
> +	mfc_debug("Initialised spin lock\n");
> +	dev->plat_dev = pdev;
> +	if (!dev->plat_dev) {
> +		dev_err(&pdev->dev, "No platform data specified\n");
> +		ret = -ENODEV;
> +		goto free_dev;
> +	}
> +	mfc_debug("Getting clocks\n");
> +	dev->clock1 = clk_get(&pdev->dev, "sclk_mfc");
> +	mfc_debug("Got clock1\n");
> +	dev->clock2 = clk_get(&pdev->dev, "mfc");
> +	mfc_debug("Got clock2\n");
> +	if (IS_ERR(dev->clock1) || IS_ERR(dev->clock2)) {
> +		dev_err(&pdev->dev, "failed to get mfc clock source\n");
> +		goto free_clk;
> +	}
> +	mfc_debug("No \"error\" in clock\n");
> +	mfc_debug("After setting clock rate\n");
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get memory region
> resource.\n");
> +		ret = -ENOENT;
> +		goto probe_out1;
> +	}
> +	size = (res->end - res->start) + 1;
> +	dev->mfc_mem = request_mem_region(res->start, size, pdev->name);
> +	if (dev->mfc_mem == NULL) {
> +		dev_err(&pdev->dev, "failed to get memory region.\n");
> +		ret = -ENOENT;
> +		goto probe_out2;
> +	}
> +	dev->base_virt_addr = ioremap(dev->mfc_mem->start,
> +			      dev->mfc_mem->end - dev->mfc_mem->start + 1);
> +	if (dev->base_virt_addr == NULL) {
> +		dev_err(&pdev->dev, "failed to ioremap address region.\n");
> +		ret = -ENOENT;
> +		goto probe_out3;
> +	}
> +	dev->regs_base = dev->base_virt_addr;
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get irq resource.\n");
> +		ret = -ENOENT;
> +		goto probe_out4;
> +	}
> +	dev->irq = res->start;
> +	ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
>
+									dev)
;
> +
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
> +		goto probe_out5;
> +	}
> +	dev->mfc_mutex = kmalloc(sizeof(struct mutex), GFP_KERNEL);
> +	if (dev->mfc_mutex == NULL) {
> +		dev_err(&pdev->dev, "Memory allocation failed\n");
> +		ret = -ENOMEM;
> +		goto probe_out6;
> +	}
> +	mutex_init(dev->mfc_mutex);
> +	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> +	if (ret)
> +		goto probe_out7;
> +	atomic_set(&dev->num_inst, 0);
> +	init_waitqueue_head(&dev->queue);
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to allocate video
> device\n");
> +		ret = -ENOMEM;
> +		goto unreg_dev;
> +	}
> +	*vfd = s5p_mfc_videodev;
> +	video_set_drvdata(vfd, dev);
> +	snprintf(vfd->name, sizeof(vfd->name), "%s", s5p_mfc_videodev.name);
> +	dev->vfd = vfd;
> +
> +	platform_set_drvdata(pdev, dev);
> +	dev->hw_lock = 0;
> +	dev->watchdog_workqueue = create_singlethread_workqueue("s5p-mfc");
> +	INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
> +	atomic_set(&dev->watchdog_cnt, 0);
> +	init_timer(&dev->watchdog_timer);
> +	dev->watchdog_timer.data = 0;
> +	dev->watchdog_timer.function = s5p_mfc_watchdog;
> +	dev->watchdog_timer.expires = jiffies +
>
+					msecs_to_jiffies(MFC_WATCHDOG_INTERV
AL);
> +	add_timer(&dev->watchdog_timer);
> +
> +	dev->alloc_ctx = vb2_cma_init_multi(&pdev->dev, 2, s5p_mem_types,
> +							s5p_mem_alignments);
> +	if (IS_ERR(dev->alloc_ctx)) {
> +		mfc_err("Couldn't prepare allocator context.\n");
> +		ret = PTR_ERR(dev->alloc_ctx);
> +		goto alloc_ctx_fail;
> +	}
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> +	if (ret) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to register video
> device\n");
> +		video_device_release(vfd);
> +		goto rel_vdev;
> +	}
> +	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
> +								vfd->num);
> +	mfc_debug("s5p_mfc_probe--\n");
> +	return 0;
> +
> +/* Deinit MFC if probe had failed */
> +rel_vdev:
> +	vb2_cma_cleanup_multi(dev->alloc_ctx);
> +alloc_ctx_fail:
> +unreg_dev:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +
> +probe_out7:
> +	if (dev->mfc_mutex) {
> +		mutex_destroy(dev->mfc_mutex);
> +		kfree(dev->mfc_mutex);
> +	}
> +probe_out6:
> +	free_irq(dev->irq, dev);
> +probe_out5:
> +probe_out4:
> +	iounmap(dev->base_virt_addr);
> +	dev->base_virt_addr = NULL;
> +probe_out3:
> +	release_resource(dev->mfc_mem);
> +	kfree(dev->mfc_mem);
> +probe_out2:
> +probe_out1:
> +	clk_put(dev->clock1);
> +	clk_put(dev->clock2);
> +free_clk:
> +
> +free_dev:
> +	kfree(dev);
> +	mfc_debug("s5p_mfc_probe-- with error\n");
> +	return ret;
> +}
> +
> +/* Remove the driver */
> +static int s5p_mfc_remove(struct platform_device *pdev)
> +{
> +	mfc_debug("s5p_mfc_remove++\n");
> +	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
> +	del_timer_sync(&dev->watchdog_timer);
> +	flush_workqueue(dev->watchdog_workqueue);
> +	destroy_workqueue(dev->watchdog_workqueue);
> +	video_unregister_device(dev->vfd);
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +	vb2_cma_cleanup_multi(dev->alloc_ctx);
> +	if (dev->mfc_mutex) {
> +		mutex_destroy(dev->mfc_mutex);
> +		kfree(dev->mfc_mutex);
> +	}
> +	mfc_debug("Will now deinit HW\n");
> +	s5p_mfc_deinit_hw(dev);
> +	free_irq(dev->irq, dev);
> +	iounmap(dev->base_virt_addr);
> +	if (dev->mfc_mem != NULL) {
> +		release_resource(dev->mfc_mem);
> +		kfree(dev->mfc_mem);
> +		dev->mfc_mem = NULL;
> +	}
> +	clk_put(dev->clock1);
> +	clk_put(dev->clock2);
> +	kfree(dev);
> +	mfc_debug("s5p_mfc_remove--\n");
> +	return 0;
> +}
> +
> +static int s5p_mfc_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_mfc_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +/* Power management */
> +static const struct dev_pm_ops s5p_mfc_pm_ops = {
> +	.suspend = s5p_mfc_suspend,
> +	.resume = s5p_mfc_resume,
> +};
> +
> +static struct platform_driver s5p_mfc_pdrv = {
> +	.probe = s5p_mfc_probe,
> +	.remove = __devexit_p(s5p_mfc_remove),
> +	.driver = {
> +		   .name = S5P_MFC_NAME,
> +		   .owner = THIS_MODULE,
> +		   .pm = &s5p_mfc_pm_ops},
> +};
> +
> +static char banner[] __initdata =
> +			"S5P MFC V4L2 Driver, (c) 2010 Samsung
Electronics\n";
> +
> +static int __init s5p_mfc_init(void)
> +{
> +	mfc_info("%s", banner);
> +	if (platform_driver_register(&s5p_mfc_pdrv) != 0) {
> +		printk(KERN_ERR "platform device registration failed..\n");
> +		return -1;
> +	}
> +	mfc_info("After init...\n");
> +	return 0;
> +}
> +
> +static void __devexit s5p_mfc_exit(void)
> +{
> +	platform_driver_unregister(&s5p_mfc_pdrv);
> +}
> +
> +module_init(s5p_mfc_init);
> +module_exit(s5p_mfc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> new file mode 100644
> index 0000000..4f18937
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> @@ -0,0 +1,190 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * This file contains definitions of enums and structs used by the codec
> + * driver.
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#ifndef S5P_MFC_COMMON_H_
> +#define S5P_MFC_COMMON_H_
> +
> +#include "regs-mfc5.h"
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +
> +#include <media/videobuf2-core.h>
> +
> +#define MFC_MAX_EXTRA_DPB       5
> +#define MFC_MAX_BUFFERS		32
> +#define MFC_FRAME_PLANES	2
> +
> +#define MFC_NUM_CONTEXTS	4
> +/* Interrupt timeout */
> +#define MFC_INT_TIMEOUT		2000
> +/* Busy wait timeout */
> +#define MFC_BW_TIMEOUT		500
> +/* Watchdog interval */
> +#define MFC_WATCHDOG_INTERVAL   1000
> +/* After how many executions watchdog should assume lock up */
> +#define MFC_WATCHDOG_CNT        10
> +
> +
> +/**
> + * enum s5p_mfc_inst_state - The state of an MFC instance.
> + */
> +enum s5p_mfc_inst_state {
> +	MFCINST_FREE = 0,
> +	MFCINST_DEC_INIT = 100,
> +	MFCINST_DEC_GOT_INST,
> +	MFCINST_DEC_HEAD_PARSED,
> +	MFCINST_DEC_BUFS_SET,
> +	MFCINST_DEC_RUNNING,
> +	MFCINST_DEC_FINISHING,
> +	MFCINST_DEC_FINISHED,
> +	MFCINST_DEC_RETURN_INST,
> +	MFCINST_DEC_ERROR,
> +	MFCINST_ENC_INIT = 200,
> +};
> +
> +/**
> + * enum s5p_mfc_queue_state - The state of buffer queue.
> + */
> +enum s5p_mfc_queue_state {
> +	QUEUE_FREE = 0,
> +	QUEUE_BUFS_REQUESTED,
> +	QUEUE_BUFS_QUERIED,
> +	QUEUE_BUFS_MMAPED,
> +};
> +
> +struct s5p_mfc_ctx;
> +
> +/**
> + * struct s5p_mfc_dev - The struct containing driver internal parameters.
> + */
> +struct s5p_mfc_dev {
> +	struct v4l2_device v4l2_dev;
> +	struct video_device *vfd;
> +	struct platform_device *plat_dev;
> +
> +	atomic_t num_inst;
> +	spinlock_t irqlock;
> +	spinlock_t condlock;
> +
> +	void __iomem *regs_base;
> +	int irq;
> +
> +	struct resource *mfc_mem;
> +	void __iomem *base_virt_addr;
> +
> +	struct mutex *mfc_mutex;
> +
> +	int int_cond;
> +	int int_type;
> +	unsigned int int_err;
> +	wait_queue_head_t queue;
> +
> +	size_t port_a;
> +	size_t port_b;
> +
> +	unsigned long hw_lock;
> +
> +	struct clk *clock1;
> +	struct clk *clock2;
> +
> +	struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
> +	int curr_ctx;
> +	unsigned long ctx_work_bits;
> +
> +	atomic_t watchdog_cnt;
> +	struct timer_list watchdog_timer;
> +	struct workqueue_struct *watchdog_workqueue;
> +	struct work_struct watchdog_work;
> +
> +	struct vb2_alloc_ctx **alloc_ctx;
> +};
> +
> +/**
> + * struct s5p_mfc_ctx - This struct contains the instance context
> + */
> +struct s5p_mfc_ctx {
> +	struct s5p_mfc_dev *dev;
> +	int num;
> +
> +	int int_cond;
> +	int int_type;
> +	unsigned int int_err;
> +	wait_queue_head_t queue;
> +
> +	struct s5p_mfc_fmt *fmt;
> +
> +	struct vb2_queue vq_src;
> +	struct vb2_queue vq_dst;
> +
> +	struct list_head src_queue;
> +	struct list_head dst_queue;
> +
> +	unsigned int src_queue_cnt;
> +	unsigned int dst_queue_cnt;
> +
> +	enum s5p_mfc_inst_state state;
> +	int inst_no;
> +
> +	/* Decoder parameters */
> +	int img_width;
> +	int img_height;
> +	int buf_width;
> +	int buf_height;
> +	int dpb_count;
> +	int total_dpb_count;
> +
> +	int luma_size;
> +	int chroma_size;
> +	int mv_size;
> +
> +	unsigned long consumed_stream;
> +	int slice_interface;
> +
> +	/* Buffers */
> +	size_t port_a;
> +	size_t port_a_size;
> +	size_t port_b;
> +	size_t port_b_size;
> +
> +
> +	enum s5p_mfc_queue_state capture_state;
> +	enum s5p_mfc_queue_state output_state;
> +
> +	size_t dec_dst_buf_luma[MFC_MAX_BUFFERS];
> +	size_t dec_dst_buf_chroma[MFC_MAX_BUFFERS];
> +
> +	int dec_dst_buf_cnt;
> +	unsigned int sequence;
> +	unsigned long dec_dst_flag;
> +	size_t dec_src_buf_size;
> +
> +	/* Control values */
> +	int codec_mode;
> +	__u32 pix_format;
> +	int loop_filter_mpeg4;
> +	int display_delay;
> +
> +	/* Buffers */
> +	size_t instance_phys;
> +	size_t instance_size;
> +	size_t desc_phys;
> +	size_t shared_phys;
> +	void *shared_virt;
> +
> +};
> +
> +#endif /* S5P_MFC_COMMON_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> new file mode 100644
> index 0000000..222ed71
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> @@ -0,0 +1,173 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * This file contains description of formats used by MFC and cotrols
> + * used by the driver.
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#ifndef S5P_MFC_CTRLS_H_
> +#define S5P_MFC_CTRLS_H_
> +
> +#include <media/v4l2-ioctl.h>
> +#include "regs-mfc5.h"
> +
> +#define MFC_FMT_DEC	0
> +#define MFC_FMT_ENC	1
> +#define MFC_FMT_RAW	2
> +
> +struct s5p_mfc_fmt {
> +	char *name;
> +	u32 fourcc;
> +	u32 codec_mode;
> +	u32 type;
> +	u32 num_planes;
> +};
> +
> +#define MFC_FORMATS_NO_CODEC -1
> +
> +static struct s5p_mfc_fmt formats[] = {
> +	{
> +	.name = "4:2:0 2 Planes 64x32 Tiles",
> +	.fourcc = V4L2_PIX_FMT_NV12MT,
> +	.codec_mode = MFC_FORMATS_NO_CODEC,
> +	.type = MFC_FMT_RAW,
> +	.num_planes = 2,
> +	 },
> +	{
> +	.name = "4:2:0 2 Planes",
> +	.fourcc = V4L2_PIX_FMT_NV12,
> +	.codec_mode = MFC_FORMATS_NO_CODEC,
> +	.type = MFC_FMT_RAW,
> +	.num_planes = 2,
> +	},
> +	{
> +	.name = "H264 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_H264,
> +	.codec_mode = S5P_FIMV_CODEC_H264_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "H263 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_H263,
> +	.codec_mode = S5P_FIMV_CODEC_H263_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "MPEG1/MPEG2 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_MPEG12,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "MPEG4 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_MPEG4,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 3.11 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX3,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX311_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 4.12 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX4,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX412_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 5.00-5.02 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX500,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX502_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 5.03 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX503,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX503_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "XviD Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_XVID,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "VC1 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_VC1,
> +	.codec_mode = S5P_FIMV_CODEC_VC1_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "VC1 RCV Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_VC1_RCV,
> +	.codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +};
> +
> +#define NUM_FORMATS ARRAY_SIZE(formats)
> +
> +static struct v4l2_queryctrl s5p_mfc_ctrls[] = {
> +/* For decoding */
> +	{
> +	.id = V4L2_CID_CODEC_DISPLAY_DELAY,
> +	.type = V4L2_CTRL_TYPE_INTEGER,
> +	.name = "",
> +	.minimum = 0,
> +	.maximum = 16383,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +	{
> +	.id = V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE,
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.name = "Mpeg4 Loop Filter Enable",
> +	.minimum = 0,
> +	.maximum = 1,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +	{
> +	.id = V4L2_CID_CODEC_SLICE_INTERFACE,
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.name = "Slice Interface Enable",
> +	.minimum = 0,
> +	.maximum = 1,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +};
> +
> +#define NUM_CTRLS ARRAY_SIZE(s5p_mfc_ctrls)
> +
> +#endif /* S5P_MFC_CTRLS_H_ */
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> new file mode 100644
> index 0000000..543f3fb
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> @@ -0,0 +1,77 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
> + *
> + * C file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains functions used to wait for command completion.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/wait.h>
> +#include <linux/sched.h>
> +#include <linux/io.h>
> +#include "regs-mfc5.h"
> +#include "s5p_mfc_intr.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
> +{
> +	if (wait_event_interruptible_timeout(dev->queue,
> +		(dev->int_cond && (dev->int_type == command
> +		|| dev->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +		msecs_to_jiffies(MFC_INT_TIMEOUT)) == 0) {
> +		mfc_err("Interrupt (%d dev) timed out.\n", dev->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (dev->queue, %d).\n", dev->int_type);
> +	if (dev->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}
> +
> +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
> +{
> +	dev->int_cond = 0;
> +	dev->int_type = 0;
> +	dev->int_err = 0;
> +}
> +
> +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> +				    int command, int interrupt)
> +{
> +	int ret;
> +	if (interrupt) {
> +		ret = wait_event_interruptible_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	} else {
> +		ret = wait_event_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	}
> +	if (ret == 0) {
> +		mfc_err("Interrupt (%d ctx) timed out.\n", ctx->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (ctx->queue, %d).\n", ctx->int_type);
> +	if (ctx->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}
> +
> +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
> +{
> +	ctx->int_cond = 0;
> +	ctx->int_type = 0;
> +	ctx->int_err = 0;
> +}
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> new file mode 100644
> index 0000000..8c531b6
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> @@ -0,0 +1,26 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * It contains waiting functions declarations.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef _S5P_MFC_INTR_H_
> +#define _S5P_MFC_INTR_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> +						int command, int interrupt);
> +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
> +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
> +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
> +
> +#endif /* _S5P_MFC_INTR_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> new file mode 100644
> index 0000000..90fa84c
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> @@ -0,0 +1,65 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_logmsg.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains debug macros
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_LOGMSG_H_
> +#define S5P_MFC_LOGMSG_H_
> +
> +/* Debug macros */
> +#define MFC_DEBUG(fmt, ...)					\
> +	do {							\
> +		printk(KERN_ERR					\
> +			"%s: " fmt, __func__, ##__VA_ARGS__);	\
> +	} while (0)
> +
> +#define MFC_ERROR(fmt, ...)					\
> +	do {							\
> +		printk(KERN_ERR					\
> +			"%s: " fmt, __func__, ##__VA_ARGS__);	\
> +	} while (0)
> +
> +#define MFC_NOTICE(fmt, ...)					\
> +	do {							\
> +		printk(KERN_NOTICE				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +#define MFC_INFO(fmt, ...)					\
> +	do {							\
> +		printk(KERN_INFO				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +#define MFC_WARN(fmt, ...)					\
> +	do {							\
> +		printk(KERN_WARNING				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +/* Uncomment the line below do enable debug messages */
> +/* #define CONFIG_VIDEO_MFC50_DEBUG */
> +
> +#ifdef CONFIG_VIDEO_MFC50_DEBUG
> +#define mfc_debug(fmt, ...)		MFC_DEBUG(fmt, ##__VA_ARGS__)
> +#warning "DEBUG in MFC is switched on!"
> +#else
> +#define mfc_debug(fmt, ...)
> +#endif /* CONFIG_VIDEO_MFC50_DEBUG */
> +
> +#define mfc_err(fmt, ...)		MFC_ERROR(fmt, ##__VA_ARGS__)
> +#define mfc_notice(fmt, ...)		MFC_NOTICE(fmt, ##__VA_ARGS__)
> +#define mfc_info(fmt, ...)		MFC_INFO(fmt, ##__VA_ARGS__)
> +#define mfc_warn(fmt, ...)		MFC_WARN(fmt, ##__VA_ARGS__)
> +
> +#endif /* S5P_MFC_LOGMSG_H_ */
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> new file mode 100644
> index 0000000..a7e4009
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> @@ -0,0 +1,32 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_memory.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * Contains memory related defines.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_MEMORY_H_
> +#define S5P_MFC_MEMORY_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +#define FIRMWARE_CODE_SIZE		0x60000	/* 384KB */
> +#define MFC_H264_INSTANCE_BUF_SIZE	0x96000	/* 600KB per H264
> instance */
> +#define MFC_INSTANCE_BUF_SIZE		0x2800	/* 10KB per
instance */
> +#define DESC_BUF_SIZE			0x20000	/* 128KB for DESC
> buffer */
> +#define SHARED_BUF_SIZE			0x01000	/* 4KB for shared
> buffer */
> +#define CPB_BUF_SIZE			0x400000/* 4MB fr decoder */
> +
> +/* Define names for CMA memory kinds used by MFC */
> +#define MFC_CMA_BANK1		"a"
> +#define MFC_CMA_BANK2		"b"
> +#define MFC_CMA_FW		"f"
> +
> +#endif /* S5P_MFC_MEMORY_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> new file mode 100644
> index 0000000..b6e2334
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> @@ -0,0 +1,800 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
> + *
> + * Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains hw related functions.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/mm.h>
> +#include <linux/io.h>
> +#include <linux/jiffies.h>
> +#include "regs-mfc5.h"
> +
> +#include "s5p_mfc_opr.h"
> +#include "s5p_mfc_common.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_memory.h"
> +
> +#include "s5p_mfc_intr.h"
> +
> +#include <linux/firmware.h>
> +#include <linux/err.h>
> +#include <linux/sched.h>
> +#include <linux/cma.h>
> +
> +static size_t s5p_mfc_phys_bitproc_buff;
> +static unsigned char *s5p_mfc_virt_bitproc_buff;
> +
> +/* #define S5P_MFC_DEBUG_REGWRITE  */
> +#ifdef S5P_MFC_DEBUG_REGWRITE
> +#undef writel
> +#define writel(v, r) do { \
> +	printk(KERN_ERR "MFCWRITE(%p): %08x\n", r, (unsigned int)v); \
> +	__raw_writel(v, r); } while (0)
> +#endif /* S5P_MFC_DEBUG_REGWRITE */
> +
> +#define READL(offset)		readl(dev->regs_base + (offset))
> +#define WRITEL(data, offset)	writel((data), dev->regs_base + (offset))
> +#define OFFSETA(x)		(((x) - dev->port_a) >> 11)
> +#define OFFSETB(x)		(((x) - dev->port_b) >> 11)
> +
> +/* Reset the device */
> +static int s5p_mfc_cmd_reset(struct s5p_mfc_dev *dev)
> +{
> +	unsigned int mc_status;
> +	unsigned long timeout;
> +	mfc_debug("s5p_mfc_cmd_reset++\n");
> +	/* Stop procedure */
> +	WRITEL(0x3f7, S5P_FIMV_SW_RESET);	/*  reset VI */

Ahm, This (WRITEL(0x3f7, S5P_FIMV_SW_RESET)) might be a problem. 
In the reset seq. of MFC driver, we checked out 
That FW(s5pc110-mfc.fw in the s5p_mfc_load_firmware()) runned by RISC core
at this point could access invalid address. It should be removed.

> +	WRITEL(0x3f6, S5P_FIMV_SW_RESET);	/*  reset RISC */
> +	WRITEL(0x3e2, S5P_FIMV_SW_RESET);	/*  All reset except for MC
> */
> +	mdelay(10);
> +	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
> +	/* Check MC status */
> +	do {
> +		if (time_after(jiffies, timeout)) {
> +			mfc_err("Timeout while resetting MFC.\n");
> +			return -EIO;
> +		}
> +		mc_status = READL(S5P_FIMV_MC_STATUS);
> +	} while (mc_status & 0x3);
> +	WRITEL(0x0, S5P_FIMV_SW_RESET);
> +	WRITEL(0x3fe, S5P_FIMV_SW_RESET);
> +	mfc_debug("s5p_mfc_cmd_reset--\n");
> +	return 0;
> +}
> +
> +/* Send a command to the MFC */
> +static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev,
> +				struct s5p_mfc_ctx *mfc_ctx, int cmd, int
arg)
> +{
> +	int cur_cmd;
> +	unsigned long timeout;
> +	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
> +	/* wait until host to risc command register becomes 'H2R_CMD_EMPTY'
> */
> +	do {
> +		if (time_after(jiffies, timeout)) {
> +			mfc_err("Timeout while waiting for hardware.\n");
> +			return -EIO;
> +		}
> +		cur_cmd = READL(S5P_FIMV_HOST2RISC_CMD);
> +	} while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
> +	WRITEL(arg, S5P_FIMV_HOST2RISC_ARG1);
> +	if (cmd == S5P_FIMV_H2R_CMD_OPEN_INSTANCE) {
> +		/* No CRC calculation (slow!) */
> +		WRITEL(0, S5P_FIMV_HOST2RISC_ARG2);
> +		/* Physical addr of the instance buffer */
> +		WRITEL(OFFSETA(mfc_ctx->instance_phys),
> +		       S5P_FIMV_HOST2RISC_ARG3);
> +		/* Size of the instance buffer */
> +		WRITEL(mfc_ctx->instance_size, S5P_FIMV_HOST2RISC_ARG4);
> +	}
> +	/* Issue the command */
> +	WRITEL(cmd, S5P_FIMV_HOST2RISC_CMD);
> +	return 0;
> +}
> +/*
> +static void s5p_mfc_cmd_sleep()
> +{
> +	WRITEL(-1, S5P_FIMV_CH_ID);
> +	WRITEL(MFC_SLEEP, S5P_FIMV_COMMAND_TYPE);
> +}
> +*/
> +
> +/*
> +static void s5p_mfc_cmd_wakeup()
> +{
> +	WRITEL(-1, S5P_FIMV_CH_ID);
> +	WRITEL(MFC_WAKEUP, S5P_FIMV_COMMAND_TYPE);
> +	mdelay(100);
> +}
> +*/

These two functions are incomplete code being developed. Right ?

> +
> +/* Allocate temporary buffers for decoding */
> +int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	void *desc_virt;
> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers++\n");
> +	mfc_ctx->desc_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +					MFC_CMA_BANK1, DESC_BUF_SIZE, 2048);
> +	if (IS_ERR_VALUE(mfc_ctx->desc_phys)) {
> +		mfc_ctx->desc_phys = 0;
> +		mfc_err("Allocating DESC buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	desc_virt = ioremap_nocache(mfc_ctx->desc_phys, DESC_BUF_SIZE);

In case of ioremap_xx() function, you might meet that msg as below
"Don't allow RAM to be mapped - this causes problems with ARMv6+ "
(arch/arm/mm/ioremap.c). so we should not use this function for this case.
I suggest that you use phys_to_virt() with some cache op. such as 
cache_clean for writing case(ex> memset) & cache_invalidate for reading
case.
(ex>reading shared mem)
It is necessary for accessing shared memory(DRAM area accessed by ARM & MFC)

> +	if (desc_virt == NULL) {
> +		cma_free(mfc_ctx->desc_phys);
> +		mfc_ctx->desc_phys = 0;
> +		mfc_err("Remapping DESC buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset(desc_virt, 0, DESC_BUF_SIZE);
> +	iounmap(desc_virt);
> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers--\n");
> +	return 0;
> +}
> +
> +/* Release temproary buffers for decoding */
> +void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	if (mfc_ctx->desc_phys) {
> +		cma_free(mfc_ctx->desc_phys);
> +		mfc_ctx->desc_phys = 0;
> +	}
> +}
> +
> +/* Allocate decoding buffers */
> +int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	unsigned int luma_size, chroma_size, mv_size;
> +	mfc_debug("s5p_mfc_alloc_dec_buffers++\n");
> +	luma_size = mfc_ctx->luma_size;
> +	chroma_size = mfc_ctx->chroma_size;
> +	mv_size = mfc_ctx->mv_size;
> +	mfc_debug("Luma size: %d Chroma size: %d MV size: %d\n",
> +		  luma_size, chroma_size, mv_size);
> +	/* Codecs have different memory requirements */
> +	switch (mfc_ctx->codec_mode) {
> +	case S5P_FIMV_CODEC_H264_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
> +			     S5P_FIMV_DEC_VERT_NB_MV_SIZE, 8192);
> +		mfc_ctx->port_b_size =
> +		    mfc_ctx->total_dpb_count * ALIGN(mv_size, 8192) * 2;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG4_DEC:
> +	case S5P_FIMV_CODEC_DIVX412_DEC:
> +	case S5P_FIMV_CODEC_DIVX311_DEC:
> +	case S5P_FIMV_CODEC_DIVX502_DEC:
> +	case S5P_FIMV_CODEC_DIVX503_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_STX_PARSER_SIZE +
> +			     S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +
> +	case S5P_FIMV_CODEC_VC1RCV_DEC:
> +	case S5P_FIMV_CODEC_VC1_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_NB_DCAC_SIZE +
> +			     3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +
> +	case S5P_FIMV_CODEC_MPEG2_DEC:
> +		mfc_ctx->port_a_size = 0;
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +	case S5P_FIMV_CODEC_H263_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_NB_DCAC_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* Allocate only if memory from bank 1 is necessary */
> +	if (mfc_ctx->port_a_size > 0) {
> +		mfc_ctx->port_a = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +				MFC_CMA_BANK1, mfc_ctx->port_a_size, 2048);
> +		if (IS_ERR_VALUE(mfc_ctx->port_a)) {
> +			mfc_ctx->port_a = 0;
> +			printk(KERN_ERR
> +			       "Buf alloc for decoding failed (port A).\n");
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	/* Allocate only if memory from bank 2 is necessary */
> +	if (mfc_ctx->port_b_size > 0) {
> +		mfc_ctx->port_b = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +				MFC_CMA_BANK2, mfc_ctx->port_b_size, 2048);
> +		if (IS_ERR_VALUE(mfc_ctx->port_b)) {
> +			mfc_ctx->port_b = 0;
> +			mfc_err("Buf alloc for decoding failed (port
B).\n");
> +			return -ENOMEM;
> +		}
> +	}
> +	mfc_debug("s5p_mfc_alloc_dec_buffers--\n");
> +
> +	return 0;
> +}
> +
> +/* Release buffers allocated for decoding */
> +void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	if (mfc_ctx->port_a) {
> +		cma_free(mfc_ctx->port_a);
> +		mfc_ctx->port_a = 0;
> +		mfc_ctx->port_a_size = 0;
> +	}
> +	if (mfc_ctx->port_b) {
> +		cma_free(mfc_ctx->port_b);
> +		mfc_ctx->port_b = 0;
> +		mfc_ctx->port_b_size = 0;
> +	}
> +}
> +
> +/* Allocate memory for instance data buffer */
> +int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	void *instance_virt;
> +	mfc_debug("s5p_mfc_alloc_instance_buffer++\n");
> +	/* According to EVT1 docu, H264 requires 600KB
> +	 * and all other need onlu 10KB */
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
> +		mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
> +		mfc_ctx->instance_size = MFC_H264_INSTANCE_BUF_SIZE;
> +	else
> +		mfc_ctx->instance_size = MFC_INSTANCE_BUF_SIZE;
> +	mfc_ctx->instance_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
> +				MFC_CMA_BANK1, mfc_ctx->instance_size,
2048);
> +	if (IS_ERR_VALUE(mfc_ctx->instance_phys)) {
> +		mfc_ctx->instance_phys = 0;
> +		mfc_err("Allocating instance buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	instance_virt = ioremap_nocache(mfc_ctx->instance_phys,
> +		mfc_ctx->instance_size);
> +	if (instance_virt == NULL) {
> +		cma_free(mfc_ctx->instance_phys);
> +		mfc_ctx->instance_phys = 0;
> +		mfc_err("Remapping instance buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset(instance_virt, 0, mfc_ctx->instance_size);
> +	iounmap(instance_virt);
> +	mfc_ctx->shared_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
> +					MFC_CMA_BANK1, SHARED_BUF_SIZE,
2048);
> +	if (IS_ERR_VALUE(mfc_ctx->shared_phys)) {
> +		mfc_ctx->shared_phys = 0;
> +		mfc_err("Allocating shared buffer failed\n");
> +		cma_free(mfc_ctx->instance_phys);
> +		return -ENOMEM;
> +	}
> +	mfc_ctx->shared_virt = ioremap_nocache(mfc_ctx->shared_phys,
> +							
SHARED_BUF_SIZE);
> +	if (!mfc_ctx->shared_virt) {
> +		cma_free(mfc_ctx->instance_phys);
> +		cma_free(mfc_ctx->shared_phys);
> +		mfc_ctx->shared_phys = 0;
> +		mfc_ctx->instance_phys = 0;
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset((void *)mfc_ctx->shared_virt, 0, SHARED_BUF_SIZE);
> +	mfc_debug("s5p_mfc_alloc_instance_buffer--\n");
> +	return 0;
> +}
> +
> +/* Release instance buffer */
> +void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	mfc_debug("s5p_mfc_release_instance_buffer++\n");
> +	if (mfc_ctx->instance_phys) {
> +		cma_free(mfc_ctx->instance_phys);
> +		mfc_ctx->instance_phys = 0;
> +	}
> +	if (mfc_ctx->shared_virt) {
> +		iounmap(mfc_ctx->shared_virt);
> +		mfc_ctx->shared_virt = 0;
> +	}
> +	if (mfc_ctx->shared_phys) {
> +		cma_free(mfc_ctx->shared_phys);
> +		mfc_ctx->shared_phys = 0;
> +	}
> +	mfc_debug("s5p_mfc_release_instance_buffer--\n");
> +}
> +
> +/* Set registers for decoding temporary buffers */
> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	WRITEL(OFFSETA(mfc_ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
> +	WRITEL(CPB_BUF_SIZE, S5P_FIMV_SI_CH0_CPB_SIZE);
> +	WRITEL(DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
> +}

I mentioned that separating 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from
s5p_mfc_set_dec_temp_buffers() is better in terms of modular design.
And what about having clear function name ? using 'temp' in the func. Name
is not specific

> +
> +/* Set registers for decoding stream buffer */
> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
> buf_addr,
> +		  unsigned int start_num_byte, unsigned int buf_size)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("inst_no : %d, buf_addr : 0x%08x, buf_size : 0x%08x
> (%d)\n",
> +		  mfc_ctx->inst_no, buf_addr, buf_size, buf_size);
> +	if (buf_addr & (2048 - 1)) {
> +		mfc_err("Source stream buffer is not aligned correctly.\n");
> +		return -EINVAL;
> +	}
> +	WRITEL(OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
> +	WRITEL(buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
> +	mfc_debug("Shared_virt: %p (start offset: %d)\n", mfc_ctx-
> >shared_virt,
> +		  start_num_byte);
> +	writel(start_num_byte, mfc_ctx->shared_virt \
> +					 + S5P_FIMV_SHARED_START_BYTE_NUM);
> +	return 0;
> +}
> +
> +/* Set decoding frame buffer */
> +int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int do_int)
> +{
> +	unsigned int frame_size, i;
> +	unsigned int frame_size_ch, frame_size_mv;
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	unsigned int dpb;
> +	size_t buf_addr1, buf_addr2;
> +	int buf_size1, buf_size2;
> +	buf_addr1 = mfc_ctx->port_a;
> +	buf_size1 = mfc_ctx->port_a_size;
> +	buf_addr2 = mfc_ctx->port_b;
> +	buf_size2 = mfc_ctx->port_b_size;
> +	mfc_debug("Buf1: %p (%d) Buf2: %p (%d)\n", (void *)buf_addr1,
> buf_size1,
> +		  (void *)buf_addr2, buf_size2);
> +	/* Enable generation of extra info */
> +/*	*(shared_mem_vir_addr + 0x0038) = 63; */
> +	mfc_debug("Total DPB COUNT: %d\n", mfc_ctx->total_dpb_count);
> +	mfc_debug("Setting display delay to %d\n", mfc_ctx->display_delay);
> +	dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & 0xFFFF0000;
> +	WRITEL(mfc_ctx->total_dpb_count | dpb,
> S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
> +	s5p_mfc_set_dec_temp_buffers(mfc_ctx);

I think, all reg. set in the s5p_mfc_set_dec_temp_buffers(mfc_ctx)is not
required
at this point. 
What about only adding 'set S5P_FIMV_SI_CH0_HOST_WR_ADR) instead of using 
s5p_mfc_set_dec_temp_buffers()? 

> +	switch (mfc_ctx->codec_mode) {
> +	case S5P_FIMV_CODEC_H264_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_IP_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG4_DEC:
> +	case S5P_FIMV_CODEC_DIVX311_DEC:
> +	case S5P_FIMV_CODEC_DIVX412_DEC:
> +	case S5P_FIMV_CODEC_DIVX502_DEC:
> +	case S5P_FIMV_CODEC_DIVX503_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SP_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_H263_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_VC1_DEC:
> +	case S5P_FIMV_CODEC_VC1RCV_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE3_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE2_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE1_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG2_DEC:
> +		break;
> +	default:
> +		mfc_err("Unknown codec set for decoding (%x).\n",
> +			mfc_ctx->codec_mode);
> +		return -EINVAL;
> +		break;
> +	}
> +	frame_size = mfc_ctx->luma_size;
> +	frame_size_ch = mfc_ctx->chroma_size;
> +	frame_size_mv = mfc_ctx->mv_size;
> +	mfc_debug("Frame size: %d ch: %d mv: %d\n", frame_size,
> frame_size_ch,
> +		  frame_size_mv);
> +	for (i = 0; i < mfc_ctx->total_dpb_count; i++) {
> +		/* Port B */
> +		mfc_debug("Luma %d: %x\n", i, mfc_ctx->dec_dst_buf_luma[i]);
> +		WRITEL(OFFSETB(mfc_ctx->dec_dst_buf_luma[i]),
> +		       S5P_FIMV_LUMA_ADR + i * 4);
> +		mfc_debug("\tChroma %d: %x\n", i, \
> +						mfc_ctx-
>dec_dst_buf_chroma[i]);
> +		WRITEL(OFFSETA(mfc_ctx->dec_dst_buf_chroma[i]),
> +		       S5P_FIMV_CHROMA_ADR + i * 4);
> +		if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
> +			mfc_debug("\tBuf2: %x, buf_size2: %d\n", buf_addr2,
> +				  buf_size2);
> +			WRITEL(OFFSETB(buf_addr2), S5P_FIMV_MV_ADR + i * 4);
> +			buf_addr2 += ALIGN(frame_size_mv, 8192);
> +			buf_size2 -= ALIGN(frame_size_mv, 8192);
> +		}
> +	}
> +	mfc_debug("Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
> +	mfc_debug("Buf 1/2 size after: %d/%d (num frames %d)\n", buf_size1,
> +		  buf_size2, mfc_ctx->total_dpb_count);
> +	if (buf_size1 < 0 || buf_size2 < 0) {
> +		mfc_debug("Not enough memory has been allocated.\n");
> +		return -1337;
> +	}
> +	writel(frame_size, mfc_ctx->shared_virt \
> +					+ S5P_FIMV_SHARED_LUMA_DPB_SIZE);
> +	writel(frame_size_ch, mfc_ctx->shared_virt \
> +					+ S5P_FIMV_SHARED_CHROMA_DPB_SIZE);
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
> +		writel(frame_size_mv, mfc_ctx->shared_virt \
> +						+ S5P_FIMV_SHARED_MV_SIZE);
> +	}
> +	if (do_int) {
> +		mfc_debug("Doing int.\n");
> +		WRITEL(((S5P_FIMV_CH_INIT_BUFS << 16) & 0x70000) | \
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +
> +	}
> +	mfc_debug("After setting buffers.\n");
> +	return 0;
> +}
> +
> +/* Allocate firmware */
> +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
> +{
> +	int err;
> +	struct cma_info mem_info_f, mem_info_a, mem_info_b;
> +	mfc_debug("s5p_mfc_alloc_firmware++\n");
> +	if (s5p_mfc_phys_bitproc_buff) {
> +		mfc_err("Attempting to allocate firmware when it seems that"
> \
> +			"it is already loaded.\n");
> +		return -ENOMEM;
> +	}
> +	/* Get memory region information and check if it is correct */
> +	err = cma_info(&mem_info_f, dev->v4l2_dev.dev, MFC_CMA_FW);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"f", mem_info_f.lower_bound, mem_info_f.upper_bound,
> +		mem_info_f.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	err = cma_info(&mem_info_a, dev->v4l2_dev.dev, MFC_CMA_BANK1);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"a", mem_info_a.lower_bound, mem_info_a.upper_bound,
> +		mem_info_a.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	err = cma_info(&mem_info_b, dev->v4l2_dev.dev, MFC_CMA_BANK2);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"b", mem_info_b.lower_bound, mem_info_b.upper_bound,
> +		mem_info_b.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	if (mem_info_f.upper_bound > mem_info_a.lower_bound) {
> +			mfc_err("Firmware has to be allocated before" \
> +			" memory for buffers (bank A).\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("Allocating memory for firmware.\n");
> +	s5p_mfc_phys_bitproc_buff = cma_alloc(dev->v4l2_dev.dev, MFC_CMA_FW,
> +						FIRMWARE_CODE_SIZE, 128 *
1024);
> +	mfc_debug("Phys addr from CMA: %08x\n", s5p_mfc_phys_bitproc_buff);
> +	if (IS_ERR_VALUE(s5p_mfc_phys_bitproc_buff)) {
> +		s5p_mfc_phys_bitproc_buff = 0;
> +		printk(KERN_ERR "Allocating bitprocessor buffer failed\n");
> +		return -ENOMEM;
> +	}
> +	if (s5p_mfc_phys_bitproc_buff & 0x0001FFFF) {
> +		mfc_err("The base memory is not aligned to 128KB.\n");
> +		cma_free(s5p_mfc_phys_bitproc_buff);
> +		return -EIO;
> +	}
> +	dev->port_a = s5p_mfc_phys_bitproc_buff;
> +	dev->port_b = mem_info_b.lower_bound;
> +	mfc_debug("Port A: %08x Port B: %08x (FW: %08x size: %08x)\n",
> +			dev->port_a, dev->port_b, s5p_mfc_phys_bitproc_buff,
> +							FIRMWARE_CODE_SIZE);
> +	s5p_mfc_virt_bitproc_buff =
> ioremap_nocache(s5p_mfc_phys_bitproc_buff,
> +							FIRMWARE_CODE_SIZE);
> +	mfc_debug("Virtual address for FW: %08lx\n",
> +				(long unsigned
int)s5p_mfc_virt_bitproc_buff);
> +	if (!s5p_mfc_virt_bitproc_buff) {
> +		mfc_err("Bitprocessor memory ioremap failed\n");
> +		cma_free(s5p_mfc_phys_bitproc_buff);
> +		s5p_mfc_phys_bitproc_buff = 0;
> +		return -EIO;;
> +	}
> +	return 0;
> +}
> +
> +/* Load firmware to MFC */
> +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
> +{
> +	struct firmware *fw_blob;
> +	int err;
> +	/* Firmare has to be present as a separate file or compiled
> +	 * into kernel. */
> +	mfc_debug("s5p_mfc_load_firmware++\n");
> +	mfc_debug("Requesting fw\n");
> +	err = request_firmware((const struct firmware **)&fw_blob,
> +				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
> +	mfc_debug("Ret of request_firmware: %d Size: %d\n", err, fw_blob-
> >size);
> +	if (err != 0) {
> +		mfc_err("Firmware is not present in the /lib/firmware
> directory"
> +						" nor compiled in
kernel.\n");
> +		return -EINVAL;
> +	}
> +	if (fw_blob->size > FIRMWARE_CODE_SIZE) {
> +		mfc_err("MFC firmware is too big to be loaded.\n");
> +		release_firmware(fw_blob);
> +		return -ENOMEM;
> +	}
> +	if (s5p_mfc_phys_bitproc_buff == 0 || s5p_mfc_phys_bitproc_buff ==
> 0) {
> +		mfc_err("MFC firmware is not allocated or was not mapped "\
>
+								"correctly.\
n");
> +		release_firmware(fw_blob);
> +		return -EINVAL;
> +	}
> +	memcpy(s5p_mfc_virt_bitproc_buff, fw_blob->data, fw_blob->size);
> +/*	If byteswap is needed then use the following instead of memcpy */
> +/*	for (i = 0; i < fw_blob->size; i += 4) {
> +		s5p_mfc_virt_bitproc_buff[i + 0] = fw_blob->data[i + 3];
> +		s5p_mfc_virt_bitproc_buff[i + 1] = fw_blob->data[i + 2];
> +		s5p_mfc_virt_bitproc_buff[i + 2] = fw_blob->data[i + 1];
> +		s5p_mfc_virt_bitproc_buff[i + 3] = fw_blob->data[i + 0];
> +	}*/
> +	release_firmware(fw_blob);
> +	mfc_debug("s5p_mfc_load_firmware--\n");
> +	return 0;
> +}
> +
> +/* Release firmware memory */
> +int s5p_mfc_release_firmware()
> +{
> +	/* Before calling this function one has to make sure
> +	 * that MFC is no longer processing */
> +	if (!s5p_mfc_phys_bitproc_buff)
> +		return -EINVAL;
> +	mfc_debug("s5p_mfc_virt_bitproc_buff: %p\n",
> s5p_mfc_virt_bitproc_buff);
> +	iounmap(s5p_mfc_virt_bitproc_buff);
> +	s5p_mfc_virt_bitproc_buff = NULL;
> +	cma_free(s5p_mfc_phys_bitproc_buff);
> +	s5p_mfc_phys_bitproc_buff = 0;
> +	return 0;
> +}
> +
> +/* Initialize hardware */
> +int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
> +{
> +	int fw_buf_size;
> +	unsigned int fw_version;
> +	int ret;
> +	mfc_debug("s5p_mfc_init_hw++\n");
> +	mfc_debug("Device pointer: %p\n", dev);
> +	if (!s5p_mfc_phys_bitproc_buff)
> +		return -EINVAL;
> +	/* 0. MFC reset */
> +	mfc_debug("MFC reset...\n");
> +	ret = s5p_mfc_cmd_reset(dev);
> +	if (ret) {
> +		mfc_err("Failed to reset MFC - timeout.\n");
> +		return ret;
> +	}
> +	mfc_debug("Done MFC reset...\n");
> +	/* 1. Set DRAM base Addr */
> +	WRITEL(dev->port_a, S5P_FIMV_MC_DRAMBASE_ADR_A); /* channelA, port0
> */
> +	WRITEL(dev->port_b, S5P_FIMV_MC_DRAMBASE_ADR_B); /* channelB, port1
> */
> +	mfc_debug("Port A: %08x, Port B: %08x\n", dev->port_a, dev->port_b);
> +	/* 2. Initialize registers of stream I/F for decoder */
> +	WRITEL(0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
> +	WRITEL(0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
> +	WRITEL(0, S5P_FIMV_RISC2HOST_CMD);
> +	WRITEL(0, S5P_FIMV_HOST2RISC_CMD);
> +	/* 3. Release reset signal to the RISC.  */
> +	WRITEL(0x3ff, S5P_FIMV_SW_RESET);
> +	mfc_debug("Will now wait for completion of firmware transfer.\n");
> +	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET))
> {
> +		mfc_err("Failed to load firmware.\n");
> +		s5p_mfc_clean_dev_int_flags(dev);
> +		return -EIO;
> +	}
> +	s5p_mfc_clean_dev_int_flags(dev);
> +	/* 4. Initialize firmware */
> +	fw_buf_size = FIRMWARE_CODE_SIZE;
> +	mfc_debug("Writing a command\n");
> +	ret = s5p_mfc_cmd_host2risc(dev, 0, S5P_FIMV_H2R_CMD_SYS_INIT,
>
+								fw_buf_size)
;
> +	if (ret) {
> +		mfc_err("Failed to send command to MFC - timeout.\n");
> +		return ret;
> +	}
> +	mfc_debug("Ok, now will write a command to init the system\n");
> +	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET))
> {
> +		mfc_err("Failed to load firmware\n");
> +		return -EIO;
> +	}
> +	dev->int_cond = 0;
> +	if (dev->int_err != 0 || dev->int_type !=
>
+						S5P_FIMV_R2H_CMD_SYS_INIT_RE
T) {
> +		/* Failure. */
> +		mfc_err("Failed to init firmware - error: %d int: %d.\n",
> +			dev->int_err, dev->int_type);
> +		return -EIO;
> +	}
> +	fw_version = READL(S5P_FIMV_FW_VERSION);
> +	mfc_info("MFC FW version : %02xyy, %02xmm, %02xdd\n",
> +			 (fw_version >> 16) & 0xff, (fw_version >> 8) &
0xff,
> +							 (fw_version) &
0xff);
> +	mfc_debug("FW_PHY_BUFFER : 0x%08x\n",
> +		  READL(S5P_FIMV_MC_DRAMBASE_ADR_A));
> +	mfc_debug("DPB_LUMA_BUFFER : 0x%08x\n",
> +		  READL(S5P_FIMV_MC_DRAMBASE_ADR_B));
> +	mfc_debug("s5p_mfc_init_hw--\n");
> +	return 0;
> +}
> +
> +/* Open a new instance and get its number */
> +int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	int ret;
> +	mfc_debug("s5p_mfc_get_inst_no++\n");
> +	mfc_debug("Requested codec mode: %d\n", mfc_ctx->codec_mode);
> +	ret = s5p_mfc_cmd_host2risc(mfc_ctx->dev, mfc_ctx, \
> +			S5P_FIMV_H2R_CMD_OPEN_INSTANCE, mfc_ctx-
>codec_mode);
> +	mfc_debug("s5p_mfc_get_inst_no--\n");
> +	return ret;
> +}
> +
> +/* Close instance */
> +int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	int ret = 0;
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("s5p_mfc_return_inst_no++\n");
> +	if (mfc_ctx->state != MFCINST_FREE) {
> +		ret = s5p_mfc_cmd_host2risc(dev, mfc_ctx,
> +			S5P_FIMV_H2R_CMD_CLOSE_INSTANCE, mfc_ctx->inst_no);
> +	} else {
> +		ret = -EINVAL;
> +	}
> +	mfc_debug("s5p_mfc_return_inst_no--\n");
> +	return ret;
> +}
> +
> +/* Initialize decoding */
> +int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("++\n");
> +
> +	mfc_debug("InstNo: %d/%d\n", mfc_ctx->inst_no,
> S5P_FIMV_CH_SEQ_HEADER);
> +
> +	mfc_debug("BUFs: %08x %08x %08x %08x %08x\n",
> +		  READL(S5P_FIMV_SI_CH0_DESC_ADR),
> +		  READL(S5P_FIMV_SI_CH0_CPB_SIZE),
> +		  READL(S5P_FIMV_SI_CH0_DESC_SIZE),
> +		  READL(S5P_FIMV_SI_CH0_SB_ST_ADR),
> +		  READL(S5P_FIMV_SI_CH0_SB_FRM_SIZE));
> +	/* Setup loop filter, for decoding this is only valid for MPEG4 */
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC) {
> +		mfc_debug("Setting loop filter to: %d\n", \
> +						mfc_ctx->loop_filter_mpeg4);
> +		WRITEL(mfc_ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
> +	} else {
> +		WRITEL(0, S5P_FIMV_ENC_LF_CTRL);
> +	}
> +	WRITEL(((mfc_ctx->slice_interface & 1)<<31) |
> +				((mfc_ctx->display_delay > 0 ? 1 : 0) <<
30) |
> +					((mfc_ctx->display_delay & 0xFF) <<
16),
>
+						S5P_FIMV_SI_CH0_DPB_CONF_CTR
L);
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_DIVX311_DEC) {
> +		mfc_debug("Setting DivX 3.11 resolution to %dx%d\n",
> +			mfc_ctx->img_width, mfc_ctx->img_height);
> +		WRITEL(mfc_ctx->img_width, S5P_FIMV_SI_DIVX311_HRESOL);
> +		WRITEL(mfc_ctx->img_height, S5P_FIMV_SI_DIVX311_VRESOL);
> +	}

This must be a good location for adding 'setting
S5P_FIMV_SI_CH0_HOST_WR_ADR'

> +	WRITEL(((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) | (mfc_ctx-
> >inst_no),
> +						
S5P_FIMV_SI_CH0_INST_ID);
> +	return 0;
> +}
> +
> +/* Decode a single frame */
> +int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int last_frame)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("Setting flags to %08lx (free: %d WTF: %d)\n",
> +				mfc_ctx->dec_dst_flag, mfc_ctx-
>dst_queue_cnt,
> +						mfc_ctx->dec_dst_buf_cnt);
> +	WRITEL(mfc_ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);

You already have reg. setting for S5P_FIMV_SI_CH0_HOST_WR_ADR. 
Good !

> +	/* Issue different commands to instance basing on whether it
> +	 * is the last frame or not. */
> +	if (!last_frame)
> +		WRITEL((S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +	else
> +		WRITEL((S5P_FIMV_CH_LAST_FRAME << 16 & 0x70000) |
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +	mfc_debug("Decoding a usual frame.\n");
> +	return 0;
> +}
> +
> +/* Deinitialize hardware */
> +void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
> +{
> +	s5p_mfc_cmd_reset(dev);
> +}
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> new file mode 100644
> index 0000000..162509f
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> @@ -0,0 +1,90 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * Contains declarations of hw related functions.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_OPR_H_
> +#define S5P_MFC_OPR_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_release_firmware(void);
> +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev);
> +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev);
> +int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
> +
> +int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev);
> +int s5p_mfc_set_sleep(struct s5p_mfc_ctx *mfc_ctx);
> +int s5p_mfc_set_wakeup(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int
do_int);
> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
> buf_addr,
> +						  unsigned int
start_num_byte,
> +						  unsigned int buf_size);
> +
> +int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int
last_frame);
> +
> +/* Instance handling */
> +int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx);
> +int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx);
> +
> +/* Memory allocation */
> +int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
> +
> +/* Getting parameters from MFC */
> +#define s5p_mfc_get_h_crop(ctx)		readl((ctx)->shared_virt + \
> +						S5P_FIMV_SHARED_CROP_INFO_H)
> +#define s5p_mfc_get_v_crop(ctx)		readl((ctx)->shared_virt + \
> +						S5P_FIMV_SHARED_CROP_INFO_V)
> +#define s5p_mfc_get_dspl_y_adr()	(readl(dev->regs_base + \
> +					S5P_FIMV_SI_DISPLAY_Y_ADR) << 11)
> +#define s5p_mfc_get_dspl_status()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_DISPLAY_STATUS)
> +#define s5p_mfc_get_frame_type()	(readl(dev->regs_base + \
> +						S5P_FIMV_DECODE_FRAME_TYPE)
\
> +					& S5P_FIMV_DECODE_FRAME_MASK)
> +#define s5p_mfc_get_consumed_stream()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_DEC_FRM_SIZE)
> +#define s5p_mfc_get_int_reason()	(readl(dev->regs_base + \
> +					S5P_FIMV_RISC2HOST_CMD) & 0x1FFFF)
> +#define s5p_mfc_get_int_err()		readl(dev->regs_base + \
> +						S5P_FIMV_RISC2HOST_ARG2)
> +#define s5p_mfc_get_img_width()		readl(dev->regs_base + \
> +						S5P_FIMV_SI_HRESOL)
> +#define s5p_mfc_get_img_height()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_VRESOL)
> +#define s5p_mfc_get_dpb_count()		readl(dev->regs_base + \
> +						S5P_FIMV_SI_BUF_NUMBER)
> +#define s5p_mfc_get_inst_no()		readl(dev->regs_base + \
> +						S5P_FIMV_RISC2HOST_ARG1)
> +#define s5p_mfc_get_pic_time_top(ctx)	readl((ctx)->shared_virt + \
>
+						S5P_FIMV_SHARED_PIC_TIME_TOP
)
> +#define s5p_mfc_get_pic_time_bottom(ctx) readl((ctx)->shared_virt + \
>
+						S5P_FIMV_SHARED_PIC_TIME_BOT
TOM)
> +
> +/* Interrupt handling routines */
> +#define s5p_mfc_clear_int_flags()				\
> +do {								\
> +	writel(0, dev->regs_base + S5P_FIMV_RISC_HOST_INT);	\
> +	writel(0, dev->regs_base + S5P_FIMV_RISC2HOST_CMD);	\
> +	writel(0xffff, dev->regs_base + S5P_FIMV_SI_RTN_CHID);	\
> +} while (0)
> +#endif /* S5P_MFC_OPR_H_ */
> --
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-14 12:56     ` Kamil Debski
@ 2010-10-15  4:27       ` Jaeryul Oh
  2010-10-20 14:29         ` Kamil Debski
  0 siblings, 1 reply; 18+ messages in thread
From: Jaeryul Oh @ 2010-10-15  4:27 UTC (permalink / raw)
  To: 'Kamil Debski', linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Thank you for your reply about my comments. 
Refer to as below.

k.debski@samsung.com wrote:
> Hi, Peter.
> 
> > I have some first comments about this code.
> > and Generally, Making patch set separately will be more helpful to
> > everyone.
> 
> Thank you for your comments.
> 
> > k.debski@samsung.com wrote:
> > > Multi Format Codec 5.1 is a module available on S5PC110 and S5PC210
> > > Samsung SoCs. Hardware is capable of handling a range of video codecs
> > > and this driver provides V4L2 interface for video decoding.
> > >
> > > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> 
> [...]
> 
> > >
> > > diff --git a/drivers/media/video/Kconfig
> > b/drivers/media/video/Kconfig
> > > index 6d0bd36..1d0b91e 100644
> > > --- a/drivers/media/video/Kconfig
> > > +++ b/drivers/media/video/Kconfig
> > > @@ -1047,4 +1047,12 @@ config  VIDEO_SAMSUNG_S5P_FIMC
> > >  	  This is a v4l2 driver for the S5P camera interface
> > >  	  (video postprocessor)
> > >
> > > +config VIDEO_SAMSUNG_S5P_MFC
> > > +	tristate "Samsung S5P MFC 5.0 Video Codec"
> > > +	depends on VIDEO_V4L2 && CMA
> > > +	select VIDEOBUF2_CMA
> > > +	default n
> > > +	help
> > > +	    MFC 5.0 driver for V4L2.
> > > +
> > >  endif # V4L_MEM2MEM_DRIVERS
> >
> > What about unifying MFC version as a MFC 5.1, because we are using MFC
> > HW
> > ver.(MFC 5.1.x)
> > in the C110/C210 chip.
> 
> Right, will fix this.
> 
> [...]
> 
> > > diff --git a/drivers/media/video/s5p-mfc/regs-mfc5.h
> > > b/drivers/media/video/s5p-mfc/regs-mfc5.h
> > > new file mode 100644
> > > index 0000000..8c628ad
> > > --- /dev/null
> > > +++ b/drivers/media/video/s5p-mfc/regs-mfc5.h
> > > @@ -0,0 +1,305 @@
> > > +/*
> > > + *
> > > + * Register definition file for Samsung MFC V4.0 & V5.0 Interface
> > (FIMV)
> > > driver
> > > + *
> > > + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> > > + * http://www.samsung.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.
> > > +*/
> >
> > Incorrect comment in the header, MFC V4.0 is not included
> >
> 
> Right again.
> 
> > > +
> > > +#ifndef _REGS_FIMV_H
> > > +#define _REGS_FIMV_H
> > > +
> > > +#define S5P_FIMV_REG_SIZE	(S5P_FIMV_END_ADDR -
S5P_FIMV_START_ADDR)
> > > +#define S5P_FIMV_REG_COUNT	((S5P_FIMV_END_ADDR -
> > > S5P_FIMV_START_ADDR) / 4)
> > > +
> > > +#define S5P_FIMV_START_ADDR	0x0000
> > > +#define S5P_FIMV_END_ADDR	0xe008
> > > +
> > > +#define S5P_FIMV_SW_RESET	0x0000
> > > +#define S5P_FIMV_RISC_HOST_INT	0x0008
> > > +/* Command from HOST to RISC */
> > > +#define S5P_FIMV_HOST2RISC_CMD	0x0030
> > > +#define S5P_FIMV_HOST2RISC_ARG1	0x0034
> > > +#define S5P_FIMV_HOST2RISC_ARG2	0x0038
> > > +#define S5P_FIMV_HOST2RISC_ARG3	0x003c
> > > +#define S5P_FIMV_HOST2RISC_ARG4	0x0040
> > > +/* Command from RISC to HOST */
> > > +#define S5P_FIMV_RISC2HOST_CMD	0x0044
> > > +#define S5P_FIMV_RISC2HOST_ARG1	0x0048
> > > +#define S5P_FIMV_RISC2HOST_ARG2	0x004c
> > > +#define S5P_FIMV_RISC2HOST_ARG3	0x0050
> > > +#define S5P_FIMV_RISC2HOST_ARG4	0x0054
> > > +
> > > +#define S5P_FIMV_FW_VERSION	0x0058
> > > +#define S5P_FIMV_SYS_MEM_SZ	0x005c
> > > +#define S5P_FIMV_FW_STATUS	0x0080
> > > +/* Memory controller register */
> > > +#define S5P_FIMV_MC_DRAMBASE_ADR_A	0x0508
> > > +#define S5P_FIMV_MC_DRAMBASE_ADR_B	0x050c
> > > +#define S5P_FIMV_MC_STATUS	0x0510
> > > +
> > > +/***** In case of 2 master *****/
> >
> > This comment(In case of 2 master) is meaningless. it was used at the
> > beginning
> > step of development.
> 
> Will fix this too.
> 
> > > +/* Common register */
> > > +#define S5P_FIMV_SYS_MEM_ADR	0x0600 /* firmware buffer */
> > > +#define S5P_FIMV_CPB_BUF_ADR	0x0604 /* stream buffer */
> > > +#define S5P_FIMV_DESC_BUF_ADR	0x0608 /* descriptor buffer */
> > > +/* H264 decoding */
> > > +#define S5P_FIMV_VERT_NB_MV_ADR	0x068c /* vertical neighbor motion
> > > vector */
> > > +#define S5P_FIMV_VERT_NB_IP_ADR	0x0690 /* neighbor pixels for
> intra
> 
> [...]
> 
> > displayed pic
> > > */
> > > +#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma address of
> > displayed
> > > pic */
> > > +#define S5P_FIMV_SI_DEC_FRM_SIZE 0x2018 /* the number of frames
> > decoded
> > > */
> 
> > S5P_FIMV_SI_DEC_FRM_SIZE does actually means
> > "consumed number of bytes to decode a frame"
> 
> Ok, I've changed the name of the define to reflect the purpose of the
> register.
> 
> > > +#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded
> > picture */
> > > +#define S5P_FIMV_SI_FRAME_TYPE	0x2020 /* frame type such as
> > skip/I/P/B
> > > */
> > > +
> > > +#define S5P_FIMV_SI_CH0_SB_ST_ADR	0x2044 /* start addr of
> > stream buf
> > > */
> > > +#define S5P_FIMV_SI_CH0_SB_FRM_SIZE	0x2048 /* size of stream
> buf
> > */
> > > +#define S5P_FIMV_SI_CH0_DESC_ADR	0x204c /* addr of descriptor buf
> > > */
> > > +/* Encoder for MPEG4 */
> > > +#define S5P_FIMV_ENC_MPEG4_QUART_PXL	0xe008 /* qpel
> interpolation
> > ctrl
> > > */
> > > +
> > > +/* Additional */
> > > +#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL   0x2068 /* DPB Config Control
> > > Register */
> > > +#define S5P_FIMV_SI_CH0_RELEASE_BUF     0x2060 /* DPB release buffer
> > > register */
> > > +#define S5P_FIMV_SI_CH0_HOST_WR_ADR	0x2064
> >
> > S5P_FIMV_SI_CH0_HOST_WR_ADR means 'address of shared memory'
> > if comments is needed
> 
> Added the comment.
> 
> [...]
> 
> > > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c
> > > b/drivers/media/video/s5p-mfc/s5p_mfc.c
> > > new file mode 100644
> > > index 0000000..f549ab6
> > > --- /dev/null
> > > +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
> 
> [...]
> 
> > > +
> > > +void s5p_mfc_error_cleanup_queue(struct list_head *lh, \
> > > +						struct vb2_queue *vq)
> > > +{
> > > +	struct vb2_buffer *b;
> > > +	int i;
> > > +	spin_lock(&dev->irqlock);
> > > +	while (!list_empty(lh)) {
> > > +		b = list_entry(lh->next, struct vb2_buffer, drv_entry);
> > > +		for (i = 0; i < b->num_planes; i++)
> > > +			vb2_set_plane_payload(b, i, 0);
> > > +		spin_unlock(&dev->irqlock);
> > > +		vb2_buffer_done(b, VB2_BUF_STATE_ERROR);
> > > +		spin_lock(&dev->irqlock);
> > > +		list_del(&b->drv_entry);
> > > +	}
> > > +	spin_unlock(&dev->irqlock);
> > > +}
> >
> > How about adding new line b/w local var defintions & running code thru
> > whole function for readability.
> 
> Ok, can do that.
> 
> >
> > > +
> > > +void s5p_mfc_watchdog(unsigned long arg)
> > > +{
> > > +	if (test_bit(0, &dev->hw_lock))
> > > +		atomic_inc(&dev->watchdog_cnt);
> > > +	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
> > > +		/* This means that hw is busy and no interrupts were
> > > +		 * generated by hw for the Nth time of running this
> > > +		 * watchdog timer. This usually means a serious hw
> > > +		 * error. Now it is time to kill all instances and
> > > +		 * reset the MFC. */
> > > +		mfc_err("Time out during waiting for HW.\n");
> > > +		queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
> > > +	}
> > > +	dev->watchdog_timer.expires +=
> > > msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
> > > +	add_timer(&dev->watchdog_timer);
> > > +}
> > > +
> 
> [...]
> 
> > > +			if (mutex_locked)
> > > +				mutex_unlock(dev->mfc_mutex);
> > > +			return;
> > > +		}
> > > +	}
> > > +	if (mutex_locked)
> > > +		mutex_unlock(dev->mfc_mutex);
> > > +}
> >
> > Does MFC_NUM_CONTEXTS means max num of contexts ? what about
> > MFC_MAX_CONTEXT_NUM ?
> > and this number is absolutely dependent on what max size used,
> > what kinds of codec used, etc so, you had better use configuration
> > param.
> > for example>
> >    #ifdef CONFIG_VIDEO_MFC_MAX_INSTANCE
> >    #define MFC_MAX_CONTEXT_NUM (CONFIG_VIDEO_MFC_MAX_INSTANCE)
> >    #endif
> 
> I don't know if this is necessary. MFC_NUM_CONTEXTS can be fixed at
> the maximum number allowed by MFC hw: 16. I highly doubt someone
> will open that many contexts. Increasing this number will not
> significantly increase storage space used by MFC if no contexts are
> used. It will only change size of one pointer array
> ( struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS]; ).
> 
In many project, user can open many contexts according to the scenario of
not only phone but also netbook, tablet. 'cause MFC supports multiple
instance.
That's why I suggested this param. be configurable.
> >
> > > +
> > > +/* Check whether a context should be run on hardware */
> > > +int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
> > > +{
> > > +	mfc_debug("s5p_mfc_ctx_ready: src=%d, dst=%d, state=%d\n",
> > > +		  ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
> > > +	/* Context is to parse header */
> > > +	if (ctx->src_queue_cnt >= 1 && ctx->state ==
> > MFCINST_DEC_GOT_INST)
> > > +		return 1;
> > > +	/* Context is to decode a frame */
> > > +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_RUNNING
> > &&
> > > +					ctx->dst_queue_cnt >= ctx-
> > >dpb_count)
> > > +		return 1;
> > > +	/* Context is to return last frame */
> > > +	if (ctx->state == MFCINST_DEC_FINISHING &&
> > > +	    ctx->dst_queue_cnt >= ctx->dpb_count)
> > > +		return 1;
> > > +	/* Context is to set buffers */
> > > +	if (ctx->src_queue_cnt >= 1 &&
> > > +	    ctx->state == MFCINST_DEC_HEAD_PARSED &&
> > > +	    ctx->capture_state == QUEUE_BUFS_MMAPED)
> > > +		return 1;
> > > +	mfc_debug("s5p_mfc_ctx_ready: ctx is not ready.\n");
> > > +	return 0;
> > > +}
> > > +
> 
> [...]
> 
> > > +/* Reqeust buffers */
> > > +static int vidioc_reqbufs(struct file *file, void *priv,
> > > +					  struct v4l2_requestbuffers
> > *reqbufs)
> > > +{
> > > +	struct s5p_mfc_ctx *ctx = priv;
> > > +	int ret = 0;
> > > +	mfc_debug("vidioc_reqbufs++\n");
> > > +	mfc_debug("Memory type: %d\n", reqbufs->memory);
> > > +	if (reqbufs->memory != V4L2_MEMORY_MMAP) {
> > > +		mfc_err("Only V4L2_MEMORY_MAP is supported.\n");
> > > +		return -EINVAL;
> > > +	}
> > > +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> > > +		/* Can only request buffers after an instance has been
> > > opened.*/
> > > +		if (ctx->state == MFCINST_DEC_GOT_INST) {
> > > +			/* Decoding */
> > > +			if (ctx->output_state != QUEUE_FREE) {
> > > +				mfc_err("Bufs have already been
> > requested.\n");
> > > +				return -EINVAL;
> > > +			}
> > > +			ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
> > > +			if (ret) {
> > > +				mfc_err("vb2_reqbufs on output failed.\n");
> > > +				return ret;
> > > +			}
> > > +			mfc_debug("vb2_reqbufs: %d\n", ret);
> > > +			ctx->output_state = QUEUE_BUFS_REQUESTED;
> > > +		}
> > > +	}
> > > +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> > > +		if (ctx->capture_state != QUEUE_FREE) {
> > > +			mfc_err("Bufs have already been requested.\n");
> > > +			return -EINVAL;
> > > +		}
> > > +		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> > > +		if (ret) {
> > > +			mfc_err("vb2_reqbufs on output failed.\n");
> >
> > error message should be mfc_err("vb2_reqbufs on capture failed.\n");
> 
> Right.
> 
> >
> > > +			return ret;
> > > +		}
> > > +		if (reqbufs->count < ctx->dpb_count) {
> > > +			mfc_err("Not enough buffers allocated.\n");
> > > +			reqbufs->count = 0;
> > > +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> > > +			return -ENOMEM;
> > > +		}
> > > +		ctx->total_dpb_count = reqbufs->count;
> >
> > if 'ctx->total_dpb_count = reqbufs->count;' is here in code, Hmm....
> > let's suppose that reqbufs->count = 16 & ctx->dpb_count = 10,
> > in that case, ctx->total_dpb_count is 16,
> > but *buf_count(in the s5p_mfc_buf_negotiate()) is 15
> > so, it is not matched & it affect __vb2_queue_alloc(xx, num_buffers,
> > xx)
> > i think, is is due to location of 'the ctx->total_dpb_count = reqbufs-
> > >count;'
> > and No warning message for exceeding MFC_MAX_EXTRA_DPB
> > what do you think about that ?
> 
> The number of buffers requested by the user is stored in reqbufs->count.
> Then vb2_reqbufs() is called, which calls the s5p_mfc_buf_negotiate()
> callback function. It may change the number of buffers to allocate.
> Then vb2_reqbufs() tries to allocate the number of buffers returned in
> *buf_count, but it may allocate less if it runs out of memory. If so it
> will adjust reqbufs->count accordingly.
> 
> Now the driver has to check if reqbufs->count is less than the minimum
> number of buffers required by MFC. If so it has to free the allocated
> buffers and fail with -ENOMEM, as further processing is impossible.
> The number of allocated buffers can be smaller than *buf_count set in
> s5p_mfc_buf_negotiate() yet still enough for MFC to work. So I think
> this place to set ctx->total_dpb_count is good.
> 
> In the scenario you have given as an example the s5p_mfc_buf_negotiate()
> would change the value given by the user application from 16 to 15 and
> this would be the number that vb2_reqbufs tries to allocate. If there
> is little memory then it could allocate only 14 buffers, 15 if there is
> enough memory. Then ctx->total_dpb_count would be set to reqbufs->count
> after call to vb2_reqbufs (which is 14 or 15 depending on the available
> memory).
Yes, what you said looks right. I missed some code sequence.
Thanks
> 
> > > +		ret = s5p_mfc_alloc_dec_buffers(ctx);
> > > +		if (ret) {
> > > +			mfc_err("Failed to allocate decoding buffers.\n");
> > > +			reqbufs->count = 0;
> > > +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> > > +			return -ENOMEM;
> > > +		}
> > > +		ctx->capture_state = QUEUE_BUFS_REQUESTED;
> > > +	}
> > > +	mfc_debug("vidioc_reqbufs--\n");
> > > +	return ret;
> > > +}
> > > +
> 
> [...]
> 
> > > +/* Handle frame decoding interrupt */
> > > +static void s5p_mfc_handle_frame_int(struct s5p_mfc_ctx *ctx, \
> > > +					unsigned int reason, unsigned int
> > err)
> > > +{
> 
> [...]
> 
> > > +	/* A frame has been decoded and is in the buffer  */
> > > +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
> > > +	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
> > > +		ctx->sequence++;
> > > +		/* If frame is same as previous then skip and do not
> > dequeue
> > > */
> > > +		if (dec_frame_type !=  S5P_FIMV_DECODE_FRAME_SKIPPED) {
> > > +		/* The MFC returns address of the buffer, now we have to
> > > +		 * check which videobuf does it correspond to */
> > > +		list_for_each_entry(dst_buf, &ctx->dst_queue, drv_entry) {
> > > +			mfc_debug("Listing: %d\n", dst_buf->v4l2_buf.index);
> > > +			/* This is the buffer we're looking for */
> > > +			mfc_debug("paddr: %p mfc: %p\n",
> > > +					(void *)vb2_plane_paddr(dst_buf, 1),
> > > +							(void
> > *)dst_ret_addr);
> >
> > what is the purpose of this debug message ?
> > mfc_debug("paddr: %p mfc: %p\n",(void *)vb2_plane_paddr(dst_buf, 1),
> >                                                                 (void
> > *)dst_ret_addr);
> > do you want to compare Y address?
> > it should be like this, vb2_plane_paddr(dst_buf, 0) with dst_ret_addr.
> > and dst_y_ret_addr is better than dst_ret_addr,
> > making debug msg clear is important 'cause codec debugging is not easy.
> 
> You're right that this debug is unclear and displays the wrong value.
> Changing dst_ret_addr name to dst_ret_addr_y seems very reasonable to
> make the code more clear. I should remove many debug messages before
> posting the final version. They were very useful during development,
> but they may clutter the code too much.
Let's get make better together
> 
> > > +			if (vb2_plane_paddr(dst_buf, 0) == dst_ret_addr) {
> > > +				list_del(&dst_buf->drv_entry);
> > > +				ctx->dst_queue_cnt--;
> > > +				mfc_debug("Flag before: %lx (%d)\n",
> > > +							ctx->dec_dst_flag,
> > > +						dst_buf->v4l2_buf.index);
> > > +				dst_buf->v4l2_buf.sequence = ctx->sequence;
> 
> 
> [...]
> 
> > > +/* Interrupt processing */
> > > +static irqreturn_t s5p_mfc_irq(int irq, void *priv)
> > > +{
> > > +	struct vb2_buffer *src_buf;
> > > +	struct s5p_mfc_ctx *ctx;
> > > +	unsigned int reason;
> > > +	unsigned int err;
> > > +	mfc_debug("s5p_mfc_irq++\n");
> > > +	/* Reset the timeout watchdog */
> > > +	atomic_set(&dev->watchdog_cnt, 0);
> > > +	ctx = dev->ctx[dev->curr_ctx];
> > > +	/* Get the reason of interrupt and the error code */
> > > +	reason = s5p_mfc_get_int_reason();
> > > +	err = s5p_mfc_get_int_err();
> > > +	mfc_debug("Int reason: %d (error: %08x)\n", reason, err);
> > > +	switch (reason) {
> > > +	case S5P_FIMV_R2H_CMD_DECODE_ERR_RET:
> > > +		/* An error has occured */
> > > +		if (ctx->state == MFCINST_DEC_RUNNING && err >= 145)
> >
> > What about using MACRO instead of using 145(no)
> 
> Yes, will definitely change this.
> 
> [...]
> 
> > > +/* MFC probe function */
> > > +static int s5p_mfc_probe(struct platform_device *pdev)
> > > +{
> > > +	struct video_device *vfd;
> > > +	struct resource *res;
> > > +	int ret = -ENOENT;
> > > +	size_t size;
> > > +	mfc_debug("s5p_mfc_probe++\n");
> > > +	dev = kzalloc(sizeof *dev, GFP_KERNEL);
> > > +	if (!dev) {
> > > +		dev_err(&pdev->dev, "Not enough memoty for MFC device.\n");
> >
> > incorrect spelling (memoty)
> 
> Yes, thanks.
> 
> [...]
> 
> > > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> > > b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> > > new file mode 100644
> > > index 0000000..90fa84c
> > > --- /dev/null
> > > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> 
> [...]
> 
> > > +/* Uncomment the line below do enable debug messages */
> > > +/* #define CONFIG_VIDEO_MFC50_DEBUG */
> >
> > why don't you make configuration(CONFIG_VIDEO_MFC51_DEBUG)
> > in the Kconfig.
> 
> I am planning to do this as a module parameter, so it could
> be set when loading the module.
> 
> Best regards,
> Kamil Debski
> 


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
  2010-10-14  6:25   ` Jaeryul Oh
  2010-10-15  4:06   ` Jaeryul Oh
@ 2010-10-18  8:30   ` Jaeryul Oh
  2010-10-20 14:30     ` Kamil Debski
  2010-10-21 10:59   ` Jaeryul Oh
  2010-10-26 11:58   ` Jaeryul Oh
  4 siblings, 1 reply; 18+ messages in thread
From: Jaeryul Oh @ 2010-10-18  8:30 UTC (permalink / raw)
  To: 'Kamil Debski', linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Hi, Kamil
This is third feedback about watchdog timer. 
(s5p_mfc.c)

k.debski@xxxxxxxxxxx wrote:
> Multi Format Codec 5.1 is a module available on S5PC110 and S5PC210
> Samsung SoCs. Hardware is capable of handling a range of video codecs
> and this driver provides V4L2 interface for video decoding.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig                  |    8 +
>  drivers/media/video/Makefile                 |    1 +
>  drivers/media/video/s5p-mfc/Makefile         |    3 +
>  drivers/media/video/s5p-mfc/regs-mfc5.h      |  305 +++++
>  drivers/media/video/s5p-mfc/s5p_mfc.c        | 1879
> ++++++++++++++++++++++++++
>  drivers/media/video/s5p-mfc/s5p_mfc_common.h |  190 +++
>  drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h  |  173 +++
>  drivers/media/video/s5p-mfc/s5p_mfc_intr.c   |   77 ++
>  drivers/media/video/s5p-mfc/s5p_mfc_intr.h   |   26 +
>  drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h |   65 +
>  drivers/media/video/s5p-mfc/s5p_mfc_memory.h |   32 +
>  drivers/media/video/s5p-mfc/s5p_mfc_opr.c    |  800 +++++++++++
>  drivers/media/video/s5p-mfc/s5p_mfc_opr.h    |   90 ++
>  13 files changed, 3649 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/s5p-mfc/Makefile
>  create mode 100644 drivers/media/video/s5p-mfc/regs-mfc5.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_common.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_intr.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_memory.h
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.c
>  create mode 100644 drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 6d0bd36..1d0b91e 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -1047,4 +1047,12 @@ config  VIDEO_SAMSUNG_S5P_FIMC
>  	  This is a v4l2 driver for the S5P camera interface
>  	  (video postprocessor)
> 
> +config VIDEO_SAMSUNG_S5P_MFC
> +	tristate "Samsung S5P MFC 5.0 Video Codec"
> +	depends on VIDEO_V4L2 && CMA
> +	select VIDEOBUF2_CMA
> +	default n
> +	help
> +	    MFC 5.0 driver for V4L2.
> +
>  endif # V4L_MEM2MEM_DRIVERS
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 4146700..117a3cb 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -178,6 +178,7 @@ obj-$(CONFIG_VIDEO_PXA27x)		+=
pxa_camera.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2)	+= sh_mobile_csi2.o
>  obj-$(CONFIG_VIDEO_SH_MOBILE_CEU)	+= sh_mobile_ceu_camera.o
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
> +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
> 
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
> 
> diff --git a/drivers/media/video/s5p-mfc/Makefile
> b/drivers/media/video/s5p-mfc/Makefile
> new file mode 100644
> index 0000000..69b6294
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
> +s5p-mfc-y := s5p_mfc.o s5p_mfc_intr.o  s5p_mfc_opr.o
> +
> diff --git a/drivers/media/video/s5p-mfc/regs-mfc5.h
> b/drivers/media/video/s5p-mfc/regs-mfc5.h
> new file mode 100644
> index 0000000..8c628ad
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/regs-mfc5.h
> @@ -0,0 +1,305 @@
> +/*
> + *
> + * Register definition file for Samsung MFC V4.0 & V5.0 Interface (FIMV)
> driver
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> +*/
> +
> +#ifndef _REGS_FIMV_H
> +#define _REGS_FIMV_H
> +
> +#define S5P_FIMV_REG_SIZE	(S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
> +#define S5P_FIMV_REG_COUNT	((S5P_FIMV_END_ADDR -
> S5P_FIMV_START_ADDR) / 4)
> +
> +#define S5P_FIMV_START_ADDR	0x0000
> +#define S5P_FIMV_END_ADDR	0xe008
> +
> +#define S5P_FIMV_SW_RESET	0x0000
> +#define S5P_FIMV_RISC_HOST_INT	0x0008
> +/* Command from HOST to RISC */
> +#define S5P_FIMV_HOST2RISC_CMD	0x0030
> +#define S5P_FIMV_HOST2RISC_ARG1	0x0034
> +#define S5P_FIMV_HOST2RISC_ARG2	0x0038
> +#define S5P_FIMV_HOST2RISC_ARG3	0x003c
> +#define S5P_FIMV_HOST2RISC_ARG4	0x0040
> +/* Command from RISC to HOST */
> +#define S5P_FIMV_RISC2HOST_CMD	0x0044
> +#define S5P_FIMV_RISC2HOST_ARG1	0x0048
> +#define S5P_FIMV_RISC2HOST_ARG2	0x004c
> +#define S5P_FIMV_RISC2HOST_ARG3	0x0050
> +#define S5P_FIMV_RISC2HOST_ARG4	0x0054
> +
> +#define S5P_FIMV_FW_VERSION	0x0058
> +#define S5P_FIMV_SYS_MEM_SZ	0x005c
> +#define S5P_FIMV_FW_STATUS	0x0080
> +/* Memory controller register */
> +#define S5P_FIMV_MC_DRAMBASE_ADR_A	0x0508
> +#define S5P_FIMV_MC_DRAMBASE_ADR_B	0x050c
> +#define S5P_FIMV_MC_STATUS	0x0510
> +
> +/***** In case of 2 master *****/
> +/* Common register */
> +#define S5P_FIMV_SYS_MEM_ADR	0x0600 /* firmware buffer */
> +#define S5P_FIMV_CPB_BUF_ADR	0x0604 /* stream buffer */
> +#define S5P_FIMV_DESC_BUF_ADR	0x0608 /* descriptor buffer */
> +/* H264 decoding */
> +#define S5P_FIMV_VERT_NB_MV_ADR	0x068c /* vertical neighbor motion
> vector */
> +#define S5P_FIMV_VERT_NB_IP_ADR	0x0690 /* neighbor pixels for intra
pred
> */
> +#define S5P_FIMV_H264_LUMA_ADR	0x0700 /* Luma0 ~ Luma18 700 */
> +#define S5P_FIMV_H264_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma18 614
> */
> +#define S5P_FIMV_MV_ADR		0x0780 /* H264 motion vector 660
780 */
> +/* H263/MPEG4/MPEG2/VC-1/ decoding */
> +#define S5P_FIMV_NB_DCAC_ADR	0x068c /* neighbor AC/DC coeff. buffer
> */
> +#define S5P_FIMV_UP_NB_MV_ADR	0x0690 /* upper neighbor motion
vector
> buffer */
> +#define S5P_FIMV_SA_MV_ADR	0x0694 /* subseq. anchor motion vector
> buffer */
> +#define S5P_FIMV_OT_LINE_ADR	0x0698 /* overlap transform line buffer
> */
> +#define S5P_FIMV_BITPLANE3_ADR	0x069C /* bitplane3 addr */
> +#define S5P_FIMV_BITPLANE2_ADR	0x06A0 /* bitplane2 addr */
> +#define S5P_FIMV_BITPLANE1_ADR	0x06A4 /* bitplane1 addr */
> +#define S5P_FIMV_SP_ADR		0x06A8 /* syntax parser addr */
> +#define S5P_FIMV_LUMA_ADR	0x0700 /* Luma0 ~ Luma5 */
> +#define S5P_FIMV_CHROMA_ADR	0x0600 /* Chroma0 ~ Chroma5 */
> +/* Encoder register */
> +#define S5P_FIMV_ENC_UP_MV_ADR		0x0600 /* upper motion
vector addr
> */
> +#define S5P_FIMV_ENC_COZERO_FLAG_ADR	0x0610 /* direct cozero flag addr
> */
> +#define S5P_FIMV_ENC_UP_INTRA_MD_ADR	0x0608 /* upper intra MD addr */
> +#define S5P_FIMV_ENC_UP_INTRA_PRED_ADR	0x0740 /* upper intra PRED
addr */
> +#define S5P_FIMV_ENC_NB_DCAC_ADR	0x0604 /* entropy engine's
> neighbor
> +						inform and AC/DC coeff. */
> +
> +#define S5P_FIMV_ENC_CUR_LUMA_ADR	0x0718 /* current Luma addr */
> +#define S5P_FIMV_ENC_CUR_CHROMA_ADR	0x071C /* current Chroma addr */
> +
> +#define S5P_FIMV_ENC_REF0_LUMA_ADR	0x061c /* ref0 Luma addr */
> +#define S5P_FIMV_ENC_REF0_CHROMA_ADR	0x0700 /* ref0 Chroma addr */
> +#define S5P_FIMV_ENC_REF1_LUMA_ADR	0x0620 /* ref1 Luma addr */
> +#define S5P_FIMV_ENC_REF1_CHROMA_ADR	0x0704 /* ref1 Chroma addr */
> +#define S5P_FIMV_ENC_REF2_LUMA_ADR	0x0710 /* ref2 Luma addr */
> +#define S5P_FIMV_ENC_REF2_CHROMA_ADR	0x0708 /* ref2 Chroma addr */
> +#define S5P_FIMV_ENC_REF3_LUMA_ADR	0x0714 /* ref3 Luma addr */
> +#define S5P_FIMV_ENC_REF3_CHROMA_ADR	0x070c /* ref3 Chroma addr */
> +
> +/* Codec common register */
> +#define S5P_FIMV_ENC_HSIZE_PX		0x0818 /* frame width at
encoder
> */
> +#define S5P_FIMV_ENC_VSIZE_PX		0x081c /* frame height at
encoder
> */
> +#define S5P_FIMV_ENC_PROFILE		0x0830 /* profile register */
> +#define S5P_FIMV_ENC_PIC_STRUCT		0x083c /* picture
field/frame flag
> */
> +#define S5P_FIMV_ENC_LF_CTRL		0x0848 /* loop filter control */
> +#define S5P_FIMV_ENC_ALPHA_OFF		0x084c /* loop filter alpha
offset
> */
> +#define S5P_FIMV_ENC_BETA_OFF		0x0850 /* loop filter beta
offset
> */
> +#define S5P_FIMV_MR_BUSIF_CTRL		0x0854 /* hidden, bus
interface
> ctrl */
> +#define S5P_FIMV_ENC_PXL_CACHE_CTRL	0x0a00 /* pixel cache control */
> +
> +/* Channel & stream interface register */
> +#define S5P_FIMV_SI_RTN_CHID	0x2000 /* Return CH instance ID register
> */
> +#define S5P_FIMV_SI_CH0_INST_ID	0x2040 /* codec instance ID */
> +#define S5P_FIMV_SI_CH1_INST_ID	0x2080 /* codec instance ID */
> +/* Decoder */
> +#define S5P_FIMV_SI_VRESOL	0x2004 /* vertical resolution of decoder
> */
> +#define S5P_FIMV_SI_HRESOL	0x2008 /* horizontal resolution of
> decoder */
> +#define S5P_FIMV_SI_BUF_NUMBER	0x200c /* number of frames in the
> decoded pic */
> +#define S5P_FIMV_SI_DISPLAY_Y_ADR 0x2010 /* luma address of displayed pic
> */
> +#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma address of displayed
> pic */
> +#define S5P_FIMV_SI_DEC_FRM_SIZE 0x2018 /* the number of frames decoded
> */
> +#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded picture */
> +#define S5P_FIMV_SI_FRAME_TYPE	0x2020 /* frame type such as
skip/I/P/B
> */
> +
> +#define S5P_FIMV_SI_CH0_SB_ST_ADR	0x2044 /* start addr of stream buf
> */
> +#define S5P_FIMV_SI_CH0_SB_FRM_SIZE	0x2048 /* size of stream buf */
> +#define S5P_FIMV_SI_CH0_DESC_ADR	0x204c /* addr of descriptor buf
> */
> +#define S5P_FIMV_SI_CH0_CPB_SIZE	0x2058 /* max size of coded pic.
> buf */
> +#define S5P_FIMV_SI_CH0_DESC_SIZE	0x205c /* max size of descriptor
> buf */
> +
> +#define S5P_FIMV_SI_CH1_SB_ST_ADR	0x2084 /* start addr of stream buf
> */
> +#define S5P_FIMV_SI_CH1_SB_FRM_SIZE	0x2088 /* size of stream buf */
> +#define S5P_FIMV_SI_CH1_DESC_ADR	0x208c /* addr of descriptor buf
> */
> +#define S5P_FIMV_SI_CH1_CPB_SIZE	0x2098 /* max size of coded pic.
> buf */
> +#define S5P_FIMV_SI_CH1_DESC_SIZE	0x209c /* max size of descriptor
> buf */
> +
> +#define S5P_FIMV_SI_DIVX311_HRESOL	0x2054 /* horizontal resolution */
> +#define S5P_FIMV_SI_DIVX311_VRESOL	0x2050 /* vertical resolution */
> +#define S5P_FIMV_CRC_LUMA0	0x2030 /* luma crc data per frame(top
> field)*/
> +#define S5P_FIMV_CRC_CHROMA0	0x2034 /* chroma crc data per frame(top
> field)*/
> +#define S5P_FIMV_CRC_LUMA1	0x2038 /* luma crc data per bottom field
> */
> +#define S5P_FIMV_CRC_CHROMA1	0x203c /* chroma crc data per bottom
> field */
> +
> +/* Display status */
> +#define S5P_FIMV_DEC_STATUS_DECODING_ONLY		0
> +#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY		1
> +#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY		2
> +#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY		3
> +#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK	7
> +#define S5P_FIMV_DEC_STATUS_PROGRESSIVE			(0<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE			(1<<3)
> +#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK		(1<<3)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO		(0<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR		(1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK		(1<<4)
> +#define S5P_FIMV_DEC_STATUS_CRC_GENERATED		(1<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED		(0<<5)
> +#define S5P_FIMV_DEC_STATUS_CRC_MASK			(1<<5)
> +
> +/* Decode frame address */
> +#define S5P_FIMV_DECODE_Y_ADR			0x2024
> +#define S5P_FIMV_DECODE_C_ADR			0x2028
> +
> +/* Decoded frame tpe */
> +#define S5P_FIMV_DECODE_FRAME_TYPE		0x2020
> +#define S5P_FIMV_DECODE_FRAME_MASK		7
> +
> +#define S5P_FIMV_DECODE_FRAME_SKIPPED		0
> +#define S5P_FIMV_DECODE_FRAME_I_FRAME		1
> +#define S5P_FIMV_DECODE_FRAME_P_FRAME		2
> +#define S5P_FIMV_DECODE_FRAME_202_FRAME		3
> +#define S5P_FIMV_DECODE_FRAME_OTHER_FRAME	4
> +
> +/* Sizes of buffers required for decoding */
> +#define S5P_FIMV_DEC_NB_IP_SIZE			(32*1024)
> +#define S5P_FIMV_DEC_VERT_NB_MV_SIZE		(16*1024)
> +#define S5P_FIMV_DEC_NB_DCAC_SIZE		(16*1024)
> +#define S5P_FIMV_DEC_UPNB_MV_SIZE		(68*1024)
> +#define S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE		(136*1024)
> +#define S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE     (32*1024)
> +#define S5P_FIMV_DEC_VC1_BITPLANE_SIZE		(2*1024)
> +#define S5P_FIMV_DEC_STX_PARSER_SIZE		(68*1024)
> +
> +
> +/* Encoder */
> +#define S5P_FIMV_ENC_SI_STRM_SIZE	0x2004 /* stream size */
> +#define S5P_FIMV_ENC_SI_PIC_CNT		0x2008 /* picture count */
> +#define S5P_FIMV_ENC_SI_WRITE_PTR	0x200c /* write pointer */
> +#define S5P_FIMV_ENC_SI_SLICE_TYPE	0x2010 /* slice type(I/P/B/IDR) */
> +
> +#define S5P_FIMV_ENC_SI_CH0_SB_U_ADR	0x2044 /* addr of upper stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH0_SB_L_ADR	0x2048 /* addr of lower stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH0_SB_SIZE	0x204c /* size of stream buf */
> +#define S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR	0x2050 /* current Luma addr
*/
> +#define S5P_FIMV_ENC_SI_CH0_CUR_C_ADR	0x2054 /* current Chroma
addr */
> +#define S5P_FIMV_ENC_SI_CH0_FRAME_QP	0x2058 /* frame QP */
> +#define S5P_FIMV_ENC_SI_CH0_SLICE_ARG	0x205c /* slice argument */
> +
> +#define S5P_FIMV_ENC_SI_CH1_SB_U_ADR	0x2084 /* addr of upper stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH1_SB_L_ADR	0x2088 /* addr of lower stream buf
> */
> +#define S5P_FIMV_ENC_SI_CH1_SB_SIZE	0x208c /* size of stream buf */
> +#define S5P_FIMV_ENC_SI_CH1_CUR_Y_ADR	0x2090 /* current Luma addr
*/
> +#define S5P_FIMV_ENC_SI_CH1_CUR_C_ADR	0x2094 /* current Chroma
addr */
> +#define S5P_FIMV_ENC_SI_CH1_FRAME_QP	0x2098 /* frame QP */
> +#define S5P_FIMV_ENC_SI_CH1_SLICE_ARG	0x209c /* slice argument */
> +
> +#define S5P_FIMV_ENC_STR_BF_U_FULL	0xc004 /* upper stream buf full */
> +#define S5P_FIMV_ENC_STR_BF_U_EMPTY	0xc008 /* upper stream buf empty
> */
> +#define S5P_FIMV_ENC_STR_BF_L_FULL	0xc00c /* lower stream buf full */
> +#define S5P_FIMV_ENC_STR_BF_L_EMPTY	0xc010 /* lower stream buf empty
> */
> +#define S5P_FIMV_ENC_STR_STATUS		0xc018 /* stream buf
interrupt
> status */
> +#define S5P_FIMV_ENC_SF_EPB_ON_CTRL	0xc054 /* stream control */
> +#define S5P_FIMV_ENC_SF_BUF_CTRL	0xc058 /* buffer control */
> +#define S5P_FIMV_ENC_BF_MODE_CTRL	0xc05c /* fifo level control */
> +
> +#define S5P_FIMV_ENC_PIC_TYPE_CTRL	0xc504 /* pic type level control
> */
> +#define S5P_FIMV_ENC_B_RECON_WRITE_ON	0xc508 /* B frame recon
write ctrl
> */
> +#define S5P_FIMV_ENC_MSLICE_CTRL	0xc50c /* multi slice control */
> +#define S5P_FIMV_ENC_MSLICE_MB		0xc510 /* MB number in the
one
> slice */
> +#define S5P_FIMV_ENC_MSLICE_BYTE	0xc514 /* byte number for one
> slice */
> +#define S5P_FIMV_ENC_CIR_CTRL		0xc518 /* number of intra
refresh
> MB */
> +#define S5P_FIMV_ENC_MAP_FOR_CUR	0xc51c /* linear or 64x32 tiled
> mode */
> +#define S5P_FIMV_ENC_PADDING_CTRL	0xc520 /* padding control */
> +#define S5P_FIMV_ENC_INT_MASK		0xc528 /* interrupt mask */
> +
> +#define S5P_FIMV_ENC_RC_CONFIG		0xc5a0 /* RC config */
> +#define S5P_FIMV_ENC_RC_FRAME_RATE	0xc5a4 /* frame rate */
> +#define S5P_FIMV_ENC_RC_BIT_RATE	0xc5a8 /* bit rate */
> +#define S5P_FIMV_ENC_RC_QBOUND		0xc5ac /* max/min QP */
> +#define S5P_FIMV_ENC_RC_RPARA		0xc5b0 /* rate control
reaction
> coeff */
> +#define S5P_FIMV_ENC_RC_MB_CTRL		0xc5b4 /* MB adaptive
scaling */
> +
> +/* Encoder for H264 */
> +#define S5P_FIMV_ENC_ENTRP_MODE		0xd004 /* CAVLC or CABAC */
> +#define S5P_FIMV_ENC_H264_ALPHA_OFF	0xd008 /* loop filter alpha offset
> */
> +#define S5P_FIMV_ENC_H264_BETA_OFF	0xd00c /* loop filter beta offset
> */
> +#define S5P_FIMV_ENC_H264_NUM_OF_REF	0xd010 /* number of reference for
> P/B */
> +#define S5P_FIMV_ENC_H264_MDINTER_WGT	0xd01c /* inter weighted
parameter
> */
> +#define S5P_FIMV_ENC_H264_MDINTRA_WGT	0xd020 /* intra weighted
parameter
> */
> +#define S5P_FIMV_ENC_H264_TRANS_FLAG	0xd034 /* 8x8 transform flag in
> PPS &
> +								high
profile */
> +/* Encoder for MPEG4 */
> +#define S5P_FIMV_ENC_MPEG4_QUART_PXL	0xe008 /* qpel interpolation ctrl
> */
> +
> +/* Additional */
> +#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL   0x2068 /* DPB Config Control
> Register */
> +#define S5P_FIMV_SI_CH0_RELEASE_BUF     0x2060 /* DPB release buffer
> register */
> +#define S5P_FIMV_SI_CH0_HOST_WR_ADR	0x2064
> +
> +/* #define S5P_FIMV_RET_VALUE              0xc08 */
> +#define S5P_FIMV_ENC_B_RECON_WRITE_ON   0xc508 /* B frame recon write
> ctrl */
> +
> +#define S5P_FIMV_ENC_REF_B_LUMA_ADR     0x062c /* ref B Luma addr */
> +#define S5P_FIMV_ENC_REF_B_CHROMA_ADR   0x0630 /* ref B Chroma addr */
> +
> +#define S5P_FIMV_ENCODED_Y_ADDR         0x2014 /* the address of the
> encoded
> +							luminance picture */
> +#define S5P_FIMV_ENCODED_C_ADDR         0x2018 /* the address of the
> encoded
> +							chrominance
picture*/
> +
> +/* Codec numbers  */
> +#define S5P_FIMV_CODEC_H264_DEC			0
> +#define S5P_FIMV_CODEC_VC1_DEC			1
> +#define S5P_FIMV_CODEC_MPEG4_DEC		2
> +#define S5P_FIMV_CODEC_MPEG2_DEC		3
> +#define S5P_FIMV_CODEC_H263_DEC			4
> +#define S5P_FIMV_CODEC_VC1RCV_DEC		5
> +#define S5P_FIMV_CODEC_DIVX311_DEC		6
> +#define S5P_FIMV_CODEC_DIVX412_DEC		7
> +#define S5P_FIMV_CODEC_DIVX502_DEC		8
> +#define S5P_FIMV_CODEC_DIVX503_DEC		9
> +
> +#define S5P_FIMV_CODEC_H264_ENC			16
> +#define S5P_FIMV_CODEC_MPEG4_ENC		17
> +#define S5P_FIMV_CODEC_H263_ENC			18
> +
> +/* Channel Control Register */
> +#define S5P_FIMV_CH_SEQ_HEADER		1
> +#define S5P_FIMV_CH_FRAME_START		2
> +#define S5P_FIMV_CH_LAST_FRAME		3
> +#define S5P_FIMV_CH_INIT_BUFS		4
> +#define S5P_FIMV_CH_FRAME_START_REALLOC	5
> +
> +
> +/* Host to RISC command */
> +#define S5P_FIMV_H2R_CMD_EMPTY		0
> +#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE	1
> +#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE	2
> +#define S5P_FIMV_H2R_CMD_SYS_INIT	3
> +
> +#define S5P_FIMV_R2H_CMD_EMPTY			0
> +#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET	1
> +#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET	2
> +#define S5P_FIMV_R2H_CMD_ERROR_RET		3
> +#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET		4
> +#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET		5
> +#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET		6
> +#define S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET	7
> +#define S5P_FIMV_R2H_CMD_SYS_INIT_RET		8
> +#define S5P_FIMV_R2H_CMD_FW_STATUS_RET		9
> +#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET	15
> +#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET		16
> +#define S5P_FIMV_R2H_CMD_DECODE_ERR_RET		32
> +
> +/* Shared memory registers' offsets */
> +
> +/* An offset of the start position in the stream when
> + * the start position is not aligned */
> +#define S5P_FIMV_SHARED_CROP_INFO_H		0x0020
> +#define S5P_FIMV_SHARED_CROP_INFO_V		0x0024
> +#define S5P_FIMV_SHARED_START_BYTE_NUM		0x0018
> +#define S5P_FIMV_SHARED_LUMA_DPB_SIZE		0x0064
> +#define S5P_FIMV_SHARED_CHROMA_DPB_SIZE		0x0068
> +#define S5P_FIMV_SHARED_MV_SIZE			0x006C
> +#define S5P_FIMV_SHARED_PIC_TIME_TOP		0x0010
> +#define S5P_FIMV_SHARED_PIC_TIME_BOTTOM		0x0014
> +
> +#endif /* _REGS_FIMV_H */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c
> b/drivers/media/video/s5p-mfc/s5p_mfc.c
> new file mode 100644
> index 0000000..f549ab6
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
> @@ -0,0 +1,1879 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#include <linux/io.h>
> +#include <linux/sched.h>
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/version.h>
> +#include <linux/workqueue.h>
> +#include <linux/videodev2.h>
> +#include <media/videobuf2-cma.h>
> +#include <media/videobuf2-core.h>
> +#include "regs-mfc5.h"
> +
> +#include "s5p_mfc_opr.h"
> +#include "s5p_mfc_intr.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_memory.h"
> +#include "s5p_mfc_ctrls.h"
> +
> +#define S5P_MFC_NAME	"s5p-mfc5"
> +
> +/* Offset base used to differentiate between CAPTURE and OUTPUT
> +*  while mmaping */
> +#define DST_QUEUE_OFF_BASE      (TASK_SIZE / 2)
> +
> +struct s5p_mfc_dev *dev;
> +static const char *s5p_mem_types[] = {"b", "a"};
> +static unsigned long s5p_mem_alignments[] = {8192, 8192};
> +
> +/* Function prototypes */
> +static void s5p_mfc_try_run(void);
> +
> +/* Helper functions for interrupt processing */
> +/* Remove from hw execution round robin */
> +static inline void clear_work_bit(struct s5p_mfc_ctx *ctx)
> +{
> +	spin_lock(&dev->condlock);
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	spin_unlock(&dev->condlock);
> +}
> +
> +/* Wake up context wait_queue */
> +static inline void wake_up_ctx(struct s5p_mfc_ctx *ctx,
> +	unsigned int reason, unsigned int err)
> +{
> +	ctx->int_cond = 1;
> +	ctx->int_type = reason;
> +	ctx->int_err = err;
> +	wake_up_interruptible(&ctx->queue);
> +}
> +
> +/* Wake up device wait_queue */
> +static inline void wake_up_dev(unsigned int reason, unsigned int err)
> +{
> +	dev->int_cond = 1;
> +	dev->int_type = reason;
> +	dev->int_err = err;
> +	wake_up_interruptible(&dev->queue);
> +}
> +
> +void s5p_mfc_error_cleanup_queue(struct list_head *lh, \
> +						struct vb2_queue *vq)
> +{
> +	struct vb2_buffer *b;
> +	int i;
> +	spin_lock(&dev->irqlock);
> +	while (!list_empty(lh)) {
> +		b = list_entry(lh->next, struct vb2_buffer, drv_entry);
> +		for (i = 0; i < b->num_planes; i++)
> +			vb2_set_plane_payload(b, i, 0);
> +		spin_unlock(&dev->irqlock);
> +		vb2_buffer_done(b, VB2_BUF_STATE_ERROR);
> +		spin_lock(&dev->irqlock);
> +		list_del(&b->drv_entry);
> +	}
> +	spin_unlock(&dev->irqlock);
> +}
> +
> +void s5p_mfc_watchdog(unsigned long arg)
> +{
> +	if (test_bit(0, &dev->hw_lock))
> +		atomic_inc(&dev->watchdog_cnt);
> +	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
> +		/* This means that hw is busy and no interrupts were
> +		 * generated by hw for the Nth time of running this
> +		 * watchdog timer. This usually means a serious hw
> +		 * error. Now it is time to kill all instances and
> +		 * reset the MFC. */
> +		mfc_err("Time out during waiting for HW.\n");
> +		queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
> +	}
> +	dev->watchdog_timer.expires +=
> msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
> +	add_timer(&dev->watchdog_timer);
> +}
> +
> +static void s5p_mfc_watchdog_worker(struct work_struct *work)
> +{
> +	struct s5p_mfc_ctx *ctx;
> +	int i, ret;
> +	int mutex_locked;
> +	mfc_err("Driver timeout error handling.\n");
> +	/* Lock the mutex that protects open and release.
> +	 * This is necessary as they may load and unload firmware. */
> +	mutex_locked = mutex_trylock(dev->mfc_mutex);
> +	if (!mutex_locked)
> +		mfc_err("Ok. This is not good. Some instance may be " \
>
+							"closing/opening.\n"
);
> +	clk_disable(dev->clock1);
> +	clk_disable(dev->clock2);
> +	spin_lock(&dev->irqlock);
> +	for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
> +		ctx = dev->ctx[i];
> +		if (ctx) {
> +			ctx->state = MFCINST_DEC_ERROR;
> +			spin_unlock(&dev->irqlock);
> +			s5p_mfc_error_cleanup_queue(&ctx->dst_queue,
> +				&ctx->vq_dst);
> +			s5p_mfc_error_cleanup_queue(&ctx->src_queue,
> +				&ctx->vq_src);
> +			spin_lock(&dev->irqlock);
> +			clear_work_bit(ctx);
> +			wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_DECODE_ERR_RET,
0);
> +		}
> +	}
> +	clear_bit(0, &dev->hw_lock);
> +	spin_unlock(&dev->irqlock);
> +	/* Double check if there is at least one instance running.
> +	 * If no instance is in memory than no firmware should be present
> */
> +	if (atomic_read(&dev->num_inst) > 0) {
> +		ret = s5p_mfc_load_firmware(dev);
> +		if (ret != 0) {
> +			mfc_err("Failed to reload FW. This is bad.\n");
> +			if (mutex_locked)
> +				mutex_unlock(dev->mfc_mutex);
> +			return;
> +		}
> +		clk_enable(dev->clock1);
> +		clk_enable(dev->clock2);
> +		ret = s5p_mfc_init_hw(dev);
> +		if (ret != 0) {
> +			mfc_err("Failed to reinitialize FW. This is
bad.\n");
> +			if (mutex_locked)
> +				mutex_unlock(dev->mfc_mutex);
> +			return;
> +		}
> +	}
> +	if (mutex_locked)
> +		mutex_unlock(dev->mfc_mutex);
> +}
> +
> +/* Check whether a context should be run on hardware */
> +int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
> +{
> +	mfc_debug("s5p_mfc_ctx_ready: src=%d, dst=%d, state=%d\n",
> +		  ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
> +	/* Context is to parse header */
> +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_GOT_INST)
> +		return 1;
> +	/* Context is to decode a frame */
> +	if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_DEC_RUNNING &&
> +					ctx->dst_queue_cnt >= ctx-
>dpb_count)
> +		return 1;
> +	/* Context is to return last frame */
> +	if (ctx->state == MFCINST_DEC_FINISHING &&
> +	    ctx->dst_queue_cnt >= ctx->dpb_count)
> +		return 1;
> +	/* Context is to set buffers */
> +	if (ctx->src_queue_cnt >= 1 &&
> +	    ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +	    ctx->capture_state == QUEUE_BUFS_MMAPED)
> +		return 1;
> +	mfc_debug("s5p_mfc_ctx_ready: ctx is not ready.\n");
> +	return 0;
> +}
> +
> +/* Query control */
> +static struct v4l2_queryctrl *get_ctrl(int id)
> +{
> +	int i;
> +	for (i = 0; i < NUM_CTRLS; ++i)
> +		if (id == s5p_mfc_ctrls[i].id)
> +			return &s5p_mfc_ctrls[i];
> +	return NULL;
> +}
> +
> +/* Query capabilities of the device */
> +static int vidioc_querycap(struct file *file, void *priv,
> +			   struct v4l2_capability *cap)
> +{
> +	strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
> +	strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
> +	cap->bus_info[0] = 0;
> +	cap->version = KERNEL_VERSION(1, 0, 0);
> +	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
> +						    | V4L2_CAP_STREAMING;
> +	return 0;
> +}
> +
> +/* Enumerate format */
> +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	int i, j = 0;
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (mplane && formats[i].num_planes == 1)
> +			continue;
> +		else if (!mplane && formats[i].num_planes > 1)
> +			continue;
> +		if (out && formats[i].type != MFC_FMT_RAW)
> +			continue;
> +		else if (!out && formats[i].type != MFC_FMT_DEC)
> +			continue;
> +
> +		if (j == f->index)
> +			break;
> +		++j;
> +	}
> +	if (i == ARRAY_SIZE(formats))
> +		return -EINVAL;
> +	fmt = &formats[i];
> +	strlcpy(f->description, fmt->name, sizeof(f->description));
> +	f->pixelformat = fmt->fourcc;
> +	return 0;
> +}
> +
> +static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, false, false);
> +}
> +
> +static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, true, false);
> +}
> +
> +static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, false, true);
> +}
> +
> +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
> +							struct v4l2_fmtdesc
*f)
> +{
> +	return vidioc_enum_fmt(f, true, true);
> +}
> +
> +/* Get format */
> +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format
> *f)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_g_fmt++\n");
> +	mfc_debug("f->type = %d ctx->state = %d\n", f->type, ctx->state);
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	    ctx->state == MFCINST_DEC_GOT_INST) {
> +		/* If the MFC is parsing the header,
> +		 * so wait until it is finished */
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_wait_for_done_ctx(ctx,
S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
> \
> +									1);
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
> +	    ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +	    ctx->state < MFCINST_ENC_INIT) {
> +		/* This is run on CAPTURE (deocde output) */
> +		/* Width and height are set to the dimensions
> +		   of the movie, the buffer is bigger and
> +		   further processing stages should crop to this
> +		   rectangle. */
> +		f->fmt.pix_mp.width = ctx->buf_width;
> +		f->fmt.pix_mp.height = ctx->buf_height;
> +		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> +		f->fmt.pix_mp.num_planes = 2;
> +		/* Set pixelformat to the format in which MFC
> +		   outputs the decoded frame */
> +		f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx->buf_width;
> +		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx->luma_size;
> +		f->fmt.pix_mp.plane_fmt[1].bytesperline = ctx->buf_width;
> +		f->fmt.pix_mp.plane_fmt[1].sizeimage = ctx->chroma_size;
> +	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		/* This is run on OUTPUT
> +		   The buffer contains compressed image
> +		   so width and height have no meaning */
> +		f->fmt.pix_mp.width = 1;
> +		f->fmt.pix_mp.height = 1;
> +		f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = ctx-
> >dec_src_buf_size;
> +		f->fmt.pix_mp.plane_fmt[0].sizeimage = ctx-
>dec_src_buf_size;
> +		f->fmt.pix_mp.pixelformat = ctx->fmt->fourcc;
> +		f->fmt.pix_mp.num_planes = ctx->fmt->num_planes;
> +	} else {
> +		mfc_err("Format could not be read\n");
> +		mfc_debug("vidioc_g_fmt-- (error)\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_g_fmt--\n");
> +	return 0;
> +}
> +
> +/* Find selected format description */
> +static struct s5p_mfc_fmt *find_format(struct v4l2_format *f)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	unsigned int i;
> +	for (i = 0; i < NUM_FORMATS; ++i) {
> +		fmt = &formats[i];
> +		if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
> +			break;
> +	}
> +	if (i == NUM_FORMATS)
> +		return NULL;
> +	return fmt;
> +}
> +
> +/* Try format */
> +static int vidioc_try_fmt(struct file *file, void *priv, struct
> v4l2_format *f)
> +{
> +	struct s5p_mfc_fmt *fmt;
> +	mfc_debug("Type is %d\n", f->type);
> +	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_err("Currently only decoding is supported.\n");
> +		return -EINVAL;
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		fmt = find_format(f);
> +		if (!fmt) {
> +			mfc_err("Unsupported format.\n");
> +			return -EINVAL;
> +		}
> +		if (fmt->type != MFC_FMT_DEC) {
> +			mfc_err("\n");
> +			return -EINVAL;
> +		}
> +		if (f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) {
> +			mfc_err("Application is required to specify"
> +				" input buffer size (via sizeimage)\n");
> +			return -EINVAL;
> +		}
> +		/* As this buffer will contain compressed data, the size is
> set
> +		 * to the maximum size.
> +		 * Width and height are left intact as they may be relevant
> for
> +		 * DivX 3.11 decoding. */
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline =
> +					f-
>fmt.pix_mp.plane_fmt[0].sizeimage;
> +	}
> +	return 0;
> +}
> +
> +/* Set format */
> +static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format
> *f)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	unsigned long flags;
> +	int ret = 0;
> +	struct s5p_mfc_fmt *fmt;
> +	mfc_debug("vidioc_s_fmt++\n");
> +	ret = vidioc_try_fmt(file, priv, f);
> +	if (ret)
> +		return ret;
> +	mutex_lock(&ctx->vq_src.vb_lock);
> +	mutex_lock(&ctx->vq_dst.vb_lock);
> +	if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
> +		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		fmt = find_format(f);
> +		if (!fmt || fmt->codec_mode == MFC_FORMATS_NO_CODEC) {
> +			mfc_err("Unknown codec.\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		if (fmt->type != MFC_FMT_DEC) {
> +			mfc_err("Wrong format selected - you should choose"
\
> +						" format for decoding.\n");
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		ctx->fmt = fmt;
> +		ctx->codec_mode = fmt->codec_mode;
> +		mfc_debug("The codec number is: %d\n", ctx->codec_mode);
> +		ctx->pix_format = f->fmt.pix_mp.pixelformat;
> +		if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_DIVX3) {
> +			f->fmt.pix_mp.height = 1;
> +			f->fmt.pix_mp.width = 1;
> +		} else {
> +			ctx->img_height = f->fmt.pix_mp.height;
> +			ctx->img_width = f->fmt.pix_mp.width;
> +		}
> +		mfc_debug("s_fmt w/h: %dx%d, ctx: %dx%d\n", f-
> >fmt.pix_mp.width,
> +			f->fmt.pix_mp.height, ctx->img_width, ctx-
>img_height);
> +		ctx->dec_src_buf_size =	f-
> >fmt.pix_mp.plane_fmt[0].sizeimage;
> +		f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
> +		ctx->state = MFCINST_DEC_INIT;
> +		ctx->dec_dst_buf_cnt = 0;
> +		ctx->capture_state = QUEUE_FREE;
> +		ctx->output_state = QUEUE_FREE;
> +		s5p_mfc_alloc_instance_buffer(ctx);
> +		s5p_mfc_alloc_dec_temp_buffers(ctx);
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_try_run();
> +		if (s5p_mfc_wait_for_done_ctx(ctx, \
> +				S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
> +			/* Error or timeout */
> +			mfc_err("Error getting instance from hardware.\n");
> +			s5p_mfc_release_instance_buffer(ctx);
> +			s5p_mfc_release_dec_buffers(ctx);
> +			ret = -EAGAIN;
> +			goto out;
> +		}
> +		mfc_debug("Got instance number: %d\n", ctx->inst_no);
> +	}
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_err("Currently only decoding is supported.\n");
> +		ret = -EINVAL;
> +	}
> +out:
> +	mutex_unlock(&ctx->vq_dst.vb_lock);
> +	mutex_unlock(&ctx->vq_src.vb_lock);
> +	mfc_debug("vidioc_s_fmt--\n");
> +	return ret;
> +}
> +
> +/* Reqeust buffers */
> +static int vidioc_reqbufs(struct file *file, void *priv,
> +					  struct v4l2_requestbuffers
*reqbufs)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = 0;
> +	mfc_debug("vidioc_reqbufs++\n");
> +	mfc_debug("Memory type: %d\n", reqbufs->memory);
> +	if (reqbufs->memory != V4L2_MEMORY_MMAP) {
> +		mfc_err("Only V4L2_MEMORY_MAP is supported.\n");
> +		return -EINVAL;
> +	}
> +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		/* Can only request buffers after an instance has been
> opened.*/
> +		if (ctx->state == MFCINST_DEC_GOT_INST) {
> +			/* Decoding */
> +			if (ctx->output_state != QUEUE_FREE) {
> +				mfc_err("Bufs have already been
requested.\n");
> +				return -EINVAL;
> +			}
> +			ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
> +			if (ret) {
> +				mfc_err("vb2_reqbufs on output failed.\n");
> +				return ret;
> +			}
> +			mfc_debug("vb2_reqbufs: %d\n", ret);
> +			ctx->output_state = QUEUE_BUFS_REQUESTED;
> +		}
> +	}
> +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		if (ctx->capture_state != QUEUE_FREE) {
> +			mfc_err("Bufs have already been requested.\n");
> +			return -EINVAL;
> +		}
> +		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +		if (ret) {
> +			mfc_err("vb2_reqbufs on output failed.\n");
> +			return ret;
> +		}
> +		if (reqbufs->count < ctx->dpb_count) {
> +			mfc_err("Not enough buffers allocated.\n");
> +			reqbufs->count = 0;
> +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +			return -ENOMEM;
> +		}
> +		ctx->total_dpb_count = reqbufs->count;
> +		ret = s5p_mfc_alloc_dec_buffers(ctx);
> +		if (ret) {
> +			mfc_err("Failed to allocate decoding buffers.\n");
> +			reqbufs->count = 0;
> +			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
> +			return -ENOMEM;
> +		}
> +		ctx->capture_state = QUEUE_BUFS_REQUESTED;
> +	}
> +	mfc_debug("vidioc_reqbufs--\n");
> +	return ret;
> +}
> +
> +/* Query buffer */
> +static int vidioc_querybuf(struct file *file, void *priv,
> +						   struct v4l2_buffer *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	int i;
> +	mfc_debug("vidioc_querybuf++\n");
> +	if (buf->memory != V4L2_MEMORY_MMAP) {
> +		mfc_err("Only mmaped buffers can be used.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("State: %d, buf->type: %d\n", ctx->state, buf->type);
> +	if (ctx->state == MFCINST_DEC_GOT_INST &&
> +			buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		ret = vb2_querybuf(&ctx->vq_src, buf);
> +	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +			buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		ret = vb2_querybuf(&ctx->vq_dst, buf);
> +		for (i = 0; i < buf->length; i++)
> +			buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
> +	} else {
> +		mfc_err("vidioc_querybuf called in an inappropriate
> state.\n");
> +		ret = -EINVAL;
> +	}
> +	mfc_debug("vidioc_querybuf--\n");
> +	return ret;
> +}
> +
> +/* Queue a buffer */
> +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer
> *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_qbuf++\n");
> +	mfc_debug("Enqueued buf: %d (type = %d)\n", buf->index, buf->type);
> +	if (ctx->state == MFCINST_DEC_ERROR) {
> +		mfc_err("Call on QBUF after unrecoverable error.\n");
> +		return -EIO;
> +	}
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		return vb2_qbuf(&ctx->vq_src, buf);
> +	else
> +		return vb2_qbuf(&ctx->vq_dst, buf);
> +	mfc_debug("vidioc_qbuf--\n");
> +	return -EINVAL;
> +}
> +
> +/* Dequeue a buffer */
> +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer
> *buf)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	mfc_debug("vidioc_dqbuf++\n");
> +	mfc_debug("Addr: %p %p %p Type: %d\n", &ctx->vq_src, buf, buf-
> >m.planes,
> +								  buf-
>type);
> +	if (ctx->state == MFCINST_DEC_ERROR) {
> +		mfc_err("Call on DQBUF after unrecoverable error.\n");
> +		return -EIO;
> +	}
> +	if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		ret = vb2_dqbuf(&ctx->vq_src, buf,
> +				      file->f_flags & O_NONBLOCK);
> +	} else {
> +		ret = vb2_dqbuf(&ctx->vq_dst, buf,
> +				      file->f_flags & O_NONBLOCK);
> +	}
> +	mfc_debug("vidioc_dqbuf--\n");
> +	return ret;
> +}
> +
> +/* Stream on */
> +static int vidioc_streamon(struct file *file, void *priv,
> +			   enum v4l2_buf_type type)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = -EINVAL;
> +	unsigned long flags;
> +	mfc_debug("vidioc_streamon++\n");
> +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		ret = vb2_streamon(&ctx->vq_src, type);
> +	else
> +		ret = vb2_streamon(&ctx->vq_dst, type);
> +	mfc_debug("ctx->src_queue_cnt = %d ctx->state = %d "
> +		  "ctx->dst_queue_cnt = %d ctx->dpb_count = %d\n",
> +		  ctx->src_queue_cnt, ctx->state, ctx->dst_queue_cnt,
> +		  ctx->dpb_count);
> +	/* If context is ready then schedule it to run */
> +	if (s5p_mfc_ctx_ready(ctx)) {
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +	}
> +	s5p_mfc_try_run();
> +	mfc_debug("vidioc_streamon--\n");
> +	return ret;
> +}
> +
> +/* Stream off, which equals to a pause */
> +static int vidioc_streamoff(struct file *file, void *priv,
> +			    enum v4l2_buf_type type)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret;
> +	mfc_debug("vidioc_streamoff++\n");
> +	ret = -EINVAL;
> +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		ret = vb2_streamoff(&ctx->vq_src, type);
> +	else
> +		ret = vb2_streamoff(&ctx->vq_dst, type);
> +	mfc_debug("vidioc_streamoff--\n");
> +	return ret;
> +}
> +
> +/* Query a ctrl */
> +static int vidioc_queryctrl(struct file *file, void *priv,
> +			    struct v4l2_queryctrl *qc)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(qc->id);
> +	if (!c)
> +		return -EINVAL;
> +	*qc = *c;
> +	return 0;
> +}
> +
> +/* Get ctrl */
> +static int vidioc_g_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	mfc_debug("vidioc_g_ctrl++\n");
> +	switch (ctrl->id) {
> +	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
> +		ctrl->value = ctx->loop_filter_mpeg4;
> +		break;
> +	case V4L2_CID_CODEC_DISPLAY_DELAY:
> +		ctrl->value = ctx->display_delay;
> +		break;
> +	case V4L2_CID_CODEC_REQ_NUM_BUFS:
> +		if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +		    ctx->state < MFCINST_ENC_INIT) {
> +			ctrl->value = ctx->dpb_count;
> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> +			/* Should wait for the header to be parsed */
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_wait_for_done_ctx(ctx, \
> +					S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 1);
> +			if (ctx->state >= MFCINST_DEC_HEAD_PARSED &&
> +			    ctx->state < MFCINST_ENC_INIT) {
> +				ctrl->value = ctx->dpb_count;
> +			} else {
> +				v4l2_err(&dev->v4l2_dev,
> +						 "Decoding not
initialised.\n");
> +				return -EINVAL;
> +			}
> +		} else {
> +			v4l2_err(&dev->v4l2_dev,
> +						 "Decoding not
initialised.\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_CODEC_SLICE_INTERFACE:
> +		ctrl->value = ctx->slice_interface;
> +		break;
> +	default:
> +		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_g_ctrl--\n");
> +	return 0;
> +}
> +
> +/* Check whether a ctrl value if correct */
> +static int check_ctrl_val(struct s5p_mfc_ctx *ctx, struct v4l2_control
> *ctrl)
> +{
> +	struct v4l2_queryctrl *c;
> +	c = get_ctrl(ctrl->id);
> +	if (!c)
> +		return -EINVAL;
> +	if (ctrl->value < c->minimum || ctrl->value > c->maximum
> +	    || (c->step != 0 && ctrl->value % c->step != 0)) {
> +		v4l2_err(&dev->v4l2_dev, "Invalid control value\n");
> +		return -ERANGE;
> +	}
> +	return 0;
> +}
> +
> +/* Set a ctrl */
> +static int vidioc_s_ctrl(struct file *file, void *priv,
> +			 struct v4l2_control *ctrl)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	int ret = 0;
> +	int stream_on;
> +	mfc_debug("vidioc_s_ctrl++\n");
> +	stream_on = ctx->vq_src.streaming || ctx->vq_dst.streaming;
> +	ret = check_ctrl_val(ctx, ctrl);
> +	if (ret != 0)
> +		return ret;
> +	switch (ctrl->id) {
> +	case V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->loop_filter_mpeg4 = ctrl->value;
> +		break;
> +	case V4L2_CID_CODEC_DISPLAY_DELAY:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->display_delay = ctrl->value;
> +		break;
> +	case V4L2_CID_CODEC_SLICE_INTERFACE:
> +		if (stream_on)
> +			return -EBUSY;
> +		ctx->slice_interface = ctrl->value;
> +		break;
> +	default:
> +		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("vidioc_s_ctrl--\n");
> +	return 0;
> +}
> +/* Get cropping information */
> +static int vidioc_g_crop(struct file *file, void *priv,
> +		struct v4l2_crop *cr)
> +{
> +	struct s5p_mfc_ctx *ctx = priv;
> +	u32 left, right, top, bottom;
> +	mfc_debug("vidioc_g_crop++\n");
> +	if (ctx->state != MFCINST_DEC_HEAD_PARSED &&
> +	ctx->state != MFCINST_DEC_RUNNING && ctx->state !=
> MFCINST_DEC_FINISHING
> +					&& ctx->state !=
MFCINST_DEC_FINISHED) {
> +			mfc_debug("vidioc_g_crop--(err)\n");
> +			return -EINVAL;
> +		}
> +	if (ctx->fmt->fourcc == V4L2_PIX_FMT_H264) {
> +		left = s5p_mfc_get_h_crop(ctx);
> +		right = left >> 16;
> +		left = left & 0xFFFF;
> +		top = s5p_mfc_get_v_crop(ctx);
> +		bottom = top >> 16;
> +		top = top & 0xFFFF;
> +		cr->c.left = left;
> +		cr->c.top = top;
> +		cr->c.width = ctx->img_width - left - right;
> +		cr->c.height = ctx->img_height - top - bottom;
> +		mfc_debug("Cropping info [h264]: l=%d t=%d w=%d h=%d (r=%d "
> \
> +				"b=%d fw=%d fh=%d\n", left, top, cr-
>c.width, \
> +				cr->c.height, right, bottom, ctx-
>buf_width, \
> +							ctx->buf_height);
> +	} else {
> +		cr->c.left = 0;
> +		cr->c.top = 0;
> +		cr->c.width = ctx->img_width;
> +		cr->c.height = ctx->img_height;
> +		mfc_debug("Cropping info: w=%d h=%d fw=%d fh=%d\n", cr-
> >c.width,
> +				cr->c.height, ctx->buf_width, ctx-
>buf_height);
> +	}
> +	mfc_debug("vidioc_g_crop--\n");
> +	return 0;
> +}
> +
> +/* v4l2_ioctl_ops */
> +static const struct v4l2_ioctl_ops s5p_mfc_ioctl_ops = {
> +	.vidioc_querycap = vidioc_querycap,
> +	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
> +	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
> +	.vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
> +	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
> +	.vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
> +	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
> +	.vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
> +	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
> +	.vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
> +	.vidioc_reqbufs = vidioc_reqbufs,
> +	.vidioc_querybuf = vidioc_querybuf,
> +	.vidioc_qbuf = vidioc_qbuf,
> +	.vidioc_dqbuf = vidioc_dqbuf,
> +	.vidioc_streamon = vidioc_streamon,
> +	.vidioc_streamoff = vidioc_streamoff,
> +	.vidioc_queryctrl = vidioc_queryctrl,
> +	.vidioc_g_ctrl = vidioc_g_ctrl,
> +	.vidioc_s_ctrl = vidioc_s_ctrl,
> +	.vidioc_g_crop = vidioc_g_crop,
> +};
> +
> +/* Negotiate buffers */
> +static int s5p_mfc_buf_negotiate(struct vb2_queue *vq, unsigned int
> *buf_count,
> +						unsigned int *plane_count)
> +{
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	mfc_debug("s5p_mfc_buf_negotiate++\n");
> +	/* Video output for decoding (source)
> +	 * this can be set after getting an instance */
> +	if (ctx->state == MFCINST_DEC_GOT_INST &&
> +	    vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_debug("setting for VIDEO output\n");
> +		/* A single plane is required for input */
> +		*plane_count = 1;
> +		if (*buf_count < 1)
> +			*buf_count = 1;
> +		if (*buf_count > MFC_MAX_BUFFERS)
> +			*buf_count = MFC_MAX_BUFFERS;
> +	/* Video capture for decoding (destination)
> +	 * this can be set after the header was parsed */
> +	} else if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +		   vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_debug("setting for VIDEO capture\n");
> +		/* Output plane count is 2 - one for Y and one for CbCr */
> +		*plane_count = 2;
> +		/* Setup buffer count */
> +		if (*buf_count < ctx->dpb_count)
> +			*buf_count = ctx->dpb_count;
> +		if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
> +			*buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
> +		if (*buf_count > MFC_MAX_BUFFERS)
> +			*buf_count = MFC_MAX_BUFFERS;
> +	} else {
> +		mfc_err("State seems invalid. State = %d, vq->type = %d\n",
> +							ctx->state, vq-
>type);
> +		return -EINVAL;
> +	}
> +	mfc_debug("%s, buffer count=%d, plane count=%d type=0x%x\n",
> __func__,
> +					*buf_count, *plane_count, vq->type);
> +	mfc_debug("s5p_mfc_buf_negotiate--\n");
> +	return 0;
> +}
> +
> +/* Setup plane */
> +static int s5p_mfc_buf_setup_plane(struct vb2_queue *vq,
> +			   unsigned int plane, unsigned long *plane_size)
> +{
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	mfc_debug("s5p_mfc_buf_setup_plane++\n");
> +	if (ctx->state == MFCINST_DEC_HEAD_PARSED &&
> +	    vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		switch (plane) {
> +		case 0:
> +			/* Plane 0 is for luma (Y) */
> +			*plane_size = ctx->luma_size;
> +			break;
> +		case 1:
> +			/* Plane 1 is for chroma (C) */
> +			*plane_size = ctx->chroma_size;
> +			break;
> +		default:
> +			mfc_err("%s, invalid plane=%d\n", __func__, plane);
> +			return -EINVAL;
> +		}
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
> +		   ctx->state == MFCINST_DEC_GOT_INST) {
> +		if (plane != 0) {
> +			mfc_err("%s, invalid plane=%d\n", __func__, plane);
> +			return -EINVAL;
> +		}
> +		/* dec_src_buf_size was set in s_fmt */
> +		*plane_size = ctx->dec_src_buf_size;
> +	} else {
> +		mfc_err("Currently only decoding is supported."\
> +						" Decoding not
initalised.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("%s, plane=%d, size=%lu\n", __func__, plane, *plane_size);
> +	mfc_debug("s5p_mfc_buf_setup_plane--\n");
> +	return 0;
> +}
> +
> +/* Prepare a buffer */
> +static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	unsigned int i;
> +	mfc_debug("s5p_mfc_buf_prepare++\n");
> +	BUG_ON(NULL == ctx->fmt);
> +	if (!ctx->fmt) {
> +		mfc_err("Format passed to the function is null.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("Addr: %p (%d)\n", (void *)vb2_plane_paddr(vb, 0),
> +							vb->v4l2_buf.index);
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
> +			mfc_debug("s5p_mfc_buf_prepare--\n");
> +			return 0;
> +		}
> +		for (i = 0; i <= ctx->fmt->num_planes ; i++) {
> +			if (vb2_plane_paddr(vb, i) == 0) {
> +				mfc_err("Plane mem not allocated.\n");
> +				return -EINVAL;
> +			}
> +		}
> +		if (vb2_plane_size(vb, 0) < ctx->luma_size ||
> +			vb2_plane_size(vb, 1) < ctx->chroma_size) {
> +			mfc_err("Plane buffer (CAPTURE) is too small.\n");
> +			return -EINVAL;
> +		}
> +		mfc_debug("Size: 0=%lu 2=%lu\n", vb2_plane_size(vb, 0),
> +							vb2_plane_size(vb,
1));
> +		i = vb->v4l2_buf.index;
> +		ctx->dec_dst_buf_luma[i] = vb2_plane_paddr(vb, 0);
> +		ctx->dec_dst_buf_chroma[i] = vb2_plane_paddr(vb, 1);
> +		ctx->dec_dst_buf_cnt++;
> +		if (ctx->dec_dst_buf_cnt == ctx->total_dpb_count)
> +			ctx->capture_state = QUEUE_BUFS_MMAPED;
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		if (vb2_plane_paddr(vb, 0)  == 0) {
> +			mfc_err("Plane memory not allocated.\n");
> +			return -EINVAL;
> +		}
> +		mfc_debug("Plane size: %ld, ctx->dec_src_buf_size: %d\n",
> +				vb2_plane_size(vb, 0), ctx-
>dec_src_buf_size);
> +		if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
> +			mfc_err("Plane buffer (OUTPUT) is too small.\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		mfc_err("s5p_mfc_buf_prepare: unknown queue type.\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("s5p_mfc_buf_prepare--\n");
> +	return 0;
> +}
> +
> +/* Try running an operation on hardware */
> +static void s5p_mfc_try_run()
> +{
> +	struct vb2_buffer *temp_vb;
> +	struct s5p_mfc_ctx *ctx;
> +	int new_ctx;
> +	unsigned long flags;
> +	unsigned int cnt;
> +	unsigned int ret;
> +	mfc_debug("Try run dev: %p\n", dev);
> +	/* Check whether hardware is not running */
> +	if (test_and_set_bit(0, &dev->hw_lock) == 0) {
> +		/* Choose the context to run */
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		mfc_debug("Previos context: %d (bit field %08lx)\n",
> +					  dev->curr_ctx, dev-
>ctx_work_bits);
> +		new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
> +		cnt = 0;
> +		while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
> +			new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
> +			cnt++;
> +			if (cnt > MFC_NUM_CONTEXTS) {
> +				/* No contexts to run */
> +				spin_unlock_irqrestore(&dev->condlock,
flags);
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No ctx is scheduled to be
run.\n");
> +				return;
> +			}
> +		}
> +		mfc_debug("New context: %d\n", new_ctx);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		ctx = dev->ctx[new_ctx];
> +		mfc_debug("Seting new context to %p\n", ctx);
> +		/* Got context to run in ctx */
> +		mfc_debug("ctx->dst_queue_cnt=%d ctx->dpb_count=%d" \
> +				" ctx->src_queue_cnt=%d\n", ctx-
> >dst_queue_cnt,\
> +					ctx->dpb_count, ctx->src_queue_cnt);
> +		mfc_debug("ctx->state=%d\n", ctx->state);
> +		/* Last frame has already been sent to MFC
> +		 * Now obtaining frames from MFC buffer */
> +		if (ctx->state == MFCINST_DEC_FINISHING) {
> +			s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx, 1);
> +		} else if (ctx->state == MFCINST_DEC_RUNNING) {
> +			/* Frames are being decoded */
> +			if (list_empty(&ctx->src_queue)) {
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No source buffers.\n");
> +				return;
> +			}
> +			/* Get the next source buffer */
> +			temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +			mfc_debug("Temp vb: %p\n", temp_vb);
> +			mfc_debug("Src Addr: %08lx\n",
> +						vb2_plane_paddr(temp_vb,
0));
> +			s5p_mfc_set_dec_stream_buffer(ctx, \
> +			      vb2_plane_paddr(temp_vb, 0), 0, \
> +			      temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx,
> +				temp_vb->v4l2_planes[0].bytesused == 0);
> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> +			/* Preparing decoding - getting instance number */
> +			mfc_debug("Getting instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +/*			s5p_mfc_set_dec_temp_buffers(ctx);
> + *			Removed per request by Peter, check if MFC works OK
*/
> +			ret = s5p_mfc_open_inst(ctx);
> +			if (ret) {
> +				mfc_err("Failed to create a new
instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +		} else if (ctx->state == MFCINST_DEC_RETURN_INST) {
> +			/* Closing decoding instance  */
> +			mfc_debug("Returning instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			ret = s5p_mfc_return_inst_no(ctx);
> +			if (ret) {
> +				mfc_err("Failed to return an instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +
> +		} else if (ctx->state == MFCINST_DEC_GOT_INST) {
> +			/* Initializing decoding - parsing header */
> +			mfc_debug("Preparing to init decoding.\n");
> +			temp_vb = list_entry(ctx->src_queue.next,
> +						struct vb2_buffer,
drv_entry);
> +			s5p_mfc_set_dec_temp_buffers(ctx);
> +			mfc_debug("Header size: %d\n", \
> +					temp_vb->v4l2_planes[0].bytesused);
> +			s5p_mfc_set_dec_stream_buffer(ctx,\
> +					vb2_plane_paddr(temp_vb, 0), 0,
> +					 temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			mfc_debug("paddr: %08x\n", \
> +				(int)phys_to_virt(vb2_plane_paddr(temp_vb,
0)));
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_init_decode(ctx);
> +		} else if (ctx->state == MFCINST_DEC_HEAD_PARSED) {
> +			/* Header was parsed now starting processing
> +			 * First set the output frame buffers
> +			 * s5p_mfc_alloc_dec_buffers(ctx); */
> +			if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
> +				temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +				mfc_debug("Header size: %d\n",
> +					temp_vb->v4l2_planes[0].bytesused);
> +				s5p_mfc_set_dec_stream_buffer(ctx, \
> +					vb2_plane_paddr(temp_vb, 0), 0, \
> +					temp_vb->v4l2_planes[0].bytesused);
> +				dev->curr_ctx = ctx->num;
> +				s5p_mfc_clean_ctx_int_flags(ctx);
> +				s5p_mfc_set_dec_frame_buffer(ctx, 1);
> +			} else {
> +				mfc_err("It seems that not all
destionation" \
> +				    " buffers were mmaped.\nMFC requires
that" \
> +				    " all destination are mmaped before" \
> +				    " starting processing.\n");
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +			}
> +		} else {
> +			/* Free hardware lock */
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
> +				mfc_err("Failed to unlock hardware.\n");
> +				return;
> +			}
> +		}
> +	} else {
> +		/* This is perfectly ok, the scheduled ctx should wait */
> +		mfc_debug("Couldn't lock HW.\n");
> +	}
> +}
> +
> +/* Queue buffer */
> +static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *vq = vb->vb2_queue;
> +	struct s5p_mfc_ctx *ctx = vq->drv_priv;
> +	unsigned long flags;
> +	mfc_debug("s5p_mfc_buf_queue++\n");
> +	if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		mfc_debug("Src queue: %p\n", &ctx->src_queue);
> +		mfc_debug("Adding to src: %p (%08lx)\n", vb,
> +							vb2_plane_paddr(vb,
0));
> +		list_add_tail(&vb->drv_entry, &ctx->src_queue);
> +		ctx->src_queue_cnt++;
> +	} else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		mfc_debug("Dst queue: %p\n", &ctx->dst_queue);
> +		mfc_debug("Adding to dst: %p (%lx)\n", vb, \
> +						  vb2_plane_paddr(vb, 0));
> +		mfc_debug("ADDING Flag before: %lx (%d)\n", ctx-
> >dec_dst_flag, \
> +							vb->v4l2_buf.index);
> +		/* Mark destination as available for use by MFC */
> +		set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
> +		mfc_debug("ADDING Flag after: %lx\n", ctx->dec_dst_flag);
> +		list_add_tail(&vb->drv_entry, &ctx->dst_queue);
> +		ctx->dst_queue_cnt++;
> +	} else {
> +		mfc_err("Unsupported buffer type (%d)\n", vq->type);
> +	}
> +	if (s5p_mfc_ctx_ready(ctx)) {
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +	}
> +	s5p_mfc_try_run();
> +	mfc_debug("s5p_mfc_buf_queue--\n");
> +}
> +
> +/* Videobuf opts */
> +static struct vb2_ops s5p_mfc_qops = {
> +	.buf_queue = s5p_mfc_buf_queue,
> +	.queue_negotiate = s5p_mfc_buf_negotiate,
> +	.plane_setup = s5p_mfc_buf_setup_plane,
> +	.buf_prepare = s5p_mfc_buf_prepare,
> +};
> +
> +/* Handle frame decoding interrupt */
> +static void s5p_mfc_handle_frame_int(struct s5p_mfc_ctx *ctx, \
> +					unsigned int reason, unsigned int
err)
> +{
> +	size_t dst_ret_addr;
> +	unsigned int dst_frame_status;
> +	unsigned int dec_frame_type;
> +	struct vb2_buffer *src_buf, *dst_buf;
> +	dst_ret_addr = s5p_mfc_get_dspl_y_adr();
> +	dst_frame_status = s5p_mfc_get_dspl_status()
> +				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
> +	mfc_debug("Orig Status: %d\n", s5p_mfc_get_dspl_status());
> +	dec_frame_type = s5p_mfc_get_frame_type();
> +	mfc_debug("Status: %x, addr Y: %08x\n", dst_frame_status,
> dst_ret_addr);
> +	mfc_debug("Decode_y: %08x, frame type: %d\n", dst_ret_addr, \
>
+								dec_frame_ty
pe);
> +	spin_lock(&dev->irqlock);
> +	/* All frames remaining in the buffer have been extracted  */
> +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
> +		ctx->state = MFCINST_DEC_FINISHED;
> +		mfc_debug("Decided to finish\n");
> +		ctx->sequence++;
> +		while (!list_empty(&ctx->dst_queue)) {
> +			dst_buf = list_entry(ctx->dst_queue.next,
> +					     struct vb2_buffer, drv_entry);
> +			mfc_debug("Cleaning up buffer: %d\n",
> +						  dst_buf->v4l2_buf.index);
> +			vb2_set_plane_payload(dst_buf, 0, 0);
> +			vb2_set_plane_payload(dst_buf, 1, 0);
> +			list_del(&dst_buf->drv_entry);
> +			ctx->dst_queue_cnt--;
> +			dst_buf->v4l2_buf.sequence = (ctx->sequence++);
> +			if (s5p_mfc_get_pic_time_top(ctx) ==
> +				s5p_mfc_get_pic_time_bottom(ctx))
> +				dst_buf->v4l2_buf.field = V4L2_FIELD_NONE;
> +			else
> +				dst_buf->v4l2_buf.field =
V4L2_FIELD_INTERLACED;
> +			spin_unlock(&dev->irqlock);
> +			mfc_debug("Finishing Flags before: %08ld\n",
> +							ctx->dec_dst_flag);
> +			ctx->dec_dst_flag &= ~(1 << dst_buf-
>v4l2_buf.index);
> +			mfc_debug("Finishing Flags after: %08ld\n",
> +							ctx->dec_dst_flag);
> +			vb2_buffer_done(dst_buf, VB2_BUF_STATE_DONE);
> +			spin_lock(&dev->irqlock);
> +			mfc_debug("Cleaned up buffer: %d\n",
> +				  dst_buf->v4l2_buf.index);
> +		}
> +		mfc_debug("After cleanup\n");
> +	}
> +
> +	/* A frame has been decoded and is in the buffer  */
> +	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
> +	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
> +		ctx->sequence++;
> +		/* If frame is same as previous then skip and do not dequeue
> */
> +		if (dec_frame_type !=  S5P_FIMV_DECODE_FRAME_SKIPPED) {
> +		/* The MFC returns address of the buffer, now we have to
> +		 * check which videobuf does it correspond to */
> +		list_for_each_entry(dst_buf, &ctx->dst_queue, drv_entry) {
> +			mfc_debug("Listing: %d\n", dst_buf->v4l2_buf.index);
> +			/* This is the buffer we're looking for */
> +			mfc_debug("paddr: %p mfc: %p\n",
> +					(void *)vb2_plane_paddr(dst_buf, 1),
> +							(void
*)dst_ret_addr);
> +			if (vb2_plane_paddr(dst_buf, 0) == dst_ret_addr) {
> +				list_del(&dst_buf->drv_entry);
> +				ctx->dst_queue_cnt--;
> +				mfc_debug("Flag before: %lx (%d)\n",
> +							ctx->dec_dst_flag,
> +						dst_buf->v4l2_buf.index);
> +				dst_buf->v4l2_buf.sequence = ctx->sequence;
> +				if (s5p_mfc_get_pic_time_top(ctx) ==
> +					s5p_mfc_get_pic_time_bottom(ctx))
> +					dst_buf->v4l2_buf.field =
>
+								V4L2_FIELD_N
ONE;
> +				else
> +					dst_buf->v4l2_buf.field =
>
+							V4L2_FIELD_INTERLACE
D;
> +				mfc_debug("Field: %d\n",
> +						dst_buf->v4l2_buf.field);
> +				mfc_debug("Freed -> sequence: %d index:
%d\n",
> +						dst_buf->v4l2_buf.field,
> +						dst_buf->v4l2_buf.index);
> +				vb2_set_plane_payload(dst_buf, 0,
> +								ctx-
>luma_size);
> +				vb2_set_plane_payload(dst_buf, 1,
> +							ctx->chroma_size);
> +				spin_unlock(&dev->irqlock);
> +				clear_bit(dst_buf->v4l2_buf.index,
> +							&ctx->dec_dst_flag);
> +				if (err) {
> +					vb2_buffer_done(dst_buf,
>
+							VB2_BUF_STATE_ERROR)
;
> +				} else {
> +					vb2_buffer_done(dst_buf,
> +							VB2_BUF_STATE_DONE);
> +				}
> +				spin_lock(&dev->irqlock);
> +				mfc_debug("Flag after: %lx\n",
> +					  ctx->dec_dst_flag);
> +				break;
> +			}
> +		}
> +		}
> +	} else {
> +		mfc_debug("No frame decode.\n");
> +	}
> +	/* Mark source buffer as complete */
> +	if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
> +		&& !list_empty(&ctx->src_queue)) {
> +		src_buf = list_entry(ctx->src_queue.next, struct vb2_buffer,
> +								drv_entry);
> +		mfc_debug("Packed PB test. Size: %d, prev off:"\
> +			" %ld now con: %d\n", src_buf-
> >v4l2_planes[0].bytesused,
> +			ctx->consumed_stream,
s5p_mfc_get_consumed_stream());
> +		ctx->consumed_stream += s5p_mfc_get_consumed_stream();
> +		if (dec_frame_type == S5P_FIMV_DECODE_FRAME_P_FRAME
> +						&& ctx->consumed_stream <
> +					src_buf->v4l2_planes[0].bytesused) {
> +			/* Run MFC again on the same buffer */
> +			mfc_debug("Running again the same buffer.\n");
> +			s5p_mfc_set_dec_stream_buffer(ctx,
> +						vb2_plane_paddr(src_buf, 0),
> +							ctx-
>consumed_stream,
> +					src_buf->v4l2_planes[0].bytesused -
> +							ctx-
>consumed_stream);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			spin_unlock(&dev->irqlock);
> +			s5p_mfc_clear_int_flags();
> +			wake_up_ctx(ctx, reason, err);
> +			s5p_mfc_decode_one_frame(ctx, 0);
> +			return;
> +		} else {
> +			mfc_debug("MFC needs next buffer..\n");
> +			/* Advance to next buffer */
> +			ctx->consumed_stream = 0;
> +			list_del(&src_buf->drv_entry);
> +			ctx->src_queue_cnt--;
> +			if (vb2_plane_size(src_buf, 0) == 0) {
> +				mfc_debug("Setting ctx->state to FINISH\n");
> +				ctx->state = MFCINST_DEC_FINISHING;
> +			}
> +			spin_unlock(&dev->irqlock);
> +			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
> +
> +		}
> +	} else {
> +		spin_unlock(&dev->irqlock);
> +	}
> +	mfc_debug("Assesing whether this context should be run again.\n");
> +	if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_DEC_FINISHING)
> +				    || ctx->dst_queue_cnt < ctx->dpb_count)
{
> +		mfc_debug("No need to run again.\n");
> +		clear_work_bit(ctx);
> +	}
> +	mfc_debug("After assesing whether this context should be run
> again.\n");
> +	s5p_mfc_clear_int_flags();
> +	wake_up_ctx(ctx, reason, err);
> +	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +		BUG();
> +	s5p_mfc_try_run();
> +}
> +
> +/* Error handling for interrupt */
> +static inline void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
> +	unsigned int reason, unsigned int err)
> +{
> +	mfc_err("Interrupt Error: %08x\n", err);
> +	s5p_mfc_clear_int_flags();
> +	wake_up_dev(reason, err);
> +	/* If no context is available then all necessary
> +	 * processing has been done. */
> +	if (ctx == 0)
> +		return;
> +	/* Error recovery is dependent on the state of context */
> +	switch (ctx->state) {
> +	case MFCINST_DEC_INIT:
> +		/* This error had to happen while acquireing instance */
> +	case MFCINST_DEC_GOT_INST:
> +		/* This error had to happen while parsing the header */
> +	case MFCINST_DEC_HEAD_PARSED:
> +		/* This error had to happen while setting dst buffers */
> +	case MFCINST_DEC_RETURN_INST:
> +		/* This error had to happen while releasing instance */
> +		clear_work_bit(ctx);
> +		wake_up_ctx(ctx, reason, err);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		break;
> +	case MFCINST_DEC_FINISHING:
> +	case MFCINST_DEC_FINISHED:
> +	case MFCINST_DEC_RUNNING:
> +		/* It is higly probable that an error occured
> +		 * while decoding a frame */
> +		clear_work_bit(ctx);
> +		ctx->state = MFCINST_DEC_ERROR;
> +		/* Mark all dst buffers as having an error */
> +		s5p_mfc_error_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
> +		/* Mark all src buffers as having an error */
> +		s5p_mfc_error_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		break;
> +	default:
> +		mfc_err("Encountered an error interrupt which had not been"
> \
> +								"
handled.\n");
> +		break;
> +	}
> +	return;
> +}
> +
> +/* Interrupt processing */
> +static irqreturn_t s5p_mfc_irq(int irq, void *priv)
> +{
> +	struct vb2_buffer *src_buf;
> +	struct s5p_mfc_ctx *ctx;
> +	unsigned int reason;
> +	unsigned int err;
> +	mfc_debug("s5p_mfc_irq++\n");
> +	/* Reset the timeout watchdog */
> +	atomic_set(&dev->watchdog_cnt, 0);
> +	ctx = dev->ctx[dev->curr_ctx];
> +	/* Get the reason of interrupt and the error code */
> +	reason = s5p_mfc_get_int_reason();
> +	err = s5p_mfc_get_int_err();
> +	mfc_debug("Int reason: %d (error: %08x)\n", reason, err);
> +	switch (reason) {
> +	case S5P_FIMV_R2H_CMD_DECODE_ERR_RET:
> +		/* An error has occured */
> +		if (ctx->state == MFCINST_DEC_RUNNING && err >= 145)
> +			s5p_mfc_handle_frame_int(ctx, reason, err);
> +		else
> +			s5p_mfc_handle_error(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
> +	case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
> +		s5p_mfc_handle_frame_int(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
> +		if (ctx->fmt->fourcc != V4L2_PIX_FMT_DIVX3) {
> +			ctx->img_width = s5p_mfc_get_img_width();
> +			ctx->img_height = s5p_mfc_get_img_height();
> +		}
> +		ctx->buf_width = ALIGN(ctx->img_width, 128);
> +		ctx->buf_height = ALIGN(ctx->img_height, 32);
> +		mfc_debug("SEQ Done: ctx:%dx%d, buf:%dx%d\n", ctx-
>img_width,
> +			ctx->img_height, ctx->buf_width, ctx->buf_height);
> +		ctx->luma_size = ALIGN(ctx->buf_width * ctx->buf_height,
> 8192);
> +		ctx->chroma_size = ALIGN(ctx->buf_width *
> +					ALIGN(ctx->img_height / 2, 32),
8192);
> +		if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
> +			ctx->mv_size = ALIGN(ctx->buf_width *
> +					 ALIGN(ctx->buf_height / 4, 32),
8192);
> +		else
> +			ctx->mv_size = 0;
> +		ctx->dpb_count = s5p_mfc_get_dpb_count();
> +		ctx->state = MFCINST_DEC_HEAD_PARSED;
> +		s5p_mfc_clear_int_flags();
> +		clear_work_bit(ctx);
> +		if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +			BUG();
> +		s5p_mfc_try_run();
> +		wake_up_ctx(ctx, reason, err);
> +		break;
> +	case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
> +		ctx->inst_no = s5p_mfc_get_inst_no();
> +		ctx->state = MFCINST_DEC_GOT_INST;
> +		clear_work_bit(ctx);
> +		wake_up_interruptible(&ctx->queue);
> +		goto irq_cleanup_hw;
> +		break;
> +	case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
> +		clear_work_bit(ctx);
> +		ctx->state = MFCINST_FREE;
> +		wake_up(&ctx->queue);
> +		goto irq_cleanup_hw;
> +		break;
> +	case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
> +	case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
> +		if (ctx)
> +			clear_work_bit(ctx);
> +		s5p_mfc_clear_int_flags();
> +		wake_up_dev(reason, err);
> +		clear_bit(0, &dev->hw_lock);
> +		break;
> +	case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
> +		s5p_mfc_clear_int_flags();
> +		ctx->int_type = reason;
> +		ctx->int_err = err;
> +		ctx->int_cond = 1;
> +		spin_lock(&dev->condlock);
> +		clear_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock(&dev->condlock);
> +		if (err == 0) {
> +			ctx->state = MFCINST_DEC_RUNNING;
> +			spin_lock(&dev->irqlock);
> +			src_buf =
> +			    list_entry(ctx->src_queue.next,
> +				       struct vb2_buffer, drv_entry);
> +			list_del(&src_buf->drv_entry);
> +			ctx->src_queue_cnt--;
> +			spin_unlock(&dev->irqlock);
> +			vb2_buffer_done(src_buf, VB2_BUF_STATE_DONE);
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +				BUG();
> +			wake_up_interruptible(&ctx->queue);
> +			s5p_mfc_try_run();
> +		} else {
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +				BUG();
> +			wake_up_interruptible(&ctx->queue);
> +		}
> +		break;
> +	default:
> +		mfc_debug("Unknown int reason.\n");
> +		s5p_mfc_clear_int_flags();
> +	}
> +	mfc_debug("s5p_mfc_irq--\n");
> +	return IRQ_HANDLED;
> +irq_cleanup_hw:
> +	s5p_mfc_clear_int_flags();
> +	ctx->int_type = reason;
> +	ctx->int_err = err;
> +	ctx->int_cond = 1;
> +	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
> +		mfc_err("Failed to unlock hw.\n");
> +	s5p_mfc_try_run();
> +	mfc_debug("s5p_mfc_irq-- (via irq_cleanup_hw)\n");
> +	return IRQ_HANDLED;
> +}
> +
> +/* Open an MFC node */
> +static int s5p_mfc_open(struct file *file)
> +{
> +	struct s5p_mfc_ctx *ctx = NULL;
> +	unsigned long flags;
> +	int ret = 0;
> +	mfc_debug("s5p_mfc_open++\n");
> +	mutex_lock(dev->mfc_mutex);
> +	atomic_inc(&dev->num_inst);	/* It is guarded by mfc_mutex */
> +	/* Allocate memory for context */
> +	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
> +	if (!ctx) {
> +		mfc_err("Not enough memory.\n");
> +		ret = -ENOMEM;
> +		goto out_open;
> +	}
> +	file->private_data = ctx;
> +	ctx->dev = dev;
> +	INIT_LIST_HEAD(&ctx->src_queue);
> +	INIT_LIST_HEAD(&ctx->dst_queue);
> +	ctx->src_queue_cnt = 0;
> +	ctx->dst_queue_cnt = 0;
> +	/* Get context number */
> +	ctx->num = 0;
> +	while (dev->ctx[ctx->num]) {
> +		ctx->num++;
> +		if (ctx->num >= MFC_NUM_CONTEXTS) {
> +			mfc_err("Too many open contexts.\n");
> +			ret = -EAGAIN;
> +			goto out_open;
> +		}
> +	}
> +	/* Mark context as idle */
> +	spin_lock_irqsave(&dev->condlock, flags);
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	spin_unlock_irqrestore(&dev->condlock, flags);
> +	dev->ctx[ctx->num] = ctx;
> +	/* Default format */
> +	ctx->fmt = &formats[0];
> +	/* Load firmware if this is the first instance */
> +	if (atomic_read(&dev->num_inst) == 1) {
> +		/* Load the FW */
> +		ret = s5p_mfc_alloc_firmware(dev);
> +		if (ret != 0)
> +			goto out_open_2a;
> +		ret = s5p_mfc_load_firmware(dev);
> +		if (ret != 0)
> +			goto out_open_2;
> +		mfc_debug("Enabling clocks.\n");
> +		clk_enable(dev->clock1);
> +		clk_enable(dev->clock2);
> +		/* Init the FW */
> +		ret = s5p_mfc_init_hw(dev);
> +		if (ret != 0)
> +			goto out_open_3;
> +	}
> +	/* Init videobuf2 queue for CAPTURE */
> +	ret = vb2_queue_init(&ctx->vq_dst, &s5p_mfc_qops,
> +			       dev->alloc_ctx[0], &dev->irqlock,
> +			       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, ctx);
> +	if (ret) {
> +		mfc_err("Failed to initialize videobuf2 queue (capture)\n");
> +		goto out_open_3;
> +	}
> +	/* Init videobuf2 queue for OUTPUT */
> +	ret = vb2_queue_init(&ctx->vq_src, &s5p_mfc_qops,
> +			       dev->alloc_ctx[1], &dev->irqlock,
> +			       V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, ctx);
> +	if (ret) {
> +		mfc_err("Failed to initialize videobuf2 queue (output)\n");
> +		goto out_open_3;
> +	}
> +	vb2_set_alloc_ctx(&ctx->vq_dst, dev->alloc_ctx[1], 1);
> +	init_waitqueue_head(&ctx->queue);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_open--\n");
> +	return ret;
> +	/* Deinit when failure occured */
> +out_open_3:
> +	if (atomic_read(&dev->num_inst) == 1) {
> +		clk_disable(dev->clock1);
> +		clk_disable(dev->clock2);
> +		s5p_mfc_release_firmware();
> +	}
> +out_open_2:
> +	s5p_mfc_release_firmware();
> +out_open_2a:
> +	dev->ctx[ctx->num] = 0;
> +	kfree(ctx);
> +out_open:
> +	atomic_dec(&dev->num_inst);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_open-- with error\n");
> +	return ret;
> +}
> +
> +/* Release MFC context */
> +static int s5p_mfc_release(struct file *file)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	unsigned long flags;
> +	mfc_debug("s5p_mfc_release++\n");
> +	mutex_lock(dev->mfc_mutex);
> +	/* Stop all the processing */
> +	vb2_queue_release(&ctx->vq_src);
> +	vb2_queue_release(&ctx->vq_dst);
> +	/* Mark context as idle */
> +	clear_bit(ctx->num, &dev->ctx_work_bits);
> +	/* If instance was initialised then
> +	 * return instance and free reosurces */
> +	if (ctx->state < MFCINST_ENC_INIT && ctx->state >= MFCINST_DEC_INIT)
> {
> +		ctx->state = MFCINST_DEC_RETURN_INST;
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		set_bit(ctx->num, &dev->ctx_work_bits);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		s5p_mfc_clean_ctx_int_flags(ctx);
> +		s5p_mfc_try_run();
> +		/* Wait until instance is returned or timeout occured */
> +		if (s5p_mfc_wait_for_done_ctx
> +		    (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
> +			mfc_err("Error returning instance.\n");
> +		}
> +		/* Free resources */
> +		s5p_mfc_release_dec_buffers(ctx);
> +		s5p_mfc_release_instance_buffer(ctx);
> +		s5p_mfc_release_dec_temp_buffers(ctx);
> +	}
> +	/* hardware locking scheme */
> +	if (dev->curr_ctx == ctx->num)
> +		clear_bit(0, &dev->hw_lock);
> +	atomic_dec(&dev->num_inst);
> +	if (atomic_read(&dev->num_inst) == 0) {
> +		/* Actually this is also protected by mfc_mutex */
> +		s5p_mfc_deinit_hw(dev);
> +		mfc_debug("Disabling clocks...\n");
> +		clk_disable(dev->clock1);
> +		clk_disable(dev->clock2);
> +		s5p_mfc_release_firmware();
> +	}
> +	dev->ctx[ctx->num] = 0;
> +	kfree(ctx);
> +	mutex_unlock(dev->mfc_mutex);
> +	mfc_debug("s5p_mfc_release--\n");
> +	return 0;
> +}
> +
> +/* Poll */
> +static unsigned int s5p_mfc_poll(struct file *file,
> +				 struct poll_table_struct *wait)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	return vb2_poll(&ctx->vq_dst, file, wait);
> +}
> +
> +/* Mmap */
> +static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct s5p_mfc_ctx *ctx = file->private_data;
> +	int ret;
> +	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> +	mfc_debug("s5p_mfc_mmap++\n");
> +	if (offset < DST_QUEUE_OFF_BASE) {
> +		mfc_debug("mmaping source.\n");
> +		ret = vb2_mmap(&ctx->vq_src, vma);
> +	} else {		/* capture */
> +		mfc_debug("mmaping destination.\n");
> +		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
> +		ret = vb2_mmap(&ctx->vq_dst, vma);
> +	}
> +	mfc_debug("s5p_mfc_mmap--\n");
> +	return ret;
> +}
> +
> +/* v4l2 ops */
> +static const struct v4l2_file_operations s5p_mfc_fops = {
> +	.owner = THIS_MODULE,
> +	.open = s5p_mfc_open,
> +	.release = s5p_mfc_release,
> +	.poll = s5p_mfc_poll,
> +	.ioctl = video_ioctl2,
> +	.mmap = s5p_mfc_mmap,
> +};
> +
> +/* videodec structure */
> +static struct video_device s5p_mfc_videodev = {
> +	.name = S5P_MFC_NAME,
> +	.fops = &s5p_mfc_fops,
> +	.ioctl_ops = &s5p_mfc_ioctl_ops,
> +	.minor = -1,
> +	.release = video_device_release,
> +};
> +
> +/* MFC probe function */
> +static int s5p_mfc_probe(struct platform_device *pdev)
> +{
> +	struct video_device *vfd;
> +	struct resource *res;
> +	int ret = -ENOENT;
> +	size_t size;
> +	mfc_debug("s5p_mfc_probe++\n");
> +	dev = kzalloc(sizeof *dev, GFP_KERNEL);
> +	if (!dev) {
> +		dev_err(&pdev->dev, "Not enough memoty for MFC device.\n");
> +		return -ENOMEM;
> +	}
> +	mfc_debug("Ok, allocated memory for device structure.\n");
> +	spin_lock_init(&dev->irqlock);
> +	spin_lock_init(&dev->condlock);
> +	mfc_debug("Initialised spin lock\n");
> +	dev->plat_dev = pdev;
> +	if (!dev->plat_dev) {
> +		dev_err(&pdev->dev, "No platform data specified\n");
> +		ret = -ENODEV;
> +		goto free_dev;
> +	}
> +	mfc_debug("Getting clocks\n");
> +	dev->clock1 = clk_get(&pdev->dev, "sclk_mfc");
> +	mfc_debug("Got clock1\n");
> +	dev->clock2 = clk_get(&pdev->dev, "mfc");
> +	mfc_debug("Got clock2\n");
> +	if (IS_ERR(dev->clock1) || IS_ERR(dev->clock2)) {
> +		dev_err(&pdev->dev, "failed to get mfc clock source\n");
> +		goto free_clk;
> +	}
> +	mfc_debug("No \"error\" in clock\n");
> +	mfc_debug("After setting clock rate\n");
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get memory region
> resource.\n");
> +		ret = -ENOENT;
> +		goto probe_out1;
> +	}
> +	size = (res->end - res->start) + 1;
> +	dev->mfc_mem = request_mem_region(res->start, size, pdev->name);
> +	if (dev->mfc_mem == NULL) {
> +		dev_err(&pdev->dev, "failed to get memory region.\n");
> +		ret = -ENOENT;
> +		goto probe_out2;
> +	}
> +	dev->base_virt_addr = ioremap(dev->mfc_mem->start,
> +			      dev->mfc_mem->end - dev->mfc_mem->start + 1);
> +	if (dev->base_virt_addr == NULL) {
> +		dev_err(&pdev->dev, "failed to ioremap address region.\n");
> +		ret = -ENOENT;
> +		goto probe_out3;
> +	}
> +	dev->regs_base = dev->base_virt_addr;
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (res == NULL) {
> +		dev_err(&pdev->dev, "failed to get irq resource.\n");
> +		ret = -ENOENT;
> +		goto probe_out4;
> +	}
> +	dev->irq = res->start;
> +	ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
>
+									dev)
;
> +
> +	if (ret != 0) {
> +		dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
> +		goto probe_out5;
> +	}
> +	dev->mfc_mutex = kmalloc(sizeof(struct mutex), GFP_KERNEL);
> +	if (dev->mfc_mutex == NULL) {
> +		dev_err(&pdev->dev, "Memory allocation failed\n");
> +		ret = -ENOMEM;
> +		goto probe_out6;
> +	}
> +	mutex_init(dev->mfc_mutex);
> +	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> +	if (ret)
> +		goto probe_out7;
> +	atomic_set(&dev->num_inst, 0);
> +	init_waitqueue_head(&dev->queue);
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to allocate video
> device\n");
> +		ret = -ENOMEM;
> +		goto unreg_dev;
> +	}
> +	*vfd = s5p_mfc_videodev;
> +	video_set_drvdata(vfd, dev);
> +	snprintf(vfd->name, sizeof(vfd->name), "%s", s5p_mfc_videodev.name);
> +	dev->vfd = vfd;
> +
> +	platform_set_drvdata(pdev, dev);
> +	dev->hw_lock = 0;
> +	dev->watchdog_workqueue = create_singlethread_workqueue("s5p-mfc");
> +	INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
> +	atomic_set(&dev->watchdog_cnt, 0);
> +	init_timer(&dev->watchdog_timer);
> +	dev->watchdog_timer.data = 0;
> +	dev->watchdog_timer.function = s5p_mfc_watchdog;
> +	dev->watchdog_timer.expires = jiffies +
>
+					msecs_to_jiffies(MFC_WATCHDOG_INTERV
AL);
> +	add_timer(&dev->watchdog_timer);

Watch_dog single thread runs right after probing MFC, but this doesn't look
like
nice way in terms of purpose of this timer which is for error handling in
the 
middle of decoding. What about moving point running this timer to the
open().
And it should be stopped in release time. Of course, dev->num_inst should
be 
considered.
 
> +
> +	dev->alloc_ctx = vb2_cma_init_multi(&pdev->dev, 2, s5p_mem_types,
> +							s5p_mem_alignments);
> +	if (IS_ERR(dev->alloc_ctx)) {
> +		mfc_err("Couldn't prepare allocator context.\n");
> +		ret = PTR_ERR(dev->alloc_ctx);
> +		goto alloc_ctx_fail;
> +	}
> +
> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
> +	if (ret) {
> +		v4l2_err(&dev->v4l2_dev, "Failed to register video
> device\n");
> +		video_device_release(vfd);
> +		goto rel_vdev;
> +	}
> +	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
> +								vfd->num);
> +	mfc_debug("s5p_mfc_probe--\n");
> +	return 0;
> +
> +/* Deinit MFC if probe had failed */
> +rel_vdev:
> +	vb2_cma_cleanup_multi(dev->alloc_ctx);
> +alloc_ctx_fail:
> +unreg_dev:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +
> +probe_out7:
> +	if (dev->mfc_mutex) {
> +		mutex_destroy(dev->mfc_mutex);
> +		kfree(dev->mfc_mutex);
> +	}
> +probe_out6:
> +	free_irq(dev->irq, dev);
> +probe_out5:
> +probe_out4:
> +	iounmap(dev->base_virt_addr);
> +	dev->base_virt_addr = NULL;
> +probe_out3:
> +	release_resource(dev->mfc_mem);
> +	kfree(dev->mfc_mem);
> +probe_out2:
> +probe_out1:
> +	clk_put(dev->clock1);
> +	clk_put(dev->clock2);
> +free_clk:
> +
> +free_dev:
> +	kfree(dev);
> +	mfc_debug("s5p_mfc_probe-- with error\n");
> +	return ret;
> +}
> +
> +/* Remove the driver */
> +static int s5p_mfc_remove(struct platform_device *pdev)
> +{
> +	mfc_debug("s5p_mfc_remove++\n");
> +	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
> +	del_timer_sync(&dev->watchdog_timer);
> +	flush_workqueue(dev->watchdog_workqueue);
> +	destroy_workqueue(dev->watchdog_workqueue);
> +	video_unregister_device(dev->vfd);
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +	vb2_cma_cleanup_multi(dev->alloc_ctx);
> +	if (dev->mfc_mutex) {
> +		mutex_destroy(dev->mfc_mutex);
> +		kfree(dev->mfc_mutex);
> +	}
> +	mfc_debug("Will now deinit HW\n");
> +	s5p_mfc_deinit_hw(dev);
> +	free_irq(dev->irq, dev);
> +	iounmap(dev->base_virt_addr);
> +	if (dev->mfc_mem != NULL) {
> +		release_resource(dev->mfc_mem);
> +		kfree(dev->mfc_mem);
> +		dev->mfc_mem = NULL;
> +	}
> +	clk_put(dev->clock1);
> +	clk_put(dev->clock2);
> +	kfree(dev);
> +	mfc_debug("s5p_mfc_remove--\n");
> +	return 0;
> +}
> +
> +static int s5p_mfc_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_mfc_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +/* Power management */
> +static const struct dev_pm_ops s5p_mfc_pm_ops = {
> +	.suspend = s5p_mfc_suspend,
> +	.resume = s5p_mfc_resume,
> +};
> +
> +static struct platform_driver s5p_mfc_pdrv = {
> +	.probe = s5p_mfc_probe,
> +	.remove = __devexit_p(s5p_mfc_remove),
> +	.driver = {
> +		   .name = S5P_MFC_NAME,
> +		   .owner = THIS_MODULE,
> +		   .pm = &s5p_mfc_pm_ops},
> +};
> +
> +static char banner[] __initdata =
> +			"S5P MFC V4L2 Driver, (c) 2010 Samsung
Electronics\n";
> +
> +static int __init s5p_mfc_init(void)
> +{
> +	mfc_info("%s", banner);
> +	if (platform_driver_register(&s5p_mfc_pdrv) != 0) {
> +		printk(KERN_ERR "platform device registration failed..\n");
> +		return -1;
> +	}
> +	mfc_info("After init...\n");
> +	return 0;
> +}
> +
> +static void __devexit s5p_mfc_exit(void)
> +{
> +	platform_driver_unregister(&s5p_mfc_pdrv);
> +}
> +
> +module_init(s5p_mfc_init);
> +module_exit(s5p_mfc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> new file mode 100644
> index 0000000..4f18937
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
> @@ -0,0 +1,190 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * This file contains definitions of enums and structs used by the codec
> + * driver.
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#ifndef S5P_MFC_COMMON_H_
> +#define S5P_MFC_COMMON_H_
> +
> +#include "regs-mfc5.h"
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +
> +#include <media/videobuf2-core.h>
> +
> +#define MFC_MAX_EXTRA_DPB       5
> +#define MFC_MAX_BUFFERS		32
> +#define MFC_FRAME_PLANES	2
> +
> +#define MFC_NUM_CONTEXTS	4
> +/* Interrupt timeout */
> +#define MFC_INT_TIMEOUT		2000
> +/* Busy wait timeout */
> +#define MFC_BW_TIMEOUT		500
> +/* Watchdog interval */
> +#define MFC_WATCHDOG_INTERVAL   1000
> +/* After how many executions watchdog should assume lock up */
> +#define MFC_WATCHDOG_CNT        10
> +
> +
> +/**
> + * enum s5p_mfc_inst_state - The state of an MFC instance.
> + */
> +enum s5p_mfc_inst_state {
> +	MFCINST_FREE = 0,
> +	MFCINST_DEC_INIT = 100,
> +	MFCINST_DEC_GOT_INST,
> +	MFCINST_DEC_HEAD_PARSED,
> +	MFCINST_DEC_BUFS_SET,
> +	MFCINST_DEC_RUNNING,
> +	MFCINST_DEC_FINISHING,
> +	MFCINST_DEC_FINISHED,
> +	MFCINST_DEC_RETURN_INST,
> +	MFCINST_DEC_ERROR,
> +	MFCINST_ENC_INIT = 200,
> +};
> +
> +/**
> + * enum s5p_mfc_queue_state - The state of buffer queue.
> + */
> +enum s5p_mfc_queue_state {
> +	QUEUE_FREE = 0,
> +	QUEUE_BUFS_REQUESTED,
> +	QUEUE_BUFS_QUERIED,
> +	QUEUE_BUFS_MMAPED,
> +};
> +
> +struct s5p_mfc_ctx;
> +
> +/**
> + * struct s5p_mfc_dev - The struct containing driver internal parameters.
> + */
> +struct s5p_mfc_dev {
> +	struct v4l2_device v4l2_dev;
> +	struct video_device *vfd;
> +	struct platform_device *plat_dev;
> +
> +	atomic_t num_inst;
> +	spinlock_t irqlock;
> +	spinlock_t condlock;
> +
> +	void __iomem *regs_base;
> +	int irq;
> +
> +	struct resource *mfc_mem;
> +	void __iomem *base_virt_addr;
> +
> +	struct mutex *mfc_mutex;
> +
> +	int int_cond;
> +	int int_type;
> +	unsigned int int_err;
> +	wait_queue_head_t queue;
> +
> +	size_t port_a;
> +	size_t port_b;
> +
> +	unsigned long hw_lock;
> +
> +	struct clk *clock1;
> +	struct clk *clock2;
> +
> +	struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
> +	int curr_ctx;
> +	unsigned long ctx_work_bits;
> +
> +	atomic_t watchdog_cnt;
> +	struct timer_list watchdog_timer;
> +	struct workqueue_struct *watchdog_workqueue;
> +	struct work_struct watchdog_work;
> +
> +	struct vb2_alloc_ctx **alloc_ctx;
> +};
> +
> +/**
> + * struct s5p_mfc_ctx - This struct contains the instance context
> + */
> +struct s5p_mfc_ctx {
> +	struct s5p_mfc_dev *dev;
> +	int num;
> +
> +	int int_cond;
> +	int int_type;
> +	unsigned int int_err;
> +	wait_queue_head_t queue;
> +
> +	struct s5p_mfc_fmt *fmt;
> +
> +	struct vb2_queue vq_src;
> +	struct vb2_queue vq_dst;
> +
> +	struct list_head src_queue;
> +	struct list_head dst_queue;
> +
> +	unsigned int src_queue_cnt;
> +	unsigned int dst_queue_cnt;
> +
> +	enum s5p_mfc_inst_state state;
> +	int inst_no;
> +
> +	/* Decoder parameters */
> +	int img_width;
> +	int img_height;
> +	int buf_width;
> +	int buf_height;
> +	int dpb_count;
> +	int total_dpb_count;
> +
> +	int luma_size;
> +	int chroma_size;
> +	int mv_size;
> +
> +	unsigned long consumed_stream;
> +	int slice_interface;
> +
> +	/* Buffers */
> +	size_t port_a;
> +	size_t port_a_size;
> +	size_t port_b;
> +	size_t port_b_size;
> +
> +
> +	enum s5p_mfc_queue_state capture_state;
> +	enum s5p_mfc_queue_state output_state;
> +
> +	size_t dec_dst_buf_luma[MFC_MAX_BUFFERS];
> +	size_t dec_dst_buf_chroma[MFC_MAX_BUFFERS];
> +
> +	int dec_dst_buf_cnt;
> +	unsigned int sequence;
> +	unsigned long dec_dst_flag;
> +	size_t dec_src_buf_size;
> +
> +	/* Control values */
> +	int codec_mode;
> +	__u32 pix_format;
> +	int loop_filter_mpeg4;
> +	int display_delay;
> +
> +	/* Buffers */
> +	size_t instance_phys;
> +	size_t instance_size;
> +	size_t desc_phys;
> +	size_t shared_phys;
> +	void *shared_virt;
> +
> +};
> +
> +#endif /* S5P_MFC_COMMON_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> new file mode 100644
> index 0000000..222ed71
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrls.h
> @@ -0,0 +1,173 @@
> +/*
> + * Samsung S5P Multi Format Codec v 5.0
> + *
> + * This file contains description of formats used by MFC and cotrols
> + * used by the driver.
> + *
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * Kamil Debski, <k.debski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> the
> + * Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#ifndef S5P_MFC_CTRLS_H_
> +#define S5P_MFC_CTRLS_H_
> +
> +#include <media/v4l2-ioctl.h>
> +#include "regs-mfc5.h"
> +
> +#define MFC_FMT_DEC	0
> +#define MFC_FMT_ENC	1
> +#define MFC_FMT_RAW	2
> +
> +struct s5p_mfc_fmt {
> +	char *name;
> +	u32 fourcc;
> +	u32 codec_mode;
> +	u32 type;
> +	u32 num_planes;
> +};
> +
> +#define MFC_FORMATS_NO_CODEC -1
> +
> +static struct s5p_mfc_fmt formats[] = {
> +	{
> +	.name = "4:2:0 2 Planes 64x32 Tiles",
> +	.fourcc = V4L2_PIX_FMT_NV12MT,
> +	.codec_mode = MFC_FORMATS_NO_CODEC,
> +	.type = MFC_FMT_RAW,
> +	.num_planes = 2,
> +	 },
> +	{
> +	.name = "4:2:0 2 Planes",
> +	.fourcc = V4L2_PIX_FMT_NV12,
> +	.codec_mode = MFC_FORMATS_NO_CODEC,
> +	.type = MFC_FMT_RAW,
> +	.num_planes = 2,
> +	},
> +	{
> +	.name = "H264 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_H264,
> +	.codec_mode = S5P_FIMV_CODEC_H264_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "H263 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_H263,
> +	.codec_mode = S5P_FIMV_CODEC_H263_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "MPEG1/MPEG2 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_MPEG12,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "MPEG4 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_MPEG4,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 3.11 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX3,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX311_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 4.12 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX4,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX412_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 5.00-5.02 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX500,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX502_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "DivX 5.03 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_DIVX503,
> +	.codec_mode = S5P_FIMV_CODEC_DIVX503_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "XviD Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_XVID,
> +	.codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "VC1 Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_VC1,
> +	.codec_mode = S5P_FIMV_CODEC_VC1_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +	{
> +	.name = "VC1 RCV Encoded Stream",
> +	.fourcc = V4L2_PIX_FMT_VC1_RCV,
> +	.codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
> +	.type = MFC_FMT_DEC,
> +	.num_planes = 1,
> +	},
> +};
> +
> +#define NUM_FORMATS ARRAY_SIZE(formats)
> +
> +static struct v4l2_queryctrl s5p_mfc_ctrls[] = {
> +/* For decoding */
> +	{
> +	.id = V4L2_CID_CODEC_DISPLAY_DELAY,
> +	.type = V4L2_CTRL_TYPE_INTEGER,
> +	.name = "",
> +	.minimum = 0,
> +	.maximum = 16383,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +	{
> +	.id = V4L2_CID_CODEC_LOOP_FILTER_MPEG4_ENABLE,
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.name = "Mpeg4 Loop Filter Enable",
> +	.minimum = 0,
> +	.maximum = 1,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +	{
> +	.id = V4L2_CID_CODEC_SLICE_INTERFACE,
> +	.type = V4L2_CTRL_TYPE_BOOLEAN,
> +	.name = "Slice Interface Enable",
> +	.minimum = 0,
> +	.maximum = 1,
> +	.step = 1,
> +	.default_value = 0,
> +	},
> +};
> +
> +#define NUM_CTRLS ARRAY_SIZE(s5p_mfc_ctrls)
> +
> +#endif /* S5P_MFC_CTRLS_H_ */
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> new file mode 100644
> index 0000000..543f3fb
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> @@ -0,0 +1,77 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
> + *
> + * C file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains functions used to wait for command completion.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/wait.h>
> +#include <linux/sched.h>
> +#include <linux/io.h>
> +#include "regs-mfc5.h"
> +#include "s5p_mfc_intr.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
> +{
> +	if (wait_event_interruptible_timeout(dev->queue,
> +		(dev->int_cond && (dev->int_type == command
> +		|| dev->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +		msecs_to_jiffies(MFC_INT_TIMEOUT)) == 0) {
> +		mfc_err("Interrupt (%d dev) timed out.\n", dev->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (dev->queue, %d).\n", dev->int_type);
> +	if (dev->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}
> +
> +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
> +{
> +	dev->int_cond = 0;
> +	dev->int_type = 0;
> +	dev->int_err = 0;
> +}
> +
> +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> +				    int command, int interrupt)
> +{
> +	int ret;
> +	if (interrupt) {
> +		ret = wait_event_interruptible_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	} else {
> +		ret = wait_event_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	}
> +	if (ret == 0) {
> +		mfc_err("Interrupt (%d ctx) timed out.\n", ctx->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (ctx->queue, %d).\n", ctx->int_type);
> +	if (ctx->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}
> +
> +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
> +{
> +	ctx->int_cond = 0;
> +	ctx->int_type = 0;
> +	ctx->int_err = 0;
> +}
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> new file mode 100644
> index 0000000..8c531b6
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
> @@ -0,0 +1,26 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * It contains waiting functions declarations.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef _S5P_MFC_INTR_H_
> +#define _S5P_MFC_INTR_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> +						int command, int interrupt);
> +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
> +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
> +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
> +
> +#endif /* _S5P_MFC_INTR_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> new file mode 100644
> index 0000000..90fa84c
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_logmsg.h
> @@ -0,0 +1,65 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_logmsg.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains debug macros
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_LOGMSG_H_
> +#define S5P_MFC_LOGMSG_H_
> +
> +/* Debug macros */
> +#define MFC_DEBUG(fmt, ...)					\
> +	do {							\
> +		printk(KERN_ERR					\
> +			"%s: " fmt, __func__, ##__VA_ARGS__);	\
> +	} while (0)
> +
> +#define MFC_ERROR(fmt, ...)					\
> +	do {							\
> +		printk(KERN_ERR					\
> +			"%s: " fmt, __func__, ##__VA_ARGS__);	\
> +	} while (0)
> +
> +#define MFC_NOTICE(fmt, ...)					\
> +	do {							\
> +		printk(KERN_NOTICE				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +#define MFC_INFO(fmt, ...)					\
> +	do {							\
> +		printk(KERN_INFO				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +#define MFC_WARN(fmt, ...)					\
> +	do {							\
> +		printk(KERN_WARNING				\
> +			fmt, ##__VA_ARGS__);			\
> +	} while (0)
> +
> +/* Uncomment the line below do enable debug messages */
> +/* #define CONFIG_VIDEO_MFC50_DEBUG */
> +
> +#ifdef CONFIG_VIDEO_MFC50_DEBUG
> +#define mfc_debug(fmt, ...)		MFC_DEBUG(fmt, ##__VA_ARGS__)
> +#warning "DEBUG in MFC is switched on!"
> +#else
> +#define mfc_debug(fmt, ...)
> +#endif /* CONFIG_VIDEO_MFC50_DEBUG */
> +
> +#define mfc_err(fmt, ...)		MFC_ERROR(fmt, ##__VA_ARGS__)
> +#define mfc_notice(fmt, ...)		MFC_NOTICE(fmt, ##__VA_ARGS__)
> +#define mfc_info(fmt, ...)		MFC_INFO(fmt, ##__VA_ARGS__)
> +#define mfc_warn(fmt, ...)		MFC_WARN(fmt, ##__VA_ARGS__)
> +
> +#endif /* S5P_MFC_LOGMSG_H_ */
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> new file mode 100644
> index 0000000..a7e4009
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_memory.h
> @@ -0,0 +1,32 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_memory.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * Contains memory related defines.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_MEMORY_H_
> +#define S5P_MFC_MEMORY_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +#define FIRMWARE_CODE_SIZE		0x60000	/* 384KB */
> +#define MFC_H264_INSTANCE_BUF_SIZE	0x96000	/* 600KB per H264
> instance */
> +#define MFC_INSTANCE_BUF_SIZE		0x2800	/* 10KB per
instance */
> +#define DESC_BUF_SIZE			0x20000	/* 128KB for DESC
> buffer */
> +#define SHARED_BUF_SIZE			0x01000	/* 4KB for shared
> buffer */
> +#define CPB_BUF_SIZE			0x400000/* 4MB fr decoder */
> +
> +/* Define names for CMA memory kinds used by MFC */
> +#define MFC_CMA_BANK1		"a"
> +#define MFC_CMA_BANK2		"b"
> +#define MFC_CMA_FW		"f"
> +
> +#endif /* S5P_MFC_MEMORY_H_ */
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> new file mode 100644
> index 0000000..b6e2334
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
> @@ -0,0 +1,800 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
> + *
> + * Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains hw related functions.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/mm.h>
> +#include <linux/io.h>
> +#include <linux/jiffies.h>
> +#include "regs-mfc5.h"
> +
> +#include "s5p_mfc_opr.h"
> +#include "s5p_mfc_common.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_memory.h"
> +
> +#include "s5p_mfc_intr.h"
> +
> +#include <linux/firmware.h>
> +#include <linux/err.h>
> +#include <linux/sched.h>
> +#include <linux/cma.h>
> +
> +static size_t s5p_mfc_phys_bitproc_buff;
> +static unsigned char *s5p_mfc_virt_bitproc_buff;
> +
> +/* #define S5P_MFC_DEBUG_REGWRITE  */
> +#ifdef S5P_MFC_DEBUG_REGWRITE
> +#undef writel
> +#define writel(v, r) do { \
> +	printk(KERN_ERR "MFCWRITE(%p): %08x\n", r, (unsigned int)v); \
> +	__raw_writel(v, r); } while (0)
> +#endif /* S5P_MFC_DEBUG_REGWRITE */
> +
> +#define READL(offset)		readl(dev->regs_base + (offset))
> +#define WRITEL(data, offset)	writel((data), dev->regs_base + (offset))
> +#define OFFSETA(x)		(((x) - dev->port_a) >> 11)
> +#define OFFSETB(x)		(((x) - dev->port_b) >> 11)
> +
> +/* Reset the device */
> +static int s5p_mfc_cmd_reset(struct s5p_mfc_dev *dev)
> +{
> +	unsigned int mc_status;
> +	unsigned long timeout;
> +	mfc_debug("s5p_mfc_cmd_reset++\n");
> +	/* Stop procedure */
> +	WRITEL(0x3f7, S5P_FIMV_SW_RESET);	/*  reset VI */
> +	WRITEL(0x3f6, S5P_FIMV_SW_RESET);	/*  reset RISC */
> +	WRITEL(0x3e2, S5P_FIMV_SW_RESET);	/*  All reset except for MC
> */
> +	mdelay(10);
> +	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
> +	/* Check MC status */
> +	do {
> +		if (time_after(jiffies, timeout)) {
> +			mfc_err("Timeout while resetting MFC.\n");
> +			return -EIO;
> +		}
> +		mc_status = READL(S5P_FIMV_MC_STATUS);
> +	} while (mc_status & 0x3);
> +	WRITEL(0x0, S5P_FIMV_SW_RESET);
> +	WRITEL(0x3fe, S5P_FIMV_SW_RESET);
> +	mfc_debug("s5p_mfc_cmd_reset--\n");
> +	return 0;
> +}
> +
> +/* Send a command to the MFC */
> +static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev,
> +				struct s5p_mfc_ctx *mfc_ctx, int cmd, int
arg)
> +{
> +	int cur_cmd;
> +	unsigned long timeout;
> +	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
> +	/* wait until host to risc command register becomes 'H2R_CMD_EMPTY'
> */
> +	do {
> +		if (time_after(jiffies, timeout)) {
> +			mfc_err("Timeout while waiting for hardware.\n");
> +			return -EIO;
> +		}
> +		cur_cmd = READL(S5P_FIMV_HOST2RISC_CMD);
> +	} while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
> +	WRITEL(arg, S5P_FIMV_HOST2RISC_ARG1);
> +	if (cmd == S5P_FIMV_H2R_CMD_OPEN_INSTANCE) {
> +		/* No CRC calculation (slow!) */
> +		WRITEL(0, S5P_FIMV_HOST2RISC_ARG2);
> +		/* Physical addr of the instance buffer */
> +		WRITEL(OFFSETA(mfc_ctx->instance_phys),
> +		       S5P_FIMV_HOST2RISC_ARG3);
> +		/* Size of the instance buffer */
> +		WRITEL(mfc_ctx->instance_size, S5P_FIMV_HOST2RISC_ARG4);
> +	}
> +	/* Issue the command */
> +	WRITEL(cmd, S5P_FIMV_HOST2RISC_CMD);
> +	return 0;
> +}
> +/*
> +static void s5p_mfc_cmd_sleep()
> +{
> +	WRITEL(-1, S5P_FIMV_CH_ID);
> +	WRITEL(MFC_SLEEP, S5P_FIMV_COMMAND_TYPE);
> +}
> +*/
> +
> +/*
> +static void s5p_mfc_cmd_wakeup()
> +{
> +	WRITEL(-1, S5P_FIMV_CH_ID);
> +	WRITEL(MFC_WAKEUP, S5P_FIMV_COMMAND_TYPE);
> +	mdelay(100);
> +}
> +*/
> +
> +/* Allocate temporary buffers for decoding */
> +int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	void *desc_virt;
> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers++\n");
> +	mfc_ctx->desc_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +					MFC_CMA_BANK1, DESC_BUF_SIZE, 2048);
> +	if (IS_ERR_VALUE(mfc_ctx->desc_phys)) {
> +		mfc_ctx->desc_phys = 0;
> +		mfc_err("Allocating DESC buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	desc_virt = ioremap_nocache(mfc_ctx->desc_phys, DESC_BUF_SIZE);
> +	if (desc_virt == NULL) {
> +		cma_free(mfc_ctx->desc_phys);
> +		mfc_ctx->desc_phys = 0;
> +		mfc_err("Remapping DESC buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset(desc_virt, 0, DESC_BUF_SIZE);
> +	iounmap(desc_virt);
> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers--\n");
> +	return 0;
> +}
> +
> +/* Release temproary buffers for decoding */
> +void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	if (mfc_ctx->desc_phys) {
> +		cma_free(mfc_ctx->desc_phys);
> +		mfc_ctx->desc_phys = 0;
> +	}
> +}
> +
> +/* Allocate decoding buffers */
> +int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	unsigned int luma_size, chroma_size, mv_size;
> +	mfc_debug("s5p_mfc_alloc_dec_buffers++\n");
> +	luma_size = mfc_ctx->luma_size;
> +	chroma_size = mfc_ctx->chroma_size;
> +	mv_size = mfc_ctx->mv_size;
> +	mfc_debug("Luma size: %d Chroma size: %d MV size: %d\n",
> +		  luma_size, chroma_size, mv_size);
> +	/* Codecs have different memory requirements */
> +	switch (mfc_ctx->codec_mode) {
> +	case S5P_FIMV_CODEC_H264_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
> +			     S5P_FIMV_DEC_VERT_NB_MV_SIZE, 8192);
> +		mfc_ctx->port_b_size =
> +		    mfc_ctx->total_dpb_count * ALIGN(mv_size, 8192) * 2;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG4_DEC:
> +	case S5P_FIMV_CODEC_DIVX412_DEC:
> +	case S5P_FIMV_CODEC_DIVX311_DEC:
> +	case S5P_FIMV_CODEC_DIVX502_DEC:
> +	case S5P_FIMV_CODEC_DIVX503_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_STX_PARSER_SIZE +
> +			     S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +
> +	case S5P_FIMV_CODEC_VC1RCV_DEC:
> +	case S5P_FIMV_CODEC_VC1_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_NB_DCAC_SIZE +
> +			     3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +
> +	case S5P_FIMV_CODEC_MPEG2_DEC:
> +		mfc_ctx->port_a_size = 0;
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +	case S5P_FIMV_CODEC_H263_DEC:
> +		mfc_ctx->port_a_size =
> +		    ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
> +			     S5P_FIMV_DEC_UPNB_MV_SIZE +
> +			     S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
> +			     S5P_FIMV_DEC_NB_DCAC_SIZE, 8192);
> +		mfc_ctx->port_b_size = 0;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* Allocate only if memory from bank 1 is necessary */
> +	if (mfc_ctx->port_a_size > 0) {
> +		mfc_ctx->port_a = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +				MFC_CMA_BANK1, mfc_ctx->port_a_size, 2048);
> +		if (IS_ERR_VALUE(mfc_ctx->port_a)) {
> +			mfc_ctx->port_a = 0;
> +			printk(KERN_ERR
> +			       "Buf alloc for decoding failed (port A).\n");
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	/* Allocate only if memory from bank 2 is necessary */
> +	if (mfc_ctx->port_b_size > 0) {
> +		mfc_ctx->port_b = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> +				MFC_CMA_BANK2, mfc_ctx->port_b_size, 2048);
> +		if (IS_ERR_VALUE(mfc_ctx->port_b)) {
> +			mfc_ctx->port_b = 0;
> +			mfc_err("Buf alloc for decoding failed (port
B).\n");
> +			return -ENOMEM;
> +		}
> +	}
> +	mfc_debug("s5p_mfc_alloc_dec_buffers--\n");
> +
> +	return 0;
> +}
> +
> +/* Release buffers allocated for decoding */
> +void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	if (mfc_ctx->port_a) {
> +		cma_free(mfc_ctx->port_a);
> +		mfc_ctx->port_a = 0;
> +		mfc_ctx->port_a_size = 0;
> +	}
> +	if (mfc_ctx->port_b) {
> +		cma_free(mfc_ctx->port_b);
> +		mfc_ctx->port_b = 0;
> +		mfc_ctx->port_b_size = 0;
> +	}
> +}
> +
> +/* Allocate memory for instance data buffer */
> +int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	void *instance_virt;
> +	mfc_debug("s5p_mfc_alloc_instance_buffer++\n");
> +	/* According to EVT1 docu, H264 requires 600KB
> +	 * and all other need onlu 10KB */
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
> +		mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
> +		mfc_ctx->instance_size = MFC_H264_INSTANCE_BUF_SIZE;
> +	else
> +		mfc_ctx->instance_size = MFC_INSTANCE_BUF_SIZE;
> +	mfc_ctx->instance_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
> +				MFC_CMA_BANK1, mfc_ctx->instance_size,
2048);
> +	if (IS_ERR_VALUE(mfc_ctx->instance_phys)) {
> +		mfc_ctx->instance_phys = 0;
> +		mfc_err("Allocating instance buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	instance_virt = ioremap_nocache(mfc_ctx->instance_phys,
> +		mfc_ctx->instance_size);
> +	if (instance_virt == NULL) {
> +		cma_free(mfc_ctx->instance_phys);
> +		mfc_ctx->instance_phys = 0;
> +		mfc_err("Remapping instance buffer failed.\n");
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset(instance_virt, 0, mfc_ctx->instance_size);
> +	iounmap(instance_virt);
> +	mfc_ctx->shared_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev, \
> +					MFC_CMA_BANK1, SHARED_BUF_SIZE,
2048);
> +	if (IS_ERR_VALUE(mfc_ctx->shared_phys)) {
> +		mfc_ctx->shared_phys = 0;
> +		mfc_err("Allocating shared buffer failed\n");
> +		cma_free(mfc_ctx->instance_phys);
> +		return -ENOMEM;
> +	}
> +	mfc_ctx->shared_virt = ioremap_nocache(mfc_ctx->shared_phys,
> +							
SHARED_BUF_SIZE);
> +	if (!mfc_ctx->shared_virt) {
> +		cma_free(mfc_ctx->instance_phys);
> +		cma_free(mfc_ctx->shared_phys);
> +		mfc_ctx->shared_phys = 0;
> +		mfc_ctx->instance_phys = 0;
> +		return -ENOMEM;
> +	}
> +	/* Zero content of the allocated memory, in future this might be
> done
> +	 * by cma_alloc */
> +	memset((void *)mfc_ctx->shared_virt, 0, SHARED_BUF_SIZE);
> +	mfc_debug("s5p_mfc_alloc_instance_buffer--\n");
> +	return 0;
> +}
> +
> +/* Release instance buffer */
> +void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	mfc_debug("s5p_mfc_release_instance_buffer++\n");
> +	if (mfc_ctx->instance_phys) {
> +		cma_free(mfc_ctx->instance_phys);
> +		mfc_ctx->instance_phys = 0;
> +	}
> +	if (mfc_ctx->shared_virt) {
> +		iounmap(mfc_ctx->shared_virt);
> +		mfc_ctx->shared_virt = 0;
> +	}
> +	if (mfc_ctx->shared_phys) {
> +		cma_free(mfc_ctx->shared_phys);
> +		mfc_ctx->shared_phys = 0;
> +	}
> +	mfc_debug("s5p_mfc_release_instance_buffer--\n");
> +}
> +
> +/* Set registers for decoding temporary buffers */
> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	WRITEL(OFFSETA(mfc_ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
> +	WRITEL(CPB_BUF_SIZE, S5P_FIMV_SI_CH0_CPB_SIZE);
> +	WRITEL(DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
> +}
> +
> +/* Set registers for decoding stream buffer */
> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
> buf_addr,
> +		  unsigned int start_num_byte, unsigned int buf_size)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("inst_no : %d, buf_addr : 0x%08x, buf_size : 0x%08x
> (%d)\n",
> +		  mfc_ctx->inst_no, buf_addr, buf_size, buf_size);
> +	if (buf_addr & (2048 - 1)) {
> +		mfc_err("Source stream buffer is not aligned correctly.\n");
> +		return -EINVAL;
> +	}
> +	WRITEL(OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
> +	WRITEL(buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
> +	mfc_debug("Shared_virt: %p (start offset: %d)\n", mfc_ctx-
> >shared_virt,
> +		  start_num_byte);
> +	writel(start_num_byte, mfc_ctx->shared_virt \
> +					 + S5P_FIMV_SHARED_START_BYTE_NUM);
> +	return 0;
> +}
> +
> +/* Set decoding frame buffer */
> +int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int do_int)
> +{
> +	unsigned int frame_size, i;
> +	unsigned int frame_size_ch, frame_size_mv;
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	unsigned int dpb;
> +	size_t buf_addr1, buf_addr2;
> +	int buf_size1, buf_size2;
> +	buf_addr1 = mfc_ctx->port_a;
> +	buf_size1 = mfc_ctx->port_a_size;
> +	buf_addr2 = mfc_ctx->port_b;
> +	buf_size2 = mfc_ctx->port_b_size;
> +	mfc_debug("Buf1: %p (%d) Buf2: %p (%d)\n", (void *)buf_addr1,
> buf_size1,
> +		  (void *)buf_addr2, buf_size2);
> +	/* Enable generation of extra info */
> +/*	*(shared_mem_vir_addr + 0x0038) = 63; */
> +	mfc_debug("Total DPB COUNT: %d\n", mfc_ctx->total_dpb_count);
> +	mfc_debug("Setting display delay to %d\n", mfc_ctx->display_delay);
> +	dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & 0xFFFF0000;
> +	WRITEL(mfc_ctx->total_dpb_count | dpb,
> S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
> +	s5p_mfc_set_dec_temp_buffers(mfc_ctx);
> +	switch (mfc_ctx->codec_mode) {
> +	case S5P_FIMV_CODEC_H264_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_IP_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG4_DEC:
> +	case S5P_FIMV_CODEC_DIVX311_DEC:
> +	case S5P_FIMV_CODEC_DIVX412_DEC:
> +	case S5P_FIMV_CODEC_DIVX502_DEC:
> +	case S5P_FIMV_CODEC_DIVX503_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SP_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_H263_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_VC1_DEC:
> +	case S5P_FIMV_CODEC_VC1RCV_DEC:
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_NB_DCAC_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_OT_LINE_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_UP_NB_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_SA_MV_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE3_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE2_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_BITPLANE1_ADR);
> +		buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
> +		break;
> +	case S5P_FIMV_CODEC_MPEG2_DEC:
> +		break;
> +	default:
> +		mfc_err("Unknown codec set for decoding (%x).\n",
> +			mfc_ctx->codec_mode);
> +		return -EINVAL;
> +		break;
> +	}
> +	frame_size = mfc_ctx->luma_size;
> +	frame_size_ch = mfc_ctx->chroma_size;
> +	frame_size_mv = mfc_ctx->mv_size;
> +	mfc_debug("Frame size: %d ch: %d mv: %d\n", frame_size,
> frame_size_ch,
> +		  frame_size_mv);
> +	for (i = 0; i < mfc_ctx->total_dpb_count; i++) {
> +		/* Port B */
> +		mfc_debug("Luma %d: %x\n", i, mfc_ctx->dec_dst_buf_luma[i]);
> +		WRITEL(OFFSETB(mfc_ctx->dec_dst_buf_luma[i]),
> +		       S5P_FIMV_LUMA_ADR + i * 4);
> +		mfc_debug("\tChroma %d: %x\n", i, \
> +						mfc_ctx-
>dec_dst_buf_chroma[i]);
> +		WRITEL(OFFSETA(mfc_ctx->dec_dst_buf_chroma[i]),
> +		       S5P_FIMV_CHROMA_ADR + i * 4);
> +		if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
> +			mfc_debug("\tBuf2: %x, buf_size2: %d\n", buf_addr2,
> +				  buf_size2);
> +			WRITEL(OFFSETB(buf_addr2), S5P_FIMV_MV_ADR + i * 4);
> +			buf_addr2 += ALIGN(frame_size_mv, 8192);
> +			buf_size2 -= ALIGN(frame_size_mv, 8192);
> +		}
> +	}
> +	mfc_debug("Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
> +	mfc_debug("Buf 1/2 size after: %d/%d (num frames %d)\n", buf_size1,
> +		  buf_size2, mfc_ctx->total_dpb_count);
> +	if (buf_size1 < 0 || buf_size2 < 0) {
> +		mfc_debug("Not enough memory has been allocated.\n");
> +		return -1337;
> +	}
> +	writel(frame_size, mfc_ctx->shared_virt \
> +					+ S5P_FIMV_SHARED_LUMA_DPB_SIZE);
> +	writel(frame_size_ch, mfc_ctx->shared_virt \
> +					+ S5P_FIMV_SHARED_CHROMA_DPB_SIZE);
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
> +		writel(frame_size_mv, mfc_ctx->shared_virt \
> +						+ S5P_FIMV_SHARED_MV_SIZE);
> +	}
> +	if (do_int) {
> +		mfc_debug("Doing int.\n");
> +		WRITEL(((S5P_FIMV_CH_INIT_BUFS << 16) & 0x70000) | \
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +
> +	}
> +	mfc_debug("After setting buffers.\n");
> +	return 0;
> +}
> +
> +/* Allocate firmware */
> +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
> +{
> +	int err;
> +	struct cma_info mem_info_f, mem_info_a, mem_info_b;
> +	mfc_debug("s5p_mfc_alloc_firmware++\n");
> +	if (s5p_mfc_phys_bitproc_buff) {
> +		mfc_err("Attempting to allocate firmware when it seems that"
> \
> +			"it is already loaded.\n");
> +		return -ENOMEM;
> +	}
> +	/* Get memory region information and check if it is correct */
> +	err = cma_info(&mem_info_f, dev->v4l2_dev.dev, MFC_CMA_FW);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"f", mem_info_f.lower_bound, mem_info_f.upper_bound,
> +		mem_info_f.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	err = cma_info(&mem_info_a, dev->v4l2_dev.dev, MFC_CMA_BANK1);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"a", mem_info_a.lower_bound, mem_info_a.upper_bound,
> +		mem_info_a.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	err = cma_info(&mem_info_b, dev->v4l2_dev.dev, MFC_CMA_BANK2);
> +	mfc_debug("Area \"%s\" is from %08x to %08x and has size %08x",
> +		"b", mem_info_b.lower_bound, mem_info_b.upper_bound,
> +		mem_info_b.total_size);
> +	if (err) {
> +		mfc_err("Couldn't get memory information from CMA.\n");
> +		return -EINVAL;
> +	}
> +	if (mem_info_f.upper_bound > mem_info_a.lower_bound) {
> +			mfc_err("Firmware has to be allocated before" \
> +			" memory for buffers (bank A).\n");
> +		return -EINVAL;
> +	}
> +	mfc_debug("Allocating memory for firmware.\n");
> +	s5p_mfc_phys_bitproc_buff = cma_alloc(dev->v4l2_dev.dev, MFC_CMA_FW,
> +						FIRMWARE_CODE_SIZE, 128 *
1024);
> +	mfc_debug("Phys addr from CMA: %08x\n", s5p_mfc_phys_bitproc_buff);
> +	if (IS_ERR_VALUE(s5p_mfc_phys_bitproc_buff)) {
> +		s5p_mfc_phys_bitproc_buff = 0;
> +		printk(KERN_ERR "Allocating bitprocessor buffer failed\n");
> +		return -ENOMEM;
> +	}
> +	if (s5p_mfc_phys_bitproc_buff & 0x0001FFFF) {
> +		mfc_err("The base memory is not aligned to 128KB.\n");
> +		cma_free(s5p_mfc_phys_bitproc_buff);
> +		return -EIO;
> +	}
> +	dev->port_a = s5p_mfc_phys_bitproc_buff;
> +	dev->port_b = mem_info_b.lower_bound;
> +	mfc_debug("Port A: %08x Port B: %08x (FW: %08x size: %08x)\n",
> +			dev->port_a, dev->port_b, s5p_mfc_phys_bitproc_buff,
> +							FIRMWARE_CODE_SIZE);
> +	s5p_mfc_virt_bitproc_buff =
> ioremap_nocache(s5p_mfc_phys_bitproc_buff,
> +							FIRMWARE_CODE_SIZE);
> +	mfc_debug("Virtual address for FW: %08lx\n",
> +				(long unsigned
int)s5p_mfc_virt_bitproc_buff);
> +	if (!s5p_mfc_virt_bitproc_buff) {
> +		mfc_err("Bitprocessor memory ioremap failed\n");
> +		cma_free(s5p_mfc_phys_bitproc_buff);
> +		s5p_mfc_phys_bitproc_buff = 0;
> +		return -EIO;;
> +	}
> +	return 0;
> +}
> +
> +/* Load firmware to MFC */
> +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
> +{
> +	struct firmware *fw_blob;
> +	int err;
> +	/* Firmare has to be present as a separate file or compiled
> +	 * into kernel. */
> +	mfc_debug("s5p_mfc_load_firmware++\n");
> +	mfc_debug("Requesting fw\n");
> +	err = request_firmware((const struct firmware **)&fw_blob,
> +				     "s5pc110-mfc.fw", dev->v4l2_dev.dev);
> +	mfc_debug("Ret of request_firmware: %d Size: %d\n", err, fw_blob-
> >size);
> +	if (err != 0) {
> +		mfc_err("Firmware is not present in the /lib/firmware
> directory"
> +						" nor compiled in
kernel.\n");
> +		return -EINVAL;
> +	}
> +	if (fw_blob->size > FIRMWARE_CODE_SIZE) {
> +		mfc_err("MFC firmware is too big to be loaded.\n");
> +		release_firmware(fw_blob);
> +		return -ENOMEM;
> +	}
> +	if (s5p_mfc_phys_bitproc_buff == 0 || s5p_mfc_phys_bitproc_buff ==
> 0) {
> +		mfc_err("MFC firmware is not allocated or was not mapped "\
>
+								"correctly.\
n");
> +		release_firmware(fw_blob);
> +		return -EINVAL;
> +	}
> +	memcpy(s5p_mfc_virt_bitproc_buff, fw_blob->data, fw_blob->size);
> +/*	If byteswap is needed then use the following instead of memcpy */
> +/*	for (i = 0; i < fw_blob->size; i += 4) {
> +		s5p_mfc_virt_bitproc_buff[i + 0] = fw_blob->data[i + 3];
> +		s5p_mfc_virt_bitproc_buff[i + 1] = fw_blob->data[i + 2];
> +		s5p_mfc_virt_bitproc_buff[i + 2] = fw_blob->data[i + 1];
> +		s5p_mfc_virt_bitproc_buff[i + 3] = fw_blob->data[i + 0];
> +	}*/
> +	release_firmware(fw_blob);
> +	mfc_debug("s5p_mfc_load_firmware--\n");
> +	return 0;
> +}
> +
> +/* Release firmware memory */
> +int s5p_mfc_release_firmware()
> +{
> +	/* Before calling this function one has to make sure
> +	 * that MFC is no longer processing */
> +	if (!s5p_mfc_phys_bitproc_buff)
> +		return -EINVAL;
> +	mfc_debug("s5p_mfc_virt_bitproc_buff: %p\n",
> s5p_mfc_virt_bitproc_buff);
> +	iounmap(s5p_mfc_virt_bitproc_buff);
> +	s5p_mfc_virt_bitproc_buff = NULL;
> +	cma_free(s5p_mfc_phys_bitproc_buff);
> +	s5p_mfc_phys_bitproc_buff = 0;
> +	return 0;
> +}
> +
> +/* Initialize hardware */
> +int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
> +{
> +	int fw_buf_size;
> +	unsigned int fw_version;
> +	int ret;
> +	mfc_debug("s5p_mfc_init_hw++\n");
> +	mfc_debug("Device pointer: %p\n", dev);
> +	if (!s5p_mfc_phys_bitproc_buff)
> +		return -EINVAL;
> +	/* 0. MFC reset */
> +	mfc_debug("MFC reset...\n");
> +	ret = s5p_mfc_cmd_reset(dev);
> +	if (ret) {
> +		mfc_err("Failed to reset MFC - timeout.\n");
> +		return ret;
> +	}
> +	mfc_debug("Done MFC reset...\n");
> +	/* 1. Set DRAM base Addr */
> +	WRITEL(dev->port_a, S5P_FIMV_MC_DRAMBASE_ADR_A); /* channelA, port0
> */
> +	WRITEL(dev->port_b, S5P_FIMV_MC_DRAMBASE_ADR_B); /* channelB, port1
> */
> +	mfc_debug("Port A: %08x, Port B: %08x\n", dev->port_a, dev->port_b);
> +	/* 2. Initialize registers of stream I/F for decoder */
> +	WRITEL(0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
> +	WRITEL(0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
> +	WRITEL(0, S5P_FIMV_RISC2HOST_CMD);
> +	WRITEL(0, S5P_FIMV_HOST2RISC_CMD);
> +	/* 3. Release reset signal to the RISC.  */
> +	WRITEL(0x3ff, S5P_FIMV_SW_RESET);
> +	mfc_debug("Will now wait for completion of firmware transfer.\n");
> +	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET))
> {
> +		mfc_err("Failed to load firmware.\n");
> +		s5p_mfc_clean_dev_int_flags(dev);
> +		return -EIO;
> +	}
> +	s5p_mfc_clean_dev_int_flags(dev);
> +	/* 4. Initialize firmware */
> +	fw_buf_size = FIRMWARE_CODE_SIZE;
> +	mfc_debug("Writing a command\n");
> +	ret = s5p_mfc_cmd_host2risc(dev, 0, S5P_FIMV_H2R_CMD_SYS_INIT,
>
+								fw_buf_size)
;
> +	if (ret) {
> +		mfc_err("Failed to send command to MFC - timeout.\n");
> +		return ret;
> +	}
> +	mfc_debug("Ok, now will write a command to init the system\n");
> +	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET))
> {
> +		mfc_err("Failed to load firmware\n");
> +		return -EIO;
> +	}
> +	dev->int_cond = 0;
> +	if (dev->int_err != 0 || dev->int_type !=
>
+						S5P_FIMV_R2H_CMD_SYS_INIT_RE
T) {
> +		/* Failure. */
> +		mfc_err("Failed to init firmware - error: %d int: %d.\n",
> +			dev->int_err, dev->int_type);
> +		return -EIO;
> +	}
> +	fw_version = READL(S5P_FIMV_FW_VERSION);
> +	mfc_info("MFC FW version : %02xyy, %02xmm, %02xdd\n",
> +			 (fw_version >> 16) & 0xff, (fw_version >> 8) &
0xff,
> +							 (fw_version) &
0xff);
> +	mfc_debug("FW_PHY_BUFFER : 0x%08x\n",
> +		  READL(S5P_FIMV_MC_DRAMBASE_ADR_A));
> +	mfc_debug("DPB_LUMA_BUFFER : 0x%08x\n",
> +		  READL(S5P_FIMV_MC_DRAMBASE_ADR_B));
> +	mfc_debug("s5p_mfc_init_hw--\n");
> +	return 0;
> +}
> +
> +/* Open a new instance and get its number */
> +int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	int ret;
> +	mfc_debug("s5p_mfc_get_inst_no++\n");
> +	mfc_debug("Requested codec mode: %d\n", mfc_ctx->codec_mode);
> +	ret = s5p_mfc_cmd_host2risc(mfc_ctx->dev, mfc_ctx, \
> +			S5P_FIMV_H2R_CMD_OPEN_INSTANCE, mfc_ctx-
>codec_mode);
> +	mfc_debug("s5p_mfc_get_inst_no--\n");
> +	return ret;
> +}
> +
> +/* Close instance */
> +int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	int ret = 0;
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("s5p_mfc_return_inst_no++\n");
> +	if (mfc_ctx->state != MFCINST_FREE) {
> +		ret = s5p_mfc_cmd_host2risc(dev, mfc_ctx,
> +			S5P_FIMV_H2R_CMD_CLOSE_INSTANCE, mfc_ctx->inst_no);
> +	} else {
> +		ret = -EINVAL;
> +	}
> +	mfc_debug("s5p_mfc_return_inst_no--\n");
> +	return ret;
> +}
> +
> +/* Initialize decoding */
> +int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("++\n");
> +
> +	mfc_debug("InstNo: %d/%d\n", mfc_ctx->inst_no,
> S5P_FIMV_CH_SEQ_HEADER);
> +
> +	mfc_debug("BUFs: %08x %08x %08x %08x %08x\n",
> +		  READL(S5P_FIMV_SI_CH0_DESC_ADR),
> +		  READL(S5P_FIMV_SI_CH0_CPB_SIZE),
> +		  READL(S5P_FIMV_SI_CH0_DESC_SIZE),
> +		  READL(S5P_FIMV_SI_CH0_SB_ST_ADR),
> +		  READL(S5P_FIMV_SI_CH0_SB_FRM_SIZE));
> +	/* Setup loop filter, for decoding this is only valid for MPEG4 */
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC) {
> +		mfc_debug("Setting loop filter to: %d\n", \
> +						mfc_ctx->loop_filter_mpeg4);
> +		WRITEL(mfc_ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
> +	} else {
> +		WRITEL(0, S5P_FIMV_ENC_LF_CTRL);
> +	}
> +	WRITEL(((mfc_ctx->slice_interface & 1)<<31) |
> +				((mfc_ctx->display_delay > 0 ? 1 : 0) <<
30) |
> +					((mfc_ctx->display_delay & 0xFF) <<
16),
>
+						S5P_FIMV_SI_CH0_DPB_CONF_CTR
L);
> +	if (mfc_ctx->codec_mode == S5P_FIMV_CODEC_DIVX311_DEC) {
> +		mfc_debug("Setting DivX 3.11 resolution to %dx%d\n",
> +			mfc_ctx->img_width, mfc_ctx->img_height);
> +		WRITEL(mfc_ctx->img_width, S5P_FIMV_SI_DIVX311_HRESOL);
> +		WRITEL(mfc_ctx->img_height, S5P_FIMV_SI_DIVX311_VRESOL);
> +	}
> +	WRITEL(((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) | (mfc_ctx-
> >inst_no),
> +						
S5P_FIMV_SI_CH0_INST_ID);
> +	return 0;
> +}
> +
> +/* Decode a single frame */
> +int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int last_frame)
> +{
> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> +	mfc_debug("Setting flags to %08lx (free: %d WTF: %d)\n",
> +				mfc_ctx->dec_dst_flag, mfc_ctx-
>dst_queue_cnt,
> +						mfc_ctx->dec_dst_buf_cnt);
> +	WRITEL(mfc_ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
> +	/* Issue different commands to instance basing on whether it
> +	 * is the last frame or not. */
> +	if (!last_frame)
> +		WRITEL((S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +	else
> +		WRITEL((S5P_FIMV_CH_LAST_FRAME << 16 & 0x70000) |
> +				(mfc_ctx->inst_no),
S5P_FIMV_SI_CH0_INST_ID);
> +	mfc_debug("Decoding a usual frame.\n");
> +	return 0;
> +}
> +
> +/* Deinitialize hardware */
> +void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
> +{
> +	s5p_mfc_cmd_reset(dev);
> +}
> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> new file mode 100644
> index 0000000..162509f
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
> @@ -0,0 +1,90 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
> + *
> + * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * Contains declarations of hw related functions.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#ifndef S5P_MFC_OPR_H_
> +#define S5P_MFC_OPR_H_
> +
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_release_firmware(void);
> +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev);
> +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev);
> +int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
> +
> +int s5p_mfc_init_decode(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev);
> +int s5p_mfc_set_sleep(struct s5p_mfc_ctx *mfc_ctx);
> +int s5p_mfc_set_wakeup(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *mfc_ctx, int
do_int);
> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
> buf_addr,
> +						  unsigned int
start_num_byte,
> +						  unsigned int buf_size);
> +
> +int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *mfc_ctx, int
last_frame);
> +
> +/* Instance handling */
> +int s5p_mfc_open_inst(struct s5p_mfc_ctx *mfc_ctx);
> +int s5p_mfc_return_inst_no(struct s5p_mfc_ctx *mfc_ctx);
> +
> +/* Memory allocation */
> +int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_alloc_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_dec_buffers(struct s5p_mfc_ctx *mfc_ctx);
> +
> +int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
> +void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *mfc_ctx);
> +
> +/* Getting parameters from MFC */
> +#define s5p_mfc_get_h_crop(ctx)		readl((ctx)->shared_virt + \
> +						S5P_FIMV_SHARED_CROP_INFO_H)
> +#define s5p_mfc_get_v_crop(ctx)		readl((ctx)->shared_virt + \
> +						S5P_FIMV_SHARED_CROP_INFO_V)
> +#define s5p_mfc_get_dspl_y_adr()	(readl(dev->regs_base + \
> +					S5P_FIMV_SI_DISPLAY_Y_ADR) << 11)
> +#define s5p_mfc_get_dspl_status()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_DISPLAY_STATUS)
> +#define s5p_mfc_get_frame_type()	(readl(dev->regs_base + \
> +						S5P_FIMV_DECODE_FRAME_TYPE)
\
> +					& S5P_FIMV_DECODE_FRAME_MASK)
> +#define s5p_mfc_get_consumed_stream()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_DEC_FRM_SIZE)
> +#define s5p_mfc_get_int_reason()	(readl(dev->regs_base + \
> +					S5P_FIMV_RISC2HOST_CMD) & 0x1FFFF)
> +#define s5p_mfc_get_int_err()		readl(dev->regs_base + \
> +						S5P_FIMV_RISC2HOST_ARG2)
> +#define s5p_mfc_get_img_width()		readl(dev->regs_base + \
> +						S5P_FIMV_SI_HRESOL)
> +#define s5p_mfc_get_img_height()	readl(dev->regs_base + \
> +						S5P_FIMV_SI_VRESOL)
> +#define s5p_mfc_get_dpb_count()		readl(dev->regs_base + \
> +						S5P_FIMV_SI_BUF_NUMBER)
> +#define s5p_mfc_get_inst_no()		readl(dev->regs_base + \
> +						S5P_FIMV_RISC2HOST_ARG1)
> +#define s5p_mfc_get_pic_time_top(ctx)	readl((ctx)->shared_virt + \
>
+						S5P_FIMV_SHARED_PIC_TIME_TOP
)
> +#define s5p_mfc_get_pic_time_bottom(ctx) readl((ctx)->shared_virt + \
>
+						S5P_FIMV_SHARED_PIC_TIME_BOT
TOM)
> +
> +/* Interrupt handling routines */
> +#define s5p_mfc_clear_int_flags()				\
> +do {								\
> +	writel(0, dev->regs_base + S5P_FIMV_RISC_HOST_INT);	\
> +	writel(0, dev->regs_base + S5P_FIMV_RISC2HOST_CMD);	\
> +	writel(0xffff, dev->regs_base + S5P_FIMV_SI_RTN_CHID);	\
> +} while (0)
> +#endif /* S5P_MFC_OPR_H_ */
> --
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-15  4:06   ` Jaeryul Oh
@ 2010-10-20 14:25     ` Kamil Debski
  2010-10-21  0:03       ` Jaeryul Oh
  0 siblings, 1 reply; 18+ messages in thread
From: Kamil Debski @ 2010-10-20 14:25 UTC (permalink / raw)
  To: jaeryul.oh, linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

>Hi, Kamil
>This is second feedback about the HW op related code. 
>(s5p_mfc_opr.c & s5p_mfc.c)

Hi, Peter

Thanks for the comments. I have replied to them below. I would be grateful
if you could cut out non relevant parts of the code in your replies. It was
difficult to find your comments in such a big email. I hope I have found all
of them.

[...]

>> +			s5p_mfc_set_dec_stream_buffer(ctx, \
>> +			      vb2_plane_paddr(temp_vb, 0), 0, \
>> +			      temp_vb->v4l2_planes[0].bytesused);
>> +			dev->curr_ctx = ctx->num;
>> +			s5p_mfc_clean_ctx_int_flags(ctx);
>> +			s5p_mfc_decode_one_frame(ctx,
>> +				temp_vb->v4l2_planes[0].bytesused == 0);
>> +		} else if (ctx->state == MFCINST_DEC_INIT) {
>> +			/* Preparing decoding - getting instance number */
>> +			mfc_debug("Getting instance number\n");
>> +			dev->curr_ctx = ctx->num;
>> +			s5p_mfc_clean_ctx_int_flags(ctx);
>> +/*			s5p_mfc_set_dec_temp_buffers(ctx);
>> + *			Removed per request by Peter, check if MFC works OK
>*/

>Yes, you don't have to set s5p_mfc_set_dec_temp_buffers(ctx)at this point
>'cause this is only required before parsing header of the stream except for

>setting shared memory, 
>so, I think, separating 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from the 
>s5p_mfc_set_dec_temp_buffers() is needed. So I mean
>remove  'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from 
>s5p_mfc_set_dec_temp_buffers(ctx);, then add 'setting
>S5P_FIMV_SI_CH0_HOST_WR_ADR'
>in the 3 functions (s5p_mfc_set_dec_frame_buffer(),s5p_mfc_init_decode(),
>s5p_mfc_decode_one_frame()) 
>I also commented suggested loc. of 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR'

Right, I've changed this.


>> +			ret = s5p_mfc_open_inst(ctx);
>> +			if (ret) {
>> +				mfc_err("Failed to create a new
instance.\n");
>> +				ctx->state = MFCINST_DEC_ERROR;
>> +			}
>> +		} else if (ctx->state == MFCINST_DEC_RETURN_INST) {
>> +			/* Closing decoding instance  */
>> +			mfc_debug("Returning instance number\n");
>> +			dev->curr_ctx = ctx->num;
>> +			s5p_mfc_clean_ctx_int_flags(ctx);
>> +			ret = s5p_mfc_return_inst_no(ctx);
>> +			if (ret) {
>> +				mfc_err("Failed to return an instance.\n");
>> +				ctx->state = MFCINST_DEC_ERROR;
>> +			}

[...]

>> +	}
>> +	/* Init videobuf2 queue for CAPTURE */
>> +	ret = vb2_queue_init(&ctx->vq_dst, &s5p_mfc_qops,
>> +			       dev->alloc_ctx[0], &dev->irqlock,
>> +			       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, ctx);
>> +	if (ret) {
>> +		mfc_err("Failed to initialize videobuf2 queue (capture)\n");
>> +		goto out_open_3;
>> +	}
>> +	/* Init videobuf2 queue for OUTPUT */
>> +	ret = vb2_queue_init(&ctx->vq_src, &s5p_mfc_qops,
>> +			       dev->alloc_ctx[1], &dev->irqlock,
>> +			       V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, ctx);
>> +	if (ret) {
>> +		mfc_err("Failed to initialize videobuf2 queue (output)\n");
>> +		goto out_open_3;
>> +	}

>About using dev->irqlock, (spinlock_t *drv_lock) in the vb2_queue_init() is
>removed
>according to the Marek's patch (from the Hans Verkuil request)
> : http://www.spinics.net/lists/linux-media/msg23902.html
>Does it mean that we don't have to use drv_lock in the driver ?
>What actually purpose of drv_lock that you used in the MFC driver ?

I'll remove this when videobuf2 will be merged. The version posted by Marek
is still not final, so it is better to wait until a final version is worked
out in the community.


>> +	vb2_set_alloc_ctx(&ctx->vq_dst, dev->alloc_ctx[1], 1);
>> +	init_waitqueue_head(&ctx->queue);
>> +	mutex_unlock(dev->mfc_mutex);
>> +	mfc_debug("s5p_mfc_open--\n");
>> +	return ret;
>> +	/* Deinit when failure occured */
>> +out_open_3:
>> +	if (atomic_read(&dev->num_inst) == 1) {
>> +		clk_disable(dev->clock1);


[...]

>> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers++\n");
>> +	mfc_ctx->desc_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
>> +					MFC_CMA_BANK1, DESC_BUF_SIZE, 2048);
>> +	if (IS_ERR_VALUE(mfc_ctx->desc_phys)) {
>> +		mfc_ctx->desc_phys = 0;
>> +		mfc_err("Allocating DESC buffer failed.\n");
>> +		return -ENOMEM;
>> +	}
>> +	desc_virt = ioremap_nocache(mfc_ctx->desc_phys, DESC_BUF_SIZE);

>In case of ioremap_xx() function, you might meet that msg as below
>"Don't allow RAM to be mapped - this causes problems with ARMv6+ "
>(arch/arm/mm/ioremap.c). so we should not use this function for this case.
>I suggest that you use phys_to_virt() with some cache op. such as 
>cache_clean for writing case(ex> memset) & cache_invalidate for reading
>case.
>(ex>reading shared mem)
>It is necessary for accessing shared memory(DRAM area accessed by ARM &
MFC)

At this moment we are waiting for the final CMA version and we don't know 
whether phys_to_virt will work with it. Now it has been changed from bug to 
a warning by Russel.

>> +	if (desc_virt == NULL) {
>> +		cma_free(mfc_ctx->desc_phys);
>> +		mfc_ctx->desc_phys = 0;
>> +		mfc_err("Remapping DESC buffer failed.\n");
>> +		return -ENOMEM;
>> +	}
>> +	/* Zero content of the allocated memory, in future this might be
>> done
>> +	 * by cma_alloc */
>> +	memset(desc_virt, 0, DESC_BUF_SIZE);
>> +	iounmap(desc_virt);
>> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers--\n");

[...]

>> +	mfc_debug("s5p_mfc_release_instance_buffer--\n");
>> +}
>> +
>> +/* Set registers for decoding temporary buffers */
>> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
>> +{
>> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
>> +	WRITEL(OFFSETA(mfc_ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
>> +	WRITEL(CPB_BUF_SIZE, S5P_FIMV_SI_CH0_CPB_SIZE);
>> +	WRITEL(DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
>> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
>> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
>> +}

>I mentioned that separating 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from
>s5p_mfc_set_dec_temp_buffers() is better in terms of modular design.
>And what about having clear function name ? using 'temp' in the func. Name
>is not specific

What name do you suggest? If I name the function
s5p_mfc_set_dec_desc_buffer()
it will contain no information that other registers are set too.

>> +
>> +/* Set registers for decoding stream buffer */
>> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
>> buf_addr,
>> +		  unsigned int start_num_byte, unsigned int buf_size)
>> +{
>> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
>> +	mfc_debug("inst_no : %d, buf_addr : 0x%08x, buf_size : 0x%08x
>> (%d)\n",
>> +		  mfc_ctx->inst_no, buf_addr, buf_size, buf_size);
>> +	if (buf_addr & (2048 - 1)) {
>> +		mfc_err("Source stream buffer is not aligned correctly.\n");
>> +		return -EINVAL;
>> +	}

[...]

>> +	buf_size2 = mfc_ctx->port_b_size;
>> +	mfc_debug("Buf1: %p (%d) Buf2: %p (%d)\n", (void *)buf_addr1,
>> buf_size1,
>> +		  (void *)buf_addr2, buf_size2);
>> +	/* Enable generation of extra info */
>> +/*	*(shared_mem_vir_addr + 0x0038) = 63; */
>> +	mfc_debug("Total DPB COUNT: %d\n", mfc_ctx->total_dpb_count);
>> +	mfc_debug("Setting display delay to %d\n", mfc_ctx->display_delay);
>> +	dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & 0xFFFF0000;
>> +	WRITEL(mfc_ctx->total_dpb_count | dpb,
>> S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
>> +	s5p_mfc_set_dec_temp_buffers(mfc_ctx);

>I think, all reg. set in the s5p_mfc_set_dec_temp_buffers(mfc_ctx)is not
>required
>at this point. 
>What about only adding 'set S5P_FIMV_SI_CH0_HOST_WR_ADR) instead of using 
>s5p_mfc_set_dec_temp_buffers()? 

Done.

>> +	switch (mfc_ctx->codec_mode) {
>> +	case S5P_FIMV_CODEC_H264_DEC:
>> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_MV_ADR);
>> +		buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
>> +		buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
>> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_IP_ADR);
>> +		buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
>> +		buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
>> +		break;
>> +	case S5P_FIMV_CODEC_MPEG4_DEC:
>> +	case S5P_FIMV_CODEC_DIVX311_DEC:
>> +	case S5P_FIMV_CODEC_DIVX412_DEC:
>> +	case S5P_FIMV_CODEC_DIVX502_DEC:

Best regards,
--
Kamil Debski
Linux Platform Group
Samsung Poland R&D Center


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-15  4:27       ` Jaeryul Oh
@ 2010-10-20 14:29         ` Kamil Debski
  0 siblings, 0 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-20 14:29 UTC (permalink / raw)
  To: jaeryul.oh, linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

>Thank you for your reply about my comments. 
>Refer to as below.

Hi,


>> 
>> I don't know if this is necessary. MFC_NUM_CONTEXTS can be fixed at 
>> the maximum number allowed by MFC hw: 16. I highly doubt someone will 
>> open that many contexts. Increasing this number will not significantly 
>> increase storage space used by MFC if no contexts are used. It will 
>> only change size of one pointer array ( struct s5p_mfc_ctx 
>> *ctx[MFC_NUM_CONTEXTS]; ).
>> 
>In many project, user can open many contexts according to the scenario
>of not only phone but also netbook, tablet. 'cause MFC supports multiple
>instance. That's why I suggested this param. be configurable.

I see no problem with setting it to the maximum number allowed by MFC - 16.
There would be no use of setting a smaller value. Except a _minimal_ saving
of memory.

>> >
>> > > +
>> > > +/* Check whether a context should be run on hardware */ int 
>> > > +s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx) {
>> > > +	mfc_debug("s5p_mfc_ctx_ready: src=%d, dst=%d, state=%d\n",
>> > > +		  ctx->src_queue_cnt, ctx->dst_queue_cnt,
ctx->state);
>> > > +	/* Context is to parse header */
>> > > +	if (ctx->src_queue_cnt >= 1 && ctx->state ==
>> > MFCINST_DEC_GOT_INST)
>> > > +		return 1;
>> > > +	/* Context is to decode a frame */
>> > > +	if (ctx->src_queue_cnt >= 1 && ctx->state ==
MFCINST_DEC_RUNNING
>> > &&
>> > > +					ctx->dst_queue_cnt >= ctx-


--
Kamil Debski
Linux Platform Group
Samsung Poland R&D Center


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-18  8:30   ` Jaeryul Oh
@ 2010-10-20 14:30     ` Kamil Debski
  0 siblings, 0 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-20 14:30 UTC (permalink / raw)
  To: jaeryul.oh, linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

>Hi, Kamil
>This is third feedback about watchdog timer. 
>(s5p_mfc.c)

Hi, Peter

Thanks for pointing that out, enabling and disabling watchdog in
open/release is reasonable.

>[...]

>> +	platform_set_drvdata(pdev, dev);
>> +	dev->hw_lock = 0;
>> +	dev->watchdog_workqueue = create_singlethread_workqueue("s5p-mfc");
>> +	INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
>> +	atomic_set(&dev->watchdog_cnt, 0);
>> +	init_timer(&dev->watchdog_timer);
>> +	dev->watchdog_timer.data = 0;
>> +	dev->watchdog_timer.function = s5p_mfc_watchdog;
>> +	dev->watchdog_timer.expires = jiffies +
>> + msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
>> +	add_timer(&dev->watchdog_timer);

>Watch_dog single thread runs right after probing MFC, but this doesn't look
>like
>nice way in terms of purpose of this timer which is for error handling in
>the 
>middle of decoding. What about moving point running this timer to the
>open().
>And it should be stopped in release time. Of course, dev->num_inst should
>be 
>considered.

Yes, I agree. I've changed that.

> 
>> +
>> +	dev->alloc_ctx = vb2_cma_init_multi(&pdev->dev, 2, s5p_mem_types,
>> +							s5p_mem_alignments);
>> +	if (IS_ERR(dev->alloc_ctx)) {
>> +		mfc_err("Couldn't prepare allocator context.\n");
>> +		ret = PTR_ERR(dev->alloc_ctx);
>> +		goto alloc_ctx_fail;
>> +	}
>> +
>> +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
>> +	if (ret) {
>> +		v4l2_err(&dev->v4l2_dev, "Failed to register video
>> device\n");
>> +		video_device_release(vfd);
>> +		goto rel_vdev;
>> +	}
>>

--
Kamil Debski
Linux Platform Group
Samsung Poland R&D Center


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-20 14:25     ` Kamil Debski
@ 2010-10-21  0:03       ` Jaeryul Oh
  2010-10-21 12:52         ` Kamil Debski
  0 siblings, 1 reply; 18+ messages in thread
From: Jaeryul Oh @ 2010-10-21  0:03 UTC (permalink / raw)
  To: 'Kamil Debski', linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

I commented as belows, 
And you missed one important things 'cause there were my comments
in the very long email which is strongly fixed in the reset seq.


[....]
> +#define READL(offset)		readl(dev->regs_base + (offset))
> +#define WRITEL(data, offset)	writel((data), dev->regs_base + (offset))
> +#define OFFSETA(x)		(((x) - dev->port_a) >> 11)
> +#define OFFSETB(x)		(((x) - dev->port_b) >> 11)
> +
> +/* Reset the device */
> +static int s5p_mfc_cmd_reset(struct s5p_mfc_dev *dev)
> +{
> +	unsigned int mc_status;
> +	unsigned long timeout;
> +	mfc_debug("s5p_mfc_cmd_reset++\n");
> +	/* Stop procedure */
> +	WRITEL(0x3f7, S5P_FIMV_SW_RESET);	/*  reset VI */

Ahm, This (WRITEL(0x3f7, S5P_FIMV_SW_RESET)) might be a problem. 
In the reset seq. of MFC driver, we checked out 
That FW(s5pc110-mfc.fw in the s5p_mfc_load_firmware()) runned by RISC core
at this point could access invalid address. It should be removed.


> >Hi, Kamil
> >This is second feedback about the HW op related code.
> >(s5p_mfc_opr.c & s5p_mfc.c)
> 
> Hi, Peter
> 
> Thanks for the comments. I have replied to them below. I would be grateful
> if you could cut out non relevant parts of the code in your replies. It
> was
> difficult to find your comments in such a big email. I hope I have found
> all
> of them.
> 
> [...]
> 
> >> +			s5p_mfc_set_dec_stream_buffer(ctx, \
> >> +			      vb2_plane_paddr(temp_vb, 0), 0, \
> >> +			      temp_vb->v4l2_planes[0].bytesused);
> >> +			dev->curr_ctx = ctx->num;
> >> +			s5p_mfc_clean_ctx_int_flags(ctx);
> >> +			s5p_mfc_decode_one_frame(ctx,
> >> +				temp_vb->v4l2_planes[0].bytesused == 0);
> >> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> >> +			/* Preparing decoding - getting instance number */
> >> +			mfc_debug("Getting instance number\n");
> >> +			dev->curr_ctx = ctx->num;
> >> +			s5p_mfc_clean_ctx_int_flags(ctx);
> >> +/*			s5p_mfc_set_dec_temp_buffers(ctx);
> >> + *			Removed per request by Peter, check if MFC
works OK
> >*/
> 
> >Yes, you don't have to set s5p_mfc_set_dec_temp_buffers(ctx)at this point
> >'cause this is only required before parsing header of the stream except
> for
> 
> >setting shared memory,
> >so, I think, separating 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from the
> >s5p_mfc_set_dec_temp_buffers() is needed. So I mean
> >remove  'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from
> >s5p_mfc_set_dec_temp_buffers(ctx);, then add 'setting
> >S5P_FIMV_SI_CH0_HOST_WR_ADR'
> >in the 3 functions (s5p_mfc_set_dec_frame_buffer(),s5p_mfc_init_decode(),
> >s5p_mfc_decode_one_frame())
> >I also commented suggested loc. of 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR'
> 
> Right, I've changed this.
> 
> 
> >> +			ret = s5p_mfc_open_inst(ctx);
> >> +			if (ret) {
> >> +				mfc_err("Failed to create a new
> instance.\n");
> >> +				ctx->state = MFCINST_DEC_ERROR;
> >> +			}
> >> +		} else if (ctx->state == MFCINST_DEC_RETURN_INST) {
> >> +			/* Closing decoding instance  */
> >> +			mfc_debug("Returning instance number\n");
> >> +			dev->curr_ctx = ctx->num;
> >> +			s5p_mfc_clean_ctx_int_flags(ctx);
> >> +			ret = s5p_mfc_return_inst_no(ctx);
> >> +			if (ret) {
> >> +				mfc_err("Failed to return an instance.\n");
> >> +				ctx->state = MFCINST_DEC_ERROR;
> >> +			}
> 
> [...]
> 
> >> +	}
> >> +	/* Init videobuf2 queue for CAPTURE */
> >> +	ret = vb2_queue_init(&ctx->vq_dst, &s5p_mfc_qops,
> >> +			       dev->alloc_ctx[0], &dev->irqlock,
> >> +			       V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, ctx);
> >> +	if (ret) {
> >> +		mfc_err("Failed to initialize videobuf2 queue (capture)\n");
> >> +		goto out_open_3;
> >> +	}
> >> +	/* Init videobuf2 queue for OUTPUT */
> >> +	ret = vb2_queue_init(&ctx->vq_src, &s5p_mfc_qops,
> >> +			       dev->alloc_ctx[1], &dev->irqlock,
> >> +			       V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, ctx);
> >> +	if (ret) {
> >> +		mfc_err("Failed to initialize videobuf2 queue (output)\n");
> >> +		goto out_open_3;
> >> +	}
> 
> >About using dev->irqlock, (spinlock_t *drv_lock) in the vb2_queue_init()
> is
> >removed
> >according to the Marek's patch (from the Hans Verkuil request)
> > : http://www.spinics.net/lists/linux-media/msg23902.html
> >Does it mean that we don't have to use drv_lock in the driver ?
> >What actually purpose of drv_lock that you used in the MFC driver ?
> 
> I'll remove this when videobuf2 will be merged. The version posted by
> Marek
> is still not final, so it is better to wait until a final version is
> worked
> out in the community.

OK, I will wait until final version is released.

> 
> 
> >> +	vb2_set_alloc_ctx(&ctx->vq_dst, dev->alloc_ctx[1], 1);
> >> +	init_waitqueue_head(&ctx->queue);
> >> +	mutex_unlock(dev->mfc_mutex);
> >> +	mfc_debug("s5p_mfc_open--\n");
> >> +	return ret;
> >> +	/* Deinit when failure occured */
> >> +out_open_3:
> >> +	if (atomic_read(&dev->num_inst) == 1) {
> >> +		clk_disable(dev->clock1);
> 
> 
> [...]
> 
> >> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers++\n");
> >> +	mfc_ctx->desc_phys = cma_alloc(mfc_ctx->dev->v4l2_dev.dev,
> >> +					MFC_CMA_BANK1, DESC_BUF_SIZE, 2048);
> >> +	if (IS_ERR_VALUE(mfc_ctx->desc_phys)) {
> >> +		mfc_ctx->desc_phys = 0;
> >> +		mfc_err("Allocating DESC buffer failed.\n");
> >> +		return -ENOMEM;
> >> +	}
> >> +	desc_virt = ioremap_nocache(mfc_ctx->desc_phys, DESC_BUF_SIZE);
> 
> >In case of ioremap_xx() function, you might meet that msg as below
> >"Don't allow RAM to be mapped - this causes problems with ARMv6+ "
> >(arch/arm/mm/ioremap.c). so we should not use this function for this
case.
> >I suggest that you use phys_to_virt() with some cache op. such as
> >cache_clean for writing case(ex> memset) & cache_invalidate for reading
> >case.
> >(ex>reading shared mem)
> >It is necessary for accessing shared memory(DRAM area accessed by ARM &
> MFC)
> 
> At this moment we are waiting for the final CMA version and we don't know
> whether phys_to_virt will work with it. Now it has been changed from bug
> to
> a warning by Russel.

Do you mean final version of CMA includes that kinds of function 
(ioremap, phys_to_virt)?
Anyway, using with phys_to_virt() doesn't look like having a problem in my
environment 
only if it is used with cache function as below.
static void s5p_mfc_mem_cache_clean(const void *start_addr, unsigned long
size)
{
	unsigned long paddr;
	dmac_map_area(start_addr, size, DMA_TO_DEVICE);
	/*
	 * virtual & phsical addrees mapped directly, so we can convert
	 * the address just using offset
	 */
	paddr = __pa((unsigned long)start_addr);
	outer_clean_range(paddr, paddr + size);	
}
static void s5p_mfc_mem_cache_inv(const void *start_addr, unsigned long
size)
{
	unsigned long paddr;
	paddr = __pa((unsigned long)start_addr);
	outer_inv_range(paddr, paddr + size);
	dmac_unmap_area(start_addr, size, DMA_FROM_DEVICE);	

}

> 
> >> +	if (desc_virt == NULL) {
> >> +		cma_free(mfc_ctx->desc_phys);
> >> +		mfc_ctx->desc_phys = 0;
> >> +		mfc_err("Remapping DESC buffer failed.\n");
> >> +		return -ENOMEM;
> >> +	}
> >> +	/* Zero content of the allocated memory, in future this might be
> >> done
> >> +	 * by cma_alloc */
> >> +	memset(desc_virt, 0, DESC_BUF_SIZE);
> >> +	iounmap(desc_virt);
> >> +	mfc_debug("s5p_mfc_alloc_dec_temp_buffers--\n");
> 
> [...]
> 
> >> +	mfc_debug("s5p_mfc_release_instance_buffer--\n");
> >> +}
> >> +
> >> +/* Set registers for decoding temporary buffers */
> >> +void s5p_mfc_set_dec_temp_buffers(struct s5p_mfc_ctx *mfc_ctx)
> >> +{
> >> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> >> +	WRITEL(OFFSETA(mfc_ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
> >> +	WRITEL(CPB_BUF_SIZE, S5P_FIMV_SI_CH0_CPB_SIZE);
> >> +	WRITEL(DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
> >> +	WRITEL(mfc_ctx->shared_phys - mfc_ctx->dev->port_a,
> >> +	       S5P_FIMV_SI_CH0_HOST_WR_ADR);
> >> +}
> 
> >I mentioned that separating 'setting S5P_FIMV_SI_CH0_HOST_WR_ADR' from
> >s5p_mfc_set_dec_temp_buffers() is better in terms of modular design.
> >And what about having clear function name ? using 'temp' in the func.
> Name
> >is not specific
> 
> What name do you suggest? If I name the function
> s5p_mfc_set_dec_desc_buffer()
> it will contain no information that other registers are set too.
> 

Yes, s5p_mfc_set_dec_desc_buffer() has clear name, what about moving  
S5P_FIMV_SI_CH0_CPB_SIZE to in the s5p_mfc_set_dec_stream_buffer()?

> >> +
> >> +/* Set registers for decoding stream buffer */
> >> +int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *mfc_ctx, int
> >> buf_addr,
> >> +		  unsigned int start_num_byte, unsigned int buf_size)
> >> +{
> >> +	struct s5p_mfc_dev *dev = mfc_ctx->dev;
> >> +	mfc_debug("inst_no : %d, buf_addr : 0x%08x, buf_size : 0x%08x
> >> (%d)\n",
> >> +		  mfc_ctx->inst_no, buf_addr, buf_size, buf_size);
> >> +	if (buf_addr & (2048 - 1)) {
> >> +		mfc_err("Source stream buffer is not aligned correctly.\n");
> >> +		return -EINVAL;
> >> +	}
> 
> [...]
> 
> >> +	buf_size2 = mfc_ctx->port_b_size;
> >> +	mfc_debug("Buf1: %p (%d) Buf2: %p (%d)\n", (void *)buf_addr1,
> >> buf_size1,
> >> +		  (void *)buf_addr2, buf_size2);
> >> +	/* Enable generation of extra info */
> >> +/*	*(shared_mem_vir_addr + 0x0038) = 63; */
> >> +	mfc_debug("Total DPB COUNT: %d\n", mfc_ctx->total_dpb_count);
> >> +	mfc_debug("Setting display delay to %d\n", mfc_ctx->display_delay);
> >> +	dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & 0xFFFF0000;
> >> +	WRITEL(mfc_ctx->total_dpb_count | dpb,
> >> S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
> >> +	s5p_mfc_set_dec_temp_buffers(mfc_ctx);
> 
> >I think, all reg. set in the s5p_mfc_set_dec_temp_buffers(mfc_ctx)is not
> >required
> >at this point.
> >What about only adding 'set S5P_FIMV_SI_CH0_HOST_WR_ADR) instead of using
> >s5p_mfc_set_dec_temp_buffers()?
> 
> Done.
> 
> >> +	switch (mfc_ctx->codec_mode) {
> >> +	case S5P_FIMV_CODEC_H264_DEC:
> >> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_MV_ADR);
> >> +		buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> >> +		buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
> >> +		WRITEL(OFFSETA(buf_addr1), S5P_FIMV_VERT_NB_IP_ADR);
> >> +		buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
> >> +		buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
> >> +		break;
> >> +	case S5P_FIMV_CODEC_MPEG4_DEC:
> >> +	case S5P_FIMV_CODEC_DIVX311_DEC:
> >> +	case S5P_FIMV_CODEC_DIVX412_DEC:
> >> +	case S5P_FIMV_CODEC_DIVX502_DEC:
> 
> Best regards,
> --
> Kamil Debski
> Linux Platform Group
> Samsung Poland R&D Center


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
                     ` (2 preceding siblings ...)
  2010-10-18  8:30   ` Jaeryul Oh
@ 2010-10-21 10:59   ` Jaeryul Oh
  2010-10-21 12:56     ` Kamil Debski
  2010-10-26 11:58   ` Jaeryul Oh
  4 siblings, 1 reply; 18+ messages in thread
From: Jaeryul Oh @ 2010-10-21 10:59 UTC (permalink / raw)
  To: 'Kamil Debski', linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

I have some opinion about the usage of wait_event_interruptible_timeout()


k.debski@samsung.com wrote:
[snip]

> +
> diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> new file mode 100644
> index 0000000..543f3fb
> --- /dev/null
> +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> @@ -0,0 +1,77 @@
> +/*
> + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
> + *
> + * C file for Samsung MFC (Multi Function Codec - FIMV) driver
> + * This file contains functions used to wait for command completion.
> + *
> + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> + * http://www.samsung.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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/wait.h>
> +#include <linux/sched.h>
> +#include <linux/io.h>
> +#include "regs-mfc5.h"
> +#include "s5p_mfc_intr.h"
> +#include "s5p_mfc_logmsg.h"
> +#include "s5p_mfc_common.h"
> +
> +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
> +{
> +	if (wait_event_interruptible_timeout(dev->queue,
> +		(dev->int_cond && (dev->int_type == command
> +		|| dev->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +		msecs_to_jiffies(MFC_INT_TIMEOUT)) == 0) {
> +		mfc_err("Interrupt (%d dev) timed out.\n", dev->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (dev->queue, %d).\n", dev->int_type);
> +	if (dev->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}


You used wait_event_interruptible_timeout() in the driver, but this
function is 
considered not only int. by MFC but also int. by some signal. I'm wondering
whether we have to consider interrupt by signal in the middle of hw
operation.
and also I cannot see some operation(handler) in case of wake-up by signal.
So, why don’t you remove the interruptible function or add some operation
in case of 
wake-up by signal. (refer to the other driver in the kernel)

> +
> +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
> +{
> +	dev->int_cond = 0;
> +	dev->int_type = 0;
> +	dev->int_err = 0;
> +}
> +
> +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> +				    int command, int interrupt)
> +{
> +	int ret;
> +	if (interrupt) {
> +		ret = wait_event_interruptible_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	} else {
> +		ret = wait_event_timeout(ctx->queue,
> +				(ctx->int_cond && (ctx->int_type == command
> +			|| ctx->int_type ==
S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> +	}
> +	if (ret == 0) {
> +		mfc_err("Interrupt (%d ctx) timed out.\n", ctx->int_type);
> +		return 1;
> +	}
> +	mfc_debug("Finished waiting (ctx->queue, %d).\n", ctx->int_type);
> +	if (ctx->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> +		return 1;
> +	return 0;
> +}
> +
> +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
> +{
> +	ctx->int_cond = 0;
> +	ctx->int_type = 0;
> +	ctx->int_err = 0;
> +}

[snip]

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


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-21  0:03       ` Jaeryul Oh
@ 2010-10-21 12:52         ` Kamil Debski
  0 siblings, 0 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-21 12:52 UTC (permalink / raw)
  To: jaeryul.oh, linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Hi,

 
> I commented as belows,
> And you missed one important things 'cause there were my comments
> in the very long email which is strongly fixed in the reset seq.

Yes, thanks for your suggestion.
 
> 
> [....]
> > +#define READL(offset)		readl(dev->regs_base + (offset))
> > +#define WRITEL(data, offset)	writel((data), dev->regs_base +
> (offset))
> > +#define OFFSETA(x)		(((x) - dev->port_a) >> 11)
> > +#define OFFSETB(x)		(((x) - dev->port_b) >> 11)
> > +
> > +/* Reset the device */
> > +static int s5p_mfc_cmd_reset(struct s5p_mfc_dev *dev)
> > +{
> > +	unsigned int mc_status;
> > +	unsigned long timeout;
> > +	mfc_debug("s5p_mfc_cmd_reset++\n");
> > +	/* Stop procedure */
> > +	WRITEL(0x3f7, S5P_FIMV_SW_RESET);	/*  reset VI */
> 
> Ahm, This (WRITEL(0x3f7, S5P_FIMV_SW_RESET)) might be a problem.
> In the reset seq. of MFC driver, we checked out
> That FW(s5pc110-mfc.fw in the s5p_mfc_load_firmware()) runned by RISC
> core
> at this point could access invalid address. It should be removed.
> 

Thanks for pointing this out. I will remove it in the next version.

[snip]

Best regards
--
Kamil Debski
Linux Platform Group
Samsung Poland R&D Center


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-21 10:59   ` Jaeryul Oh
@ 2010-10-21 12:56     ` Kamil Debski
  0 siblings, 0 replies; 18+ messages in thread
From: Kamil Debski @ 2010-10-21 12:56 UTC (permalink / raw)
  To: jaeryul.oh, linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Hello,

> I have some opinion about the usage of
> wait_event_interruptible_timeout()
> 
> 
> k.debski@samsung.com wrote:
> [snip]
> 
> > +
> > diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> > b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> > new file mode 100644
> > index 0000000..543f3fb
> > --- /dev/null
> > +++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
> > @@ -0,0 +1,77 @@
> > +/*
> > + * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
> > + *
> > + * C file for Samsung MFC (Multi Function Codec - FIMV) driver
> > + * This file contains functions used to wait for command completion.
> > + *
> > + * Kamil Debski, Copyright (c) 2010 Samsung Electronics
> > + * http://www.samsung.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.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/errno.h>
> > +#include <linux/wait.h>
> > +#include <linux/sched.h>
> > +#include <linux/io.h>
> > +#include "regs-mfc5.h"
> > +#include "s5p_mfc_intr.h"
> > +#include "s5p_mfc_logmsg.h"
> > +#include "s5p_mfc_common.h"
> > +
> > +int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
> > +{
> > +	if (wait_event_interruptible_timeout(dev->queue,
> > +		(dev->int_cond && (dev->int_type == command
> > +		|| dev->int_type == S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> > +		msecs_to_jiffies(MFC_INT_TIMEOUT)) == 0) {
> > +		mfc_err("Interrupt (%d dev) timed out.\n", dev->int_type);
> > +		return 1;
> > +	}
> > +	mfc_debug("Finished waiting (dev->queue, %d).\n", dev->int_type);
> > +	if (dev->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> > +		return 1;
> > +	return 0;
> > +}
> 
> 
> You used wait_event_interruptible_timeout() in the driver, but this
> function is
> considered not only int. by MFC but also int. by some signal. I'm
> wondering
> whether we have to consider interrupt by signal in the middle of hw
> operation.
> and also I cannot see some operation(handler) in case of wake-up by
> signal.
> So, why don't you remove the interruptible function or add some
> operation
> in case of
> wake-up by signal. (refer to the other driver in the kernel)

I can see a situation when handling a signal might be useful. If for some
reason (corrupt firmware file for example) the MFC fails to initialize then
the
application will seem "frozen" to the user. The user can press Ctrl-C and
expects the app to terminate. This would not happen if the used wait
procedure
was non interruptible.

Therefore I think adding code to handle signal is better than changing this
wait
to non interruptible.

> 
> > +
> > +void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
> > +{
> > +	dev->int_cond = 0;
> > +	dev->int_type = 0;
> > +	dev->int_err = 0;
> > +}
> > +
> > +int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
> > +				    int command, int interrupt)
> > +{
> > +	int ret;
> > +	if (interrupt) {
> > +		ret = wait_event_interruptible_timeout(ctx->queue,
> > +				(ctx->int_cond && (ctx->int_type == command
> > +			|| ctx->int_type ==
> S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> > +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> > +	} else {
> > +		ret = wait_event_timeout(ctx->queue,
> > +				(ctx->int_cond && (ctx->int_type == command
> > +			|| ctx->int_type ==
> S5P_FIMV_R2H_CMD_DECODE_ERR_RET)),
> > +					msecs_to_jiffies(MFC_INT_TIMEOUT));
> > +	}
> > +	if (ret == 0) {
> > +		mfc_err("Interrupt (%d ctx) timed out.\n", ctx->int_type);
> > +		return 1;
> > +	}
> > +	mfc_debug("Finished waiting (ctx->queue, %d).\n", ctx->int_type);
> > +	if (ctx->int_type == S5P_FIMV_R2H_CMD_ERROR_RET)
> > +		return 1;
> > +	return 0;
> > +}
> > +
> > +void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
> > +{
> > +	ctx->int_cond = 0;
> > +	ctx->int_type = 0;
> > +	ctx->int_err = 0;
> > +}
> 
> [snip]
> 

Best regards,
--
Kamil Debski
Linux Platform Group
Samsung Poland R&D Center


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

* RE: [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver
  2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
                     ` (3 preceding siblings ...)
  2010-10-21 10:59   ` Jaeryul Oh
@ 2010-10-26 11:58   ` Jaeryul Oh
  4 siblings, 0 replies; 18+ messages in thread
From: Jaeryul Oh @ 2010-10-26 11:58 UTC (permalink / raw)
  To: 'Kamil Debski', linux-media, linux-samsung-soc
  Cc: m.szyprowski, pawel, kyungmin.park, kgene.kim

Hi, Kamil, 
there is one meaningless parameter.

k.debski@samsung.com wrote:
[snip]
> +}
> +
> +/* Try running an operation on hardware */
> +static void s5p_mfc_try_run()
> +{
> +	struct vb2_buffer *temp_vb;
> +	struct s5p_mfc_ctx *ctx;
> +	int new_ctx;
> +	unsigned long flags;
> +	unsigned int cnt;
> +	unsigned int ret;
> +	mfc_debug("Try run dev: %p\n", dev);
> +	/* Check whether hardware is not running */
> +	if (test_and_set_bit(0, &dev->hw_lock) == 0) {
> +		/* Choose the context to run */
> +		spin_lock_irqsave(&dev->condlock, flags);
> +		mfc_debug("Previos context: %d (bit field %08lx)\n",
> +					  dev->curr_ctx, dev-
>ctx_work_bits);
> +		new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
> +		cnt = 0;
> +		while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
> +			new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
> +			cnt++;
> +			if (cnt > MFC_NUM_CONTEXTS) {
> +				/* No contexts to run */
> +				spin_unlock_irqrestore(&dev->condlock,
flags);
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No ctx is scheduled to be
run.\n");
> +				return;
> +			}
> +		}
> +		mfc_debug("New context: %d\n", new_ctx);
> +		spin_unlock_irqrestore(&dev->condlock, flags);
> +		ctx = dev->ctx[new_ctx];
> +		mfc_debug("Seting new context to %p\n", ctx);
> +		/* Got context to run in ctx */
> +		mfc_debug("ctx->dst_queue_cnt=%d ctx->dpb_count=%d" \
> +				" ctx->src_queue_cnt=%d\n", ctx-
> >dst_queue_cnt,\
> +					ctx->dpb_count, ctx->src_queue_cnt);
> +		mfc_debug("ctx->state=%d\n", ctx->state);
> +		/* Last frame has already been sent to MFC
> +		 * Now obtaining frames from MFC buffer */
> +		if (ctx->state == MFCINST_DEC_FINISHING) {
> +			s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx, 1);
> +		} else if (ctx->state == MFCINST_DEC_RUNNING) {
> +			/* Frames are being decoded */
> +			if (list_empty(&ctx->src_queue)) {
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +				mfc_debug("No source buffers.\n");
> +				return;
> +			}
> +			/* Get the next source buffer */
> +			temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +			mfc_debug("Temp vb: %p\n", temp_vb);
> +			mfc_debug("Src Addr: %08lx\n",
> +						vb2_plane_paddr(temp_vb,
0));
> +			s5p_mfc_set_dec_stream_buffer(ctx, \
> +			      vb2_plane_paddr(temp_vb, 0), 0, \
> +			      temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_decode_one_frame(ctx,
> +				temp_vb->v4l2_planes[0].bytesused == 0);
> +		} else if (ctx->state == MFCINST_DEC_INIT) {
> +			/* Preparing decoding - getting instance number */
> +			mfc_debug("Getting instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +/*			s5p_mfc_set_dec_temp_buffers(ctx);
> + *			Removed per request by Peter, check if MFC works OK
*/
> +			ret = s5p_mfc_open_inst(ctx);
> +			if (ret) {
> +				mfc_err("Failed to create a new
instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +		} else if (ctx->state == MFCINST_DEC_RETURN_INST) {
> +			/* Closing decoding instance  */
> +			mfc_debug("Returning instance number\n");
> +			dev->curr_ctx = ctx->num;
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			ret = s5p_mfc_return_inst_no(ctx);
> +			if (ret) {
> +				mfc_err("Failed to return an instance.\n");
> +				ctx->state = MFCINST_DEC_ERROR;
> +			}
> +
> +		} else if (ctx->state == MFCINST_DEC_GOT_INST) {
> +			/* Initializing decoding - parsing header */
> +			mfc_debug("Preparing to init decoding.\n");
> +			temp_vb = list_entry(ctx->src_queue.next,
> +						struct vb2_buffer,
drv_entry);
> +			s5p_mfc_set_dec_temp_buffers(ctx);
> +			mfc_debug("Header size: %d\n", \
> +					temp_vb->v4l2_planes[0].bytesused);
> +			s5p_mfc_set_dec_stream_buffer(ctx,\
> +					vb2_plane_paddr(temp_vb, 0), 0,
> +					 temp_vb->v4l2_planes[0].bytesused);
> +			dev->curr_ctx = ctx->num;
> +			mfc_debug("paddr: %08x\n", \
> +				(int)phys_to_virt(vb2_plane_paddr(temp_vb,
0)));
> +			s5p_mfc_clean_ctx_int_flags(ctx);
> +			s5p_mfc_init_decode(ctx);
> +		} else if (ctx->state == MFCINST_DEC_HEAD_PARSED) {
> +			/* Header was parsed now starting processing
> +			 * First set the output frame buffers
> +			 * s5p_mfc_alloc_dec_buffers(ctx); */
> +			if (ctx->capture_state == QUEUE_BUFS_MMAPED) {
> +				temp_vb = list_entry(ctx->src_queue.next, \
> +						struct vb2_buffer,
drv_entry);
> +				mfc_debug("Header size: %d\n",
> +					temp_vb->v4l2_planes[0].bytesused);
> +				s5p_mfc_set_dec_stream_buffer(ctx, \
> +					vb2_plane_paddr(temp_vb, 0), 0, \
> +					temp_vb->v4l2_planes[0].bytesused);
> +				dev->curr_ctx = ctx->num;
> +				s5p_mfc_clean_ctx_int_flags(ctx);
> +				s5p_mfc_set_dec_frame_buffer(ctx, 1);

What is second parameter (value = 1) ? In the
s5p_mfc_set_dec_frame_buffer(), 
Always, if (do_int) is runned. Is there any purpose ? otherwise, remove
this param.

> +			} else {
> +				mfc_err("It seems that not all
destionation" \
> +				    " buffers were mmaped.\nMFC requires
that" \
> +				    " all destination are mmaped before" \
> +				    " starting processing.\n");
> +				if (test_and_clear_bit(0, &dev->hw_lock) ==
0)
> {
> +					mfc_err("Failed to unlock
hardware.\n");
> +					return;
> +				}
> +			}
> +		} else {
> +			/* Free hardware lock */
> +			if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
> +				mfc_err("Failed to unlock hardware.\n");
> +				return;
> +			}
> +		}
> +	} else {
> +		/* This is perfectly ok, the scheduled ctx should wait */
> +		mfc_debug("Couldn't lock HW.\n");
> +	}
> +}
> +

[snip]

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


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

end of thread, other threads:[~2010-10-26 11:58 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-13 11:09 [RFC/PATCH 0/4] Multi Format Codec 5.0 driver for S5PC110 SoC Kamil Debski
2010-10-13 11:09 ` [PATCH 1/4] MFC: Changes in include/linux/videodev2.h for MFC 5.1 codec Kamil Debski
2010-10-13 11:09 ` [PATCH 2/4] MFC: Add MFC 5.1 driver to plat-s5p Kamil Debski
2010-10-13 11:09 ` [PATCH 3/4] MFC: Add MFC 5.1 V4L2 driver Kamil Debski
2010-10-14  6:25   ` Jaeryul Oh
2010-10-14 12:56     ` Kamil Debski
2010-10-15  4:27       ` Jaeryul Oh
2010-10-20 14:29         ` Kamil Debski
2010-10-15  4:06   ` Jaeryul Oh
2010-10-20 14:25     ` Kamil Debski
2010-10-21  0:03       ` Jaeryul Oh
2010-10-21 12:52         ` Kamil Debski
2010-10-18  8:30   ` Jaeryul Oh
2010-10-20 14:30     ` Kamil Debski
2010-10-21 10:59   ` Jaeryul Oh
2010-10-21 12:56     ` Kamil Debski
2010-10-26 11:58   ` Jaeryul Oh
2010-10-13 11:09 ` [PATCH 4/4] s5pc110: Enable MFC 5.1 on Goni Kamil Debski

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