143ecec16SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-or-later
243ecec16SMauro Carvalho Chehab /*
343ecec16SMauro Carvalho Chehab  * Samsung S5P Multi Format Codec v 5.1
443ecec16SMauro Carvalho Chehab  *
543ecec16SMauro Carvalho Chehab  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
643ecec16SMauro Carvalho Chehab  * Kamil Debski, <k.debski@samsung.com>
743ecec16SMauro Carvalho Chehab  */
843ecec16SMauro Carvalho Chehab 
943ecec16SMauro Carvalho Chehab #include <linux/clk.h>
1043ecec16SMauro Carvalho Chehab #include <linux/delay.h>
1143ecec16SMauro Carvalho Chehab #include <linux/interrupt.h>
1243ecec16SMauro Carvalho Chehab #include <linux/io.h>
1343ecec16SMauro Carvalho Chehab #include <linux/module.h>
1443ecec16SMauro Carvalho Chehab #include <linux/platform_device.h>
1543ecec16SMauro Carvalho Chehab #include <linux/sched.h>
1643ecec16SMauro Carvalho Chehab #include <linux/slab.h>
1743ecec16SMauro Carvalho Chehab #include <linux/videodev2.h>
1843ecec16SMauro Carvalho Chehab #include <media/v4l2-event.h>
1943ecec16SMauro Carvalho Chehab #include <linux/workqueue.h>
2043ecec16SMauro Carvalho Chehab #include <linux/of.h>
2143ecec16SMauro Carvalho Chehab #include <linux/of_device.h>
2243ecec16SMauro Carvalho Chehab #include <linux/of_reserved_mem.h>
2343ecec16SMauro Carvalho Chehab #include <media/videobuf2-v4l2.h>
2443ecec16SMauro Carvalho Chehab #include "s5p_mfc_common.h"
2543ecec16SMauro Carvalho Chehab #include "s5p_mfc_ctrl.h"
2643ecec16SMauro Carvalho Chehab #include "s5p_mfc_debug.h"
2743ecec16SMauro Carvalho Chehab #include "s5p_mfc_dec.h"
2843ecec16SMauro Carvalho Chehab #include "s5p_mfc_enc.h"
2943ecec16SMauro Carvalho Chehab #include "s5p_mfc_intr.h"
3043ecec16SMauro Carvalho Chehab #include "s5p_mfc_iommu.h"
3143ecec16SMauro Carvalho Chehab #include "s5p_mfc_opr.h"
3243ecec16SMauro Carvalho Chehab #include "s5p_mfc_cmd.h"
3343ecec16SMauro Carvalho Chehab #include "s5p_mfc_pm.h"
3443ecec16SMauro Carvalho Chehab 
3543ecec16SMauro Carvalho Chehab #define S5P_MFC_DEC_NAME	"s5p-mfc-dec"
3643ecec16SMauro Carvalho Chehab #define S5P_MFC_ENC_NAME	"s5p-mfc-enc"
3743ecec16SMauro Carvalho Chehab 
3843ecec16SMauro Carvalho Chehab int mfc_debug_level;
3940928aeaSAakarsh Jain module_param_named(debug, mfc_debug_level, int, 0644);
4043ecec16SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
4143ecec16SMauro Carvalho Chehab 
4243ecec16SMauro Carvalho Chehab static char *mfc_mem_size;
4343ecec16SMauro Carvalho Chehab module_param_named(mem, mfc_mem_size, charp, 0644);
4443ecec16SMauro Carvalho Chehab MODULE_PARM_DESC(mem, "Preallocated memory size for the firmware and context buffers");
4543ecec16SMauro Carvalho Chehab 
4643ecec16SMauro Carvalho Chehab /* Helper functions for interrupt processing */
4743ecec16SMauro Carvalho Chehab 
4843ecec16SMauro Carvalho Chehab /* Remove from hw execution round robin */
clear_work_bit(struct s5p_mfc_ctx * ctx)4943ecec16SMauro Carvalho Chehab void clear_work_bit(struct s5p_mfc_ctx *ctx)
5043ecec16SMauro Carvalho Chehab {
5143ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
5243ecec16SMauro Carvalho Chehab 
5343ecec16SMauro Carvalho Chehab 	spin_lock(&dev->condlock);
5443ecec16SMauro Carvalho Chehab 	__clear_bit(ctx->num, &dev->ctx_work_bits);
5543ecec16SMauro Carvalho Chehab 	spin_unlock(&dev->condlock);
5643ecec16SMauro Carvalho Chehab }
5743ecec16SMauro Carvalho Chehab 
5843ecec16SMauro Carvalho Chehab /* Add to hw execution round robin */
set_work_bit(struct s5p_mfc_ctx * ctx)5943ecec16SMauro Carvalho Chehab void set_work_bit(struct s5p_mfc_ctx *ctx)
6043ecec16SMauro Carvalho Chehab {
6143ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
6243ecec16SMauro Carvalho Chehab 
6343ecec16SMauro Carvalho Chehab 	spin_lock(&dev->condlock);
6443ecec16SMauro Carvalho Chehab 	__set_bit(ctx->num, &dev->ctx_work_bits);
6543ecec16SMauro Carvalho Chehab 	spin_unlock(&dev->condlock);
6643ecec16SMauro Carvalho Chehab }
6743ecec16SMauro Carvalho Chehab 
6843ecec16SMauro Carvalho Chehab /* Remove from hw execution round robin */
clear_work_bit_irqsave(struct s5p_mfc_ctx * ctx)6943ecec16SMauro Carvalho Chehab void clear_work_bit_irqsave(struct s5p_mfc_ctx *ctx)
7043ecec16SMauro Carvalho Chehab {
7143ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
7243ecec16SMauro Carvalho Chehab 	unsigned long flags;
7343ecec16SMauro Carvalho Chehab 
7443ecec16SMauro Carvalho Chehab 	spin_lock_irqsave(&dev->condlock, flags);
7543ecec16SMauro Carvalho Chehab 	__clear_bit(ctx->num, &dev->ctx_work_bits);
7643ecec16SMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->condlock, flags);
7743ecec16SMauro Carvalho Chehab }
7843ecec16SMauro Carvalho Chehab 
7943ecec16SMauro Carvalho Chehab /* Add to hw execution round robin */
set_work_bit_irqsave(struct s5p_mfc_ctx * ctx)8043ecec16SMauro Carvalho Chehab void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx)
8143ecec16SMauro Carvalho Chehab {
8243ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
8343ecec16SMauro Carvalho Chehab 	unsigned long flags;
8443ecec16SMauro Carvalho Chehab 
8543ecec16SMauro Carvalho Chehab 	spin_lock_irqsave(&dev->condlock, flags);
8643ecec16SMauro Carvalho Chehab 	__set_bit(ctx->num, &dev->ctx_work_bits);
8743ecec16SMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->condlock, flags);
8843ecec16SMauro Carvalho Chehab }
8943ecec16SMauro Carvalho Chehab 
s5p_mfc_get_new_ctx(struct s5p_mfc_dev * dev)9043ecec16SMauro Carvalho Chehab int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
9143ecec16SMauro Carvalho Chehab {
9243ecec16SMauro Carvalho Chehab 	unsigned long flags;
9343ecec16SMauro Carvalho Chehab 	int ctx;
9443ecec16SMauro Carvalho Chehab 
9543ecec16SMauro Carvalho Chehab 	spin_lock_irqsave(&dev->condlock, flags);
9643ecec16SMauro Carvalho Chehab 	ctx = dev->curr_ctx;
9743ecec16SMauro Carvalho Chehab 	do {
9843ecec16SMauro Carvalho Chehab 		ctx = (ctx + 1) % MFC_NUM_CONTEXTS;
9943ecec16SMauro Carvalho Chehab 		if (ctx == dev->curr_ctx) {
10043ecec16SMauro Carvalho Chehab 			if (!test_bit(ctx, &dev->ctx_work_bits))
10143ecec16SMauro Carvalho Chehab 				ctx = -EAGAIN;
10243ecec16SMauro Carvalho Chehab 			break;
10343ecec16SMauro Carvalho Chehab 		}
10443ecec16SMauro Carvalho Chehab 	} while (!test_bit(ctx, &dev->ctx_work_bits));
10543ecec16SMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->condlock, flags);
10643ecec16SMauro Carvalho Chehab 
10743ecec16SMauro Carvalho Chehab 	return ctx;
10843ecec16SMauro Carvalho Chehab }
10943ecec16SMauro Carvalho Chehab 
11043ecec16SMauro Carvalho Chehab /* Wake up context wait_queue */
wake_up_ctx(struct s5p_mfc_ctx * ctx,unsigned int reason,unsigned int err)11143ecec16SMauro Carvalho Chehab static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
11243ecec16SMauro Carvalho Chehab 			unsigned int err)
11343ecec16SMauro Carvalho Chehab {
11443ecec16SMauro Carvalho Chehab 	ctx->int_cond = 1;
11543ecec16SMauro Carvalho Chehab 	ctx->int_type = reason;
11643ecec16SMauro Carvalho Chehab 	ctx->int_err = err;
11743ecec16SMauro Carvalho Chehab 	wake_up(&ctx->queue);
11843ecec16SMauro Carvalho Chehab }
11943ecec16SMauro Carvalho Chehab 
12043ecec16SMauro Carvalho Chehab /* Wake up device wait_queue */
wake_up_dev(struct s5p_mfc_dev * dev,unsigned int reason,unsigned int err)12143ecec16SMauro Carvalho Chehab static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
12243ecec16SMauro Carvalho Chehab 			unsigned int err)
12343ecec16SMauro Carvalho Chehab {
12443ecec16SMauro Carvalho Chehab 	dev->int_cond = 1;
12543ecec16SMauro Carvalho Chehab 	dev->int_type = reason;
12643ecec16SMauro Carvalho Chehab 	dev->int_err = err;
12743ecec16SMauro Carvalho Chehab 	wake_up(&dev->queue);
12843ecec16SMauro Carvalho Chehab }
12943ecec16SMauro Carvalho Chehab 
s5p_mfc_cleanup_queue(struct list_head * lh,struct vb2_queue * vq)13043ecec16SMauro Carvalho Chehab void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
13143ecec16SMauro Carvalho Chehab {
13243ecec16SMauro Carvalho Chehab 	struct s5p_mfc_buf *b;
13343ecec16SMauro Carvalho Chehab 	int i;
13443ecec16SMauro Carvalho Chehab 
13543ecec16SMauro Carvalho Chehab 	while (!list_empty(lh)) {
13643ecec16SMauro Carvalho Chehab 		b = list_entry(lh->next, struct s5p_mfc_buf, list);
13743ecec16SMauro Carvalho Chehab 		for (i = 0; i < b->b->vb2_buf.num_planes; i++)
13843ecec16SMauro Carvalho Chehab 			vb2_set_plane_payload(&b->b->vb2_buf, i, 0);
13943ecec16SMauro Carvalho Chehab 		vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR);
14043ecec16SMauro Carvalho Chehab 		list_del(&b->list);
14143ecec16SMauro Carvalho Chehab 	}
14243ecec16SMauro Carvalho Chehab }
14343ecec16SMauro Carvalho Chehab 
s5p_mfc_watchdog(struct timer_list * t)14443ecec16SMauro Carvalho Chehab static void s5p_mfc_watchdog(struct timer_list *t)
14543ecec16SMauro Carvalho Chehab {
14643ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = from_timer(dev, t, watchdog_timer);
14743ecec16SMauro Carvalho Chehab 
14843ecec16SMauro Carvalho Chehab 	if (test_bit(0, &dev->hw_lock))
14943ecec16SMauro Carvalho Chehab 		atomic_inc(&dev->watchdog_cnt);
15043ecec16SMauro Carvalho Chehab 	if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
151203ef345SAakarsh Jain 		/*
152203ef345SAakarsh Jain 		 * This means that hw is busy and no interrupts were
15343ecec16SMauro Carvalho Chehab 		 * generated by hw for the Nth time of running this
15443ecec16SMauro Carvalho Chehab 		 * watchdog timer. This usually means a serious hw
15543ecec16SMauro Carvalho Chehab 		 * error. Now it is time to kill all instances and
156203ef345SAakarsh Jain 		 * reset the MFC.
157203ef345SAakarsh Jain 		 */
15843ecec16SMauro Carvalho Chehab 		mfc_err("Time out during waiting for HW\n");
15943ecec16SMauro Carvalho Chehab 		schedule_work(&dev->watchdog_work);
16043ecec16SMauro Carvalho Chehab 	}
16143ecec16SMauro Carvalho Chehab 	dev->watchdog_timer.expires = jiffies +
16243ecec16SMauro Carvalho Chehab 					msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
16343ecec16SMauro Carvalho Chehab 	add_timer(&dev->watchdog_timer);
16443ecec16SMauro Carvalho Chehab }
16543ecec16SMauro Carvalho Chehab 
s5p_mfc_watchdog_worker(struct work_struct * work)16643ecec16SMauro Carvalho Chehab static void s5p_mfc_watchdog_worker(struct work_struct *work)
16743ecec16SMauro Carvalho Chehab {
16843ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev;
16943ecec16SMauro Carvalho Chehab 	struct s5p_mfc_ctx *ctx;
17043ecec16SMauro Carvalho Chehab 	unsigned long flags;
17143ecec16SMauro Carvalho Chehab 	int mutex_locked;
17243ecec16SMauro Carvalho Chehab 	int i, ret;
17343ecec16SMauro Carvalho Chehab 
17443ecec16SMauro Carvalho Chehab 	dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
17543ecec16SMauro Carvalho Chehab 
17643ecec16SMauro Carvalho Chehab 	mfc_err("Driver timeout error handling\n");
177203ef345SAakarsh Jain 	/*
178203ef345SAakarsh Jain 	 * Lock the mutex that protects open and release.
179203ef345SAakarsh Jain 	 * This is necessary as they may load and unload firmware.
180203ef345SAakarsh Jain 	 */
18143ecec16SMauro Carvalho Chehab 	mutex_locked = mutex_trylock(&dev->mfc_mutex);
18243ecec16SMauro Carvalho Chehab 	if (!mutex_locked)
18343ecec16SMauro Carvalho Chehab 		mfc_err("Error: some instance may be closing/opening\n");
18443ecec16SMauro Carvalho Chehab 	spin_lock_irqsave(&dev->irqlock, flags);
18543ecec16SMauro Carvalho Chehab 
18643ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
18743ecec16SMauro Carvalho Chehab 
18843ecec16SMauro Carvalho Chehab 	for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
18943ecec16SMauro Carvalho Chehab 		ctx = dev->ctx[i];
19043ecec16SMauro Carvalho Chehab 		if (!ctx)
19143ecec16SMauro Carvalho Chehab 			continue;
19243ecec16SMauro Carvalho Chehab 		ctx->state = MFCINST_ERROR;
19343ecec16SMauro Carvalho Chehab 		s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
19443ecec16SMauro Carvalho Chehab 		s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
19543ecec16SMauro Carvalho Chehab 		clear_work_bit(ctx);
19643ecec16SMauro Carvalho Chehab 		wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0);
19743ecec16SMauro Carvalho Chehab 	}
19843ecec16SMauro Carvalho Chehab 	clear_bit(0, &dev->hw_lock);
19943ecec16SMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->irqlock, flags);
20043ecec16SMauro Carvalho Chehab 
20143ecec16SMauro Carvalho Chehab 	/* De-init MFC */
20243ecec16SMauro Carvalho Chehab 	s5p_mfc_deinit_hw(dev);
20343ecec16SMauro Carvalho Chehab 
204203ef345SAakarsh Jain 	/*
205203ef345SAakarsh Jain 	 * Double check if there is at least one instance running.
206203ef345SAakarsh Jain 	 * If no instance is in memory than no firmware should be present
207203ef345SAakarsh Jain 	 */
20843ecec16SMauro Carvalho Chehab 	if (dev->num_inst > 0) {
20943ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_load_firmware(dev);
21043ecec16SMauro Carvalho Chehab 		if (ret) {
21143ecec16SMauro Carvalho Chehab 			mfc_err("Failed to reload FW\n");
21243ecec16SMauro Carvalho Chehab 			goto unlock;
21343ecec16SMauro Carvalho Chehab 		}
21443ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_on();
21543ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_init_hw(dev);
21643ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
21743ecec16SMauro Carvalho Chehab 		if (ret)
21843ecec16SMauro Carvalho Chehab 			mfc_err("Failed to reinit FW\n");
21943ecec16SMauro Carvalho Chehab 	}
22043ecec16SMauro Carvalho Chehab unlock:
22143ecec16SMauro Carvalho Chehab 	if (mutex_locked)
22243ecec16SMauro Carvalho Chehab 		mutex_unlock(&dev->mfc_mutex);
22343ecec16SMauro Carvalho Chehab }
22443ecec16SMauro Carvalho Chehab 
s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx * ctx)22543ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
22643ecec16SMauro Carvalho Chehab {
22743ecec16SMauro Carvalho Chehab 	struct s5p_mfc_buf *dst_buf;
22843ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
22943ecec16SMauro Carvalho Chehab 
23043ecec16SMauro Carvalho Chehab 	ctx->state = MFCINST_FINISHED;
23143ecec16SMauro Carvalho Chehab 	ctx->sequence++;
23243ecec16SMauro Carvalho Chehab 	while (!list_empty(&ctx->dst_queue)) {
23343ecec16SMauro Carvalho Chehab 		dst_buf = list_entry(ctx->dst_queue.next,
23443ecec16SMauro Carvalho Chehab 				     struct s5p_mfc_buf, list);
23543ecec16SMauro Carvalho Chehab 		mfc_debug(2, "Cleaning up buffer: %d\n",
23643ecec16SMauro Carvalho Chehab 					  dst_buf->b->vb2_buf.index);
23743ecec16SMauro Carvalho Chehab 		vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0, 0);
23843ecec16SMauro Carvalho Chehab 		vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1, 0);
23943ecec16SMauro Carvalho Chehab 		list_del(&dst_buf->list);
24043ecec16SMauro Carvalho Chehab 		dst_buf->flags |= MFC_BUF_FLAG_EOS;
24143ecec16SMauro Carvalho Chehab 		ctx->dst_queue_cnt--;
24243ecec16SMauro Carvalho Chehab 		dst_buf->b->sequence = (ctx->sequence++);
24343ecec16SMauro Carvalho Chehab 
24443ecec16SMauro Carvalho Chehab 		if (s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_top, ctx) ==
24543ecec16SMauro Carvalho Chehab 			s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_bot, ctx))
24643ecec16SMauro Carvalho Chehab 			dst_buf->b->field = V4L2_FIELD_NONE;
24743ecec16SMauro Carvalho Chehab 		else
24843ecec16SMauro Carvalho Chehab 			dst_buf->b->field = V4L2_FIELD_INTERLACED;
24943ecec16SMauro Carvalho Chehab 		dst_buf->b->flags |= V4L2_BUF_FLAG_LAST;
25043ecec16SMauro Carvalho Chehab 
25143ecec16SMauro Carvalho Chehab 		ctx->dec_dst_flag &= ~(1 << dst_buf->b->vb2_buf.index);
25243ecec16SMauro Carvalho Chehab 		vb2_buffer_done(&dst_buf->b->vb2_buf, VB2_BUF_STATE_DONE);
25343ecec16SMauro Carvalho Chehab 	}
25443ecec16SMauro Carvalho Chehab }
25543ecec16SMauro Carvalho Chehab 
s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx * ctx)25643ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
25743ecec16SMauro Carvalho Chehab {
25843ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
25943ecec16SMauro Carvalho Chehab 	struct s5p_mfc_buf *dst_buf, *src_buf;
26043ecec16SMauro Carvalho Chehab 	u32 dec_y_addr;
26143ecec16SMauro Carvalho Chehab 	unsigned int frame_type;
26243ecec16SMauro Carvalho Chehab 
26343ecec16SMauro Carvalho Chehab 	/* Make sure we actually have a new frame before continuing. */
26443ecec16SMauro Carvalho Chehab 	frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);
26543ecec16SMauro Carvalho Chehab 	if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED)
26643ecec16SMauro Carvalho Chehab 		return;
26743ecec16SMauro Carvalho Chehab 	dec_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev);
26843ecec16SMauro Carvalho Chehab 
269203ef345SAakarsh Jain 	/*
270203ef345SAakarsh Jain 	 * Copy timestamp / timecode from decoded src to dst and set
271203ef345SAakarsh Jain 	 * appropriate flags.
272203ef345SAakarsh Jain 	 */
27343ecec16SMauro Carvalho Chehab 	src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
27443ecec16SMauro Carvalho Chehab 	list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
27543ecec16SMauro Carvalho Chehab 		u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0);
27643ecec16SMauro Carvalho Chehab 
27743ecec16SMauro Carvalho Chehab 		if (addr == dec_y_addr) {
27843ecec16SMauro Carvalho Chehab 			dst_buf->b->timecode = src_buf->b->timecode;
27943ecec16SMauro Carvalho Chehab 			dst_buf->b->vb2_buf.timestamp =
28043ecec16SMauro Carvalho Chehab 						src_buf->b->vb2_buf.timestamp;
28143ecec16SMauro Carvalho Chehab 			dst_buf->b->flags &=
28243ecec16SMauro Carvalho Chehab 				~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
28343ecec16SMauro Carvalho Chehab 			dst_buf->b->flags |=
28443ecec16SMauro Carvalho Chehab 				src_buf->b->flags
28543ecec16SMauro Carvalho Chehab 				& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
28643ecec16SMauro Carvalho Chehab 			switch (frame_type) {
28743ecec16SMauro Carvalho Chehab 			case S5P_FIMV_DECODE_FRAME_I_FRAME:
28843ecec16SMauro Carvalho Chehab 				dst_buf->b->flags |=
28943ecec16SMauro Carvalho Chehab 						V4L2_BUF_FLAG_KEYFRAME;
29043ecec16SMauro Carvalho Chehab 				break;
29143ecec16SMauro Carvalho Chehab 			case S5P_FIMV_DECODE_FRAME_P_FRAME:
29243ecec16SMauro Carvalho Chehab 				dst_buf->b->flags |=
29343ecec16SMauro Carvalho Chehab 						V4L2_BUF_FLAG_PFRAME;
29443ecec16SMauro Carvalho Chehab 				break;
29543ecec16SMauro Carvalho Chehab 			case S5P_FIMV_DECODE_FRAME_B_FRAME:
29643ecec16SMauro Carvalho Chehab 				dst_buf->b->flags |=
29743ecec16SMauro Carvalho Chehab 						V4L2_BUF_FLAG_BFRAME;
29843ecec16SMauro Carvalho Chehab 				break;
29943ecec16SMauro Carvalho Chehab 			default:
300203ef345SAakarsh Jain 				/*
301203ef345SAakarsh Jain 				 * Don't know how to handle
302203ef345SAakarsh Jain 				 * S5P_FIMV_DECODE_FRAME_OTHER_FRAME.
303203ef345SAakarsh Jain 				 */
30443ecec16SMauro Carvalho Chehab 				mfc_debug(2, "Unexpected frame type: %d\n",
30543ecec16SMauro Carvalho Chehab 						frame_type);
30643ecec16SMauro Carvalho Chehab 			}
30743ecec16SMauro Carvalho Chehab 			break;
30843ecec16SMauro Carvalho Chehab 		}
30943ecec16SMauro Carvalho Chehab 	}
31043ecec16SMauro Carvalho Chehab }
31143ecec16SMauro Carvalho Chehab 
s5p_mfc_handle_frame_new(struct s5p_mfc_ctx * ctx,unsigned int err)31243ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
31343ecec16SMauro Carvalho Chehab {
31443ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
31543ecec16SMauro Carvalho Chehab 	struct s5p_mfc_buf  *dst_buf;
31643ecec16SMauro Carvalho Chehab 	u32 dspl_y_addr;
31743ecec16SMauro Carvalho Chehab 	unsigned int frame_type;
31843ecec16SMauro Carvalho Chehab 
31943ecec16SMauro Carvalho Chehab 	dspl_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev);
32043ecec16SMauro Carvalho Chehab 	if (IS_MFCV6_PLUS(dev))
32143ecec16SMauro Carvalho Chehab 		frame_type = s5p_mfc_hw_call(dev->mfc_ops,
32243ecec16SMauro Carvalho Chehab 			get_disp_frame_type, ctx);
32343ecec16SMauro Carvalho Chehab 	else
32443ecec16SMauro Carvalho Chehab 		frame_type = s5p_mfc_hw_call(dev->mfc_ops,
32543ecec16SMauro Carvalho Chehab 			get_dec_frame_type, dev);
32643ecec16SMauro Carvalho Chehab 
32743ecec16SMauro Carvalho Chehab 	/* If frame is same as previous then skip and do not dequeue */
32843ecec16SMauro Carvalho Chehab 	if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
32943ecec16SMauro Carvalho Chehab 		if (!ctx->after_packed_pb)
33043ecec16SMauro Carvalho Chehab 			ctx->sequence++;
33143ecec16SMauro Carvalho Chehab 		ctx->after_packed_pb = 0;
33243ecec16SMauro Carvalho Chehab 		return;
33343ecec16SMauro Carvalho Chehab 	}
33443ecec16SMauro Carvalho Chehab 	ctx->sequence++;
335203ef345SAakarsh Jain 	/*
336203ef345SAakarsh Jain 	 * The MFC returns address of the buffer, now we have to
337203ef345SAakarsh Jain 	 * check which vb2_buffer does it correspond to
338203ef345SAakarsh Jain 	 */
33943ecec16SMauro Carvalho Chehab 	list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
34043ecec16SMauro Carvalho Chehab 		u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0);
34143ecec16SMauro Carvalho Chehab 
34243ecec16SMauro Carvalho Chehab 		/* Check if this is the buffer we're looking for */
34343ecec16SMauro Carvalho Chehab 		if (addr == dspl_y_addr) {
34443ecec16SMauro Carvalho Chehab 			list_del(&dst_buf->list);
34543ecec16SMauro Carvalho Chehab 			ctx->dst_queue_cnt--;
34643ecec16SMauro Carvalho Chehab 			dst_buf->b->sequence = ctx->sequence;
34743ecec16SMauro Carvalho Chehab 			if (s5p_mfc_hw_call(dev->mfc_ops,
34843ecec16SMauro Carvalho Chehab 					get_pic_type_top, ctx) ==
34943ecec16SMauro Carvalho Chehab 				s5p_mfc_hw_call(dev->mfc_ops,
35043ecec16SMauro Carvalho Chehab 					get_pic_type_bot, ctx))
35143ecec16SMauro Carvalho Chehab 				dst_buf->b->field = V4L2_FIELD_NONE;
35243ecec16SMauro Carvalho Chehab 			else
35343ecec16SMauro Carvalho Chehab 				dst_buf->b->field =
35443ecec16SMauro Carvalho Chehab 							V4L2_FIELD_INTERLACED;
35543ecec16SMauro Carvalho Chehab 			vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0,
35643ecec16SMauro Carvalho Chehab 						ctx->luma_size);
35743ecec16SMauro Carvalho Chehab 			vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1,
35843ecec16SMauro Carvalho Chehab 						ctx->chroma_size);
35943ecec16SMauro Carvalho Chehab 			clear_bit(dst_buf->b->vb2_buf.index,
36043ecec16SMauro Carvalho Chehab 							&ctx->dec_dst_flag);
36143ecec16SMauro Carvalho Chehab 
36243ecec16SMauro Carvalho Chehab 			vb2_buffer_done(&dst_buf->b->vb2_buf, err ?
36343ecec16SMauro Carvalho Chehab 				VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
36443ecec16SMauro Carvalho Chehab 
36543ecec16SMauro Carvalho Chehab 			break;
36643ecec16SMauro Carvalho Chehab 		}
36743ecec16SMauro Carvalho Chehab 	}
36843ecec16SMauro Carvalho Chehab }
36943ecec16SMauro Carvalho Chehab 
37043ecec16SMauro Carvalho Chehab /* Handle frame decoding interrupt */
s5p_mfc_handle_frame(struct s5p_mfc_ctx * ctx,unsigned int reason,unsigned int err)37143ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
37243ecec16SMauro Carvalho Chehab 					unsigned int reason, unsigned int err)
37343ecec16SMauro Carvalho Chehab {
37443ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
37543ecec16SMauro Carvalho Chehab 	unsigned int dst_frame_status;
37643ecec16SMauro Carvalho Chehab 	unsigned int dec_frame_status;
37743ecec16SMauro Carvalho Chehab 	struct s5p_mfc_buf *src_buf;
37843ecec16SMauro Carvalho Chehab 	unsigned int res_change;
37943ecec16SMauro Carvalho Chehab 
38043ecec16SMauro Carvalho Chehab 	dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
38143ecec16SMauro Carvalho Chehab 				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
38243ecec16SMauro Carvalho Chehab 	dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev)
38343ecec16SMauro Carvalho Chehab 				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
38443ecec16SMauro Carvalho Chehab 	res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
38543ecec16SMauro Carvalho Chehab 				& S5P_FIMV_DEC_STATUS_RESOLUTION_MASK)
38643ecec16SMauro Carvalho Chehab 				>> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT;
38743ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
38843ecec16SMauro Carvalho Chehab 	if (ctx->state == MFCINST_RES_CHANGE_INIT)
38943ecec16SMauro Carvalho Chehab 		ctx->state = MFCINST_RES_CHANGE_FLUSH;
39043ecec16SMauro Carvalho Chehab 	if (res_change == S5P_FIMV_RES_INCREASE ||
39143ecec16SMauro Carvalho Chehab 		res_change == S5P_FIMV_RES_DECREASE) {
39243ecec16SMauro Carvalho Chehab 		ctx->state = MFCINST_RES_CHANGE_INIT;
39343ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
39443ecec16SMauro Carvalho Chehab 		wake_up_ctx(ctx, reason, err);
39543ecec16SMauro Carvalho Chehab 		WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
39643ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
39743ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
39843ecec16SMauro Carvalho Chehab 		return;
39943ecec16SMauro Carvalho Chehab 	}
40043ecec16SMauro Carvalho Chehab 	if (ctx->dpb_flush_flag)
40143ecec16SMauro Carvalho Chehab 		ctx->dpb_flush_flag = 0;
40243ecec16SMauro Carvalho Chehab 
40343ecec16SMauro Carvalho Chehab 	/* All frames remaining in the buffer have been extracted  */
40443ecec16SMauro Carvalho Chehab 	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
40543ecec16SMauro Carvalho Chehab 		if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
40643ecec16SMauro Carvalho Chehab 			static const struct v4l2_event ev_src_ch = {
40743ecec16SMauro Carvalho Chehab 				.type = V4L2_EVENT_SOURCE_CHANGE,
40843ecec16SMauro Carvalho Chehab 				.u.src_change.changes =
40943ecec16SMauro Carvalho Chehab 					V4L2_EVENT_SRC_CH_RESOLUTION,
41043ecec16SMauro Carvalho Chehab 			};
41143ecec16SMauro Carvalho Chehab 
41243ecec16SMauro Carvalho Chehab 			s5p_mfc_handle_frame_all_extracted(ctx);
41343ecec16SMauro Carvalho Chehab 			ctx->state = MFCINST_RES_CHANGE_END;
41443ecec16SMauro Carvalho Chehab 			v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
41543ecec16SMauro Carvalho Chehab 
41643ecec16SMauro Carvalho Chehab 			goto leave_handle_frame;
41743ecec16SMauro Carvalho Chehab 		} else {
41843ecec16SMauro Carvalho Chehab 			s5p_mfc_handle_frame_all_extracted(ctx);
41943ecec16SMauro Carvalho Chehab 		}
42043ecec16SMauro Carvalho Chehab 	}
42143ecec16SMauro Carvalho Chehab 
42243ecec16SMauro Carvalho Chehab 	if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)
42343ecec16SMauro Carvalho Chehab 		s5p_mfc_handle_frame_copy_time(ctx);
42443ecec16SMauro Carvalho Chehab 
42543ecec16SMauro Carvalho Chehab 	/* A frame has been decoded and is in the buffer  */
42643ecec16SMauro Carvalho Chehab 	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
42743ecec16SMauro Carvalho Chehab 	    dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
42843ecec16SMauro Carvalho Chehab 		s5p_mfc_handle_frame_new(ctx, err);
42943ecec16SMauro Carvalho Chehab 	} else {
43043ecec16SMauro Carvalho Chehab 		mfc_debug(2, "No frame decode\n");
43143ecec16SMauro Carvalho Chehab 	}
43243ecec16SMauro Carvalho Chehab 	/* Mark source buffer as complete */
43343ecec16SMauro Carvalho Chehab 	if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
43443ecec16SMauro Carvalho Chehab 		&& !list_empty(&ctx->src_queue)) {
43543ecec16SMauro Carvalho Chehab 		src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
43643ecec16SMauro Carvalho Chehab 								list);
43743ecec16SMauro Carvalho Chehab 		ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops,
43843ecec16SMauro Carvalho Chehab 						get_consumed_stream, dev);
43943ecec16SMauro Carvalho Chehab 		if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC &&
44043ecec16SMauro Carvalho Chehab 			ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC &&
44143ecec16SMauro Carvalho Chehab 			ctx->consumed_stream + STUFF_BYTE <
44243ecec16SMauro Carvalho Chehab 			src_buf->b->vb2_buf.planes[0].bytesused) {
44343ecec16SMauro Carvalho Chehab 			/* Run MFC again on the same buffer */
44443ecec16SMauro Carvalho Chehab 			mfc_debug(2, "Running again the same buffer\n");
44543ecec16SMauro Carvalho Chehab 			ctx->after_packed_pb = 1;
44643ecec16SMauro Carvalho Chehab 		} else {
44743ecec16SMauro Carvalho Chehab 			mfc_debug(2, "MFC needs next buffer\n");
44843ecec16SMauro Carvalho Chehab 			ctx->consumed_stream = 0;
44943ecec16SMauro Carvalho Chehab 			if (src_buf->flags & MFC_BUF_FLAG_EOS)
45043ecec16SMauro Carvalho Chehab 				ctx->state = MFCINST_FINISHING;
45143ecec16SMauro Carvalho Chehab 			list_del(&src_buf->list);
45243ecec16SMauro Carvalho Chehab 			ctx->src_queue_cnt--;
45343ecec16SMauro Carvalho Chehab 			if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0)
45443ecec16SMauro Carvalho Chehab 				vb2_buffer_done(&src_buf->b->vb2_buf,
45543ecec16SMauro Carvalho Chehab 						VB2_BUF_STATE_ERROR);
45643ecec16SMauro Carvalho Chehab 			else
45743ecec16SMauro Carvalho Chehab 				vb2_buffer_done(&src_buf->b->vb2_buf,
45843ecec16SMauro Carvalho Chehab 						VB2_BUF_STATE_DONE);
45943ecec16SMauro Carvalho Chehab 		}
46043ecec16SMauro Carvalho Chehab 	}
46143ecec16SMauro Carvalho Chehab leave_handle_frame:
46243ecec16SMauro Carvalho Chehab 	if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
46343ecec16SMauro Carvalho Chehab 				    || ctx->dst_queue_cnt < ctx->pb_count)
46443ecec16SMauro Carvalho Chehab 		clear_work_bit(ctx);
46543ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
46643ecec16SMauro Carvalho Chehab 	wake_up_ctx(ctx, reason, err);
46743ecec16SMauro Carvalho Chehab 	WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
46843ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
46943ecec16SMauro Carvalho Chehab 	/* if suspending, wake up device and do not try_run again*/
47043ecec16SMauro Carvalho Chehab 	if (test_bit(0, &dev->enter_suspend))
47143ecec16SMauro Carvalho Chehab 		wake_up_dev(dev, reason, err);
47243ecec16SMauro Carvalho Chehab 	else
47343ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
47443ecec16SMauro Carvalho Chehab }
47543ecec16SMauro Carvalho Chehab 
47643ecec16SMauro Carvalho Chehab /* Error handling for interrupt */
s5p_mfc_handle_error(struct s5p_mfc_dev * dev,struct s5p_mfc_ctx * ctx,unsigned int reason,unsigned int err)47743ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
47843ecec16SMauro Carvalho Chehab 		struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err)
47943ecec16SMauro Carvalho Chehab {
48043ecec16SMauro Carvalho Chehab 	mfc_err("Interrupt Error: %08x\n", err);
48143ecec16SMauro Carvalho Chehab 
48243ecec16SMauro Carvalho Chehab 	if (ctx) {
48343ecec16SMauro Carvalho Chehab 		/* Error recovery is dependent on the state of context */
48443ecec16SMauro Carvalho Chehab 		switch (ctx->state) {
48543ecec16SMauro Carvalho Chehab 		case MFCINST_RES_CHANGE_INIT:
48643ecec16SMauro Carvalho Chehab 		case MFCINST_RES_CHANGE_FLUSH:
48743ecec16SMauro Carvalho Chehab 		case MFCINST_RES_CHANGE_END:
48843ecec16SMauro Carvalho Chehab 		case MFCINST_FINISHING:
48943ecec16SMauro Carvalho Chehab 		case MFCINST_FINISHED:
49043ecec16SMauro Carvalho Chehab 		case MFCINST_RUNNING:
491203ef345SAakarsh Jain 			/*
492203ef345SAakarsh Jain 			 * It is highly probable that an error occurred
493203ef345SAakarsh Jain 			 * while decoding a frame
494203ef345SAakarsh Jain 			 */
49543ecec16SMauro Carvalho Chehab 			clear_work_bit(ctx);
49643ecec16SMauro Carvalho Chehab 			ctx->state = MFCINST_ERROR;
49743ecec16SMauro Carvalho Chehab 			/* Mark all dst buffers as having an error */
49843ecec16SMauro Carvalho Chehab 			s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
49943ecec16SMauro Carvalho Chehab 			/* Mark all src buffers as having an error */
50043ecec16SMauro Carvalho Chehab 			s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
50143ecec16SMauro Carvalho Chehab 			wake_up_ctx(ctx, reason, err);
50243ecec16SMauro Carvalho Chehab 			break;
50343ecec16SMauro Carvalho Chehab 		default:
50443ecec16SMauro Carvalho Chehab 			clear_work_bit(ctx);
50543ecec16SMauro Carvalho Chehab 			ctx->state = MFCINST_ERROR;
50643ecec16SMauro Carvalho Chehab 			wake_up_ctx(ctx, reason, err);
50743ecec16SMauro Carvalho Chehab 			break;
50843ecec16SMauro Carvalho Chehab 		}
50943ecec16SMauro Carvalho Chehab 	}
51043ecec16SMauro Carvalho Chehab 	WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
51143ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
51243ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
51343ecec16SMauro Carvalho Chehab 	wake_up_dev(dev, reason, err);
51443ecec16SMauro Carvalho Chehab }
51543ecec16SMauro Carvalho Chehab 
51643ecec16SMauro Carvalho Chehab /* Header parsing interrupt handling */
s5p_mfc_handle_seq_done(struct s5p_mfc_ctx * ctx,unsigned int reason,unsigned int err)51743ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
51843ecec16SMauro Carvalho Chehab 				 unsigned int reason, unsigned int err)
51943ecec16SMauro Carvalho Chehab {
52043ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev;
52143ecec16SMauro Carvalho Chehab 
52243ecec16SMauro Carvalho Chehab 	if (!ctx)
52343ecec16SMauro Carvalho Chehab 		return;
52443ecec16SMauro Carvalho Chehab 	dev = ctx->dev;
52543ecec16SMauro Carvalho Chehab 	if (ctx->c_ops->post_seq_start) {
52643ecec16SMauro Carvalho Chehab 		if (ctx->c_ops->post_seq_start(ctx))
52743ecec16SMauro Carvalho Chehab 			mfc_err("post_seq_start() failed\n");
52843ecec16SMauro Carvalho Chehab 	} else {
52943ecec16SMauro Carvalho Chehab 		ctx->img_width = s5p_mfc_hw_call(dev->mfc_ops, get_img_width,
53043ecec16SMauro Carvalho Chehab 				dev);
53143ecec16SMauro Carvalho Chehab 		ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height,
53243ecec16SMauro Carvalho Chehab 				dev);
53343ecec16SMauro Carvalho Chehab 
53443ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
53543ecec16SMauro Carvalho Chehab 
53643ecec16SMauro Carvalho Chehab 		ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
53743ecec16SMauro Carvalho Chehab 				dev);
53843ecec16SMauro Carvalho Chehab 		ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count,
53943ecec16SMauro Carvalho Chehab 				dev);
54043ecec16SMauro Carvalho Chehab 		if (FW_HAS_E_MIN_SCRATCH_BUF(dev))
54143ecec16SMauro Carvalho Chehab 			ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops,
54243ecec16SMauro Carvalho Chehab 						get_min_scratch_buf_size, dev);
54343ecec16SMauro Carvalho Chehab 		if (ctx->img_width == 0 || ctx->img_height == 0)
54443ecec16SMauro Carvalho Chehab 			ctx->state = MFCINST_ERROR;
54543ecec16SMauro Carvalho Chehab 		else
54643ecec16SMauro Carvalho Chehab 			ctx->state = MFCINST_HEAD_PARSED;
54743ecec16SMauro Carvalho Chehab 
54843ecec16SMauro Carvalho Chehab 		if ((ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
54943ecec16SMauro Carvalho Chehab 			ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) &&
55043ecec16SMauro Carvalho Chehab 				!list_empty(&ctx->src_queue)) {
55143ecec16SMauro Carvalho Chehab 			struct s5p_mfc_buf *src_buf;
552203ef345SAakarsh Jain 
55343ecec16SMauro Carvalho Chehab 			src_buf = list_entry(ctx->src_queue.next,
55443ecec16SMauro Carvalho Chehab 					struct s5p_mfc_buf, list);
55543ecec16SMauro Carvalho Chehab 			if (s5p_mfc_hw_call(dev->mfc_ops, get_consumed_stream,
55643ecec16SMauro Carvalho Chehab 						dev) <
55743ecec16SMauro Carvalho Chehab 					src_buf->b->vb2_buf.planes[0].bytesused)
55843ecec16SMauro Carvalho Chehab 				ctx->head_processed = 0;
55943ecec16SMauro Carvalho Chehab 			else
56043ecec16SMauro Carvalho Chehab 				ctx->head_processed = 1;
56143ecec16SMauro Carvalho Chehab 		} else {
56243ecec16SMauro Carvalho Chehab 			ctx->head_processed = 1;
56343ecec16SMauro Carvalho Chehab 		}
56443ecec16SMauro Carvalho Chehab 	}
56543ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
56643ecec16SMauro Carvalho Chehab 	clear_work_bit(ctx);
56743ecec16SMauro Carvalho Chehab 	WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
56843ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
56943ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
57043ecec16SMauro Carvalho Chehab 	wake_up_ctx(ctx, reason, err);
57143ecec16SMauro Carvalho Chehab }
57243ecec16SMauro Carvalho Chehab 
57343ecec16SMauro Carvalho Chehab /* Header parsing interrupt handling */
s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx * ctx,unsigned int reason,unsigned int err)57443ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
57543ecec16SMauro Carvalho Chehab 				 unsigned int reason, unsigned int err)
57643ecec16SMauro Carvalho Chehab {
57743ecec16SMauro Carvalho Chehab 	struct s5p_mfc_buf *src_buf;
57843ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev;
57943ecec16SMauro Carvalho Chehab 
58043ecec16SMauro Carvalho Chehab 	if (!ctx)
58143ecec16SMauro Carvalho Chehab 		return;
58243ecec16SMauro Carvalho Chehab 	dev = ctx->dev;
58343ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
58443ecec16SMauro Carvalho Chehab 	ctx->int_type = reason;
58543ecec16SMauro Carvalho Chehab 	ctx->int_err = err;
58643ecec16SMauro Carvalho Chehab 	ctx->int_cond = 1;
58743ecec16SMauro Carvalho Chehab 	clear_work_bit(ctx);
58843ecec16SMauro Carvalho Chehab 	if (err == 0) {
58943ecec16SMauro Carvalho Chehab 		ctx->state = MFCINST_RUNNING;
59043ecec16SMauro Carvalho Chehab 		if (!ctx->dpb_flush_flag && ctx->head_processed) {
59143ecec16SMauro Carvalho Chehab 			if (!list_empty(&ctx->src_queue)) {
59243ecec16SMauro Carvalho Chehab 				src_buf = list_entry(ctx->src_queue.next,
59343ecec16SMauro Carvalho Chehab 					     struct s5p_mfc_buf, list);
59443ecec16SMauro Carvalho Chehab 				list_del(&src_buf->list);
59543ecec16SMauro Carvalho Chehab 				ctx->src_queue_cnt--;
59643ecec16SMauro Carvalho Chehab 				vb2_buffer_done(&src_buf->b->vb2_buf,
59743ecec16SMauro Carvalho Chehab 						VB2_BUF_STATE_DONE);
59843ecec16SMauro Carvalho Chehab 			}
59943ecec16SMauro Carvalho Chehab 		} else {
60043ecec16SMauro Carvalho Chehab 			ctx->dpb_flush_flag = 0;
60143ecec16SMauro Carvalho Chehab 		}
60243ecec16SMauro Carvalho Chehab 		WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
60343ecec16SMauro Carvalho Chehab 
60443ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
60543ecec16SMauro Carvalho Chehab 
60643ecec16SMauro Carvalho Chehab 		wake_up(&ctx->queue);
60743ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
60843ecec16SMauro Carvalho Chehab 	} else {
60943ecec16SMauro Carvalho Chehab 		WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
61043ecec16SMauro Carvalho Chehab 
61143ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
61243ecec16SMauro Carvalho Chehab 
61343ecec16SMauro Carvalho Chehab 		wake_up(&ctx->queue);
61443ecec16SMauro Carvalho Chehab 	}
61543ecec16SMauro Carvalho Chehab }
61643ecec16SMauro Carvalho Chehab 
s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx * ctx)61743ecec16SMauro Carvalho Chehab static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx)
61843ecec16SMauro Carvalho Chehab {
61943ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
62043ecec16SMauro Carvalho Chehab 	struct s5p_mfc_buf *mb_entry;
62143ecec16SMauro Carvalho Chehab 
62243ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Stream completed\n");
62343ecec16SMauro Carvalho Chehab 
62443ecec16SMauro Carvalho Chehab 	ctx->state = MFCINST_FINISHED;
62543ecec16SMauro Carvalho Chehab 
62643ecec16SMauro Carvalho Chehab 	if (!list_empty(&ctx->dst_queue)) {
62743ecec16SMauro Carvalho Chehab 		mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
62843ecec16SMauro Carvalho Chehab 									list);
62943ecec16SMauro Carvalho Chehab 		list_del(&mb_entry->list);
63043ecec16SMauro Carvalho Chehab 		ctx->dst_queue_cnt--;
63143ecec16SMauro Carvalho Chehab 		vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, 0);
63243ecec16SMauro Carvalho Chehab 		vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE);
63343ecec16SMauro Carvalho Chehab 	}
63443ecec16SMauro Carvalho Chehab 
63543ecec16SMauro Carvalho Chehab 	clear_work_bit(ctx);
63643ecec16SMauro Carvalho Chehab 
63743ecec16SMauro Carvalho Chehab 	WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
63843ecec16SMauro Carvalho Chehab 
63943ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
64043ecec16SMauro Carvalho Chehab 	wake_up(&ctx->queue);
64143ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
64243ecec16SMauro Carvalho Chehab }
64343ecec16SMauro Carvalho Chehab 
64443ecec16SMauro Carvalho Chehab /* Interrupt processing */
s5p_mfc_irq(int irq,void * priv)64543ecec16SMauro Carvalho Chehab static irqreturn_t s5p_mfc_irq(int irq, void *priv)
64643ecec16SMauro Carvalho Chehab {
64743ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = priv;
64843ecec16SMauro Carvalho Chehab 	struct s5p_mfc_ctx *ctx;
64943ecec16SMauro Carvalho Chehab 	unsigned int reason;
65043ecec16SMauro Carvalho Chehab 	unsigned int err;
65143ecec16SMauro Carvalho Chehab 
65243ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
65343ecec16SMauro Carvalho Chehab 	/* Reset the timeout watchdog */
65443ecec16SMauro Carvalho Chehab 	atomic_set(&dev->watchdog_cnt, 0);
65543ecec16SMauro Carvalho Chehab 	spin_lock(&dev->irqlock);
65643ecec16SMauro Carvalho Chehab 	ctx = dev->ctx[dev->curr_ctx];
65743ecec16SMauro Carvalho Chehab 	/* Get the reason of interrupt and the error code */
65843ecec16SMauro Carvalho Chehab 	reason = s5p_mfc_hw_call(dev->mfc_ops, get_int_reason, dev);
65943ecec16SMauro Carvalho Chehab 	err = s5p_mfc_hw_call(dev->mfc_ops, get_int_err, dev);
66043ecec16SMauro Carvalho Chehab 	mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
66143ecec16SMauro Carvalho Chehab 	switch (reason) {
66243ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_ERR_RET:
66343ecec16SMauro Carvalho Chehab 		/* An error has occurred */
66443ecec16SMauro Carvalho Chehab 		if (ctx->state == MFCINST_RUNNING &&
66543ecec16SMauro Carvalho Chehab 			(s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >=
66643ecec16SMauro Carvalho Chehab 				dev->warn_start ||
66743ecec16SMauro Carvalho Chehab 				err == S5P_FIMV_ERR_NO_VALID_SEQ_HDR ||
66843ecec16SMauro Carvalho Chehab 				err == S5P_FIMV_ERR_INCOMPLETE_FRAME ||
66943ecec16SMauro Carvalho Chehab 				err == S5P_FIMV_ERR_TIMEOUT))
67043ecec16SMauro Carvalho Chehab 			s5p_mfc_handle_frame(ctx, reason, err);
67143ecec16SMauro Carvalho Chehab 		else
67243ecec16SMauro Carvalho Chehab 			s5p_mfc_handle_error(dev, ctx, reason, err);
67343ecec16SMauro Carvalho Chehab 		clear_bit(0, &dev->enter_suspend);
67443ecec16SMauro Carvalho Chehab 		break;
67543ecec16SMauro Carvalho Chehab 
67643ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_SLICE_DONE_RET:
67743ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_FIELD_DONE_RET:
67843ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_FRAME_DONE_RET:
67943ecec16SMauro Carvalho Chehab 		if (ctx->c_ops->post_frame_start) {
68043ecec16SMauro Carvalho Chehab 			if (ctx->c_ops->post_frame_start(ctx))
68143ecec16SMauro Carvalho Chehab 				mfc_err("post_frame_start() failed\n");
68243ecec16SMauro Carvalho Chehab 
68343ecec16SMauro Carvalho Chehab 			if (ctx->state == MFCINST_FINISHING &&
68443ecec16SMauro Carvalho Chehab 						list_empty(&ctx->ref_queue)) {
68543ecec16SMauro Carvalho Chehab 				s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
68643ecec16SMauro Carvalho Chehab 				s5p_mfc_handle_stream_complete(ctx);
68743ecec16SMauro Carvalho Chehab 				break;
68843ecec16SMauro Carvalho Chehab 			}
68943ecec16SMauro Carvalho Chehab 			s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
69043ecec16SMauro Carvalho Chehab 			WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
69143ecec16SMauro Carvalho Chehab 			s5p_mfc_clock_off();
69243ecec16SMauro Carvalho Chehab 			wake_up_ctx(ctx, reason, err);
69343ecec16SMauro Carvalho Chehab 			s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
69443ecec16SMauro Carvalho Chehab 		} else {
69543ecec16SMauro Carvalho Chehab 			s5p_mfc_handle_frame(ctx, reason, err);
69643ecec16SMauro Carvalho Chehab 		}
69743ecec16SMauro Carvalho Chehab 		break;
69843ecec16SMauro Carvalho Chehab 
69943ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_SEQ_DONE_RET:
70043ecec16SMauro Carvalho Chehab 		s5p_mfc_handle_seq_done(ctx, reason, err);
70143ecec16SMauro Carvalho Chehab 		break;
70243ecec16SMauro Carvalho Chehab 
70343ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET:
70443ecec16SMauro Carvalho Chehab 		ctx->inst_no = s5p_mfc_hw_call(dev->mfc_ops, get_inst_no, dev);
70543ecec16SMauro Carvalho Chehab 		ctx->state = MFCINST_GOT_INST;
70643ecec16SMauro Carvalho Chehab 		goto irq_cleanup_hw;
70743ecec16SMauro Carvalho Chehab 
70843ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET:
70943ecec16SMauro Carvalho Chehab 		ctx->inst_no = MFC_NO_INSTANCE_SET;
71043ecec16SMauro Carvalho Chehab 		ctx->state = MFCINST_FREE;
71143ecec16SMauro Carvalho Chehab 		goto irq_cleanup_hw;
71243ecec16SMauro Carvalho Chehab 
71343ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_SYS_INIT_RET:
71443ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_FW_STATUS_RET:
71543ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_SLEEP_RET:
71643ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_WAKEUP_RET:
71743ecec16SMauro Carvalho Chehab 		if (ctx)
71843ecec16SMauro Carvalho Chehab 			clear_work_bit(ctx);
71943ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
72043ecec16SMauro Carvalho Chehab 		clear_bit(0, &dev->hw_lock);
72143ecec16SMauro Carvalho Chehab 		clear_bit(0, &dev->enter_suspend);
72243ecec16SMauro Carvalho Chehab 		wake_up_dev(dev, reason, err);
72343ecec16SMauro Carvalho Chehab 		break;
72443ecec16SMauro Carvalho Chehab 
72543ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_INIT_BUFFERS_RET:
72643ecec16SMauro Carvalho Chehab 		s5p_mfc_handle_init_buffers(ctx, reason, err);
72743ecec16SMauro Carvalho Chehab 		break;
72843ecec16SMauro Carvalho Chehab 
72943ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_COMPLETE_SEQ_RET:
73043ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
73143ecec16SMauro Carvalho Chehab 		ctx->int_type = reason;
73243ecec16SMauro Carvalho Chehab 		ctx->int_err = err;
73343ecec16SMauro Carvalho Chehab 		s5p_mfc_handle_stream_complete(ctx);
73443ecec16SMauro Carvalho Chehab 		break;
73543ecec16SMauro Carvalho Chehab 
73643ecec16SMauro Carvalho Chehab 	case S5P_MFC_R2H_CMD_DPB_FLUSH_RET:
73743ecec16SMauro Carvalho Chehab 		ctx->state = MFCINST_RUNNING;
73843ecec16SMauro Carvalho Chehab 		goto irq_cleanup_hw;
73943ecec16SMauro Carvalho Chehab 
74043ecec16SMauro Carvalho Chehab 	default:
74143ecec16SMauro Carvalho Chehab 		mfc_debug(2, "Unknown int reason\n");
74243ecec16SMauro Carvalho Chehab 		s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
74343ecec16SMauro Carvalho Chehab 	}
74443ecec16SMauro Carvalho Chehab 	spin_unlock(&dev->irqlock);
74543ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
74643ecec16SMauro Carvalho Chehab 	return IRQ_HANDLED;
74743ecec16SMauro Carvalho Chehab irq_cleanup_hw:
74843ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
74943ecec16SMauro Carvalho Chehab 	ctx->int_type = reason;
75043ecec16SMauro Carvalho Chehab 	ctx->int_err = err;
75143ecec16SMauro Carvalho Chehab 	ctx->int_cond = 1;
75243ecec16SMauro Carvalho Chehab 	if (test_and_clear_bit(0, &dev->hw_lock) == 0)
75343ecec16SMauro Carvalho Chehab 		mfc_err("Failed to unlock hw\n");
75443ecec16SMauro Carvalho Chehab 
75543ecec16SMauro Carvalho Chehab 	s5p_mfc_clock_off();
75643ecec16SMauro Carvalho Chehab 	clear_work_bit(ctx);
75743ecec16SMauro Carvalho Chehab 	wake_up(&ctx->queue);
75843ecec16SMauro Carvalho Chehab 
75943ecec16SMauro Carvalho Chehab 	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
76043ecec16SMauro Carvalho Chehab 	spin_unlock(&dev->irqlock);
76143ecec16SMauro Carvalho Chehab 	mfc_debug(2, "Exit via irq_cleanup_hw\n");
76243ecec16SMauro Carvalho Chehab 	return IRQ_HANDLED;
76343ecec16SMauro Carvalho Chehab }
76443ecec16SMauro Carvalho Chehab 
76543ecec16SMauro Carvalho Chehab /* Open an MFC node */
s5p_mfc_open(struct file * file)76643ecec16SMauro Carvalho Chehab static int s5p_mfc_open(struct file *file)
76743ecec16SMauro Carvalho Chehab {
76843ecec16SMauro Carvalho Chehab 	struct video_device *vdev = video_devdata(file);
76943ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = video_drvdata(file);
77043ecec16SMauro Carvalho Chehab 	struct s5p_mfc_ctx *ctx = NULL;
77143ecec16SMauro Carvalho Chehab 	struct vb2_queue *q;
77243ecec16SMauro Carvalho Chehab 	int ret = 0;
77343ecec16SMauro Carvalho Chehab 
77443ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
77543ecec16SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&dev->mfc_mutex))
77643ecec16SMauro Carvalho Chehab 		return -ERESTARTSYS;
77743ecec16SMauro Carvalho Chehab 	dev->num_inst++;	/* It is guarded by mfc_mutex in vfd */
77843ecec16SMauro Carvalho Chehab 	/* Allocate memory for context */
77943ecec16SMauro Carvalho Chehab 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
78043ecec16SMauro Carvalho Chehab 	if (!ctx) {
78143ecec16SMauro Carvalho Chehab 		ret = -ENOMEM;
78243ecec16SMauro Carvalho Chehab 		goto err_alloc;
78343ecec16SMauro Carvalho Chehab 	}
78443ecec16SMauro Carvalho Chehab 	init_waitqueue_head(&ctx->queue);
78543ecec16SMauro Carvalho Chehab 	v4l2_fh_init(&ctx->fh, vdev);
78643ecec16SMauro Carvalho Chehab 	file->private_data = &ctx->fh;
78743ecec16SMauro Carvalho Chehab 	v4l2_fh_add(&ctx->fh);
78843ecec16SMauro Carvalho Chehab 	ctx->dev = dev;
78943ecec16SMauro Carvalho Chehab 	INIT_LIST_HEAD(&ctx->src_queue);
79043ecec16SMauro Carvalho Chehab 	INIT_LIST_HEAD(&ctx->dst_queue);
79143ecec16SMauro Carvalho Chehab 	ctx->src_queue_cnt = 0;
79243ecec16SMauro Carvalho Chehab 	ctx->dst_queue_cnt = 0;
79343ecec16SMauro Carvalho Chehab 	/* Get context number */
79443ecec16SMauro Carvalho Chehab 	ctx->num = 0;
79543ecec16SMauro Carvalho Chehab 	while (dev->ctx[ctx->num]) {
79643ecec16SMauro Carvalho Chehab 		ctx->num++;
79743ecec16SMauro Carvalho Chehab 		if (ctx->num >= MFC_NUM_CONTEXTS) {
79843ecec16SMauro Carvalho Chehab 			mfc_debug(2, "Too many open contexts\n");
79943ecec16SMauro Carvalho Chehab 			ret = -EBUSY;
80043ecec16SMauro Carvalho Chehab 			goto err_no_ctx;
80143ecec16SMauro Carvalho Chehab 		}
80243ecec16SMauro Carvalho Chehab 	}
80343ecec16SMauro Carvalho Chehab 	/* Mark context as idle */
80443ecec16SMauro Carvalho Chehab 	clear_work_bit_irqsave(ctx);
80543ecec16SMauro Carvalho Chehab 	dev->ctx[ctx->num] = ctx;
80643ecec16SMauro Carvalho Chehab 	if (vdev == dev->vfd_dec) {
80743ecec16SMauro Carvalho Chehab 		ctx->type = MFCINST_DECODER;
80843ecec16SMauro Carvalho Chehab 		ctx->c_ops = get_dec_codec_ops();
80943ecec16SMauro Carvalho Chehab 		s5p_mfc_dec_init(ctx);
81043ecec16SMauro Carvalho Chehab 		/* Setup ctrl handler */
81143ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_dec_ctrls_setup(ctx);
81243ecec16SMauro Carvalho Chehab 		if (ret) {
81343ecec16SMauro Carvalho Chehab 			mfc_err("Failed to setup mfc controls\n");
81443ecec16SMauro Carvalho Chehab 			goto err_ctrls_setup;
81543ecec16SMauro Carvalho Chehab 		}
81643ecec16SMauro Carvalho Chehab 	} else if (vdev == dev->vfd_enc) {
81743ecec16SMauro Carvalho Chehab 		ctx->type = MFCINST_ENCODER;
81843ecec16SMauro Carvalho Chehab 		ctx->c_ops = get_enc_codec_ops();
81943ecec16SMauro Carvalho Chehab 		/* only for encoder */
82043ecec16SMauro Carvalho Chehab 		INIT_LIST_HEAD(&ctx->ref_queue);
82143ecec16SMauro Carvalho Chehab 		ctx->ref_queue_cnt = 0;
82243ecec16SMauro Carvalho Chehab 		s5p_mfc_enc_init(ctx);
82343ecec16SMauro Carvalho Chehab 		/* Setup ctrl handler */
82443ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_enc_ctrls_setup(ctx);
82543ecec16SMauro Carvalho Chehab 		if (ret) {
82643ecec16SMauro Carvalho Chehab 			mfc_err("Failed to setup mfc controls\n");
82743ecec16SMauro Carvalho Chehab 			goto err_ctrls_setup;
82843ecec16SMauro Carvalho Chehab 		}
82943ecec16SMauro Carvalho Chehab 	} else {
83043ecec16SMauro Carvalho Chehab 		ret = -ENOENT;
83143ecec16SMauro Carvalho Chehab 		goto err_bad_node;
83243ecec16SMauro Carvalho Chehab 	}
83343ecec16SMauro Carvalho Chehab 	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
83443ecec16SMauro Carvalho Chehab 	ctx->inst_no = MFC_NO_INSTANCE_SET;
83543ecec16SMauro Carvalho Chehab 	/* Load firmware if this is the first instance */
83643ecec16SMauro Carvalho Chehab 	if (dev->num_inst == 1) {
83743ecec16SMauro Carvalho Chehab 		dev->watchdog_timer.expires = jiffies +
83843ecec16SMauro Carvalho Chehab 					msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
83943ecec16SMauro Carvalho Chehab 		add_timer(&dev->watchdog_timer);
84043ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_power_on();
84143ecec16SMauro Carvalho Chehab 		if (ret < 0) {
84243ecec16SMauro Carvalho Chehab 			mfc_err("power on failed\n");
84343ecec16SMauro Carvalho Chehab 			goto err_pwr_enable;
84443ecec16SMauro Carvalho Chehab 		}
84543ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_on();
84643ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_load_firmware(dev);
84743ecec16SMauro Carvalho Chehab 		if (ret) {
84843ecec16SMauro Carvalho Chehab 			s5p_mfc_clock_off();
84943ecec16SMauro Carvalho Chehab 			goto err_load_fw;
85043ecec16SMauro Carvalho Chehab 		}
85143ecec16SMauro Carvalho Chehab 		/* Init the FW */
85243ecec16SMauro Carvalho Chehab 		ret = s5p_mfc_init_hw(dev);
85343ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_off();
85443ecec16SMauro Carvalho Chehab 		if (ret)
85543ecec16SMauro Carvalho Chehab 			goto err_init_hw;
85643ecec16SMauro Carvalho Chehab 	}
85743ecec16SMauro Carvalho Chehab 	/* Init videobuf2 queue for CAPTURE */
85843ecec16SMauro Carvalho Chehab 	q = &ctx->vq_dst;
85943ecec16SMauro Carvalho Chehab 	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
86043ecec16SMauro Carvalho Chehab 	q->drv_priv = &ctx->fh;
86143ecec16SMauro Carvalho Chehab 	q->lock = &dev->mfc_mutex;
86243ecec16SMauro Carvalho Chehab 	if (vdev == dev->vfd_dec) {
86343ecec16SMauro Carvalho Chehab 		q->io_modes = VB2_MMAP;
86443ecec16SMauro Carvalho Chehab 		q->ops = get_dec_queue_ops();
86543ecec16SMauro Carvalho Chehab 	} else if (vdev == dev->vfd_enc) {
86643ecec16SMauro Carvalho Chehab 		q->io_modes = VB2_MMAP | VB2_USERPTR;
86743ecec16SMauro Carvalho Chehab 		q->ops = get_enc_queue_ops();
86843ecec16SMauro Carvalho Chehab 	} else {
86943ecec16SMauro Carvalho Chehab 		ret = -ENOENT;
87043ecec16SMauro Carvalho Chehab 		goto err_queue_init;
87143ecec16SMauro Carvalho Chehab 	}
87243ecec16SMauro Carvalho Chehab 	/*
87343ecec16SMauro Carvalho Chehab 	 * We'll do mostly sequential access, so sacrifice TLB efficiency for
87443ecec16SMauro Carvalho Chehab 	 * faster allocation.
87543ecec16SMauro Carvalho Chehab 	 */
87643ecec16SMauro Carvalho Chehab 	q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
87743ecec16SMauro Carvalho Chehab 	q->mem_ops = &vb2_dma_contig_memops;
87843ecec16SMauro Carvalho Chehab 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
87943ecec16SMauro Carvalho Chehab 	ret = vb2_queue_init(q);
88043ecec16SMauro Carvalho Chehab 	if (ret) {
88143ecec16SMauro Carvalho Chehab 		mfc_err("Failed to initialize videobuf2 queue(capture)\n");
88243ecec16SMauro Carvalho Chehab 		goto err_queue_init;
88343ecec16SMauro Carvalho Chehab 	}
88443ecec16SMauro Carvalho Chehab 	/* Init videobuf2 queue for OUTPUT */
88543ecec16SMauro Carvalho Chehab 	q = &ctx->vq_src;
88643ecec16SMauro Carvalho Chehab 	q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
88743ecec16SMauro Carvalho Chehab 	q->drv_priv = &ctx->fh;
88843ecec16SMauro Carvalho Chehab 	q->lock = &dev->mfc_mutex;
88943ecec16SMauro Carvalho Chehab 	if (vdev == dev->vfd_dec) {
89043ecec16SMauro Carvalho Chehab 		q->io_modes = VB2_MMAP;
89143ecec16SMauro Carvalho Chehab 		q->ops = get_dec_queue_ops();
89243ecec16SMauro Carvalho Chehab 	} else if (vdev == dev->vfd_enc) {
89343ecec16SMauro Carvalho Chehab 		q->io_modes = VB2_MMAP | VB2_USERPTR;
89443ecec16SMauro Carvalho Chehab 		q->ops = get_enc_queue_ops();
89543ecec16SMauro Carvalho Chehab 	} else {
89643ecec16SMauro Carvalho Chehab 		ret = -ENOENT;
89743ecec16SMauro Carvalho Chehab 		goto err_queue_init;
89843ecec16SMauro Carvalho Chehab 	}
89943ecec16SMauro Carvalho Chehab 	/* One way to indicate end-of-stream for MFC is to set the
90043ecec16SMauro Carvalho Chehab 	 * bytesused == 0. However by default videobuf2 handles bytesused
90143ecec16SMauro Carvalho Chehab 	 * equal to 0 as a special case and changes its value to the size
90243ecec16SMauro Carvalho Chehab 	 * of the buffer. Set the allow_zero_bytesused flag so that videobuf2
90343ecec16SMauro Carvalho Chehab 	 * will keep the value of bytesused intact.
90443ecec16SMauro Carvalho Chehab 	 */
90543ecec16SMauro Carvalho Chehab 	q->allow_zero_bytesused = 1;
90643ecec16SMauro Carvalho Chehab 
90743ecec16SMauro Carvalho Chehab 	/*
90843ecec16SMauro Carvalho Chehab 	 * We'll do mostly sequential access, so sacrifice TLB efficiency for
90943ecec16SMauro Carvalho Chehab 	 * faster allocation.
91043ecec16SMauro Carvalho Chehab 	 */
91143ecec16SMauro Carvalho Chehab 	q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
91243ecec16SMauro Carvalho Chehab 	q->mem_ops = &vb2_dma_contig_memops;
91343ecec16SMauro Carvalho Chehab 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
91443ecec16SMauro Carvalho Chehab 	ret = vb2_queue_init(q);
91543ecec16SMauro Carvalho Chehab 	if (ret) {
91643ecec16SMauro Carvalho Chehab 		mfc_err("Failed to initialize videobuf2 queue(output)\n");
91743ecec16SMauro Carvalho Chehab 		goto err_queue_init;
91843ecec16SMauro Carvalho Chehab 	}
91943ecec16SMauro Carvalho Chehab 	mutex_unlock(&dev->mfc_mutex);
92043ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
92143ecec16SMauro Carvalho Chehab 	return ret;
92243ecec16SMauro Carvalho Chehab 	/* Deinit when failure occurred */
92343ecec16SMauro Carvalho Chehab err_queue_init:
92443ecec16SMauro Carvalho Chehab 	if (dev->num_inst == 1)
92543ecec16SMauro Carvalho Chehab 		s5p_mfc_deinit_hw(dev);
92643ecec16SMauro Carvalho Chehab err_init_hw:
92743ecec16SMauro Carvalho Chehab err_load_fw:
92843ecec16SMauro Carvalho Chehab err_pwr_enable:
92943ecec16SMauro Carvalho Chehab 	if (dev->num_inst == 1) {
93043ecec16SMauro Carvalho Chehab 		if (s5p_mfc_power_off() < 0)
93143ecec16SMauro Carvalho Chehab 			mfc_err("power off failed\n");
93243ecec16SMauro Carvalho Chehab 		del_timer_sync(&dev->watchdog_timer);
93343ecec16SMauro Carvalho Chehab 	}
93443ecec16SMauro Carvalho Chehab err_ctrls_setup:
93543ecec16SMauro Carvalho Chehab 	s5p_mfc_dec_ctrls_delete(ctx);
93643ecec16SMauro Carvalho Chehab err_bad_node:
93743ecec16SMauro Carvalho Chehab 	dev->ctx[ctx->num] = NULL;
93843ecec16SMauro Carvalho Chehab err_no_ctx:
93943ecec16SMauro Carvalho Chehab 	v4l2_fh_del(&ctx->fh);
94043ecec16SMauro Carvalho Chehab 	v4l2_fh_exit(&ctx->fh);
94143ecec16SMauro Carvalho Chehab 	kfree(ctx);
94243ecec16SMauro Carvalho Chehab err_alloc:
94343ecec16SMauro Carvalho Chehab 	dev->num_inst--;
94443ecec16SMauro Carvalho Chehab 	mutex_unlock(&dev->mfc_mutex);
94543ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
94643ecec16SMauro Carvalho Chehab 	return ret;
94743ecec16SMauro Carvalho Chehab }
94843ecec16SMauro Carvalho Chehab 
94943ecec16SMauro Carvalho Chehab /* Release MFC context */
s5p_mfc_release(struct file * file)95043ecec16SMauro Carvalho Chehab static int s5p_mfc_release(struct file *file)
95143ecec16SMauro Carvalho Chehab {
95243ecec16SMauro Carvalho Chehab 	struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
95343ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
95443ecec16SMauro Carvalho Chehab 
95543ecec16SMauro Carvalho Chehab 	/* if dev is null, do cleanup that doesn't need dev */
95643ecec16SMauro Carvalho Chehab 	mfc_debug_enter();
95743ecec16SMauro Carvalho Chehab 	if (dev)
95843ecec16SMauro Carvalho Chehab 		mutex_lock(&dev->mfc_mutex);
95943ecec16SMauro Carvalho Chehab 	vb2_queue_release(&ctx->vq_src);
96043ecec16SMauro Carvalho Chehab 	vb2_queue_release(&ctx->vq_dst);
96143ecec16SMauro Carvalho Chehab 	if (dev) {
96243ecec16SMauro Carvalho Chehab 		s5p_mfc_clock_on();
96343ecec16SMauro Carvalho Chehab 
96443ecec16SMauro Carvalho Chehab 		/* Mark context as idle */
96543ecec16SMauro Carvalho Chehab 		clear_work_bit_irqsave(ctx);
96643ecec16SMauro Carvalho Chehab 		/*
96743ecec16SMauro Carvalho Chehab 		 * If instance was initialised and not yet freed,
96843ecec16SMauro Carvalho Chehab 		 * return instance and free resources
96943ecec16SMauro Carvalho Chehab 		 */
97043ecec16SMauro Carvalho Chehab 		if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) {
97143ecec16SMauro Carvalho Chehab 			mfc_debug(2, "Has to free instance\n");
97243ecec16SMauro Carvalho Chehab 			s5p_mfc_close_mfc_inst(dev, ctx);
97343ecec16SMauro Carvalho Chehab 		}
97443ecec16SMauro Carvalho Chehab 		/* hardware locking scheme */
97543ecec16SMauro Carvalho Chehab 		if (dev->curr_ctx == ctx->num)
97643ecec16SMauro Carvalho Chehab 			clear_bit(0, &dev->hw_lock);
97743ecec16SMauro Carvalho Chehab 		dev->num_inst--;
97843ecec16SMauro Carvalho Chehab 		if (dev->num_inst == 0) {
97943ecec16SMauro Carvalho Chehab 			mfc_debug(2, "Last instance\n");
98043ecec16SMauro Carvalho Chehab 			s5p_mfc_deinit_hw(dev);
98143ecec16SMauro Carvalho Chehab 			del_timer_sync(&dev->watchdog_timer);
98243ecec16SMauro Carvalho Chehab 			s5p_mfc_clock_off();
98343ecec16SMauro Carvalho Chehab 			if (s5p_mfc_power_off() < 0)
98443ecec16SMauro Carvalho Chehab 				mfc_err("Power off failed\n");
98543ecec16SMauro Carvalho Chehab 		} else {
98643ecec16SMauro Carvalho Chehab 			mfc_debug(2, "Shutting down clock\n");
98743ecec16SMauro Carvalho Chehab 			s5p_mfc_clock_off();
98843ecec16SMauro Carvalho Chehab 		}
98943ecec16SMauro Carvalho Chehab 	}
99043ecec16SMauro Carvalho Chehab 	if (dev)
99143ecec16SMauro Carvalho Chehab 		dev->ctx[ctx->num] = NULL;
99243ecec16SMauro Carvalho Chehab 	s5p_mfc_dec_ctrls_delete(ctx);
99343ecec16SMauro Carvalho Chehab 	v4l2_fh_del(&ctx->fh);
99443ecec16SMauro Carvalho Chehab 	/* vdev is gone if dev is null */
99543ecec16SMauro Carvalho Chehab 	if (dev)
99643ecec16SMauro Carvalho Chehab 		v4l2_fh_exit(&ctx->fh);
99743ecec16SMauro Carvalho Chehab 	kfree(ctx);
99843ecec16SMauro Carvalho Chehab 	mfc_debug_leave();
99943ecec16SMauro Carvalho Chehab 	if (dev)
100043ecec16SMauro Carvalho Chehab 		mutex_unlock(&dev->mfc_mutex);
100143ecec16SMauro Carvalho Chehab 
100243ecec16SMauro Carvalho Chehab 	return 0;
100343ecec16SMauro Carvalho Chehab }
100443ecec16SMauro Carvalho Chehab 
100543ecec16SMauro Carvalho Chehab /* Poll */
s5p_mfc_poll(struct file * file,struct poll_table_struct * wait)100643ecec16SMauro Carvalho Chehab static __poll_t s5p_mfc_poll(struct file *file,
100743ecec16SMauro Carvalho Chehab 				 struct poll_table_struct *wait)
100843ecec16SMauro Carvalho Chehab {
100943ecec16SMauro Carvalho Chehab 	struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
101043ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = ctx->dev;
101143ecec16SMauro Carvalho Chehab 	struct vb2_queue *src_q, *dst_q;
101243ecec16SMauro Carvalho Chehab 	struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
101343ecec16SMauro Carvalho Chehab 	__poll_t rc = 0;
101443ecec16SMauro Carvalho Chehab 	unsigned long flags;
101543ecec16SMauro Carvalho Chehab 
101643ecec16SMauro Carvalho Chehab 	mutex_lock(&dev->mfc_mutex);
101743ecec16SMauro Carvalho Chehab 	src_q = &ctx->vq_src;
101843ecec16SMauro Carvalho Chehab 	dst_q = &ctx->vq_dst;
101943ecec16SMauro Carvalho Chehab 	/*
102043ecec16SMauro Carvalho Chehab 	 * There has to be at least one buffer queued on each queued_list, which
102143ecec16SMauro Carvalho Chehab 	 * means either in driver already or waiting for driver to claim it
102243ecec16SMauro Carvalho Chehab 	 * and start processing.
102343ecec16SMauro Carvalho Chehab 	 */
1024a0799442SHans Verkuil 	if ((!vb2_is_streaming(src_q) || list_empty(&src_q->queued_list)) &&
1025a0799442SHans Verkuil 	    (!vb2_is_streaming(dst_q) || list_empty(&dst_q->queued_list))) {
102643ecec16SMauro Carvalho Chehab 		rc = EPOLLERR;
102743ecec16SMauro Carvalho Chehab 		goto end;
102843ecec16SMauro Carvalho Chehab 	}
102943ecec16SMauro Carvalho Chehab 	mutex_unlock(&dev->mfc_mutex);
103043ecec16SMauro Carvalho Chehab 	poll_wait(file, &ctx->fh.wait, wait);
103143ecec16SMauro Carvalho Chehab 	poll_wait(file, &src_q->done_wq, wait);
103243ecec16SMauro Carvalho Chehab 	poll_wait(file, &dst_q->done_wq, wait);
103343ecec16SMauro Carvalho Chehab 	mutex_lock(&dev->mfc_mutex);
103443ecec16SMauro Carvalho Chehab 	if (v4l2_event_pending(&ctx->fh))
103543ecec16SMauro Carvalho Chehab 		rc |= EPOLLPRI;
103643ecec16SMauro Carvalho Chehab 	spin_lock_irqsave(&src_q->done_lock, flags);
103743ecec16SMauro Carvalho Chehab 	if (!list_empty(&src_q->done_list))
103843ecec16SMauro Carvalho Chehab 		src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
103943ecec16SMauro Carvalho Chehab 								done_entry);
104043ecec16SMauro Carvalho Chehab 	if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
104143ecec16SMauro Carvalho Chehab 				|| src_vb->state == VB2_BUF_STATE_ERROR))
104243ecec16SMauro Carvalho Chehab 		rc |= EPOLLOUT | EPOLLWRNORM;
104343ecec16SMauro Carvalho Chehab 	spin_unlock_irqrestore(&src_q->done_lock, flags);
104443ecec16SMauro Carvalho Chehab 	spin_lock_irqsave(&dst_q->done_lock, flags);
104543ecec16SMauro Carvalho Chehab 	if (!list_empty(&dst_q->done_list))
104643ecec16SMauro Carvalho Chehab 		dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
104743ecec16SMauro Carvalho Chehab 								done_entry);
104843ecec16SMauro Carvalho Chehab 	if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
104943ecec16SMauro Carvalho Chehab 				|| dst_vb->state == VB2_BUF_STATE_ERROR))
105043ecec16SMauro Carvalho Chehab 		rc |= EPOLLIN | EPOLLRDNORM;
105143ecec16SMauro Carvalho Chehab 	spin_unlock_irqrestore(&dst_q->done_lock, flags);
105243ecec16SMauro Carvalho Chehab end:
105343ecec16SMauro Carvalho Chehab 	mutex_unlock(&dev->mfc_mutex);
105443ecec16SMauro Carvalho Chehab 	return rc;
105543ecec16SMauro Carvalho Chehab }
105643ecec16SMauro Carvalho Chehab 
105743ecec16SMauro Carvalho Chehab /* Mmap */
s5p_mfc_mmap(struct file * file,struct vm_area_struct * vma)105843ecec16SMauro Carvalho Chehab static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
105943ecec16SMauro Carvalho Chehab {
106043ecec16SMauro Carvalho Chehab 	struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
106143ecec16SMauro Carvalho Chehab 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
106243ecec16SMauro Carvalho Chehab 	int ret;
106343ecec16SMauro Carvalho Chehab 
106443ecec16SMauro Carvalho Chehab 	if (offset < DST_QUEUE_OFF_BASE) {
1065fd5bd6d2SColin Ian King 		mfc_debug(2, "mmapping source\n");
106643ecec16SMauro Carvalho Chehab 		ret = vb2_mmap(&ctx->vq_src, vma);
106743ecec16SMauro Carvalho Chehab 	} else {		/* capture */
1068fd5bd6d2SColin Ian King 		mfc_debug(2, "mmapping destination\n");
106943ecec16SMauro Carvalho Chehab 		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
107043ecec16SMauro Carvalho Chehab 		ret = vb2_mmap(&ctx->vq_dst, vma);
107143ecec16SMauro Carvalho Chehab 	}
107243ecec16SMauro Carvalho Chehab 	return ret;
107343ecec16SMauro Carvalho Chehab }
107443ecec16SMauro Carvalho Chehab 
107543ecec16SMauro Carvalho Chehab /* v4l2 ops */
107643ecec16SMauro Carvalho Chehab static const struct v4l2_file_operations s5p_mfc_fops = {
107743ecec16SMauro Carvalho Chehab 	.owner = THIS_MODULE,
107843ecec16SMauro Carvalho Chehab 	.open = s5p_mfc_open,
107943ecec16SMauro Carvalho Chehab 	.release = s5p_mfc_release,
108043ecec16SMauro Carvalho Chehab 	.poll = s5p_mfc_poll,
108143ecec16SMauro Carvalho Chehab 	.unlocked_ioctl = video_ioctl2,
108243ecec16SMauro Carvalho Chehab 	.mmap = s5p_mfc_mmap,
108343ecec16SMauro Carvalho Chehab };
108443ecec16SMauro Carvalho Chehab 
108543ecec16SMauro Carvalho Chehab /* DMA memory related helper functions */
s5p_mfc_memdev_release(struct device * dev)108643ecec16SMauro Carvalho Chehab static void s5p_mfc_memdev_release(struct device *dev)
108743ecec16SMauro Carvalho Chehab {
108843ecec16SMauro Carvalho Chehab 	of_reserved_mem_device_release(dev);
108943ecec16SMauro Carvalho Chehab }
109043ecec16SMauro Carvalho Chehab 
s5p_mfc_alloc_memdev(struct device * dev,const char * name,unsigned int idx)109143ecec16SMauro Carvalho Chehab static struct device *s5p_mfc_alloc_memdev(struct device *dev,
109243ecec16SMauro Carvalho Chehab 					   const char *name, unsigned int idx)
109343ecec16SMauro Carvalho Chehab {
109443ecec16SMauro Carvalho Chehab 	struct device *child;
109543ecec16SMauro Carvalho Chehab 	int ret;
109643ecec16SMauro Carvalho Chehab 
109743ecec16SMauro Carvalho Chehab 	child = devm_kzalloc(dev, sizeof(*child), GFP_KERNEL);
109843ecec16SMauro Carvalho Chehab 	if (!child)
109943ecec16SMauro Carvalho Chehab 		return NULL;
110043ecec16SMauro Carvalho Chehab 
110143ecec16SMauro Carvalho Chehab 	device_initialize(child);
110243ecec16SMauro Carvalho Chehab 	dev_set_name(child, "%s:%s", dev_name(dev), name);
110343ecec16SMauro Carvalho Chehab 	child->parent = dev;
110443ecec16SMauro Carvalho Chehab 	child->coherent_dma_mask = dev->coherent_dma_mask;
110543ecec16SMauro Carvalho Chehab 	child->dma_mask = dev->dma_mask;
110643ecec16SMauro Carvalho Chehab 	child->release = s5p_mfc_memdev_release;
110743ecec16SMauro Carvalho Chehab 	child->dma_parms = devm_kzalloc(dev, sizeof(*child->dma_parms),
110843ecec16SMauro Carvalho Chehab 					GFP_KERNEL);
110943ecec16SMauro Carvalho Chehab 	if (!child->dma_parms)
111043ecec16SMauro Carvalho Chehab 		goto err;
111143ecec16SMauro Carvalho Chehab 
111243ecec16SMauro Carvalho Chehab 	/*
111343ecec16SMauro Carvalho Chehab 	 * The memdevs are not proper OF platform devices, so in order for them
111443ecec16SMauro Carvalho Chehab 	 * to be treated as valid DMA masters we need a bit of a hack to force
111543ecec16SMauro Carvalho Chehab 	 * them to inherit the MFC node's DMA configuration.
111643ecec16SMauro Carvalho Chehab 	 */
111743ecec16SMauro Carvalho Chehab 	of_dma_configure(child, dev->of_node, true);
111843ecec16SMauro Carvalho Chehab 
111943ecec16SMauro Carvalho Chehab 	if (device_add(child) == 0) {
112043ecec16SMauro Carvalho Chehab 		ret = of_reserved_mem_device_init_by_idx(child, dev->of_node,
112143ecec16SMauro Carvalho Chehab 							 idx);
112243ecec16SMauro Carvalho Chehab 		if (ret == 0)
112343ecec16SMauro Carvalho Chehab 			return child;
112443ecec16SMauro Carvalho Chehab 		device_del(child);
112543ecec16SMauro Carvalho Chehab 	}
112643ecec16SMauro Carvalho Chehab err:
112743ecec16SMauro Carvalho Chehab 	put_device(child);
112843ecec16SMauro Carvalho Chehab 	return NULL;
112943ecec16SMauro Carvalho Chehab }
113043ecec16SMauro Carvalho Chehab 
s5p_mfc_configure_2port_memory(struct s5p_mfc_dev * mfc_dev)113143ecec16SMauro Carvalho Chehab static int s5p_mfc_configure_2port_memory(struct s5p_mfc_dev *mfc_dev)
113243ecec16SMauro Carvalho Chehab {
113343ecec16SMauro Carvalho Chehab 	struct device *dev = &mfc_dev->plat_dev->dev;
113443ecec16SMauro Carvalho Chehab 	void *bank2_virt;
113543ecec16SMauro Carvalho Chehab 	dma_addr_t bank2_dma_addr;
113643ecec16SMauro Carvalho Chehab 	unsigned long align_size = 1 << MFC_BASE_ALIGN_ORDER;
113743ecec16SMauro Carvalho Chehab 	int ret;
113843ecec16SMauro Carvalho Chehab 
113943ecec16SMauro Carvalho Chehab 	/*
114043ecec16SMauro Carvalho Chehab 	 * Create and initialize virtual devices for accessing
114143ecec16SMauro Carvalho Chehab 	 * reserved memory regions.
114243ecec16SMauro Carvalho Chehab 	 */
114343ecec16SMauro Carvalho Chehab 	mfc_dev->mem_dev[BANK_L_CTX] = s5p_mfc_alloc_memdev(dev, "left",
114443ecec16SMauro Carvalho Chehab 							   BANK_L_CTX);
114543ecec16SMauro Carvalho Chehab 	if (!mfc_dev->mem_dev[BANK_L_CTX])
114643ecec16SMauro Carvalho Chehab 		return -ENODEV;
114743ecec16SMauro Carvalho Chehab 	mfc_dev->mem_dev[BANK_R_CTX] = s5p_mfc_alloc_memdev(dev, "right",
114843ecec16SMauro Carvalho Chehab 							   BANK_R_CTX);
114943ecec16SMauro Carvalho Chehab 	if (!mfc_dev->mem_dev[BANK_R_CTX]) {
115043ecec16SMauro Carvalho Chehab 		device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
115143ecec16SMauro Carvalho Chehab 		return -ENODEV;
115243ecec16SMauro Carvalho Chehab 	}
115343ecec16SMauro Carvalho Chehab 
115443ecec16SMauro Carvalho Chehab 	/* Allocate memory for firmware and initialize both banks addresses */
115543ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_alloc_firmware(mfc_dev);
115643ecec16SMauro Carvalho Chehab 	if (ret) {
115743ecec16SMauro Carvalho Chehab 		device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
115843ecec16SMauro Carvalho Chehab 		device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
115943ecec16SMauro Carvalho Chehab 		return ret;
116043ecec16SMauro Carvalho Chehab 	}
116143ecec16SMauro Carvalho Chehab 
116243ecec16SMauro Carvalho Chehab 	mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->fw_buf.dma;
116343ecec16SMauro Carvalho Chehab 
116443ecec16SMauro Carvalho Chehab 	bank2_virt = dma_alloc_coherent(mfc_dev->mem_dev[BANK_R_CTX],
116543ecec16SMauro Carvalho Chehab 				       align_size, &bank2_dma_addr, GFP_KERNEL);
116643ecec16SMauro Carvalho Chehab 	if (!bank2_virt) {
116743ecec16SMauro Carvalho Chehab 		s5p_mfc_release_firmware(mfc_dev);
116843ecec16SMauro Carvalho Chehab 		device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
116943ecec16SMauro Carvalho Chehab 		device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
117043ecec16SMauro Carvalho Chehab 		return -ENOMEM;
117143ecec16SMauro Carvalho Chehab 	}
117243ecec16SMauro Carvalho Chehab 
117343ecec16SMauro Carvalho Chehab 	/* Valid buffers passed to MFC encoder with LAST_FRAME command
117443ecec16SMauro Carvalho Chehab 	 * should not have address of bank2 - MFC will treat it as a null frame.
117543ecec16SMauro Carvalho Chehab 	 * To avoid such situation we set bank2 address below the pool address.
117643ecec16SMauro Carvalho Chehab 	 */
117743ecec16SMauro Carvalho Chehab 	mfc_dev->dma_base[BANK_R_CTX] = bank2_dma_addr - align_size;
117843ecec16SMauro Carvalho Chehab 
117943ecec16SMauro Carvalho Chehab 	dma_free_coherent(mfc_dev->mem_dev[BANK_R_CTX], align_size, bank2_virt,
118043ecec16SMauro Carvalho Chehab 			  bank2_dma_addr);
118143ecec16SMauro Carvalho Chehab 
118243ecec16SMauro Carvalho Chehab 	vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX],
118343ecec16SMauro Carvalho Chehab 					DMA_BIT_MASK(32));
118443ecec16SMauro Carvalho Chehab 	vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX],
118543ecec16SMauro Carvalho Chehab 					DMA_BIT_MASK(32));
118643ecec16SMauro Carvalho Chehab 
118743ecec16SMauro Carvalho Chehab 	return 0;
118843ecec16SMauro Carvalho Chehab }
118943ecec16SMauro Carvalho Chehab 
s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev * mfc_dev)119043ecec16SMauro Carvalho Chehab static void s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev *mfc_dev)
119143ecec16SMauro Carvalho Chehab {
119243ecec16SMauro Carvalho Chehab 	device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
119343ecec16SMauro Carvalho Chehab 	device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
119443ecec16SMauro Carvalho Chehab 	vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX]);
119543ecec16SMauro Carvalho Chehab 	vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX]);
119643ecec16SMauro Carvalho Chehab }
119743ecec16SMauro Carvalho Chehab 
s5p_mfc_configure_common_memory(struct s5p_mfc_dev * mfc_dev)119843ecec16SMauro Carvalho Chehab static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
119943ecec16SMauro Carvalho Chehab {
120043ecec16SMauro Carvalho Chehab 	struct device *dev = &mfc_dev->plat_dev->dev;
120143ecec16SMauro Carvalho Chehab 	unsigned long mem_size = SZ_4M;
120243ecec16SMauro Carvalho Chehab 
120343ecec16SMauro Carvalho Chehab 	if (IS_ENABLED(CONFIG_DMA_CMA) || exynos_is_iommu_available(dev))
120443ecec16SMauro Carvalho Chehab 		mem_size = SZ_8M;
120543ecec16SMauro Carvalho Chehab 
120643ecec16SMauro Carvalho Chehab 	if (mfc_mem_size)
120743ecec16SMauro Carvalho Chehab 		mem_size = memparse(mfc_mem_size, NULL);
120843ecec16SMauro Carvalho Chehab 
120943ecec16SMauro Carvalho Chehab 	mfc_dev->mem_bitmap = bitmap_zalloc(mem_size >> PAGE_SHIFT, GFP_KERNEL);
121043ecec16SMauro Carvalho Chehab 	if (!mfc_dev->mem_bitmap)
121143ecec16SMauro Carvalho Chehab 		return -ENOMEM;
121243ecec16SMauro Carvalho Chehab 
121343ecec16SMauro Carvalho Chehab 	mfc_dev->mem_virt = dma_alloc_coherent(dev, mem_size,
121443ecec16SMauro Carvalho Chehab 					       &mfc_dev->mem_base, GFP_KERNEL);
121543ecec16SMauro Carvalho Chehab 	if (!mfc_dev->mem_virt) {
121643ecec16SMauro Carvalho Chehab 		bitmap_free(mfc_dev->mem_bitmap);
121743ecec16SMauro Carvalho Chehab 		dev_err(dev, "failed to preallocate %ld MiB for the firmware and context buffers\n",
121843ecec16SMauro Carvalho Chehab 			(mem_size / SZ_1M));
121943ecec16SMauro Carvalho Chehab 		return -ENOMEM;
122043ecec16SMauro Carvalho Chehab 	}
122143ecec16SMauro Carvalho Chehab 	mfc_dev->mem_size = mem_size;
122243ecec16SMauro Carvalho Chehab 	mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->mem_base;
122343ecec16SMauro Carvalho Chehab 	mfc_dev->dma_base[BANK_R_CTX] = mfc_dev->mem_base;
122443ecec16SMauro Carvalho Chehab 
122543ecec16SMauro Carvalho Chehab 	/*
122643ecec16SMauro Carvalho Chehab 	 * MFC hardware cannot handle 0 as a base address, so mark first 128K
122743ecec16SMauro Carvalho Chehab 	 * as used (to keep required base alignment) and adjust base address
122843ecec16SMauro Carvalho Chehab 	 */
122943ecec16SMauro Carvalho Chehab 	if (mfc_dev->mem_base == (dma_addr_t)0) {
123043ecec16SMauro Carvalho Chehab 		unsigned int offset = 1 << MFC_BASE_ALIGN_ORDER;
123143ecec16SMauro Carvalho Chehab 
123243ecec16SMauro Carvalho Chehab 		bitmap_set(mfc_dev->mem_bitmap, 0, offset >> PAGE_SHIFT);
123343ecec16SMauro Carvalho Chehab 		mfc_dev->dma_base[BANK_L_CTX] += offset;
123443ecec16SMauro Carvalho Chehab 		mfc_dev->dma_base[BANK_R_CTX] += offset;
123543ecec16SMauro Carvalho Chehab 	}
123643ecec16SMauro Carvalho Chehab 
123743ecec16SMauro Carvalho Chehab 	/* Firmware allocation cannot fail in this case */
123843ecec16SMauro Carvalho Chehab 	s5p_mfc_alloc_firmware(mfc_dev);
123943ecec16SMauro Carvalho Chehab 
124043ecec16SMauro Carvalho Chehab 	mfc_dev->mem_dev[BANK_L_CTX] = mfc_dev->mem_dev[BANK_R_CTX] = dev;
124143ecec16SMauro Carvalho Chehab 	vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
124243ecec16SMauro Carvalho Chehab 
124343ecec16SMauro Carvalho Chehab 	dev_info(dev, "preallocated %ld MiB buffer for the firmware and context buffers\n",
124443ecec16SMauro Carvalho Chehab 		 (mem_size / SZ_1M));
124543ecec16SMauro Carvalho Chehab 
124643ecec16SMauro Carvalho Chehab 	return 0;
124743ecec16SMauro Carvalho Chehab }
124843ecec16SMauro Carvalho Chehab 
s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev * mfc_dev)124943ecec16SMauro Carvalho Chehab static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev)
125043ecec16SMauro Carvalho Chehab {
125143ecec16SMauro Carvalho Chehab 	struct device *dev = &mfc_dev->plat_dev->dev;
125243ecec16SMauro Carvalho Chehab 
125343ecec16SMauro Carvalho Chehab 	dma_free_coherent(dev, mfc_dev->mem_size, mfc_dev->mem_virt,
125443ecec16SMauro Carvalho Chehab 			  mfc_dev->mem_base);
125543ecec16SMauro Carvalho Chehab 	bitmap_free(mfc_dev->mem_bitmap);
125643ecec16SMauro Carvalho Chehab 	vb2_dma_contig_clear_max_seg_size(dev);
125743ecec16SMauro Carvalho Chehab }
125843ecec16SMauro Carvalho Chehab 
s5p_mfc_configure_dma_memory(struct s5p_mfc_dev * mfc_dev)125943ecec16SMauro Carvalho Chehab static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev)
126043ecec16SMauro Carvalho Chehab {
126143ecec16SMauro Carvalho Chehab 	struct device *dev = &mfc_dev->plat_dev->dev;
126243ecec16SMauro Carvalho Chehab 
126343ecec16SMauro Carvalho Chehab 	if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev))
126443ecec16SMauro Carvalho Chehab 		return s5p_mfc_configure_common_memory(mfc_dev);
126543ecec16SMauro Carvalho Chehab 	else
126643ecec16SMauro Carvalho Chehab 		return s5p_mfc_configure_2port_memory(mfc_dev);
126743ecec16SMauro Carvalho Chehab }
126843ecec16SMauro Carvalho Chehab 
s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev * mfc_dev)126943ecec16SMauro Carvalho Chehab static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev)
127043ecec16SMauro Carvalho Chehab {
127143ecec16SMauro Carvalho Chehab 	struct device *dev = &mfc_dev->plat_dev->dev;
127243ecec16SMauro Carvalho Chehab 
127343ecec16SMauro Carvalho Chehab 	s5p_mfc_release_firmware(mfc_dev);
127443ecec16SMauro Carvalho Chehab 	if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev))
127543ecec16SMauro Carvalho Chehab 		s5p_mfc_unconfigure_common_memory(mfc_dev);
127643ecec16SMauro Carvalho Chehab 	else
127743ecec16SMauro Carvalho Chehab 		s5p_mfc_unconfigure_2port_memory(mfc_dev);
127843ecec16SMauro Carvalho Chehab }
127943ecec16SMauro Carvalho Chehab 
128043ecec16SMauro Carvalho Chehab /* MFC probe function */
s5p_mfc_probe(struct platform_device * pdev)128143ecec16SMauro Carvalho Chehab static int s5p_mfc_probe(struct platform_device *pdev)
128243ecec16SMauro Carvalho Chehab {
128343ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev;
128443ecec16SMauro Carvalho Chehab 	struct video_device *vfd;
128543ecec16SMauro Carvalho Chehab 	int ret;
128643ecec16SMauro Carvalho Chehab 
128743ecec16SMauro Carvalho Chehab 	pr_debug("%s++\n", __func__);
128843ecec16SMauro Carvalho Chehab 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
128943ecec16SMauro Carvalho Chehab 	if (!dev)
129043ecec16SMauro Carvalho Chehab 		return -ENOMEM;
129143ecec16SMauro Carvalho Chehab 
129243ecec16SMauro Carvalho Chehab 	spin_lock_init(&dev->irqlock);
129343ecec16SMauro Carvalho Chehab 	spin_lock_init(&dev->condlock);
129443ecec16SMauro Carvalho Chehab 	dev->plat_dev = pdev;
129543ecec16SMauro Carvalho Chehab 	if (!dev->plat_dev) {
129643ecec16SMauro Carvalho Chehab 		mfc_err("No platform data specified\n");
129743ecec16SMauro Carvalho Chehab 		return -ENODEV;
129843ecec16SMauro Carvalho Chehab 	}
129943ecec16SMauro Carvalho Chehab 
130043ecec16SMauro Carvalho Chehab 	dev->variant = of_device_get_match_data(&pdev->dev);
130143ecec16SMauro Carvalho Chehab 	if (!dev->variant) {
130243ecec16SMauro Carvalho Chehab 		dev_err(&pdev->dev, "Failed to get device MFC hardware variant information\n");
130343ecec16SMauro Carvalho Chehab 		return -ENOENT;
130443ecec16SMauro Carvalho Chehab 	}
130543ecec16SMauro Carvalho Chehab 
130643ecec16SMauro Carvalho Chehab 	dev->regs_base = devm_platform_ioremap_resource(pdev, 0);
130743ecec16SMauro Carvalho Chehab 	if (IS_ERR(dev->regs_base))
130843ecec16SMauro Carvalho Chehab 		return PTR_ERR(dev->regs_base);
130943ecec16SMauro Carvalho Chehab 
131043ecec16SMauro Carvalho Chehab 	ret = platform_get_irq(pdev, 0);
131143ecec16SMauro Carvalho Chehab 	if (ret < 0)
131243ecec16SMauro Carvalho Chehab 		return ret;
131343ecec16SMauro Carvalho Chehab 	dev->irq = ret;
131443ecec16SMauro Carvalho Chehab 	ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
131543ecec16SMauro Carvalho Chehab 					0, pdev->name, dev);
131643ecec16SMauro Carvalho Chehab 	if (ret) {
131743ecec16SMauro Carvalho Chehab 		dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
131843ecec16SMauro Carvalho Chehab 		return ret;
131943ecec16SMauro Carvalho Chehab 	}
132043ecec16SMauro Carvalho Chehab 
132143ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_configure_dma_memory(dev);
132243ecec16SMauro Carvalho Chehab 	if (ret < 0) {
132343ecec16SMauro Carvalho Chehab 		dev_err(&pdev->dev, "failed to configure DMA memory\n");
132443ecec16SMauro Carvalho Chehab 		return ret;
132543ecec16SMauro Carvalho Chehab 	}
132643ecec16SMauro Carvalho Chehab 
132743ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_init_pm(dev);
132843ecec16SMauro Carvalho Chehab 	if (ret < 0) {
132943ecec16SMauro Carvalho Chehab 		dev_err(&pdev->dev, "failed to get mfc clock source\n");
133043ecec16SMauro Carvalho Chehab 		goto err_dma;
133143ecec16SMauro Carvalho Chehab 	}
133243ecec16SMauro Carvalho Chehab 
133343ecec16SMauro Carvalho Chehab 	/*
133443ecec16SMauro Carvalho Chehab 	 * Load fails if fs isn't mounted. Try loading anyway.
1335203ef345SAakarsh Jain 	 * _open() will load it, it fails now. Ignore failure.
133643ecec16SMauro Carvalho Chehab 	 */
133743ecec16SMauro Carvalho Chehab 	s5p_mfc_load_firmware(dev);
133843ecec16SMauro Carvalho Chehab 
133943ecec16SMauro Carvalho Chehab 	mutex_init(&dev->mfc_mutex);
134043ecec16SMauro Carvalho Chehab 	init_waitqueue_head(&dev->queue);
134143ecec16SMauro Carvalho Chehab 	dev->hw_lock = 0;
134243ecec16SMauro Carvalho Chehab 	INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
134343ecec16SMauro Carvalho Chehab 	atomic_set(&dev->watchdog_cnt, 0);
134443ecec16SMauro Carvalho Chehab 	timer_setup(&dev->watchdog_timer, s5p_mfc_watchdog, 0);
134543ecec16SMauro Carvalho Chehab 
134643ecec16SMauro Carvalho Chehab 	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
134743ecec16SMauro Carvalho Chehab 	if (ret)
134843ecec16SMauro Carvalho Chehab 		goto err_v4l2_dev_reg;
134943ecec16SMauro Carvalho Chehab 
135043ecec16SMauro Carvalho Chehab 	/* decoder */
135143ecec16SMauro Carvalho Chehab 	vfd = video_device_alloc();
135243ecec16SMauro Carvalho Chehab 	if (!vfd) {
135343ecec16SMauro Carvalho Chehab 		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
135443ecec16SMauro Carvalho Chehab 		ret = -ENOMEM;
135543ecec16SMauro Carvalho Chehab 		goto err_dec_alloc;
135643ecec16SMauro Carvalho Chehab 	}
135743ecec16SMauro Carvalho Chehab 	vfd->fops	= &s5p_mfc_fops;
135843ecec16SMauro Carvalho Chehab 	vfd->ioctl_ops	= get_dec_v4l2_ioctl_ops();
135943ecec16SMauro Carvalho Chehab 	vfd->release	= video_device_release;
136043ecec16SMauro Carvalho Chehab 	vfd->lock	= &dev->mfc_mutex;
136143ecec16SMauro Carvalho Chehab 	vfd->v4l2_dev	= &dev->v4l2_dev;
136243ecec16SMauro Carvalho Chehab 	vfd->vfl_dir	= VFL_DIR_M2M;
136343ecec16SMauro Carvalho Chehab 	vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
136443ecec16SMauro Carvalho Chehab 	set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
136543ecec16SMauro Carvalho Chehab 	snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
136643ecec16SMauro Carvalho Chehab 	dev->vfd_dec	= vfd;
136743ecec16SMauro Carvalho Chehab 	video_set_drvdata(vfd, dev);
136843ecec16SMauro Carvalho Chehab 
136943ecec16SMauro Carvalho Chehab 	/* encoder */
137043ecec16SMauro Carvalho Chehab 	vfd = video_device_alloc();
137143ecec16SMauro Carvalho Chehab 	if (!vfd) {
137243ecec16SMauro Carvalho Chehab 		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
137343ecec16SMauro Carvalho Chehab 		ret = -ENOMEM;
137443ecec16SMauro Carvalho Chehab 		goto err_enc_alloc;
137543ecec16SMauro Carvalho Chehab 	}
137643ecec16SMauro Carvalho Chehab 	vfd->fops	= &s5p_mfc_fops;
137743ecec16SMauro Carvalho Chehab 	vfd->ioctl_ops	= get_enc_v4l2_ioctl_ops();
137843ecec16SMauro Carvalho Chehab 	vfd->release	= video_device_release;
137943ecec16SMauro Carvalho Chehab 	vfd->lock	= &dev->mfc_mutex;
138043ecec16SMauro Carvalho Chehab 	vfd->v4l2_dev	= &dev->v4l2_dev;
138143ecec16SMauro Carvalho Chehab 	vfd->vfl_dir	= VFL_DIR_M2M;
138243ecec16SMauro Carvalho Chehab 	vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
138343ecec16SMauro Carvalho Chehab 	snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
138443ecec16SMauro Carvalho Chehab 	dev->vfd_enc	= vfd;
138543ecec16SMauro Carvalho Chehab 	video_set_drvdata(vfd, dev);
138643ecec16SMauro Carvalho Chehab 	platform_set_drvdata(pdev, dev);
138743ecec16SMauro Carvalho Chehab 
138843ecec16SMauro Carvalho Chehab 	/* Initialize HW ops and commands based on MFC version */
138943ecec16SMauro Carvalho Chehab 	s5p_mfc_init_hw_ops(dev);
139043ecec16SMauro Carvalho Chehab 	s5p_mfc_init_hw_cmds(dev);
139143ecec16SMauro Carvalho Chehab 	s5p_mfc_init_regs(dev);
139243ecec16SMauro Carvalho Chehab 
139343ecec16SMauro Carvalho Chehab 	/* Register decoder and encoder */
139443ecec16SMauro Carvalho Chehab 	ret = video_register_device(dev->vfd_dec, VFL_TYPE_VIDEO, 0);
139543ecec16SMauro Carvalho Chehab 	if (ret) {
139643ecec16SMauro Carvalho Chehab 		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
139743ecec16SMauro Carvalho Chehab 		goto err_dec_reg;
139843ecec16SMauro Carvalho Chehab 	}
139943ecec16SMauro Carvalho Chehab 	v4l2_info(&dev->v4l2_dev,
140043ecec16SMauro Carvalho Chehab 		  "decoder registered as /dev/video%d\n", dev->vfd_dec->num);
140143ecec16SMauro Carvalho Chehab 
140243ecec16SMauro Carvalho Chehab 	ret = video_register_device(dev->vfd_enc, VFL_TYPE_VIDEO, 0);
140343ecec16SMauro Carvalho Chehab 	if (ret) {
140443ecec16SMauro Carvalho Chehab 		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
140543ecec16SMauro Carvalho Chehab 		goto err_enc_reg;
140643ecec16SMauro Carvalho Chehab 	}
140743ecec16SMauro Carvalho Chehab 	v4l2_info(&dev->v4l2_dev,
140843ecec16SMauro Carvalho Chehab 		  "encoder registered as /dev/video%d\n", dev->vfd_enc->num);
140943ecec16SMauro Carvalho Chehab 
141043ecec16SMauro Carvalho Chehab 	pr_debug("%s--\n", __func__);
141143ecec16SMauro Carvalho Chehab 	return 0;
141243ecec16SMauro Carvalho Chehab 
141343ecec16SMauro Carvalho Chehab /* Deinit MFC if probe had failed */
141443ecec16SMauro Carvalho Chehab err_enc_reg:
141543ecec16SMauro Carvalho Chehab 	video_unregister_device(dev->vfd_dec);
1416c65c3f3aSHangyu Hua 	dev->vfd_dec = NULL;
141743ecec16SMauro Carvalho Chehab err_dec_reg:
141843ecec16SMauro Carvalho Chehab 	video_device_release(dev->vfd_enc);
141943ecec16SMauro Carvalho Chehab err_enc_alloc:
142043ecec16SMauro Carvalho Chehab 	video_device_release(dev->vfd_dec);
142143ecec16SMauro Carvalho Chehab err_dec_alloc:
142243ecec16SMauro Carvalho Chehab 	v4l2_device_unregister(&dev->v4l2_dev);
142343ecec16SMauro Carvalho Chehab err_v4l2_dev_reg:
142443ecec16SMauro Carvalho Chehab 	s5p_mfc_final_pm(dev);
142543ecec16SMauro Carvalho Chehab err_dma:
142643ecec16SMauro Carvalho Chehab 	s5p_mfc_unconfigure_dma_memory(dev);
142743ecec16SMauro Carvalho Chehab 
142843ecec16SMauro Carvalho Chehab 	pr_debug("%s-- with error\n", __func__);
142943ecec16SMauro Carvalho Chehab 	return ret;
143043ecec16SMauro Carvalho Chehab 
143143ecec16SMauro Carvalho Chehab }
143243ecec16SMauro Carvalho Chehab 
143343ecec16SMauro Carvalho Chehab /* Remove the driver */
s5p_mfc_remove(struct platform_device * pdev)1434*9709cf5bSUwe Kleine-König static void s5p_mfc_remove(struct platform_device *pdev)
143543ecec16SMauro Carvalho Chehab {
143643ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
143743ecec16SMauro Carvalho Chehab 	struct s5p_mfc_ctx *ctx;
143843ecec16SMauro Carvalho Chehab 	int i;
143943ecec16SMauro Carvalho Chehab 
144043ecec16SMauro Carvalho Chehab 	v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
144143ecec16SMauro Carvalho Chehab 
144243ecec16SMauro Carvalho Chehab 	/*
144343ecec16SMauro Carvalho Chehab 	 * Clear ctx dev pointer to avoid races between s5p_mfc_remove()
144443ecec16SMauro Carvalho Chehab 	 * and s5p_mfc_release() and s5p_mfc_release() accessing ctx->dev
144543ecec16SMauro Carvalho Chehab 	 * after s5p_mfc_remove() is run during unbind.
144643ecec16SMauro Carvalho Chehab 	 */
144743ecec16SMauro Carvalho Chehab 	mutex_lock(&dev->mfc_mutex);
144843ecec16SMauro Carvalho Chehab 	for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
144943ecec16SMauro Carvalho Chehab 		ctx = dev->ctx[i];
145043ecec16SMauro Carvalho Chehab 		if (!ctx)
145143ecec16SMauro Carvalho Chehab 			continue;
145243ecec16SMauro Carvalho Chehab 		/* clear ctx->dev */
145343ecec16SMauro Carvalho Chehab 		ctx->dev = NULL;
145443ecec16SMauro Carvalho Chehab 	}
145543ecec16SMauro Carvalho Chehab 	mutex_unlock(&dev->mfc_mutex);
145643ecec16SMauro Carvalho Chehab 
145743ecec16SMauro Carvalho Chehab 	del_timer_sync(&dev->watchdog_timer);
145843ecec16SMauro Carvalho Chehab 	flush_work(&dev->watchdog_work);
145943ecec16SMauro Carvalho Chehab 
146043ecec16SMauro Carvalho Chehab 	video_unregister_device(dev->vfd_enc);
146143ecec16SMauro Carvalho Chehab 	video_unregister_device(dev->vfd_dec);
146243ecec16SMauro Carvalho Chehab 	v4l2_device_unregister(&dev->v4l2_dev);
146343ecec16SMauro Carvalho Chehab 	s5p_mfc_unconfigure_dma_memory(dev);
146443ecec16SMauro Carvalho Chehab 
146543ecec16SMauro Carvalho Chehab 	s5p_mfc_final_pm(dev);
146643ecec16SMauro Carvalho Chehab }
146743ecec16SMauro Carvalho Chehab 
146843ecec16SMauro Carvalho Chehab #ifdef CONFIG_PM_SLEEP
146943ecec16SMauro Carvalho Chehab 
s5p_mfc_suspend(struct device * dev)147043ecec16SMauro Carvalho Chehab static int s5p_mfc_suspend(struct device *dev)
147143ecec16SMauro Carvalho Chehab {
147243ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev);
147343ecec16SMauro Carvalho Chehab 	int ret;
147443ecec16SMauro Carvalho Chehab 
147543ecec16SMauro Carvalho Chehab 	if (m_dev->num_inst == 0)
147643ecec16SMauro Carvalho Chehab 		return 0;
147743ecec16SMauro Carvalho Chehab 
147843ecec16SMauro Carvalho Chehab 	if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) {
147943ecec16SMauro Carvalho Chehab 		mfc_err("Error: going to suspend for a second time\n");
148043ecec16SMauro Carvalho Chehab 		return -EIO;
148143ecec16SMauro Carvalho Chehab 	}
148243ecec16SMauro Carvalho Chehab 
148343ecec16SMauro Carvalho Chehab 	/* Check if we're processing then wait if it necessary. */
148443ecec16SMauro Carvalho Chehab 	while (test_and_set_bit(0, &m_dev->hw_lock) != 0) {
148543ecec16SMauro Carvalho Chehab 		/* Try and lock the HW */
148643ecec16SMauro Carvalho Chehab 		/* Wait on the interrupt waitqueue */
148743ecec16SMauro Carvalho Chehab 		ret = wait_event_interruptible_timeout(m_dev->queue,
148843ecec16SMauro Carvalho Chehab 			m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT));
148943ecec16SMauro Carvalho Chehab 		if (ret == 0) {
149043ecec16SMauro Carvalho Chehab 			mfc_err("Waiting for hardware to finish timed out\n");
149143ecec16SMauro Carvalho Chehab 			clear_bit(0, &m_dev->enter_suspend);
149243ecec16SMauro Carvalho Chehab 			return -EIO;
149343ecec16SMauro Carvalho Chehab 		}
149443ecec16SMauro Carvalho Chehab 	}
149543ecec16SMauro Carvalho Chehab 
149643ecec16SMauro Carvalho Chehab 	ret = s5p_mfc_sleep(m_dev);
149743ecec16SMauro Carvalho Chehab 	if (ret) {
149843ecec16SMauro Carvalho Chehab 		clear_bit(0, &m_dev->enter_suspend);
149943ecec16SMauro Carvalho Chehab 		clear_bit(0, &m_dev->hw_lock);
150043ecec16SMauro Carvalho Chehab 	}
150143ecec16SMauro Carvalho Chehab 	return ret;
150243ecec16SMauro Carvalho Chehab }
150343ecec16SMauro Carvalho Chehab 
s5p_mfc_resume(struct device * dev)150443ecec16SMauro Carvalho Chehab static int s5p_mfc_resume(struct device *dev)
150543ecec16SMauro Carvalho Chehab {
150643ecec16SMauro Carvalho Chehab 	struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev);
150743ecec16SMauro Carvalho Chehab 
150843ecec16SMauro Carvalho Chehab 	if (m_dev->num_inst == 0)
150943ecec16SMauro Carvalho Chehab 		return 0;
151043ecec16SMauro Carvalho Chehab 	return s5p_mfc_wakeup(m_dev);
151143ecec16SMauro Carvalho Chehab }
151243ecec16SMauro Carvalho Chehab #endif
151343ecec16SMauro Carvalho Chehab 
151443ecec16SMauro Carvalho Chehab /* Power management */
151543ecec16SMauro Carvalho Chehab static const struct dev_pm_ops s5p_mfc_pm_ops = {
151643ecec16SMauro Carvalho Chehab 	SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
151743ecec16SMauro Carvalho Chehab };
151843ecec16SMauro Carvalho Chehab 
151943ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
152043ecec16SMauro Carvalho Chehab 	.h264_ctx	= MFC_H264_CTX_BUF_SIZE,
152143ecec16SMauro Carvalho Chehab 	.non_h264_ctx	= MFC_CTX_BUF_SIZE,
152243ecec16SMauro Carvalho Chehab 	.dsc		= DESC_BUF_SIZE,
152343ecec16SMauro Carvalho Chehab 	.shm		= SHARED_BUF_SIZE,
152443ecec16SMauro Carvalho Chehab };
152543ecec16SMauro Carvalho Chehab 
152643ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size buf_size_v5 = {
152743ecec16SMauro Carvalho Chehab 	.fw	= MAX_FW_SIZE,
152843ecec16SMauro Carvalho Chehab 	.cpb	= MAX_CPB_SIZE,
152943ecec16SMauro Carvalho Chehab 	.priv	= &mfc_buf_size_v5,
153043ecec16SMauro Carvalho Chehab };
153143ecec16SMauro Carvalho Chehab 
153243ecec16SMauro Carvalho Chehab static struct s5p_mfc_variant mfc_drvdata_v5 = {
153343ecec16SMauro Carvalho Chehab 	.version	= MFC_VERSION,
153443ecec16SMauro Carvalho Chehab 	.version_bit	= MFC_V5_BIT,
153543ecec16SMauro Carvalho Chehab 	.port_num	= MFC_NUM_PORTS,
153643ecec16SMauro Carvalho Chehab 	.buf_size	= &buf_size_v5,
153743ecec16SMauro Carvalho Chehab 	.fw_name[0]	= "s5p-mfc.fw",
153843ecec16SMauro Carvalho Chehab 	.clk_names	= {"mfc", "sclk_mfc"},
153943ecec16SMauro Carvalho Chehab 	.num_clocks	= 2,
154043ecec16SMauro Carvalho Chehab 	.use_clock_gating = true,
154143ecec16SMauro Carvalho Chehab };
154243ecec16SMauro Carvalho Chehab 
154343ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
154443ecec16SMauro Carvalho Chehab 	.dev_ctx	= MFC_CTX_BUF_SIZE_V6,
154543ecec16SMauro Carvalho Chehab 	.h264_dec_ctx	= MFC_H264_DEC_CTX_BUF_SIZE_V6,
154643ecec16SMauro Carvalho Chehab 	.other_dec_ctx	= MFC_OTHER_DEC_CTX_BUF_SIZE_V6,
154743ecec16SMauro Carvalho Chehab 	.h264_enc_ctx	= MFC_H264_ENC_CTX_BUF_SIZE_V6,
154843ecec16SMauro Carvalho Chehab 	.other_enc_ctx	= MFC_OTHER_ENC_CTX_BUF_SIZE_V6,
154943ecec16SMauro Carvalho Chehab };
155043ecec16SMauro Carvalho Chehab 
155143ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size buf_size_v6 = {
155243ecec16SMauro Carvalho Chehab 	.fw	= MAX_FW_SIZE_V6,
155343ecec16SMauro Carvalho Chehab 	.cpb	= MAX_CPB_SIZE_V6,
155443ecec16SMauro Carvalho Chehab 	.priv	= &mfc_buf_size_v6,
155543ecec16SMauro Carvalho Chehab };
155643ecec16SMauro Carvalho Chehab 
155743ecec16SMauro Carvalho Chehab static struct s5p_mfc_variant mfc_drvdata_v6 = {
155843ecec16SMauro Carvalho Chehab 	.version	= MFC_VERSION_V6,
155943ecec16SMauro Carvalho Chehab 	.version_bit	= MFC_V6_BIT,
156043ecec16SMauro Carvalho Chehab 	.port_num	= MFC_NUM_PORTS_V6,
156143ecec16SMauro Carvalho Chehab 	.buf_size	= &buf_size_v6,
156243ecec16SMauro Carvalho Chehab 	.fw_name[0]     = "s5p-mfc-v6.fw",
156343ecec16SMauro Carvalho Chehab 	/*
156443ecec16SMauro Carvalho Chehab 	 * v6-v2 firmware contains bug fixes and interface change
156543ecec16SMauro Carvalho Chehab 	 * for init buffer command
156643ecec16SMauro Carvalho Chehab 	 */
156743ecec16SMauro Carvalho Chehab 	.fw_name[1]     = "s5p-mfc-v6-v2.fw",
156843ecec16SMauro Carvalho Chehab 	.clk_names	= {"mfc"},
156943ecec16SMauro Carvalho Chehab 	.num_clocks	= 1,
157043ecec16SMauro Carvalho Chehab };
157143ecec16SMauro Carvalho Chehab 
157243ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
157343ecec16SMauro Carvalho Chehab 	.dev_ctx	= MFC_CTX_BUF_SIZE_V7,
157443ecec16SMauro Carvalho Chehab 	.h264_dec_ctx	= MFC_H264_DEC_CTX_BUF_SIZE_V7,
157543ecec16SMauro Carvalho Chehab 	.other_dec_ctx	= MFC_OTHER_DEC_CTX_BUF_SIZE_V7,
157643ecec16SMauro Carvalho Chehab 	.h264_enc_ctx	= MFC_H264_ENC_CTX_BUF_SIZE_V7,
157743ecec16SMauro Carvalho Chehab 	.other_enc_ctx	= MFC_OTHER_ENC_CTX_BUF_SIZE_V7,
157843ecec16SMauro Carvalho Chehab };
157943ecec16SMauro Carvalho Chehab 
158043ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size buf_size_v7 = {
158143ecec16SMauro Carvalho Chehab 	.fw	= MAX_FW_SIZE_V7,
158243ecec16SMauro Carvalho Chehab 	.cpb	= MAX_CPB_SIZE_V7,
158343ecec16SMauro Carvalho Chehab 	.priv	= &mfc_buf_size_v7,
158443ecec16SMauro Carvalho Chehab };
158543ecec16SMauro Carvalho Chehab 
158643ecec16SMauro Carvalho Chehab static struct s5p_mfc_variant mfc_drvdata_v7 = {
158743ecec16SMauro Carvalho Chehab 	.version	= MFC_VERSION_V7,
158843ecec16SMauro Carvalho Chehab 	.version_bit	= MFC_V7_BIT,
158943ecec16SMauro Carvalho Chehab 	.port_num	= MFC_NUM_PORTS_V7,
159043ecec16SMauro Carvalho Chehab 	.buf_size	= &buf_size_v7,
159143ecec16SMauro Carvalho Chehab 	.fw_name[0]     = "s5p-mfc-v7.fw",
1592f50ebe10SAakarsh Jain 	.clk_names	= {"mfc"},
1593f50ebe10SAakarsh Jain 	.num_clocks	= 1,
1594f50ebe10SAakarsh Jain };
1595f50ebe10SAakarsh Jain 
1596f50ebe10SAakarsh Jain static struct s5p_mfc_variant mfc_drvdata_v7_3250 = {
1597f50ebe10SAakarsh Jain 	.version        = MFC_VERSION_V7,
1598f50ebe10SAakarsh Jain 	.version_bit    = MFC_V7_BIT,
1599f50ebe10SAakarsh Jain 	.port_num       = MFC_NUM_PORTS_V7,
1600f50ebe10SAakarsh Jain 	.buf_size       = &buf_size_v7,
1601f50ebe10SAakarsh Jain 	.fw_name[0]     = "s5p-mfc-v7.fw",
160243ecec16SMauro Carvalho Chehab 	.clk_names      = {"mfc", "sclk_mfc"},
160343ecec16SMauro Carvalho Chehab 	.num_clocks     = 2,
160443ecec16SMauro Carvalho Chehab };
160543ecec16SMauro Carvalho Chehab 
160643ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
160743ecec16SMauro Carvalho Chehab 	.dev_ctx	= MFC_CTX_BUF_SIZE_V8,
160843ecec16SMauro Carvalho Chehab 	.h264_dec_ctx	= MFC_H264_DEC_CTX_BUF_SIZE_V8,
160943ecec16SMauro Carvalho Chehab 	.other_dec_ctx	= MFC_OTHER_DEC_CTX_BUF_SIZE_V8,
161043ecec16SMauro Carvalho Chehab 	.h264_enc_ctx	= MFC_H264_ENC_CTX_BUF_SIZE_V8,
161143ecec16SMauro Carvalho Chehab 	.other_enc_ctx	= MFC_OTHER_ENC_CTX_BUF_SIZE_V8,
161243ecec16SMauro Carvalho Chehab };
161343ecec16SMauro Carvalho Chehab 
161443ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size buf_size_v8 = {
161543ecec16SMauro Carvalho Chehab 	.fw	= MAX_FW_SIZE_V8,
161643ecec16SMauro Carvalho Chehab 	.cpb	= MAX_CPB_SIZE_V8,
161743ecec16SMauro Carvalho Chehab 	.priv	= &mfc_buf_size_v8,
161843ecec16SMauro Carvalho Chehab };
161943ecec16SMauro Carvalho Chehab 
162043ecec16SMauro Carvalho Chehab static struct s5p_mfc_variant mfc_drvdata_v8 = {
162143ecec16SMauro Carvalho Chehab 	.version	= MFC_VERSION_V8,
162243ecec16SMauro Carvalho Chehab 	.version_bit	= MFC_V8_BIT,
162343ecec16SMauro Carvalho Chehab 	.port_num	= MFC_NUM_PORTS_V8,
162443ecec16SMauro Carvalho Chehab 	.buf_size	= &buf_size_v8,
162543ecec16SMauro Carvalho Chehab 	.fw_name[0]     = "s5p-mfc-v8.fw",
162643ecec16SMauro Carvalho Chehab 	.clk_names	= {"mfc"},
162743ecec16SMauro Carvalho Chehab 	.num_clocks	= 1,
162843ecec16SMauro Carvalho Chehab };
162943ecec16SMauro Carvalho Chehab 
163043ecec16SMauro Carvalho Chehab static struct s5p_mfc_variant mfc_drvdata_v8_5433 = {
163143ecec16SMauro Carvalho Chehab 	.version	= MFC_VERSION_V8,
163243ecec16SMauro Carvalho Chehab 	.version_bit	= MFC_V8_BIT,
163343ecec16SMauro Carvalho Chehab 	.port_num	= MFC_NUM_PORTS_V8,
163443ecec16SMauro Carvalho Chehab 	.buf_size	= &buf_size_v8,
163543ecec16SMauro Carvalho Chehab 	.fw_name[0]     = "s5p-mfc-v8.fw",
163643ecec16SMauro Carvalho Chehab 	.clk_names	= {"pclk", "aclk", "aclk_xiu"},
163743ecec16SMauro Carvalho Chehab 	.num_clocks	= 3,
163843ecec16SMauro Carvalho Chehab };
163943ecec16SMauro Carvalho Chehab 
164043ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size_v6 mfc_buf_size_v10 = {
164143ecec16SMauro Carvalho Chehab 	.dev_ctx        = MFC_CTX_BUF_SIZE_V10,
164243ecec16SMauro Carvalho Chehab 	.h264_dec_ctx   = MFC_H264_DEC_CTX_BUF_SIZE_V10,
164343ecec16SMauro Carvalho Chehab 	.other_dec_ctx  = MFC_OTHER_DEC_CTX_BUF_SIZE_V10,
164443ecec16SMauro Carvalho Chehab 	.h264_enc_ctx   = MFC_H264_ENC_CTX_BUF_SIZE_V10,
164543ecec16SMauro Carvalho Chehab 	.hevc_enc_ctx   = MFC_HEVC_ENC_CTX_BUF_SIZE_V10,
164643ecec16SMauro Carvalho Chehab 	.other_enc_ctx  = MFC_OTHER_ENC_CTX_BUF_SIZE_V10,
164743ecec16SMauro Carvalho Chehab };
164843ecec16SMauro Carvalho Chehab 
164943ecec16SMauro Carvalho Chehab static struct s5p_mfc_buf_size buf_size_v10 = {
165043ecec16SMauro Carvalho Chehab 	.fw     = MAX_FW_SIZE_V10,
165143ecec16SMauro Carvalho Chehab 	.cpb    = MAX_CPB_SIZE_V10,
165243ecec16SMauro Carvalho Chehab 	.priv   = &mfc_buf_size_v10,
165343ecec16SMauro Carvalho Chehab };
165443ecec16SMauro Carvalho Chehab 
165543ecec16SMauro Carvalho Chehab static struct s5p_mfc_variant mfc_drvdata_v10 = {
165643ecec16SMauro Carvalho Chehab 	.version        = MFC_VERSION_V10,
165743ecec16SMauro Carvalho Chehab 	.version_bit    = MFC_V10_BIT,
165843ecec16SMauro Carvalho Chehab 	.port_num       = MFC_NUM_PORTS_V10,
165943ecec16SMauro Carvalho Chehab 	.buf_size       = &buf_size_v10,
166043ecec16SMauro Carvalho Chehab 	.fw_name[0]     = "s5p-mfc-v10.fw",
166143ecec16SMauro Carvalho Chehab };
166243ecec16SMauro Carvalho Chehab 
166343ecec16SMauro Carvalho Chehab static const struct of_device_id exynos_mfc_match[] = {
166443ecec16SMauro Carvalho Chehab 	{
166543ecec16SMauro Carvalho Chehab 		.compatible = "samsung,mfc-v5",
166643ecec16SMauro Carvalho Chehab 		.data = &mfc_drvdata_v5,
166743ecec16SMauro Carvalho Chehab 	}, {
166843ecec16SMauro Carvalho Chehab 		.compatible = "samsung,mfc-v6",
166943ecec16SMauro Carvalho Chehab 		.data = &mfc_drvdata_v6,
167043ecec16SMauro Carvalho Chehab 	}, {
167143ecec16SMauro Carvalho Chehab 		.compatible = "samsung,mfc-v7",
167243ecec16SMauro Carvalho Chehab 		.data = &mfc_drvdata_v7,
167343ecec16SMauro Carvalho Chehab 	}, {
1674f50ebe10SAakarsh Jain 		.compatible = "samsung,exynos3250-mfc",
1675f50ebe10SAakarsh Jain 		.data = &mfc_drvdata_v7_3250,
1676f50ebe10SAakarsh Jain 	}, {
167743ecec16SMauro Carvalho Chehab 		.compatible = "samsung,mfc-v8",
167843ecec16SMauro Carvalho Chehab 		.data = &mfc_drvdata_v8,
167943ecec16SMauro Carvalho Chehab 	}, {
168043ecec16SMauro Carvalho Chehab 		.compatible = "samsung,exynos5433-mfc",
168143ecec16SMauro Carvalho Chehab 		.data = &mfc_drvdata_v8_5433,
168243ecec16SMauro Carvalho Chehab 	}, {
168343ecec16SMauro Carvalho Chehab 		.compatible = "samsung,mfc-v10",
168443ecec16SMauro Carvalho Chehab 		.data = &mfc_drvdata_v10,
168543ecec16SMauro Carvalho Chehab 	},
168643ecec16SMauro Carvalho Chehab 	{},
168743ecec16SMauro Carvalho Chehab };
168843ecec16SMauro Carvalho Chehab MODULE_DEVICE_TABLE(of, exynos_mfc_match);
168943ecec16SMauro Carvalho Chehab 
169043ecec16SMauro Carvalho Chehab static struct platform_driver s5p_mfc_driver = {
169143ecec16SMauro Carvalho Chehab 	.probe		= s5p_mfc_probe,
1692*9709cf5bSUwe Kleine-König 	.remove_new	= s5p_mfc_remove,
169343ecec16SMauro Carvalho Chehab 	.driver	= {
169443ecec16SMauro Carvalho Chehab 		.name	= S5P_MFC_NAME,
169543ecec16SMauro Carvalho Chehab 		.pm	= &s5p_mfc_pm_ops,
169643ecec16SMauro Carvalho Chehab 		.of_match_table = exynos_mfc_match,
169743ecec16SMauro Carvalho Chehab 	},
169843ecec16SMauro Carvalho Chehab };
169943ecec16SMauro Carvalho Chehab 
170043ecec16SMauro Carvalho Chehab module_platform_driver(s5p_mfc_driver);
170143ecec16SMauro Carvalho Chehab 
170243ecec16SMauro Carvalho Chehab MODULE_LICENSE("GPL");
170343ecec16SMauro Carvalho Chehab MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
170443ecec16SMauro Carvalho Chehab MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");
170543ecec16SMauro Carvalho Chehab 
1706