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