18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Memory-to-memory device framework for Video for Linux 2 and videobuf.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Helper functions for devices that use videobuf buffers for both their
68c2ecf20Sopenharmony_ci * source and destination.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
98c2ecf20Sopenharmony_ci * Pawel Osciak, <pawel@osciak.com>
108c2ecf20Sopenharmony_ci * Marek Szyprowski, <m.szyprowski@samsung.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/sched.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <media/media-device.h>
178c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h>
188c2ecf20Sopenharmony_ci#include <media/v4l2-mem2mem.h>
198c2ecf20Sopenharmony_ci#include <media/v4l2-dev.h>
208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
218c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mem to mem device framework for videobuf");
258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic bool debug;
298c2ecf20Sopenharmony_cimodule_param(debug, bool, 0644);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define dprintk(fmt, arg...)						\
328c2ecf20Sopenharmony_ci	do {								\
338c2ecf20Sopenharmony_ci		if (debug)						\
348c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\
358c2ecf20Sopenharmony_ci	} while (0)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Instance is already queued on the job_queue */
398c2ecf20Sopenharmony_ci#define TRANS_QUEUED		(1 << 0)
408c2ecf20Sopenharmony_ci/* Instance is currently running in hardware */
418c2ecf20Sopenharmony_ci#define TRANS_RUNNING		(1 << 1)
428c2ecf20Sopenharmony_ci/* Instance is currently aborting */
438c2ecf20Sopenharmony_ci#define TRANS_ABORT		(1 << 2)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* The job queue is not running new jobs */
478c2ecf20Sopenharmony_ci#define QUEUE_PAUSED		(1 << 0)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Offset base for buffers on the destination queue - used to distinguish
518c2ecf20Sopenharmony_ci * between source and destination buffers when mmapping - they receive the same
528c2ecf20Sopenharmony_ci * offsets but for different queues */
538c2ecf20Sopenharmony_ci#define DST_QUEUE_OFF_BASE	(1 << 30)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cienum v4l2_m2m_entity_type {
568c2ecf20Sopenharmony_ci	MEM2MEM_ENT_TYPE_SOURCE,
578c2ecf20Sopenharmony_ci	MEM2MEM_ENT_TYPE_SINK,
588c2ecf20Sopenharmony_ci	MEM2MEM_ENT_TYPE_PROC
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const char * const m2m_entity_name[] = {
628c2ecf20Sopenharmony_ci	"source",
638c2ecf20Sopenharmony_ci	"sink",
648c2ecf20Sopenharmony_ci	"proc"
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/**
688c2ecf20Sopenharmony_ci * struct v4l2_m2m_dev - per-device context
698c2ecf20Sopenharmony_ci * @source:		&struct media_entity pointer with the source entity
708c2ecf20Sopenharmony_ci *			Used only when the M2M device is registered via
718c2ecf20Sopenharmony_ci *			v4l2_m2m_unregister_media_controller().
728c2ecf20Sopenharmony_ci * @source_pad:		&struct media_pad with the source pad.
738c2ecf20Sopenharmony_ci *			Used only when the M2M device is registered via
748c2ecf20Sopenharmony_ci *			v4l2_m2m_unregister_media_controller().
758c2ecf20Sopenharmony_ci * @sink:		&struct media_entity pointer with the sink entity
768c2ecf20Sopenharmony_ci *			Used only when the M2M device is registered via
778c2ecf20Sopenharmony_ci *			v4l2_m2m_unregister_media_controller().
788c2ecf20Sopenharmony_ci * @sink_pad:		&struct media_pad with the sink pad.
798c2ecf20Sopenharmony_ci *			Used only when the M2M device is registered via
808c2ecf20Sopenharmony_ci *			v4l2_m2m_unregister_media_controller().
818c2ecf20Sopenharmony_ci * @proc:		&struct media_entity pointer with the M2M device itself.
828c2ecf20Sopenharmony_ci * @proc_pads:		&struct media_pad with the @proc pads.
838c2ecf20Sopenharmony_ci *			Used only when the M2M device is registered via
848c2ecf20Sopenharmony_ci *			v4l2_m2m_unregister_media_controller().
858c2ecf20Sopenharmony_ci * @intf_devnode:	&struct media_intf devnode pointer with the interface
868c2ecf20Sopenharmony_ci *			with controls the M2M device.
878c2ecf20Sopenharmony_ci * @curr_ctx:		currently running instance
888c2ecf20Sopenharmony_ci * @job_queue:		instances queued to run
898c2ecf20Sopenharmony_ci * @job_spinlock:	protects job_queue
908c2ecf20Sopenharmony_ci * @job_work:		worker to run queued jobs.
918c2ecf20Sopenharmony_ci * @job_queue_flags:	flags of the queue status, %QUEUE_PAUSED.
928c2ecf20Sopenharmony_ci * @m2m_ops:		driver callbacks
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistruct v4l2_m2m_dev {
958c2ecf20Sopenharmony_ci	struct v4l2_m2m_ctx	*curr_ctx;
968c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
978c2ecf20Sopenharmony_ci	struct media_entity	*source;
988c2ecf20Sopenharmony_ci	struct media_pad	source_pad;
998c2ecf20Sopenharmony_ci	struct media_entity	sink;
1008c2ecf20Sopenharmony_ci	struct media_pad	sink_pad;
1018c2ecf20Sopenharmony_ci	struct media_entity	proc;
1028c2ecf20Sopenharmony_ci	struct media_pad	proc_pads[2];
1038c2ecf20Sopenharmony_ci	struct media_intf_devnode *intf_devnode;
1048c2ecf20Sopenharmony_ci#endif
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	struct list_head	job_queue;
1078c2ecf20Sopenharmony_ci	spinlock_t		job_spinlock;
1088c2ecf20Sopenharmony_ci	struct work_struct	job_work;
1098c2ecf20Sopenharmony_ci	unsigned long		job_queue_flags;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	const struct v4l2_m2m_ops *m2m_ops;
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
1158c2ecf20Sopenharmony_ci						enum v4l2_buf_type type)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(type))
1188c2ecf20Sopenharmony_ci		return &m2m_ctx->out_q_ctx;
1198c2ecf20Sopenharmony_ci	else
1208c2ecf20Sopenharmony_ci		return &m2m_ctx->cap_q_ctx;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistruct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
1248c2ecf20Sopenharmony_ci				       enum v4l2_buf_type type)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct v4l2_m2m_queue_ctx *q_ctx;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	q_ctx = get_queue_ctx(m2m_ctx, type);
1298c2ecf20Sopenharmony_ci	if (!q_ctx)
1308c2ecf20Sopenharmony_ci		return NULL;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return &q_ctx->q;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(v4l2_m2m_get_vq);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistruct vb2_v4l2_buffer *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct v4l2_m2m_buffer *b;
1398c2ecf20Sopenharmony_ci	unsigned long flags;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (list_empty(&q_ctx->rdy_queue)) {
1448c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
1458c2ecf20Sopenharmony_ci		return NULL;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	b = list_first_entry(&q_ctx->rdy_queue, struct v4l2_m2m_buffer, list);
1498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
1508c2ecf20Sopenharmony_ci	return &b->vb;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistruct vb2_v4l2_buffer *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct v4l2_m2m_buffer *b;
1578c2ecf20Sopenharmony_ci	unsigned long flags;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (list_empty(&q_ctx->rdy_queue)) {
1628c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
1638c2ecf20Sopenharmony_ci		return NULL;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	b = list_last_entry(&q_ctx->rdy_queue, struct v4l2_m2m_buffer, list);
1678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
1688c2ecf20Sopenharmony_ci	return &b->vb;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_last_buf);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistruct vb2_v4l2_buffer *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct v4l2_m2m_buffer *b;
1758c2ecf20Sopenharmony_ci	unsigned long flags;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
1788c2ecf20Sopenharmony_ci	if (list_empty(&q_ctx->rdy_queue)) {
1798c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
1808c2ecf20Sopenharmony_ci		return NULL;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	b = list_first_entry(&q_ctx->rdy_queue, struct v4l2_m2m_buffer, list);
1838c2ecf20Sopenharmony_ci	list_del(&b->list);
1848c2ecf20Sopenharmony_ci	q_ctx->num_rdy--;
1858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return &b->vb;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_civoid v4l2_m2m_buf_remove_by_buf(struct v4l2_m2m_queue_ctx *q_ctx,
1928c2ecf20Sopenharmony_ci				struct vb2_v4l2_buffer *vbuf)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct v4l2_m2m_buffer *b;
1958c2ecf20Sopenharmony_ci	unsigned long flags;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
1988c2ecf20Sopenharmony_ci	b = container_of(vbuf, struct v4l2_m2m_buffer, vb);
1998c2ecf20Sopenharmony_ci	list_del(&b->list);
2008c2ecf20Sopenharmony_ci	q_ctx->num_rdy--;
2018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove_by_buf);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistruct vb2_v4l2_buffer *
2068c2ecf20Sopenharmony_civ4l2_m2m_buf_remove_by_idx(struct v4l2_m2m_queue_ctx *q_ctx, unsigned int idx)
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct v4l2_m2m_buffer *b, *tmp;
2108c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *ret = NULL;
2118c2ecf20Sopenharmony_ci	unsigned long flags;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
2148c2ecf20Sopenharmony_ci	list_for_each_entry_safe(b, tmp, &q_ctx->rdy_queue, list) {
2158c2ecf20Sopenharmony_ci		if (b->vb.vb2_buf.index == idx) {
2168c2ecf20Sopenharmony_ci			list_del(&b->list);
2178c2ecf20Sopenharmony_ci			q_ctx->num_rdy--;
2188c2ecf20Sopenharmony_ci			ret = &b->vb;
2198c2ecf20Sopenharmony_ci			break;
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return ret;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove_by_idx);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/*
2298c2ecf20Sopenharmony_ci * Scheduling handlers
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_civoid *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	unsigned long flags;
2358c2ecf20Sopenharmony_ci	void *ret = NULL;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
2388c2ecf20Sopenharmony_ci	if (m2m_dev->curr_ctx)
2398c2ecf20Sopenharmony_ci		ret = m2m_dev->curr_ctx->priv;
2408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return ret;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(v4l2_m2m_get_curr_priv);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/**
2478c2ecf20Sopenharmony_ci * v4l2_m2m_try_run() - select next job to perform and run it if possible
2488c2ecf20Sopenharmony_ci * @m2m_dev: per-device context
2498c2ecf20Sopenharmony_ci *
2508c2ecf20Sopenharmony_ci * Get next transaction (if present) from the waiting jobs list and run it.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * Note that this function can run on a given v4l2_m2m_ctx context,
2538c2ecf20Sopenharmony_ci * but call .device_run for another context.
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_cistatic void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	unsigned long flags;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
2608c2ecf20Sopenharmony_ci	if (NULL != m2m_dev->curr_ctx) {
2618c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
2628c2ecf20Sopenharmony_ci		dprintk("Another instance is running, won't run now\n");
2638c2ecf20Sopenharmony_ci		return;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (list_empty(&m2m_dev->job_queue)) {
2678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
2688c2ecf20Sopenharmony_ci		dprintk("No job pending\n");
2698c2ecf20Sopenharmony_ci		return;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (m2m_dev->job_queue_flags & QUEUE_PAUSED) {
2738c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
2748c2ecf20Sopenharmony_ci		dprintk("Running new jobs is paused\n");
2758c2ecf20Sopenharmony_ci		return;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	m2m_dev->curr_ctx = list_first_entry(&m2m_dev->job_queue,
2798c2ecf20Sopenharmony_ci				   struct v4l2_m2m_ctx, queue);
2808c2ecf20Sopenharmony_ci	m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING;
2818c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	dprintk("Running job on m2m_ctx: %p\n", m2m_dev->curr_ctx);
2848c2ecf20Sopenharmony_ci	m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv);
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * __v4l2_m2m_try_queue() - queue a job
2898c2ecf20Sopenharmony_ci * @m2m_dev: m2m device
2908c2ecf20Sopenharmony_ci * @m2m_ctx: m2m context
2918c2ecf20Sopenharmony_ci *
2928c2ecf20Sopenharmony_ci * Check if this context is ready to queue a job.
2938c2ecf20Sopenharmony_ci *
2948c2ecf20Sopenharmony_ci * This function can run in interrupt context.
2958c2ecf20Sopenharmony_ci */
2968c2ecf20Sopenharmony_cistatic void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,
2978c2ecf20Sopenharmony_ci				 struct v4l2_m2m_ctx *m2m_ctx)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	unsigned long flags_job;
3008c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *dst, *src;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (!m2m_ctx->out_q_ctx.q.streaming
3058c2ecf20Sopenharmony_ci	    || !m2m_ctx->cap_q_ctx.q.streaming) {
3068c2ecf20Sopenharmony_ci		dprintk("Streaming needs to be on for both queues\n");
3078c2ecf20Sopenharmony_ci		return;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* If the context is aborted then don't schedule it */
3138c2ecf20Sopenharmony_ci	if (m2m_ctx->job_flags & TRANS_ABORT) {
3148c2ecf20Sopenharmony_ci		dprintk("Aborted context\n");
3158c2ecf20Sopenharmony_ci		goto job_unlock;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (m2m_ctx->job_flags & TRANS_QUEUED) {
3198c2ecf20Sopenharmony_ci		dprintk("On job queue already\n");
3208c2ecf20Sopenharmony_ci		goto job_unlock;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	src = v4l2_m2m_next_src_buf(m2m_ctx);
3248c2ecf20Sopenharmony_ci	dst = v4l2_m2m_next_dst_buf(m2m_ctx);
3258c2ecf20Sopenharmony_ci	if (!src && !m2m_ctx->out_q_ctx.buffered) {
3268c2ecf20Sopenharmony_ci		dprintk("No input buffers available\n");
3278c2ecf20Sopenharmony_ci		goto job_unlock;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
3308c2ecf20Sopenharmony_ci		dprintk("No output buffers available\n");
3318c2ecf20Sopenharmony_ci		goto job_unlock;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	m2m_ctx->new_frame = true;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (src && dst && dst->is_held &&
3378c2ecf20Sopenharmony_ci	    dst->vb2_buf.copied_timestamp &&
3388c2ecf20Sopenharmony_ci	    dst->vb2_buf.timestamp != src->vb2_buf.timestamp) {
3398c2ecf20Sopenharmony_ci		dst->is_held = false;
3408c2ecf20Sopenharmony_ci		v4l2_m2m_dst_buf_remove(m2m_ctx);
3418c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
3428c2ecf20Sopenharmony_ci		dst = v4l2_m2m_next_dst_buf(m2m_ctx);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
3458c2ecf20Sopenharmony_ci			dprintk("No output buffers available after returning held buffer\n");
3468c2ecf20Sopenharmony_ci			goto job_unlock;
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (src && dst && (m2m_ctx->out_q_ctx.q.subsystem_flags &
3518c2ecf20Sopenharmony_ci			   VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
3528c2ecf20Sopenharmony_ci		m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp ||
3538c2ecf20Sopenharmony_ci			dst->vb2_buf.timestamp != src->vb2_buf.timestamp;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (m2m_ctx->has_stopped) {
3568c2ecf20Sopenharmony_ci		dprintk("Device has stopped\n");
3578c2ecf20Sopenharmony_ci		goto job_unlock;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (m2m_dev->m2m_ops->job_ready
3618c2ecf20Sopenharmony_ci		&& (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
3628c2ecf20Sopenharmony_ci		dprintk("Driver not ready\n");
3638c2ecf20Sopenharmony_ci		goto job_unlock;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
3678c2ecf20Sopenharmony_ci	m2m_ctx->job_flags |= TRANS_QUEUED;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cijob_unlock:
3708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/**
3748c2ecf20Sopenharmony_ci * v4l2_m2m_try_schedule() - schedule and possibly run a job for any context
3758c2ecf20Sopenharmony_ci * @m2m_ctx: m2m context
3768c2ecf20Sopenharmony_ci *
3778c2ecf20Sopenharmony_ci * Check if this context is ready to queue a job. If suitable,
3788c2ecf20Sopenharmony_ci * run the next queued job on the mem2mem device.
3798c2ecf20Sopenharmony_ci *
3808c2ecf20Sopenharmony_ci * This function shouldn't run in interrupt context.
3818c2ecf20Sopenharmony_ci *
3828c2ecf20Sopenharmony_ci * Note that v4l2_m2m_try_schedule() can schedule one job for this context,
3838c2ecf20Sopenharmony_ci * and then run another job for another context.
3848c2ecf20Sopenharmony_ci */
3858c2ecf20Sopenharmony_civoid v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev = m2m_ctx->m2m_dev;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	__v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
3908c2ecf20Sopenharmony_ci	v4l2_m2m_try_run(m2m_dev);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_try_schedule);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/**
3958c2ecf20Sopenharmony_ci * v4l2_m2m_device_run_work() - run pending jobs for the context
3968c2ecf20Sopenharmony_ci * @work: Work structure used for scheduling the execution of this function.
3978c2ecf20Sopenharmony_ci */
3988c2ecf20Sopenharmony_cistatic void v4l2_m2m_device_run_work(struct work_struct *work)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev =
4018c2ecf20Sopenharmony_ci		container_of(work, struct v4l2_m2m_dev, job_work);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	v4l2_m2m_try_run(m2m_dev);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/**
4078c2ecf20Sopenharmony_ci * v4l2_m2m_cancel_job() - cancel pending jobs for the context
4088c2ecf20Sopenharmony_ci * @m2m_ctx: m2m context with jobs to be canceled
4098c2ecf20Sopenharmony_ci *
4108c2ecf20Sopenharmony_ci * In case of streamoff or release called on any context,
4118c2ecf20Sopenharmony_ci * 1] If the context is currently running, then abort job will be called
4128c2ecf20Sopenharmony_ci * 2] If the context is queued, then the context will be removed from
4138c2ecf20Sopenharmony_ci *    the job_queue
4148c2ecf20Sopenharmony_ci */
4158c2ecf20Sopenharmony_cistatic void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev;
4188c2ecf20Sopenharmony_ci	unsigned long flags;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	m2m_dev = m2m_ctx->m2m_dev;
4218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	m2m_ctx->job_flags |= TRANS_ABORT;
4248c2ecf20Sopenharmony_ci	if (m2m_ctx->job_flags & TRANS_RUNNING) {
4258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
4268c2ecf20Sopenharmony_ci		if (m2m_dev->m2m_ops->job_abort)
4278c2ecf20Sopenharmony_ci			m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
4288c2ecf20Sopenharmony_ci		dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
4298c2ecf20Sopenharmony_ci		wait_event(m2m_ctx->finished,
4308c2ecf20Sopenharmony_ci				!(m2m_ctx->job_flags & TRANS_RUNNING));
4318c2ecf20Sopenharmony_ci	} else if (m2m_ctx->job_flags & TRANS_QUEUED) {
4328c2ecf20Sopenharmony_ci		list_del(&m2m_ctx->queue);
4338c2ecf20Sopenharmony_ci		m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
4348c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
4358c2ecf20Sopenharmony_ci		dprintk("m2m_ctx: %p had been on queue and was removed\n",
4368c2ecf20Sopenharmony_ci			m2m_ctx);
4378c2ecf20Sopenharmony_ci	} else {
4388c2ecf20Sopenharmony_ci		/* Do nothing, was not on queue/running */
4398c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/*
4448c2ecf20Sopenharmony_ci * Schedule the next job, called from v4l2_m2m_job_finish() or
4458c2ecf20Sopenharmony_ci * v4l2_m2m_buf_done_and_job_finish().
4468c2ecf20Sopenharmony_ci */
4478c2ecf20Sopenharmony_cistatic void v4l2_m2m_schedule_next_job(struct v4l2_m2m_dev *m2m_dev,
4488c2ecf20Sopenharmony_ci				       struct v4l2_m2m_ctx *m2m_ctx)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	/*
4518c2ecf20Sopenharmony_ci	 * This instance might have more buffers ready, but since we do not
4528c2ecf20Sopenharmony_ci	 * allow more than one job on the job_queue per instance, each has
4538c2ecf20Sopenharmony_ci	 * to be scheduled separately after the previous one finishes.
4548c2ecf20Sopenharmony_ci	 */
4558c2ecf20Sopenharmony_ci	__v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/*
4588c2ecf20Sopenharmony_ci	 * We might be running in atomic context,
4598c2ecf20Sopenharmony_ci	 * but the job must be run in non-atomic context.
4608c2ecf20Sopenharmony_ci	 */
4618c2ecf20Sopenharmony_ci	schedule_work(&m2m_dev->job_work);
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci/*
4658c2ecf20Sopenharmony_ci * Assumes job_spinlock is held, called from v4l2_m2m_job_finish() or
4668c2ecf20Sopenharmony_ci * v4l2_m2m_buf_done_and_job_finish().
4678c2ecf20Sopenharmony_ci */
4688c2ecf20Sopenharmony_cistatic bool _v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
4698c2ecf20Sopenharmony_ci				 struct v4l2_m2m_ctx *m2m_ctx)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
4728c2ecf20Sopenharmony_ci		dprintk("Called by an instance not currently running\n");
4738c2ecf20Sopenharmony_ci		return false;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	list_del(&m2m_dev->curr_ctx->queue);
4778c2ecf20Sopenharmony_ci	m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
4788c2ecf20Sopenharmony_ci	wake_up(&m2m_dev->curr_ctx->finished);
4798c2ecf20Sopenharmony_ci	m2m_dev->curr_ctx = NULL;
4808c2ecf20Sopenharmony_ci	return true;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_civoid v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
4848c2ecf20Sopenharmony_ci			 struct v4l2_m2m_ctx *m2m_ctx)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	unsigned long flags;
4878c2ecf20Sopenharmony_ci	bool schedule_next;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/*
4908c2ecf20Sopenharmony_ci	 * This function should not be used for drivers that support
4918c2ecf20Sopenharmony_ci	 * holding capture buffers. Those should use
4928c2ecf20Sopenharmony_ci	 * v4l2_m2m_buf_done_and_job_finish() instead.
4938c2ecf20Sopenharmony_ci	 */
4948c2ecf20Sopenharmony_ci	WARN_ON(m2m_ctx->out_q_ctx.q.subsystem_flags &
4958c2ecf20Sopenharmony_ci		VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF);
4968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
4978c2ecf20Sopenharmony_ci	schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
4988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (schedule_next)
5018c2ecf20Sopenharmony_ci		v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(v4l2_m2m_job_finish);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_civoid v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
5068c2ecf20Sopenharmony_ci				      struct v4l2_m2m_ctx *m2m_ctx,
5078c2ecf20Sopenharmony_ci				      enum vb2_buffer_state state)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *src_buf, *dst_buf;
5108c2ecf20Sopenharmony_ci	bool schedule_next = false;
5118c2ecf20Sopenharmony_ci	unsigned long flags;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
5148c2ecf20Sopenharmony_ci	src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
5158c2ecf20Sopenharmony_ci	dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (WARN_ON(!src_buf || !dst_buf))
5188c2ecf20Sopenharmony_ci		goto unlock;
5198c2ecf20Sopenharmony_ci	dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
5208c2ecf20Sopenharmony_ci	if (!dst_buf->is_held) {
5218c2ecf20Sopenharmony_ci		v4l2_m2m_dst_buf_remove(m2m_ctx);
5228c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(dst_buf, state);
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	/*
5258c2ecf20Sopenharmony_ci	 * If the request API is being used, returning the OUTPUT
5268c2ecf20Sopenharmony_ci	 * (src) buffer will wake-up any process waiting on the
5278c2ecf20Sopenharmony_ci	 * request file descriptor.
5288c2ecf20Sopenharmony_ci	 *
5298c2ecf20Sopenharmony_ci	 * Therefore, return the CAPTURE (dst) buffer first,
5308c2ecf20Sopenharmony_ci	 * to avoid signalling the request file descriptor
5318c2ecf20Sopenharmony_ci	 * before the CAPTURE buffer is done.
5328c2ecf20Sopenharmony_ci	 */
5338c2ecf20Sopenharmony_ci	v4l2_m2m_buf_done(src_buf, state);
5348c2ecf20Sopenharmony_ci	schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
5358c2ecf20Sopenharmony_ciunlock:
5368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (schedule_next)
5398c2ecf20Sopenharmony_ci		v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(v4l2_m2m_buf_done_and_job_finish);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_civoid v4l2_m2m_suspend(struct v4l2_m2m_dev *m2m_dev)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	unsigned long flags;
5468c2ecf20Sopenharmony_ci	struct v4l2_m2m_ctx *curr_ctx;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
5498c2ecf20Sopenharmony_ci	m2m_dev->job_queue_flags |= QUEUE_PAUSED;
5508c2ecf20Sopenharmony_ci	curr_ctx = m2m_dev->curr_ctx;
5518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (curr_ctx)
5548c2ecf20Sopenharmony_ci		wait_event(curr_ctx->finished,
5558c2ecf20Sopenharmony_ci			   !(curr_ctx->job_flags & TRANS_RUNNING));
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(v4l2_m2m_suspend);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_civoid v4l2_m2m_resume(struct v4l2_m2m_dev *m2m_dev)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	unsigned long flags;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
5648c2ecf20Sopenharmony_ci	m2m_dev->job_queue_flags &= ~QUEUE_PAUSED;
5658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	v4l2_m2m_try_run(m2m_dev);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(v4l2_m2m_resume);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ciint v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
5728c2ecf20Sopenharmony_ci		     struct v4l2_requestbuffers *reqbufs)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
5758c2ecf20Sopenharmony_ci	int ret;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
5788c2ecf20Sopenharmony_ci	ret = vb2_reqbufs(vq, reqbufs);
5798c2ecf20Sopenharmony_ci	/* If count == 0, then the owner has released all buffers and he
5808c2ecf20Sopenharmony_ci	   is no longer owner of the queue. Otherwise we have an owner. */
5818c2ecf20Sopenharmony_ci	if (ret == 0)
5828c2ecf20Sopenharmony_ci		vq->owner = reqbufs->count ? file->private_data : NULL;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	return ret;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic void v4l2_m2m_adjust_mem_offset(struct vb2_queue *vq,
5898c2ecf20Sopenharmony_ci				       struct v4l2_buffer *buf)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	/* Adjust MMAP memory offsets for the CAPTURE queue */
5928c2ecf20Sopenharmony_ci	if (buf->memory == V4L2_MEMORY_MMAP && V4L2_TYPE_IS_CAPTURE(vq->type)) {
5938c2ecf20Sopenharmony_ci		if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
5948c2ecf20Sopenharmony_ci			unsigned int i;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci			for (i = 0; i < buf->length; ++i)
5978c2ecf20Sopenharmony_ci				buf->m.planes[i].m.mem_offset
5988c2ecf20Sopenharmony_ci					+= DST_QUEUE_OFF_BASE;
5998c2ecf20Sopenharmony_ci		} else {
6008c2ecf20Sopenharmony_ci			buf->m.offset += DST_QUEUE_OFF_BASE;
6018c2ecf20Sopenharmony_ci		}
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ciint v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
6068c2ecf20Sopenharmony_ci		      struct v4l2_buffer *buf)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
6098c2ecf20Sopenharmony_ci	int ret;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
6128c2ecf20Sopenharmony_ci	ret = vb2_querybuf(vq, buf);
6138c2ecf20Sopenharmony_ci	if (ret)
6148c2ecf20Sopenharmony_ci		return ret;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	/* Adjust MMAP memory offsets for the CAPTURE queue */
6178c2ecf20Sopenharmony_ci	v4l2_m2m_adjust_mem_offset(vq, buf);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	return 0;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci/*
6248c2ecf20Sopenharmony_ci * This will add the LAST flag and mark the buffer management
6258c2ecf20Sopenharmony_ci * state as stopped.
6268c2ecf20Sopenharmony_ci * This is called when the last capture buffer must be flagged as LAST
6278c2ecf20Sopenharmony_ci * in draining mode from the encoder/decoder driver buf_queue() callback
6288c2ecf20Sopenharmony_ci * or from v4l2_update_last_buf_state() when a capture buffer is available.
6298c2ecf20Sopenharmony_ci */
6308c2ecf20Sopenharmony_civoid v4l2_m2m_last_buffer_done(struct v4l2_m2m_ctx *m2m_ctx,
6318c2ecf20Sopenharmony_ci			       struct vb2_v4l2_buffer *vbuf)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	vbuf->flags |= V4L2_BUF_FLAG_LAST;
6348c2ecf20Sopenharmony_ci	vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	v4l2_m2m_mark_stopped(m2m_ctx);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_last_buffer_done);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci/* When stop command is issued, update buffer management state */
6418c2ecf20Sopenharmony_cistatic int v4l2_update_last_buf_state(struct v4l2_m2m_ctx *m2m_ctx)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *next_dst_buf;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (m2m_ctx->is_draining)
6468c2ecf20Sopenharmony_ci		return -EBUSY;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	if (m2m_ctx->has_stopped)
6498c2ecf20Sopenharmony_ci		return 0;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
6528c2ecf20Sopenharmony_ci	m2m_ctx->is_draining = true;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/*
6558c2ecf20Sopenharmony_ci	 * The processing of the last output buffer queued before
6568c2ecf20Sopenharmony_ci	 * the STOP command is expected to mark the buffer management
6578c2ecf20Sopenharmony_ci	 * state as stopped with v4l2_m2m_mark_stopped().
6588c2ecf20Sopenharmony_ci	 */
6598c2ecf20Sopenharmony_ci	if (m2m_ctx->last_src_buf)
6608c2ecf20Sopenharmony_ci		return 0;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/*
6638c2ecf20Sopenharmony_ci	 * In case the output queue is empty, try to mark the last capture
6648c2ecf20Sopenharmony_ci	 * buffer as LAST.
6658c2ecf20Sopenharmony_ci	 */
6668c2ecf20Sopenharmony_ci	next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
6678c2ecf20Sopenharmony_ci	if (!next_dst_buf) {
6688c2ecf20Sopenharmony_ci		/*
6698c2ecf20Sopenharmony_ci		 * Wait for the next queued one in encoder/decoder driver
6708c2ecf20Sopenharmony_ci		 * buf_queue() callback using the v4l2_m2m_dst_buf_is_last()
6718c2ecf20Sopenharmony_ci		 * helper or in v4l2_m2m_qbuf() if encoder/decoder is not yet
6728c2ecf20Sopenharmony_ci		 * streaming.
6738c2ecf20Sopenharmony_ci		 */
6748c2ecf20Sopenharmony_ci		m2m_ctx->next_buf_last = true;
6758c2ecf20Sopenharmony_ci		return 0;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	v4l2_m2m_last_buffer_done(m2m_ctx, next_dst_buf);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return 0;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/*
6848c2ecf20Sopenharmony_ci * Updates the encoding/decoding buffer management state, should
6858c2ecf20Sopenharmony_ci * be called from encoder/decoder drivers start_streaming()
6868c2ecf20Sopenharmony_ci */
6878c2ecf20Sopenharmony_civoid v4l2_m2m_update_start_streaming_state(struct v4l2_m2m_ctx *m2m_ctx,
6888c2ecf20Sopenharmony_ci					   struct vb2_queue *q)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	/* If start streaming again, untag the last output buffer */
6918c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(q->type))
6928c2ecf20Sopenharmony_ci		m2m_ctx->last_src_buf = NULL;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_update_start_streaming_state);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci/*
6978c2ecf20Sopenharmony_ci * Updates the encoding/decoding buffer management state, should
6988c2ecf20Sopenharmony_ci * be called from encoder/decoder driver stop_streaming()
6998c2ecf20Sopenharmony_ci */
7008c2ecf20Sopenharmony_civoid v4l2_m2m_update_stop_streaming_state(struct v4l2_m2m_ctx *m2m_ctx,
7018c2ecf20Sopenharmony_ci					  struct vb2_queue *q)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
7048c2ecf20Sopenharmony_ci		/*
7058c2ecf20Sopenharmony_ci		 * If in draining state, either mark next dst buffer as
7068c2ecf20Sopenharmony_ci		 * done or flag next one to be marked as done either
7078c2ecf20Sopenharmony_ci		 * in encoder/decoder driver buf_queue() callback using
7088c2ecf20Sopenharmony_ci		 * the v4l2_m2m_dst_buf_is_last() helper or in v4l2_m2m_qbuf()
7098c2ecf20Sopenharmony_ci		 * if encoder/decoder is not yet streaming
7108c2ecf20Sopenharmony_ci		 */
7118c2ecf20Sopenharmony_ci		if (m2m_ctx->is_draining) {
7128c2ecf20Sopenharmony_ci			struct vb2_v4l2_buffer *next_dst_buf;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci			m2m_ctx->last_src_buf = NULL;
7158c2ecf20Sopenharmony_ci			next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
7168c2ecf20Sopenharmony_ci			if (!next_dst_buf)
7178c2ecf20Sopenharmony_ci				m2m_ctx->next_buf_last = true;
7188c2ecf20Sopenharmony_ci			else
7198c2ecf20Sopenharmony_ci				v4l2_m2m_last_buffer_done(m2m_ctx,
7208c2ecf20Sopenharmony_ci							  next_dst_buf);
7218c2ecf20Sopenharmony_ci		}
7228c2ecf20Sopenharmony_ci	} else {
7238c2ecf20Sopenharmony_ci		v4l2_m2m_clear_state(m2m_ctx);
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_update_stop_streaming_state);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic void v4l2_m2m_force_last_buf_done(struct v4l2_m2m_ctx *m2m_ctx,
7298c2ecf20Sopenharmony_ci					 struct vb2_queue *q)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	struct vb2_buffer *vb;
7328c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf;
7338c2ecf20Sopenharmony_ci	unsigned int i;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (WARN_ON(q->is_output))
7368c2ecf20Sopenharmony_ci		return;
7378c2ecf20Sopenharmony_ci	if (list_empty(&q->queued_list))
7388c2ecf20Sopenharmony_ci		return;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	vb = list_first_entry(&q->queued_list, struct vb2_buffer, queued_entry);
7418c2ecf20Sopenharmony_ci	for (i = 0; i < vb->num_planes; i++)
7428c2ecf20Sopenharmony_ci		vb2_set_plane_payload(vb, i, 0);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/*
7458c2ecf20Sopenharmony_ci	 * Since the buffer hasn't been queued to the ready queue,
7468c2ecf20Sopenharmony_ci	 * mark is active and owned before marking it LAST and DONE
7478c2ecf20Sopenharmony_ci	 */
7488c2ecf20Sopenharmony_ci	vb->state = VB2_BUF_STATE_ACTIVE;
7498c2ecf20Sopenharmony_ci	atomic_inc(&q->owned_by_drv_count);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	vbuf = to_vb2_v4l2_buffer(vb);
7528c2ecf20Sopenharmony_ci	vbuf->field = V4L2_FIELD_NONE;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	v4l2_m2m_last_buffer_done(m2m_ctx, vbuf);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ciint v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
7588c2ecf20Sopenharmony_ci		  struct v4l2_buffer *buf)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
7618c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
7628c2ecf20Sopenharmony_ci	int ret;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
7658c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_CAPTURE(vq->type) &&
7668c2ecf20Sopenharmony_ci	    (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
7678c2ecf20Sopenharmony_ci		dprintk("%s: requests cannot be used with capture buffers\n",
7688c2ecf20Sopenharmony_ci			__func__);
7698c2ecf20Sopenharmony_ci		return -EPERM;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf);
7738c2ecf20Sopenharmony_ci	if (ret)
7748c2ecf20Sopenharmony_ci		return ret;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* Adjust MMAP memory offsets for the CAPTURE queue */
7778c2ecf20Sopenharmony_ci	v4l2_m2m_adjust_mem_offset(vq, buf);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/*
7808c2ecf20Sopenharmony_ci	 * If the capture queue is streaming, but streaming hasn't started
7818c2ecf20Sopenharmony_ci	 * on the device, but was asked to stop, mark the previously queued
7828c2ecf20Sopenharmony_ci	 * buffer as DONE with LAST flag since it won't be queued on the
7838c2ecf20Sopenharmony_ci	 * device.
7848c2ecf20Sopenharmony_ci	 */
7858c2ecf20Sopenharmony_ci	if (V4L2_TYPE_IS_CAPTURE(vq->type) &&
7868c2ecf20Sopenharmony_ci	    vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) &&
7878c2ecf20Sopenharmony_ci	   (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx)))
7888c2ecf20Sopenharmony_ci		v4l2_m2m_force_last_buf_done(m2m_ctx, vq);
7898c2ecf20Sopenharmony_ci	else if (!(buf->flags & V4L2_BUF_FLAG_IN_REQUEST))
7908c2ecf20Sopenharmony_ci		v4l2_m2m_try_schedule(m2m_ctx);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	return 0;
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ciint v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
7978c2ecf20Sopenharmony_ci		   struct v4l2_buffer *buf)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
8008c2ecf20Sopenharmony_ci	int ret;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
8038c2ecf20Sopenharmony_ci	ret = vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
8048c2ecf20Sopenharmony_ci	if (ret)
8058c2ecf20Sopenharmony_ci		return ret;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	/* Adjust MMAP memory offsets for the CAPTURE queue */
8088c2ecf20Sopenharmony_ci	v4l2_m2m_adjust_mem_offset(vq, buf);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	return 0;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ciint v4l2_m2m_prepare_buf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
8158c2ecf20Sopenharmony_ci			 struct v4l2_buffer *buf)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
8188c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
8198c2ecf20Sopenharmony_ci	int ret;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
8228c2ecf20Sopenharmony_ci	ret = vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf);
8238c2ecf20Sopenharmony_ci	if (ret)
8248c2ecf20Sopenharmony_ci		return ret;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* Adjust MMAP memory offsets for the CAPTURE queue */
8278c2ecf20Sopenharmony_ci	v4l2_m2m_adjust_mem_offset(vq, buf);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	return 0;
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_prepare_buf);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ciint v4l2_m2m_create_bufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
8348c2ecf20Sopenharmony_ci			 struct v4l2_create_buffers *create)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, create->format.type);
8398c2ecf20Sopenharmony_ci	return vb2_create_bufs(vq, create);
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_create_bufs);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ciint v4l2_m2m_expbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
8448c2ecf20Sopenharmony_ci		  struct v4l2_exportbuffer *eb)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, eb->type);
8498c2ecf20Sopenharmony_ci	return vb2_expbuf(vq, eb);
8508c2ecf20Sopenharmony_ci}
8518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_expbuf);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ciint v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
8548c2ecf20Sopenharmony_ci		      enum v4l2_buf_type type)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
8578c2ecf20Sopenharmony_ci	int ret;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	vq = v4l2_m2m_get_vq(m2m_ctx, type);
8608c2ecf20Sopenharmony_ci	ret = vb2_streamon(vq, type);
8618c2ecf20Sopenharmony_ci	if (!ret)
8628c2ecf20Sopenharmony_ci		v4l2_m2m_try_schedule(m2m_ctx);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	return ret;
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ciint v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
8698c2ecf20Sopenharmony_ci		       enum v4l2_buf_type type)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev;
8728c2ecf20Sopenharmony_ci	struct v4l2_m2m_queue_ctx *q_ctx;
8738c2ecf20Sopenharmony_ci	unsigned long flags_job, flags;
8748c2ecf20Sopenharmony_ci	int ret;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* wait until the current context is dequeued from job_queue */
8778c2ecf20Sopenharmony_ci	v4l2_m2m_cancel_job(m2m_ctx);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	q_ctx = get_queue_ctx(m2m_ctx, type);
8808c2ecf20Sopenharmony_ci	ret = vb2_streamoff(&q_ctx->q, type);
8818c2ecf20Sopenharmony_ci	if (ret)
8828c2ecf20Sopenharmony_ci		return ret;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	m2m_dev = m2m_ctx->m2m_dev;
8858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
8868c2ecf20Sopenharmony_ci	/* We should not be scheduled anymore, since we're dropping a queue. */
8878c2ecf20Sopenharmony_ci	if (m2m_ctx->job_flags & TRANS_QUEUED)
8888c2ecf20Sopenharmony_ci		list_del(&m2m_ctx->queue);
8898c2ecf20Sopenharmony_ci	m2m_ctx->job_flags = 0;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
8928c2ecf20Sopenharmony_ci	/* Drop queue, since streamoff returns device to the same state as after
8938c2ecf20Sopenharmony_ci	 * calling reqbufs. */
8948c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&q_ctx->rdy_queue);
8958c2ecf20Sopenharmony_ci	q_ctx->num_rdy = 0;
8968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (m2m_dev->curr_ctx == m2m_ctx) {
8998c2ecf20Sopenharmony_ci		m2m_dev->curr_ctx = NULL;
9008c2ecf20Sopenharmony_ci		wake_up(&m2m_ctx->finished);
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	return 0;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic __poll_t v4l2_m2m_poll_for_data(struct file *file,
9098c2ecf20Sopenharmony_ci				       struct v4l2_m2m_ctx *m2m_ctx,
9108c2ecf20Sopenharmony_ci				       struct poll_table_struct *wait)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	struct vb2_queue *src_q, *dst_q;
9138c2ecf20Sopenharmony_ci	__poll_t rc = 0;
9148c2ecf20Sopenharmony_ci	unsigned long flags;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	src_q = v4l2_m2m_get_src_vq(m2m_ctx);
9178c2ecf20Sopenharmony_ci	dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	poll_wait(file, &src_q->done_wq, wait);
9208c2ecf20Sopenharmony_ci	poll_wait(file, &dst_q->done_wq, wait);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	/*
9238c2ecf20Sopenharmony_ci	 * There has to be at least one buffer queued on each queued_list, which
9248c2ecf20Sopenharmony_ci	 * means either in driver already or waiting for driver to claim it
9258c2ecf20Sopenharmony_ci	 * and start processing.
9268c2ecf20Sopenharmony_ci	 */
9278c2ecf20Sopenharmony_ci	if ((!src_q->streaming || src_q->error ||
9288c2ecf20Sopenharmony_ci	     list_empty(&src_q->queued_list)) &&
9298c2ecf20Sopenharmony_ci	    (!dst_q->streaming || dst_q->error ||
9308c2ecf20Sopenharmony_ci	     (list_empty(&dst_q->queued_list) && !dst_q->last_buffer_dequeued)))
9318c2ecf20Sopenharmony_ci		return EPOLLERR;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&src_q->done_lock, flags);
9348c2ecf20Sopenharmony_ci	if (!list_empty(&src_q->done_list))
9358c2ecf20Sopenharmony_ci		rc |= EPOLLOUT | EPOLLWRNORM;
9368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&src_q->done_lock, flags);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dst_q->done_lock, flags);
9398c2ecf20Sopenharmony_ci	/*
9408c2ecf20Sopenharmony_ci	 * If the last buffer was dequeued from the capture queue, signal
9418c2ecf20Sopenharmony_ci	 * userspace. DQBUF(CAPTURE) will return -EPIPE.
9428c2ecf20Sopenharmony_ci	 */
9438c2ecf20Sopenharmony_ci	if (!list_empty(&dst_q->done_list) || dst_q->last_buffer_dequeued)
9448c2ecf20Sopenharmony_ci		rc |= EPOLLIN | EPOLLRDNORM;
9458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dst_q->done_lock, flags);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	return rc;
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci__poll_t v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
9518c2ecf20Sopenharmony_ci		       struct poll_table_struct *wait)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	struct video_device *vfd = video_devdata(file);
9548c2ecf20Sopenharmony_ci	__poll_t req_events = poll_requested_events(wait);
9558c2ecf20Sopenharmony_ci	__poll_t rc = 0;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (req_events & (EPOLLOUT | EPOLLWRNORM | EPOLLIN | EPOLLRDNORM))
9588c2ecf20Sopenharmony_ci		rc = v4l2_m2m_poll_for_data(file, m2m_ctx, wait);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
9618c2ecf20Sopenharmony_ci		struct v4l2_fh *fh = file->private_data;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci		poll_wait(file, &fh->wait, wait);
9648c2ecf20Sopenharmony_ci		if (v4l2_event_pending(fh))
9658c2ecf20Sopenharmony_ci			rc |= EPOLLPRI;
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	return rc;
9698c2ecf20Sopenharmony_ci}
9708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_poll);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ciint v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
9738c2ecf20Sopenharmony_ci			 struct vm_area_struct *vma)
9748c2ecf20Sopenharmony_ci{
9758c2ecf20Sopenharmony_ci	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
9768c2ecf20Sopenharmony_ci	struct vb2_queue *vq;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	if (offset < DST_QUEUE_OFF_BASE) {
9798c2ecf20Sopenharmony_ci		vq = v4l2_m2m_get_src_vq(m2m_ctx);
9808c2ecf20Sopenharmony_ci	} else {
9818c2ecf20Sopenharmony_ci		vq = v4l2_m2m_get_dst_vq(m2m_ctx);
9828c2ecf20Sopenharmony_ci		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	return vb2_mmap(vq, vma);
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(v4l2_m2m_mmap);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci#if defined(CONFIG_MEDIA_CONTROLLER)
9908c2ecf20Sopenharmony_civoid v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	media_remove_intf_links(&m2m_dev->intf_devnode->intf);
9938c2ecf20Sopenharmony_ci	media_devnode_remove(m2m_dev->intf_devnode);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	media_entity_remove_links(m2m_dev->source);
9968c2ecf20Sopenharmony_ci	media_entity_remove_links(&m2m_dev->sink);
9978c2ecf20Sopenharmony_ci	media_entity_remove_links(&m2m_dev->proc);
9988c2ecf20Sopenharmony_ci	media_device_unregister_entity(m2m_dev->source);
9998c2ecf20Sopenharmony_ci	media_device_unregister_entity(&m2m_dev->sink);
10008c2ecf20Sopenharmony_ci	media_device_unregister_entity(&m2m_dev->proc);
10018c2ecf20Sopenharmony_ci	kfree(m2m_dev->source->name);
10028c2ecf20Sopenharmony_ci	kfree(m2m_dev->sink.name);
10038c2ecf20Sopenharmony_ci	kfree(m2m_dev->proc.name);
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_unregister_media_controller);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_cistatic int v4l2_m2m_register_entity(struct media_device *mdev,
10088c2ecf20Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev, enum v4l2_m2m_entity_type type,
10098c2ecf20Sopenharmony_ci	struct video_device *vdev, int function)
10108c2ecf20Sopenharmony_ci{
10118c2ecf20Sopenharmony_ci	struct media_entity *entity;
10128c2ecf20Sopenharmony_ci	struct media_pad *pads;
10138c2ecf20Sopenharmony_ci	char *name;
10148c2ecf20Sopenharmony_ci	unsigned int len;
10158c2ecf20Sopenharmony_ci	int num_pads;
10168c2ecf20Sopenharmony_ci	int ret;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	switch (type) {
10198c2ecf20Sopenharmony_ci	case MEM2MEM_ENT_TYPE_SOURCE:
10208c2ecf20Sopenharmony_ci		entity = m2m_dev->source;
10218c2ecf20Sopenharmony_ci		pads = &m2m_dev->source_pad;
10228c2ecf20Sopenharmony_ci		pads[0].flags = MEDIA_PAD_FL_SOURCE;
10238c2ecf20Sopenharmony_ci		num_pads = 1;
10248c2ecf20Sopenharmony_ci		break;
10258c2ecf20Sopenharmony_ci	case MEM2MEM_ENT_TYPE_SINK:
10268c2ecf20Sopenharmony_ci		entity = &m2m_dev->sink;
10278c2ecf20Sopenharmony_ci		pads = &m2m_dev->sink_pad;
10288c2ecf20Sopenharmony_ci		pads[0].flags = MEDIA_PAD_FL_SINK;
10298c2ecf20Sopenharmony_ci		num_pads = 1;
10308c2ecf20Sopenharmony_ci		break;
10318c2ecf20Sopenharmony_ci	case MEM2MEM_ENT_TYPE_PROC:
10328c2ecf20Sopenharmony_ci		entity = &m2m_dev->proc;
10338c2ecf20Sopenharmony_ci		pads = m2m_dev->proc_pads;
10348c2ecf20Sopenharmony_ci		pads[0].flags = MEDIA_PAD_FL_SINK;
10358c2ecf20Sopenharmony_ci		pads[1].flags = MEDIA_PAD_FL_SOURCE;
10368c2ecf20Sopenharmony_ci		num_pads = 2;
10378c2ecf20Sopenharmony_ci		break;
10388c2ecf20Sopenharmony_ci	default:
10398c2ecf20Sopenharmony_ci		return -EINVAL;
10408c2ecf20Sopenharmony_ci	}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	entity->obj_type = MEDIA_ENTITY_TYPE_BASE;
10438c2ecf20Sopenharmony_ci	if (type != MEM2MEM_ENT_TYPE_PROC) {
10448c2ecf20Sopenharmony_ci		entity->info.dev.major = VIDEO_MAJOR;
10458c2ecf20Sopenharmony_ci		entity->info.dev.minor = vdev->minor;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci	len = strlen(vdev->name) + 2 + strlen(m2m_entity_name[type]);
10488c2ecf20Sopenharmony_ci	name = kmalloc(len, GFP_KERNEL);
10498c2ecf20Sopenharmony_ci	if (!name)
10508c2ecf20Sopenharmony_ci		return -ENOMEM;
10518c2ecf20Sopenharmony_ci	snprintf(name, len, "%s-%s", vdev->name, m2m_entity_name[type]);
10528c2ecf20Sopenharmony_ci	entity->name = name;
10538c2ecf20Sopenharmony_ci	entity->function = function;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(entity, num_pads, pads);
10568c2ecf20Sopenharmony_ci	if (ret) {
10578c2ecf20Sopenharmony_ci		kfree(entity->name);
10588c2ecf20Sopenharmony_ci		entity->name = NULL;
10598c2ecf20Sopenharmony_ci		return ret;
10608c2ecf20Sopenharmony_ci	}
10618c2ecf20Sopenharmony_ci	ret = media_device_register_entity(mdev, entity);
10628c2ecf20Sopenharmony_ci	if (ret) {
10638c2ecf20Sopenharmony_ci		kfree(entity->name);
10648c2ecf20Sopenharmony_ci		entity->name = NULL;
10658c2ecf20Sopenharmony_ci		return ret;
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	return 0;
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ciint v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev,
10728c2ecf20Sopenharmony_ci		struct video_device *vdev, int function)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	struct media_device *mdev = vdev->v4l2_dev->mdev;
10758c2ecf20Sopenharmony_ci	struct media_link *link;
10768c2ecf20Sopenharmony_ci	int ret;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	if (!mdev)
10798c2ecf20Sopenharmony_ci		return 0;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	/* A memory-to-memory device consists in two
10828c2ecf20Sopenharmony_ci	 * DMA engine and one video processing entities.
10838c2ecf20Sopenharmony_ci	 * The DMA engine entities are linked to a V4L interface
10848c2ecf20Sopenharmony_ci	 */
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	/* Create the three entities with their pads */
10878c2ecf20Sopenharmony_ci	m2m_dev->source = &vdev->entity;
10888c2ecf20Sopenharmony_ci	ret = v4l2_m2m_register_entity(mdev, m2m_dev,
10898c2ecf20Sopenharmony_ci			MEM2MEM_ENT_TYPE_SOURCE, vdev, MEDIA_ENT_F_IO_V4L);
10908c2ecf20Sopenharmony_ci	if (ret)
10918c2ecf20Sopenharmony_ci		return ret;
10928c2ecf20Sopenharmony_ci	ret = v4l2_m2m_register_entity(mdev, m2m_dev,
10938c2ecf20Sopenharmony_ci			MEM2MEM_ENT_TYPE_PROC, vdev, function);
10948c2ecf20Sopenharmony_ci	if (ret)
10958c2ecf20Sopenharmony_ci		goto err_rel_entity0;
10968c2ecf20Sopenharmony_ci	ret = v4l2_m2m_register_entity(mdev, m2m_dev,
10978c2ecf20Sopenharmony_ci			MEM2MEM_ENT_TYPE_SINK, vdev, MEDIA_ENT_F_IO_V4L);
10988c2ecf20Sopenharmony_ci	if (ret)
10998c2ecf20Sopenharmony_ci		goto err_rel_entity1;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Connect the three entities */
11028c2ecf20Sopenharmony_ci	ret = media_create_pad_link(m2m_dev->source, 0, &m2m_dev->proc, 0,
11038c2ecf20Sopenharmony_ci			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
11048c2ecf20Sopenharmony_ci	if (ret)
11058c2ecf20Sopenharmony_ci		goto err_rel_entity2;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	ret = media_create_pad_link(&m2m_dev->proc, 1, &m2m_dev->sink, 0,
11088c2ecf20Sopenharmony_ci			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
11098c2ecf20Sopenharmony_ci	if (ret)
11108c2ecf20Sopenharmony_ci		goto err_rm_links0;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	/* Create video interface */
11138c2ecf20Sopenharmony_ci	m2m_dev->intf_devnode = media_devnode_create(mdev,
11148c2ecf20Sopenharmony_ci			MEDIA_INTF_T_V4L_VIDEO, 0,
11158c2ecf20Sopenharmony_ci			VIDEO_MAJOR, vdev->minor);
11168c2ecf20Sopenharmony_ci	if (!m2m_dev->intf_devnode) {
11178c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11188c2ecf20Sopenharmony_ci		goto err_rm_links1;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	/* Connect the two DMA engines to the interface */
11228c2ecf20Sopenharmony_ci	link = media_create_intf_link(m2m_dev->source,
11238c2ecf20Sopenharmony_ci			&m2m_dev->intf_devnode->intf,
11248c2ecf20Sopenharmony_ci			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
11258c2ecf20Sopenharmony_ci	if (!link) {
11268c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11278c2ecf20Sopenharmony_ci		goto err_rm_devnode;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	link = media_create_intf_link(&m2m_dev->sink,
11318c2ecf20Sopenharmony_ci			&m2m_dev->intf_devnode->intf,
11328c2ecf20Sopenharmony_ci			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
11338c2ecf20Sopenharmony_ci	if (!link) {
11348c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11358c2ecf20Sopenharmony_ci		goto err_rm_intf_link;
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci	return 0;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_cierr_rm_intf_link:
11408c2ecf20Sopenharmony_ci	media_remove_intf_links(&m2m_dev->intf_devnode->intf);
11418c2ecf20Sopenharmony_cierr_rm_devnode:
11428c2ecf20Sopenharmony_ci	media_devnode_remove(m2m_dev->intf_devnode);
11438c2ecf20Sopenharmony_cierr_rm_links1:
11448c2ecf20Sopenharmony_ci	media_entity_remove_links(&m2m_dev->sink);
11458c2ecf20Sopenharmony_cierr_rm_links0:
11468c2ecf20Sopenharmony_ci	media_entity_remove_links(&m2m_dev->proc);
11478c2ecf20Sopenharmony_ci	media_entity_remove_links(m2m_dev->source);
11488c2ecf20Sopenharmony_cierr_rel_entity2:
11498c2ecf20Sopenharmony_ci	media_device_unregister_entity(&m2m_dev->proc);
11508c2ecf20Sopenharmony_ci	kfree(m2m_dev->proc.name);
11518c2ecf20Sopenharmony_cierr_rel_entity1:
11528c2ecf20Sopenharmony_ci	media_device_unregister_entity(&m2m_dev->sink);
11538c2ecf20Sopenharmony_ci	kfree(m2m_dev->sink.name);
11548c2ecf20Sopenharmony_cierr_rel_entity0:
11558c2ecf20Sopenharmony_ci	media_device_unregister_entity(m2m_dev->source);
11568c2ecf20Sopenharmony_ci	kfree(m2m_dev->source->name);
11578c2ecf20Sopenharmony_ci	return ret;
11588c2ecf20Sopenharmony_ci	return 0;
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_register_media_controller);
11618c2ecf20Sopenharmony_ci#endif
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_cistruct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops)
11648c2ecf20Sopenharmony_ci{
11658c2ecf20Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	if (!m2m_ops || WARN_ON(!m2m_ops->device_run))
11688c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
11718c2ecf20Sopenharmony_ci	if (!m2m_dev)
11728c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	m2m_dev->curr_ctx = NULL;
11758c2ecf20Sopenharmony_ci	m2m_dev->m2m_ops = m2m_ops;
11768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&m2m_dev->job_queue);
11778c2ecf20Sopenharmony_ci	spin_lock_init(&m2m_dev->job_spinlock);
11788c2ecf20Sopenharmony_ci	INIT_WORK(&m2m_dev->job_work, v4l2_m2m_device_run_work);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	return m2m_dev;
11818c2ecf20Sopenharmony_ci}
11828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_init);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_civoid v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	kfree(m2m_dev);
11878c2ecf20Sopenharmony_ci}
11888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_release);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_cistruct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
11918c2ecf20Sopenharmony_ci		void *drv_priv,
11928c2ecf20Sopenharmony_ci		int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq))
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	struct v4l2_m2m_ctx *m2m_ctx;
11958c2ecf20Sopenharmony_ci	struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx;
11968c2ecf20Sopenharmony_ci	int ret;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
11998c2ecf20Sopenharmony_ci	if (!m2m_ctx)
12008c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	m2m_ctx->priv = drv_priv;
12038c2ecf20Sopenharmony_ci	m2m_ctx->m2m_dev = m2m_dev;
12048c2ecf20Sopenharmony_ci	init_waitqueue_head(&m2m_ctx->finished);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	out_q_ctx = &m2m_ctx->out_q_ctx;
12078c2ecf20Sopenharmony_ci	cap_q_ctx = &m2m_ctx->cap_q_ctx;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
12108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
12118c2ecf20Sopenharmony_ci	spin_lock_init(&out_q_ctx->rdy_spinlock);
12128c2ecf20Sopenharmony_ci	spin_lock_init(&cap_q_ctx->rdy_spinlock);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&m2m_ctx->queue);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	ret = queue_init(drv_priv, &out_q_ctx->q, &cap_q_ctx->q);
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	if (ret)
12198c2ecf20Sopenharmony_ci		goto err;
12208c2ecf20Sopenharmony_ci	/*
12218c2ecf20Sopenharmony_ci	 * Both queues should use same the mutex to lock the m2m context.
12228c2ecf20Sopenharmony_ci	 * This lock is used in some v4l2_m2m_* helpers.
12238c2ecf20Sopenharmony_ci	 */
12248c2ecf20Sopenharmony_ci	if (WARN_ON(out_q_ctx->q.lock != cap_q_ctx->q.lock)) {
12258c2ecf20Sopenharmony_ci		ret = -EINVAL;
12268c2ecf20Sopenharmony_ci		goto err;
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci	m2m_ctx->q_lock = out_q_ctx->q.lock;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	return m2m_ctx;
12318c2ecf20Sopenharmony_cierr:
12328c2ecf20Sopenharmony_ci	kfree(m2m_ctx);
12338c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
12348c2ecf20Sopenharmony_ci}
12358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_civoid v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	/* wait until the current context is dequeued from job_queue */
12408c2ecf20Sopenharmony_ci	v4l2_m2m_cancel_job(m2m_ctx);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
12438c2ecf20Sopenharmony_ci	vb2_queue_release(&m2m_ctx->out_q_ctx.q);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	kfree(m2m_ctx);
12468c2ecf20Sopenharmony_ci}
12478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_civoid v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
12508c2ecf20Sopenharmony_ci		struct vb2_v4l2_buffer *vbuf)
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	struct v4l2_m2m_buffer *b = container_of(vbuf,
12538c2ecf20Sopenharmony_ci				struct v4l2_m2m_buffer, vb);
12548c2ecf20Sopenharmony_ci	struct v4l2_m2m_queue_ctx *q_ctx;
12558c2ecf20Sopenharmony_ci	unsigned long flags;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	q_ctx = get_queue_ctx(m2m_ctx, vbuf->vb2_buf.vb2_queue->type);
12588c2ecf20Sopenharmony_ci	if (!q_ctx)
12598c2ecf20Sopenharmony_ci		return;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
12628c2ecf20Sopenharmony_ci	list_add_tail(&b->list, &q_ctx->rdy_queue);
12638c2ecf20Sopenharmony_ci	q_ctx->num_rdy++;
12648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
12658c2ecf20Sopenharmony_ci}
12668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_civoid v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb,
12698c2ecf20Sopenharmony_ci				struct vb2_v4l2_buffer *cap_vb,
12708c2ecf20Sopenharmony_ci				bool copy_frame_flags)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	if (copy_frame_flags)
12758c2ecf20Sopenharmony_ci		mask |= V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
12768c2ecf20Sopenharmony_ci			V4L2_BUF_FLAG_BFRAME;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	cap_vb->vb2_buf.timestamp = out_vb->vb2_buf.timestamp;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	if (out_vb->flags & V4L2_BUF_FLAG_TIMECODE)
12818c2ecf20Sopenharmony_ci		cap_vb->timecode = out_vb->timecode;
12828c2ecf20Sopenharmony_ci	cap_vb->field = out_vb->field;
12838c2ecf20Sopenharmony_ci	cap_vb->flags &= ~mask;
12848c2ecf20Sopenharmony_ci	cap_vb->flags |= out_vb->flags & mask;
12858c2ecf20Sopenharmony_ci	cap_vb->vb2_buf.copied_timestamp = 1;
12868c2ecf20Sopenharmony_ci}
12878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_buf_copy_metadata);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_civoid v4l2_m2m_request_queue(struct media_request *req)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	struct media_request_object *obj, *obj_safe;
12928c2ecf20Sopenharmony_ci	struct v4l2_m2m_ctx *m2m_ctx = NULL;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	/*
12958c2ecf20Sopenharmony_ci	 * Queue all objects. Note that buffer objects are at the end of the
12968c2ecf20Sopenharmony_ci	 * objects list, after all other object types. Once buffer objects
12978c2ecf20Sopenharmony_ci	 * are queued, the driver might delete them immediately (if the driver
12988c2ecf20Sopenharmony_ci	 * processes the buffer at once), so we have to use
12998c2ecf20Sopenharmony_ci	 * list_for_each_entry_safe() to handle the case where the object we
13008c2ecf20Sopenharmony_ci	 * queue is deleted.
13018c2ecf20Sopenharmony_ci	 */
13028c2ecf20Sopenharmony_ci	list_for_each_entry_safe(obj, obj_safe, &req->objects, list) {
13038c2ecf20Sopenharmony_ci		struct v4l2_m2m_ctx *m2m_ctx_obj;
13048c2ecf20Sopenharmony_ci		struct vb2_buffer *vb;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci		if (!obj->ops->queue)
13078c2ecf20Sopenharmony_ci			continue;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci		if (vb2_request_object_is_buffer(obj)) {
13108c2ecf20Sopenharmony_ci			/* Sanity checks */
13118c2ecf20Sopenharmony_ci			vb = container_of(obj, struct vb2_buffer, req_obj);
13128c2ecf20Sopenharmony_ci			WARN_ON(!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type));
13138c2ecf20Sopenharmony_ci			m2m_ctx_obj = container_of(vb->vb2_queue,
13148c2ecf20Sopenharmony_ci						   struct v4l2_m2m_ctx,
13158c2ecf20Sopenharmony_ci						   out_q_ctx.q);
13168c2ecf20Sopenharmony_ci			WARN_ON(m2m_ctx && m2m_ctx_obj != m2m_ctx);
13178c2ecf20Sopenharmony_ci			m2m_ctx = m2m_ctx_obj;
13188c2ecf20Sopenharmony_ci		}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci		/*
13218c2ecf20Sopenharmony_ci		 * The buffer we queue here can in theory be immediately
13228c2ecf20Sopenharmony_ci		 * unbound, hence the use of list_for_each_entry_safe()
13238c2ecf20Sopenharmony_ci		 * above and why we call the queue op last.
13248c2ecf20Sopenharmony_ci		 */
13258c2ecf20Sopenharmony_ci		obj->ops->queue(obj);
13268c2ecf20Sopenharmony_ci	}
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	WARN_ON(!m2m_ctx);
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	if (m2m_ctx)
13318c2ecf20Sopenharmony_ci		v4l2_m2m_try_schedule(m2m_ctx);
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_request_queue);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci/* Videobuf2 ioctl helpers */
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_reqbufs(struct file *file, void *priv,
13388c2ecf20Sopenharmony_ci				struct v4l2_requestbuffers *rb)
13398c2ecf20Sopenharmony_ci{
13408c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	return v4l2_m2m_reqbufs(file, fh->m2m_ctx, rb);
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_reqbufs);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_create_bufs(struct file *file, void *priv,
13478c2ecf20Sopenharmony_ci				struct v4l2_create_buffers *create)
13488c2ecf20Sopenharmony_ci{
13498c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	return v4l2_m2m_create_bufs(file, fh->m2m_ctx, create);
13528c2ecf20Sopenharmony_ci}
13538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_create_bufs);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_querybuf(struct file *file, void *priv,
13568c2ecf20Sopenharmony_ci				struct v4l2_buffer *buf)
13578c2ecf20Sopenharmony_ci{
13588c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	return v4l2_m2m_querybuf(file, fh->m2m_ctx, buf);
13618c2ecf20Sopenharmony_ci}
13628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_querybuf);
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_qbuf(struct file *file, void *priv,
13658c2ecf20Sopenharmony_ci				struct v4l2_buffer *buf)
13668c2ecf20Sopenharmony_ci{
13678c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	return v4l2_m2m_qbuf(file, fh->m2m_ctx, buf);
13708c2ecf20Sopenharmony_ci}
13718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_qbuf);
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_dqbuf(struct file *file, void *priv,
13748c2ecf20Sopenharmony_ci				struct v4l2_buffer *buf)
13758c2ecf20Sopenharmony_ci{
13768c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	return v4l2_m2m_dqbuf(file, fh->m2m_ctx, buf);
13798c2ecf20Sopenharmony_ci}
13808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_dqbuf);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_prepare_buf(struct file *file, void *priv,
13838c2ecf20Sopenharmony_ci			       struct v4l2_buffer *buf)
13848c2ecf20Sopenharmony_ci{
13858c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	return v4l2_m2m_prepare_buf(file, fh->m2m_ctx, buf);
13888c2ecf20Sopenharmony_ci}
13898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_prepare_buf);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_expbuf(struct file *file, void *priv,
13928c2ecf20Sopenharmony_ci				struct v4l2_exportbuffer *eb)
13938c2ecf20Sopenharmony_ci{
13948c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	return v4l2_m2m_expbuf(file, fh->m2m_ctx, eb);
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_expbuf);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_streamon(struct file *file, void *priv,
14018c2ecf20Sopenharmony_ci				enum v4l2_buf_type type)
14028c2ecf20Sopenharmony_ci{
14038c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	return v4l2_m2m_streamon(file, fh->m2m_ctx, type);
14068c2ecf20Sopenharmony_ci}
14078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamon);
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_streamoff(struct file *file, void *priv,
14108c2ecf20Sopenharmony_ci				enum v4l2_buf_type type)
14118c2ecf20Sopenharmony_ci{
14128c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	return v4l2_m2m_streamoff(file, fh->m2m_ctx, type);
14158c2ecf20Sopenharmony_ci}
14168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamoff);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh,
14198c2ecf20Sopenharmony_ci				   struct v4l2_encoder_cmd *ec)
14208c2ecf20Sopenharmony_ci{
14218c2ecf20Sopenharmony_ci	if (ec->cmd != V4L2_ENC_CMD_STOP && ec->cmd != V4L2_ENC_CMD_START)
14228c2ecf20Sopenharmony_ci		return -EINVAL;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	ec->flags = 0;
14258c2ecf20Sopenharmony_ci	return 0;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_encoder_cmd);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh,
14308c2ecf20Sopenharmony_ci				   struct v4l2_decoder_cmd *dc)
14318c2ecf20Sopenharmony_ci{
14328c2ecf20Sopenharmony_ci	if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START)
14338c2ecf20Sopenharmony_ci		return -EINVAL;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	dc->flags = 0;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	if (dc->cmd == V4L2_DEC_CMD_STOP) {
14388c2ecf20Sopenharmony_ci		dc->stop.pts = 0;
14398c2ecf20Sopenharmony_ci	} else if (dc->cmd == V4L2_DEC_CMD_START) {
14408c2ecf20Sopenharmony_ci		dc->start.speed = 0;
14418c2ecf20Sopenharmony_ci		dc->start.format = V4L2_DEC_START_FMT_NONE;
14428c2ecf20Sopenharmony_ci	}
14438c2ecf20Sopenharmony_ci	return 0;
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd);
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci/*
14488c2ecf20Sopenharmony_ci * Updates the encoding state on ENC_CMD_STOP/ENC_CMD_START
14498c2ecf20Sopenharmony_ci * Should be called from the encoder driver encoder_cmd() callback
14508c2ecf20Sopenharmony_ci */
14518c2ecf20Sopenharmony_ciint v4l2_m2m_encoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
14528c2ecf20Sopenharmony_ci			 struct v4l2_encoder_cmd *ec)
14538c2ecf20Sopenharmony_ci{
14548c2ecf20Sopenharmony_ci	if (ec->cmd != V4L2_ENC_CMD_STOP && ec->cmd != V4L2_ENC_CMD_START)
14558c2ecf20Sopenharmony_ci		return -EINVAL;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	if (ec->cmd == V4L2_ENC_CMD_STOP)
14588c2ecf20Sopenharmony_ci		return v4l2_update_last_buf_state(m2m_ctx);
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	if (m2m_ctx->is_draining)
14618c2ecf20Sopenharmony_ci		return -EBUSY;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	if (m2m_ctx->has_stopped)
14648c2ecf20Sopenharmony_ci		m2m_ctx->has_stopped = false;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	return 0;
14678c2ecf20Sopenharmony_ci}
14688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_encoder_cmd);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci/*
14718c2ecf20Sopenharmony_ci * Updates the decoding state on DEC_CMD_STOP/DEC_CMD_START
14728c2ecf20Sopenharmony_ci * Should be called from the decoder driver decoder_cmd() callback
14738c2ecf20Sopenharmony_ci */
14748c2ecf20Sopenharmony_ciint v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
14758c2ecf20Sopenharmony_ci			 struct v4l2_decoder_cmd *dc)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START)
14788c2ecf20Sopenharmony_ci		return -EINVAL;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	if (dc->cmd == V4L2_DEC_CMD_STOP)
14818c2ecf20Sopenharmony_ci		return v4l2_update_last_buf_state(m2m_ctx);
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (m2m_ctx->is_draining)
14848c2ecf20Sopenharmony_ci		return -EBUSY;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	if (m2m_ctx->has_stopped)
14878c2ecf20Sopenharmony_ci		m2m_ctx->has_stopped = false;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	return 0;
14908c2ecf20Sopenharmony_ci}
14918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_decoder_cmd);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *priv,
14948c2ecf20Sopenharmony_ci			       struct v4l2_encoder_cmd *ec)
14958c2ecf20Sopenharmony_ci{
14968c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	return v4l2_m2m_encoder_cmd(file, fh->m2m_ctx, ec);
14998c2ecf20Sopenharmony_ci}
15008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_encoder_cmd);
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *priv,
15038c2ecf20Sopenharmony_ci			       struct v4l2_decoder_cmd *dc)
15048c2ecf20Sopenharmony_ci{
15058c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	return v4l2_m2m_decoder_cmd(file, fh->m2m_ctx, dc);
15088c2ecf20Sopenharmony_ci}
15098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_decoder_cmd);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh,
15128c2ecf20Sopenharmony_ci					     struct v4l2_decoder_cmd *dc)
15138c2ecf20Sopenharmony_ci{
15148c2ecf20Sopenharmony_ci	if (dc->cmd != V4L2_DEC_CMD_FLUSH)
15158c2ecf20Sopenharmony_ci		return -EINVAL;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	dc->flags = 0;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	return 0;
15208c2ecf20Sopenharmony_ci}
15218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_try_decoder_cmd);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ciint v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv,
15248c2ecf20Sopenharmony_ci					 struct v4l2_decoder_cmd *dc)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
15278c2ecf20Sopenharmony_ci	struct vb2_v4l2_buffer *out_vb, *cap_vb;
15288c2ecf20Sopenharmony_ci	struct v4l2_m2m_dev *m2m_dev = fh->m2m_ctx->m2m_dev;
15298c2ecf20Sopenharmony_ci	unsigned long flags;
15308c2ecf20Sopenharmony_ci	int ret;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, dc);
15338c2ecf20Sopenharmony_ci	if (ret < 0)
15348c2ecf20Sopenharmony_ci		return ret;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
15378c2ecf20Sopenharmony_ci	out_vb = v4l2_m2m_last_src_buf(fh->m2m_ctx);
15388c2ecf20Sopenharmony_ci	cap_vb = v4l2_m2m_last_dst_buf(fh->m2m_ctx);
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	/*
15418c2ecf20Sopenharmony_ci	 * If there is an out buffer pending, then clear any HOLD flag.
15428c2ecf20Sopenharmony_ci	 *
15438c2ecf20Sopenharmony_ci	 * By clearing this flag we ensure that when this output
15448c2ecf20Sopenharmony_ci	 * buffer is processed any held capture buffer will be released.
15458c2ecf20Sopenharmony_ci	 */
15468c2ecf20Sopenharmony_ci	if (out_vb) {
15478c2ecf20Sopenharmony_ci		out_vb->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
15488c2ecf20Sopenharmony_ci	} else if (cap_vb && cap_vb->is_held) {
15498c2ecf20Sopenharmony_ci		/*
15508c2ecf20Sopenharmony_ci		 * If there were no output buffers, but there is a
15518c2ecf20Sopenharmony_ci		 * capture buffer that is held, then release that
15528c2ecf20Sopenharmony_ci		 * buffer.
15538c2ecf20Sopenharmony_ci		 */
15548c2ecf20Sopenharmony_ci		cap_vb->is_held = false;
15558c2ecf20Sopenharmony_ci		v4l2_m2m_dst_buf_remove(fh->m2m_ctx);
15568c2ecf20Sopenharmony_ci		v4l2_m2m_buf_done(cap_vb, VB2_BUF_STATE_DONE);
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	return 0;
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_decoder_cmd);
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci/*
15658c2ecf20Sopenharmony_ci * v4l2_file_operations helpers. It is assumed here same lock is used
15668c2ecf20Sopenharmony_ci * for the output and the capture buffer queue.
15678c2ecf20Sopenharmony_ci */
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ciint v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma)
15708c2ecf20Sopenharmony_ci{
15718c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	return v4l2_m2m_mmap(file, fh->m2m_ctx, vma);
15748c2ecf20Sopenharmony_ci}
15758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_fop_mmap);
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci__poll_t v4l2_m2m_fop_poll(struct file *file, poll_table *wait)
15788c2ecf20Sopenharmony_ci{
15798c2ecf20Sopenharmony_ci	struct v4l2_fh *fh = file->private_data;
15808c2ecf20Sopenharmony_ci	struct v4l2_m2m_ctx *m2m_ctx = fh->m2m_ctx;
15818c2ecf20Sopenharmony_ci	__poll_t ret;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	if (m2m_ctx->q_lock)
15848c2ecf20Sopenharmony_ci		mutex_lock(m2m_ctx->q_lock);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	ret = v4l2_m2m_poll(file, m2m_ctx, wait);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	if (m2m_ctx->q_lock)
15898c2ecf20Sopenharmony_ci		mutex_unlock(m2m_ctx->q_lock);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	return ret;
15928c2ecf20Sopenharmony_ci}
15938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(v4l2_m2m_fop_poll);
15948c2ecf20Sopenharmony_ci
1595