162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/bug.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/device.h>
1662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/videodev2.h>
2162306a36Sopenharmony_ci#include <media/v4l2-device.h>
2262306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2362306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h>
2462306a36Sopenharmony_ci#include <media/v4l2-rect.h>
2562306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h>
2662306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "common.h"
2962306a36Sopenharmony_ci#include "fimc-core.h"
3062306a36Sopenharmony_ci#include "fimc-reg.h"
3162306a36Sopenharmony_ci#include "media-dev.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int fimc_capture_hw_init(struct fimc_dev *fimc)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct fimc_source_info *si = &fimc->vid_cap.source_config;
3662306a36Sopenharmony_ci	struct fimc_ctx *ctx = fimc->vid_cap.ctx;
3762306a36Sopenharmony_ci	int ret;
3862306a36Sopenharmony_ci	unsigned long flags;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (ctx == NULL || ctx->s_frame.fmt == NULL)
4162306a36Sopenharmony_ci		return -EINVAL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) {
4462306a36Sopenharmony_ci		ret = fimc_hw_camblk_cfg_writeback(fimc);
4562306a36Sopenharmony_ci		if (ret < 0)
4662306a36Sopenharmony_ci			return ret;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	spin_lock_irqsave(&fimc->slock, flags);
5062306a36Sopenharmony_ci	fimc_prepare_dma_offset(ctx, &ctx->d_frame);
5162306a36Sopenharmony_ci	fimc_set_yuv_order(ctx);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	fimc_hw_set_camera_polarity(fimc, si);
5462306a36Sopenharmony_ci	fimc_hw_set_camera_type(fimc, si);
5562306a36Sopenharmony_ci	fimc_hw_set_camera_source(fimc, si);
5662306a36Sopenharmony_ci	fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ret = fimc_set_scaler_info(ctx);
5962306a36Sopenharmony_ci	if (!ret) {
6062306a36Sopenharmony_ci		fimc_hw_set_input_path(ctx);
6162306a36Sopenharmony_ci		fimc_hw_set_prescaler(ctx);
6262306a36Sopenharmony_ci		fimc_hw_set_mainscaler(ctx);
6362306a36Sopenharmony_ci		fimc_hw_set_target_format(ctx);
6462306a36Sopenharmony_ci		fimc_hw_set_rotation(ctx);
6562306a36Sopenharmony_ci		fimc_hw_set_effect(ctx);
6662306a36Sopenharmony_ci		fimc_hw_set_output_path(ctx);
6762306a36Sopenharmony_ci		fimc_hw_set_out_dma(ctx);
6862306a36Sopenharmony_ci		if (fimc->drv_data->alpha_color)
6962306a36Sopenharmony_ci			fimc_hw_set_rgb_alpha(ctx);
7062306a36Sopenharmony_ci		clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci	spin_unlock_irqrestore(&fimc->slock, flags);
7362306a36Sopenharmony_ci	return ret;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * Reinitialize the driver so it is ready to start the streaming again.
7862306a36Sopenharmony_ci * Set fimc->state to indicate stream off and the hardware shut down state.
7962306a36Sopenharmony_ci * If not suspending (@suspend is false), return any buffers to videobuf2.
8062306a36Sopenharmony_ci * Otherwise put any owned buffers onto the pending buffers queue, so they
8162306a36Sopenharmony_ci * can be re-spun when the device is being resumed. Also perform FIMC
8262306a36Sopenharmony_ci * software reset and disable streaming on the whole pipeline if required.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_cistatic int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct fimc_vid_cap *cap = &fimc->vid_cap;
8762306a36Sopenharmony_ci	struct fimc_vid_buffer *buf;
8862306a36Sopenharmony_ci	unsigned long flags;
8962306a36Sopenharmony_ci	bool streaming;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	spin_lock_irqsave(&fimc->slock, flags);
9262306a36Sopenharmony_ci	streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
9562306a36Sopenharmony_ci			 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
9662306a36Sopenharmony_ci	if (suspend)
9762306a36Sopenharmony_ci		fimc->state |= (1 << ST_CAPT_SUSPENDED);
9862306a36Sopenharmony_ci	else
9962306a36Sopenharmony_ci		fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* Release unused buffers */
10262306a36Sopenharmony_ci	while (!suspend && !list_empty(&cap->pending_buf_q)) {
10362306a36Sopenharmony_ci		buf = fimc_pending_queue_pop(cap);
10462306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	/* If suspending put unused buffers onto pending queue */
10762306a36Sopenharmony_ci	while (!list_empty(&cap->active_buf_q)) {
10862306a36Sopenharmony_ci		buf = fimc_active_queue_pop(cap);
10962306a36Sopenharmony_ci		if (suspend)
11062306a36Sopenharmony_ci			fimc_pending_queue_add(cap, buf);
11162306a36Sopenharmony_ci		else
11262306a36Sopenharmony_ci			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	fimc_hw_reset(fimc);
11662306a36Sopenharmony_ci	cap->buf_index = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	spin_unlock_irqrestore(&fimc->slock, flags);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (streaming)
12162306a36Sopenharmony_ci		return fimc_pipeline_call(&cap->ve, set_stream, 0);
12262306a36Sopenharmony_ci	else
12362306a36Sopenharmony_ci		return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	unsigned long flags;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!fimc_capture_active(fimc))
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	spin_lock_irqsave(&fimc->slock, flags);
13462306a36Sopenharmony_ci	set_bit(ST_CAPT_SHUT, &fimc->state);
13562306a36Sopenharmony_ci	fimc_deactivate_capture(fimc);
13662306a36Sopenharmony_ci	spin_unlock_irqrestore(&fimc->slock, flags);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	wait_event_timeout(fimc->irq_queue,
13962306a36Sopenharmony_ci			   !test_bit(ST_CAPT_SHUT, &fimc->state),
14062306a36Sopenharmony_ci			   (2*HZ/10)); /* 200 ms */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return fimc_capture_state_cleanup(fimc, suspend);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * fimc_capture_config_update - apply the camera interface configuration
14762306a36Sopenharmony_ci * @ctx: FIMC capture context
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * To be called from within the interrupt handler with fimc.slock
15062306a36Sopenharmony_ci * spinlock held. It updates the camera pixel crop, rotation and
15162306a36Sopenharmony_ci * image flip in H/W.
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_cistatic int fimc_capture_config_update(struct fimc_ctx *ctx)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct fimc_dev *fimc = ctx->fimc_dev;
15662306a36Sopenharmony_ci	int ret;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	ret = fimc_set_scaler_info(ctx);
16162306a36Sopenharmony_ci	if (ret)
16262306a36Sopenharmony_ci		return ret;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	fimc_hw_set_prescaler(ctx);
16562306a36Sopenharmony_ci	fimc_hw_set_mainscaler(ctx);
16662306a36Sopenharmony_ci	fimc_hw_set_target_format(ctx);
16762306a36Sopenharmony_ci	fimc_hw_set_rotation(ctx);
16862306a36Sopenharmony_ci	fimc_hw_set_effect(ctx);
16962306a36Sopenharmony_ci	fimc_prepare_dma_offset(ctx, &ctx->d_frame);
17062306a36Sopenharmony_ci	fimc_hw_set_out_dma(ctx);
17162306a36Sopenharmony_ci	if (fimc->drv_data->alpha_color)
17262306a36Sopenharmony_ci		fimc_hw_set_rgb_alpha(ctx);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
17562306a36Sopenharmony_ci	return ret;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_civoid fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct fimc_vid_cap *cap = &fimc->vid_cap;
18162306a36Sopenharmony_ci	struct fimc_pipeline *p = to_fimc_pipeline(cap->ve.pipe);
18262306a36Sopenharmony_ci	struct v4l2_subdev *csis = p->subdevs[IDX_CSIS];
18362306a36Sopenharmony_ci	struct fimc_frame *f = &cap->ctx->d_frame;
18462306a36Sopenharmony_ci	struct fimc_vid_buffer *v_buf;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
18762306a36Sopenharmony_ci		wake_up(&fimc->irq_queue);
18862306a36Sopenharmony_ci		goto done;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!list_empty(&cap->active_buf_q) &&
19262306a36Sopenharmony_ci	    test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) {
19362306a36Sopenharmony_ci		v_buf = fimc_active_queue_pop(cap);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		v_buf->vb.vb2_buf.timestamp = ktime_get_ns();
19662306a36Sopenharmony_ci		v_buf->vb.sequence = cap->frame_count++;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		vb2_buffer_done(&v_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (!list_empty(&cap->pending_buf_q)) {
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		v_buf = fimc_pending_queue_pop(cap);
20462306a36Sopenharmony_ci		fimc_hw_set_output_addr(fimc, &v_buf->addr, cap->buf_index);
20562306a36Sopenharmony_ci		v_buf->index = cap->buf_index;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		/* Move the buffer to the capture active queue */
20862306a36Sopenharmony_ci		fimc_active_queue_add(cap, v_buf);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		dbg("next frame: %d, done frame: %d",
21162306a36Sopenharmony_ci		    fimc_hw_get_frame_index(fimc), v_buf->index);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
21462306a36Sopenharmony_ci			cap->buf_index = 0;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * Set up a buffer at MIPI-CSIS if current image format
21862306a36Sopenharmony_ci	 * requires the frame embedded data capture.
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	if (f->fmt->mdataplanes && !list_empty(&cap->active_buf_q)) {
22162306a36Sopenharmony_ci		unsigned int plane = ffs(f->fmt->mdataplanes) - 1;
22262306a36Sopenharmony_ci		unsigned int size = f->payload[plane];
22362306a36Sopenharmony_ci		s32 index = fimc_hw_get_frame_index(fimc);
22462306a36Sopenharmony_ci		void *vaddr;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		list_for_each_entry(v_buf, &cap->active_buf_q, list) {
22762306a36Sopenharmony_ci			if (v_buf->index != index)
22862306a36Sopenharmony_ci				continue;
22962306a36Sopenharmony_ci			vaddr = vb2_plane_vaddr(&v_buf->vb.vb2_buf, plane);
23062306a36Sopenharmony_ci			v4l2_subdev_call(csis, video, s_rx_buffer,
23162306a36Sopenharmony_ci					 vaddr, &size);
23262306a36Sopenharmony_ci			break;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (cap->active_buf_cnt == 0) {
23762306a36Sopenharmony_ci		if (deq_buf)
23862306a36Sopenharmony_ci			clear_bit(ST_CAPT_RUN, &fimc->state);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
24162306a36Sopenharmony_ci			cap->buf_index = 0;
24262306a36Sopenharmony_ci	} else {
24362306a36Sopenharmony_ci		set_bit(ST_CAPT_RUN, &fimc->state);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
24762306a36Sopenharmony_ci		fimc_capture_config_update(cap->ctx);
24862306a36Sopenharmony_cidone:
24962306a36Sopenharmony_ci	if (cap->active_buf_cnt == 1) {
25062306a36Sopenharmony_ci		fimc_deactivate_capture(fimc);
25162306a36Sopenharmony_ci		clear_bit(ST_CAPT_STREAM, &fimc->state);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	dbg("frame: %d, active_buf_cnt: %d",
25562306a36Sopenharmony_ci	    fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *q, unsigned int count)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct fimc_ctx *ctx = q->drv_priv;
26262306a36Sopenharmony_ci	struct fimc_dev *fimc = ctx->fimc_dev;
26362306a36Sopenharmony_ci	struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
26462306a36Sopenharmony_ci	int min_bufs;
26562306a36Sopenharmony_ci	int ret;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	vid_cap->frame_count = 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ret = fimc_capture_hw_init(fimc);
27062306a36Sopenharmony_ci	if (ret) {
27162306a36Sopenharmony_ci		fimc_capture_state_cleanup(fimc, false);
27262306a36Sopenharmony_ci		return ret;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	set_bit(ST_CAPT_PEND, &fimc->state);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (vid_cap->active_buf_cnt >= min_bufs &&
28062306a36Sopenharmony_ci	    !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
28162306a36Sopenharmony_ci		fimc_activate_capture(ctx);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
28462306a36Sopenharmony_ci			return fimc_pipeline_call(&vid_cap->ve, set_stream, 1);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *q)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct fimc_ctx *ctx = q->drv_priv;
29362306a36Sopenharmony_ci	struct fimc_dev *fimc = ctx->fimc_dev;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!fimc_capture_active(fimc))
29662306a36Sopenharmony_ci		return;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	fimc_stop_capture(fimc, false);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ciint fimc_capture_suspend(struct fimc_dev *fimc)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	bool suspend = fimc_capture_busy(fimc);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	int ret = fimc_stop_capture(fimc, suspend);
30662306a36Sopenharmony_ci	if (ret)
30762306a36Sopenharmony_ci		return ret;
30862306a36Sopenharmony_ci	return fimc_pipeline_call(&fimc->vid_cap.ve, close);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciint fimc_capture_resume(struct fimc_dev *fimc)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
31662306a36Sopenharmony_ci	struct exynos_video_entity *ve = &vid_cap->ve;
31762306a36Sopenharmony_ci	struct fimc_vid_buffer *buf;
31862306a36Sopenharmony_ci	int i;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
32162306a36Sopenharmony_ci		return 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
32462306a36Sopenharmony_ci	vid_cap->buf_index = 0;
32562306a36Sopenharmony_ci	fimc_pipeline_call(ve, open, &ve->vdev.entity, false);
32662306a36Sopenharmony_ci	fimc_capture_hw_init(fimc);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	for (i = 0; i < vid_cap->reqbufs_count; i++) {
33162306a36Sopenharmony_ci		if (list_empty(&vid_cap->pending_buf_q))
33262306a36Sopenharmony_ci			break;
33362306a36Sopenharmony_ci		buf = fimc_pending_queue_pop(vid_cap);
33462306a36Sopenharmony_ci		buffer_queue(&buf->vb.vb2_buf);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	return 0;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *vq,
34162306a36Sopenharmony_ci		       unsigned int *num_buffers, unsigned int *num_planes,
34262306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct fimc_ctx *ctx = vq->drv_priv;
34562306a36Sopenharmony_ci	struct fimc_frame *frame = &ctx->d_frame;
34662306a36Sopenharmony_ci	struct fimc_fmt *fmt = frame->fmt;
34762306a36Sopenharmony_ci	unsigned long wh = frame->f_width * frame->f_height;
34862306a36Sopenharmony_ci	int i;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (fmt == NULL)
35162306a36Sopenharmony_ci		return -EINVAL;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (*num_planes) {
35462306a36Sopenharmony_ci		if (*num_planes != fmt->memplanes)
35562306a36Sopenharmony_ci			return -EINVAL;
35662306a36Sopenharmony_ci		for (i = 0; i < *num_planes; i++)
35762306a36Sopenharmony_ci			if (sizes[i] < (wh * fmt->depth[i]) / 8)
35862306a36Sopenharmony_ci				return -EINVAL;
35962306a36Sopenharmony_ci		return 0;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	*num_planes = fmt->memplanes;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	for (i = 0; i < fmt->memplanes; i++) {
36562306a36Sopenharmony_ci		unsigned int size = (wh * fmt->depth[i]) / 8;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		if (fimc_fmt_is_user_defined(fmt->color))
36862306a36Sopenharmony_ci			sizes[i] = frame->payload[i];
36962306a36Sopenharmony_ci		else
37062306a36Sopenharmony_ci			sizes[i] = max_t(u32, size, frame->payload[i]);
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return 0;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct vb2_queue *vq = vb->vb2_queue;
37962306a36Sopenharmony_ci	struct fimc_ctx *ctx = vq->drv_priv;
38062306a36Sopenharmony_ci	int i;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (ctx->d_frame.fmt == NULL)
38362306a36Sopenharmony_ci		return -EINVAL;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) {
38662306a36Sopenharmony_ci		unsigned long size = ctx->d_frame.payload[i];
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		if (vb2_plane_size(vb, i) < size) {
38962306a36Sopenharmony_ci			v4l2_err(&ctx->fimc_dev->vid_cap.ve.vdev,
39062306a36Sopenharmony_ci				 "User buffer too small (%ld < %ld)\n",
39162306a36Sopenharmony_ci				 vb2_plane_size(vb, i), size);
39262306a36Sopenharmony_ci			return -EINVAL;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci		vb2_set_plane_payload(vb, i, size);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
40362306a36Sopenharmony_ci	struct fimc_vid_buffer *buf
40462306a36Sopenharmony_ci		= container_of(vbuf, struct fimc_vid_buffer, vb);
40562306a36Sopenharmony_ci	struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
40662306a36Sopenharmony_ci	struct fimc_dev *fimc = ctx->fimc_dev;
40762306a36Sopenharmony_ci	struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
40862306a36Sopenharmony_ci	struct exynos_video_entity *ve = &vid_cap->ve;
40962306a36Sopenharmony_ci	unsigned long flags;
41062306a36Sopenharmony_ci	int min_bufs;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	spin_lock_irqsave(&fimc->slock, flags);
41362306a36Sopenharmony_ci	fimc_prepare_addr(ctx, &buf->vb.vb2_buf, &ctx->d_frame, &buf->addr);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
41662306a36Sopenharmony_ci	    !test_bit(ST_CAPT_STREAM, &fimc->state) &&
41762306a36Sopenharmony_ci	    vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
41862306a36Sopenharmony_ci		/* Setup the buffer directly for processing. */
41962306a36Sopenharmony_ci		int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
42062306a36Sopenharmony_ci				vid_cap->buf_index;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		fimc_hw_set_output_addr(fimc, &buf->addr, buf_id);
42362306a36Sopenharmony_ci		buf->index = vid_cap->buf_index;
42462306a36Sopenharmony_ci		fimc_active_queue_add(vid_cap, buf);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS)
42762306a36Sopenharmony_ci			vid_cap->buf_index = 0;
42862306a36Sopenharmony_ci	} else {
42962306a36Sopenharmony_ci		fimc_pending_queue_add(vid_cap, buf);
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (vb2_is_streaming(&vid_cap->vbq) &&
43662306a36Sopenharmony_ci	    vid_cap->active_buf_cnt >= min_bufs &&
43762306a36Sopenharmony_ci	    !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
43862306a36Sopenharmony_ci		int ret;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		fimc_activate_capture(ctx);
44162306a36Sopenharmony_ci		spin_unlock_irqrestore(&fimc->slock, flags);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		if (test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
44462306a36Sopenharmony_ci			return;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		ret = fimc_pipeline_call(ve, set_stream, 1);
44762306a36Sopenharmony_ci		if (ret < 0)
44862306a36Sopenharmony_ci			v4l2_err(&ve->vdev, "stream on failed: %d\n", ret);
44962306a36Sopenharmony_ci		return;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	spin_unlock_irqrestore(&fimc->slock, flags);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic const struct vb2_ops fimc_capture_qops = {
45562306a36Sopenharmony_ci	.queue_setup		= queue_setup,
45662306a36Sopenharmony_ci	.buf_prepare		= buffer_prepare,
45762306a36Sopenharmony_ci	.buf_queue		= buffer_queue,
45862306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
45962306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
46062306a36Sopenharmony_ci	.start_streaming	= start_streaming,
46162306a36Sopenharmony_ci	.stop_streaming		= stop_streaming,
46262306a36Sopenharmony_ci};
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int fimc_capture_set_default_format(struct fimc_dev *fimc);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int fimc_capture_open(struct file *file)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
46962306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
47062306a36Sopenharmony_ci	struct exynos_video_entity *ve = &vc->ve;
47162306a36Sopenharmony_ci	int ret = -EBUSY;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (fimc_m2m_active(fimc))
47862306a36Sopenharmony_ci		goto unlock;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	set_bit(ST_CAPT_BUSY, &fimc->state);
48162306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&fimc->pdev->dev);
48262306a36Sopenharmony_ci	if (ret < 0)
48362306a36Sopenharmony_ci		goto unlock;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ret = v4l2_fh_open(file);
48662306a36Sopenharmony_ci	if (ret) {
48762306a36Sopenharmony_ci		pm_runtime_put_sync(&fimc->pdev->dev);
48862306a36Sopenharmony_ci		goto unlock;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (v4l2_fh_is_singular_file(file)) {
49262306a36Sopenharmony_ci		fimc_md_graph_lock(ve);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		ret = fimc_pipeline_call(ve, open, &ve->vdev.entity, true);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		if (ret == 0)
49762306a36Sopenharmony_ci			ve->vdev.entity.use_count++;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		fimc_md_graph_unlock(ve);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		if (ret == 0)
50262306a36Sopenharmony_ci			ret = fimc_capture_set_default_format(fimc);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		if (ret < 0) {
50562306a36Sopenharmony_ci			clear_bit(ST_CAPT_BUSY, &fimc->state);
50662306a36Sopenharmony_ci			pm_runtime_put_sync(&fimc->pdev->dev);
50762306a36Sopenharmony_ci			v4l2_fh_release(file);
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ciunlock:
51162306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
51262306a36Sopenharmony_ci	return ret;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int fimc_capture_release(struct file *file)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
51862306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
51962306a36Sopenharmony_ci	bool close = v4l2_fh_is_singular_file(file);
52062306a36Sopenharmony_ci	int ret;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (close && vc->streaming) {
52762306a36Sopenharmony_ci		video_device_pipeline_stop(&vc->ve.vdev);
52862306a36Sopenharmony_ci		vc->streaming = false;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	ret = _vb2_fop_release(file, NULL);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (close) {
53462306a36Sopenharmony_ci		clear_bit(ST_CAPT_BUSY, &fimc->state);
53562306a36Sopenharmony_ci		fimc_pipeline_call(&vc->ve, close);
53662306a36Sopenharmony_ci		clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		fimc_md_graph_lock(&vc->ve);
53962306a36Sopenharmony_ci		vc->ve.vdev.entity.use_count--;
54062306a36Sopenharmony_ci		fimc_md_graph_unlock(&vc->ve);
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	pm_runtime_put_sync(&fimc->pdev->dev);
54462306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return ret;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic const struct v4l2_file_operations fimc_capture_fops = {
55062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
55162306a36Sopenharmony_ci	.open		= fimc_capture_open,
55262306a36Sopenharmony_ci	.release	= fimc_capture_release,
55362306a36Sopenharmony_ci	.poll		= vb2_fop_poll,
55462306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
55562306a36Sopenharmony_ci	.mmap		= vb2_fop_mmap,
55662306a36Sopenharmony_ci};
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/*
55962306a36Sopenharmony_ci * Format and crop negotiation helpers
56062306a36Sopenharmony_ci */
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
56362306a36Sopenharmony_ci						u32 *width, u32 *height,
56462306a36Sopenharmony_ci						u32 *code, u32 *fourcc, int pad)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
56762306a36Sopenharmony_ci	struct fimc_dev *fimc = ctx->fimc_dev;
56862306a36Sopenharmony_ci	const struct fimc_variant *var = fimc->variant;
56962306a36Sopenharmony_ci	const struct fimc_pix_limit *pl = var->pix_limit;
57062306a36Sopenharmony_ci	struct fimc_frame *dst = &ctx->d_frame;
57162306a36Sopenharmony_ci	u32 depth, min_w, max_w, min_h, align_h = 3;
57262306a36Sopenharmony_ci	u32 mask = FMT_FLAGS_CAM;
57362306a36Sopenharmony_ci	struct fimc_fmt *ffmt;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Conversion from/to JPEG or User Defined format is not supported */
57662306a36Sopenharmony_ci	if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE &&
57762306a36Sopenharmony_ci	    fimc_fmt_is_user_defined(ctx->s_frame.fmt->color))
57862306a36Sopenharmony_ci		*code = ctx->s_frame.fmt->mbus_code;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad == FIMC_SD_PAD_SOURCE)
58162306a36Sopenharmony_ci		mask |= FMT_FLAGS_M2M;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (pad == FIMC_SD_PAD_SINK_FIFO)
58462306a36Sopenharmony_ci		mask = FMT_FLAGS_WRITEBACK;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	ffmt = fimc_find_format(fourcc, code, mask, 0);
58762306a36Sopenharmony_ci	if (WARN_ON(!ffmt))
58862306a36Sopenharmony_ci		return NULL;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (code)
59162306a36Sopenharmony_ci		*code = ffmt->mbus_code;
59262306a36Sopenharmony_ci	if (fourcc)
59362306a36Sopenharmony_ci		*fourcc = ffmt->fourcc;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (pad != FIMC_SD_PAD_SOURCE) {
59662306a36Sopenharmony_ci		max_w = fimc_fmt_is_user_defined(ffmt->color) ?
59762306a36Sopenharmony_ci			pl->scaler_dis_w : pl->scaler_en_w;
59862306a36Sopenharmony_ci		/* Apply the camera input interface pixel constraints */
59962306a36Sopenharmony_ci		v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4,
60062306a36Sopenharmony_ci				      height, max_t(u32, *height, 32),
60162306a36Sopenharmony_ci				      FIMC_CAMIF_MAX_HEIGHT,
60262306a36Sopenharmony_ci				      fimc_fmt_is_user_defined(ffmt->color) ?
60362306a36Sopenharmony_ci				      3 : 1,
60462306a36Sopenharmony_ci				      0);
60562306a36Sopenharmony_ci		return ffmt;
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci	/* Can't scale or crop in transparent (JPEG) transfer mode */
60862306a36Sopenharmony_ci	if (fimc_fmt_is_user_defined(ffmt->color)) {
60962306a36Sopenharmony_ci		*width  = ctx->s_frame.f_width;
61062306a36Sopenharmony_ci		*height = ctx->s_frame.f_height;
61162306a36Sopenharmony_ci		return ffmt;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci	/* Apply the scaler and the output DMA constraints */
61462306a36Sopenharmony_ci	max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
61562306a36Sopenharmony_ci	if (ctx->state & FIMC_COMPOSE) {
61662306a36Sopenharmony_ci		min_w = dst->offs_h + dst->width;
61762306a36Sopenharmony_ci		min_h = dst->offs_v + dst->height;
61862306a36Sopenharmony_ci	} else {
61962306a36Sopenharmony_ci		min_w = var->min_out_pixsize;
62062306a36Sopenharmony_ci		min_h = var->min_out_pixsize;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	if (var->min_vsize_align == 1 && !rotation)
62362306a36Sopenharmony_ci		align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	depth = fimc_get_format_depth(ffmt);
62662306a36Sopenharmony_ci	v4l_bound_align_image(width, min_w, max_w,
62762306a36Sopenharmony_ci			      ffs(var->min_out_pixsize) - 1,
62862306a36Sopenharmony_ci			      height, min_h, FIMC_CAMIF_MAX_HEIGHT,
62962306a36Sopenharmony_ci			      align_h,
63062306a36Sopenharmony_ci			      64/(ALIGN(depth, 8)));
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d",
63362306a36Sopenharmony_ci	    pad, code ? *code : 0, *width, *height,
63462306a36Sopenharmony_ci	    dst->f_width, dst->f_height);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return ffmt;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic void fimc_capture_try_selection(struct fimc_ctx *ctx,
64062306a36Sopenharmony_ci				       struct v4l2_rect *r,
64162306a36Sopenharmony_ci				       int target)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
64462306a36Sopenharmony_ci	struct fimc_dev *fimc = ctx->fimc_dev;
64562306a36Sopenharmony_ci	const struct fimc_variant *var = fimc->variant;
64662306a36Sopenharmony_ci	const struct fimc_pix_limit *pl = var->pix_limit;
64762306a36Sopenharmony_ci	struct fimc_frame *sink = &ctx->s_frame;
64862306a36Sopenharmony_ci	u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
64962306a36Sopenharmony_ci	u32 align_sz = 0, align_h = 4;
65062306a36Sopenharmony_ci	u32 max_sc_h, max_sc_v;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	/* In JPEG transparent transfer mode cropping is not supported */
65362306a36Sopenharmony_ci	if (fimc_fmt_is_user_defined(ctx->d_frame.fmt->color)) {
65462306a36Sopenharmony_ci		r->width  = sink->f_width;
65562306a36Sopenharmony_ci		r->height = sink->f_height;
65662306a36Sopenharmony_ci		r->left   = r->top = 0;
65762306a36Sopenharmony_ci		return;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci	if (target == V4L2_SEL_TGT_COMPOSE) {
66062306a36Sopenharmony_ci		u32 tmp_min_h = ffs(sink->width) - 3;
66162306a36Sopenharmony_ci		u32 tmp_min_v = ffs(sink->height) - 1;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (ctx->rotation != 90 && ctx->rotation != 270)
66462306a36Sopenharmony_ci			align_h = 1;
66562306a36Sopenharmony_ci		max_sc_h = min(SCALER_MAX_HRATIO, 1 << tmp_min_h);
66662306a36Sopenharmony_ci		max_sc_v = min(SCALER_MAX_VRATIO, 1 << tmp_min_v);
66762306a36Sopenharmony_ci		min_sz = var->min_out_pixsize;
66862306a36Sopenharmony_ci	} else {
66962306a36Sopenharmony_ci		u32 depth = fimc_get_format_depth(sink->fmt);
67062306a36Sopenharmony_ci		align_sz = 64/ALIGN(depth, 8);
67162306a36Sopenharmony_ci		min_sz = var->min_inp_pixsize;
67262306a36Sopenharmony_ci		min_w = min_h = min_sz;
67362306a36Sopenharmony_ci		max_sc_h = max_sc_v = 1;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci	/*
67662306a36Sopenharmony_ci	 * For the compose rectangle the following constraints must be met:
67762306a36Sopenharmony_ci	 * - it must fit in the sink pad format rectangle (f_width/f_height);
67862306a36Sopenharmony_ci	 * - maximum downscaling ratio is 64;
67962306a36Sopenharmony_ci	 * - maximum crop size depends if the rotator is used or not;
68062306a36Sopenharmony_ci	 * - the sink pad format width/height must be 4 multiple of the
68162306a36Sopenharmony_ci	 *   prescaler ratios determined by sink pad size and source pad crop,
68262306a36Sopenharmony_ci	 *   the prescaler ratio is returned by fimc_get_scaler_factor().
68362306a36Sopenharmony_ci	 */
68462306a36Sopenharmony_ci	max_w = min_t(u32,
68562306a36Sopenharmony_ci		      rotate ? pl->out_rot_en_w : pl->out_rot_dis_w,
68662306a36Sopenharmony_ci		      rotate ? sink->f_height : sink->f_width);
68762306a36Sopenharmony_ci	max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (target == V4L2_SEL_TGT_COMPOSE) {
69062306a36Sopenharmony_ci		min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
69162306a36Sopenharmony_ci		min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
69262306a36Sopenharmony_ci		if (rotate) {
69362306a36Sopenharmony_ci			swap(max_sc_h, max_sc_v);
69462306a36Sopenharmony_ci			swap(min_w, min_h);
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci	v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1,
69862306a36Sopenharmony_ci			      &r->height, min_h, max_h, align_h,
69962306a36Sopenharmony_ci			      align_sz);
70062306a36Sopenharmony_ci	/* Adjust left/top if crop/compose rectangle is out of bounds */
70162306a36Sopenharmony_ci	r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width);
70262306a36Sopenharmony_ci	r->top  = clamp_t(u32, r->top, 0, sink->f_height - r->height);
70362306a36Sopenharmony_ci	r->left = round_down(r->left, var->hor_offs_align);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d",
70662306a36Sopenharmony_ci	    target, r->left, r->top, r->width, r->height,
70762306a36Sopenharmony_ci	    sink->f_width, sink->f_height);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/*
71162306a36Sopenharmony_ci * The video node ioctl operations
71262306a36Sopenharmony_ci */
71362306a36Sopenharmony_cistatic int fimc_cap_querycap(struct file *file, void *priv,
71462306a36Sopenharmony_ci					struct v4l2_capability *cap)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	__fimc_vidioc_querycap(&fimc->pdev->dev, cap);
71962306a36Sopenharmony_ci	return 0;
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic int fimc_cap_enum_fmt(struct file *file, void *priv,
72362306a36Sopenharmony_ci			     struct v4l2_fmtdesc *f)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct fimc_fmt *fmt;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M,
72862306a36Sopenharmony_ci			       f->index);
72962306a36Sopenharmony_ci	if (!fmt)
73062306a36Sopenharmony_ci		return -EINVAL;
73162306a36Sopenharmony_ci	f->pixelformat = fmt->fourcc;
73262306a36Sopenharmony_ci	return 0;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic struct media_entity *fimc_pipeline_get_head(struct media_entity *me)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	struct media_pad *pad = &me->pads[0];
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) {
74062306a36Sopenharmony_ci		pad = media_pad_remote_pad_first(pad);
74162306a36Sopenharmony_ci		if (!pad)
74262306a36Sopenharmony_ci			break;
74362306a36Sopenharmony_ci		me = pad->entity;
74462306a36Sopenharmony_ci		pad = &me->pads[0];
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return me;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci/**
75162306a36Sopenharmony_ci * fimc_pipeline_try_format - negotiate and/or set formats at pipeline
75262306a36Sopenharmony_ci *                            elements
75362306a36Sopenharmony_ci * @ctx: FIMC capture context
75462306a36Sopenharmony_ci * @tfmt: media bus format to try/set on subdevs
75562306a36Sopenharmony_ci * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output)
75662306a36Sopenharmony_ci * @set: true to set format on subdevs, false to try only
75762306a36Sopenharmony_ci */
75862306a36Sopenharmony_cistatic int fimc_pipeline_try_format(struct fimc_ctx *ctx,
75962306a36Sopenharmony_ci				    struct v4l2_mbus_framefmt *tfmt,
76062306a36Sopenharmony_ci				    struct fimc_fmt **fmt_id,
76162306a36Sopenharmony_ci				    bool set)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct fimc_dev *fimc = ctx->fimc_dev;
76462306a36Sopenharmony_ci	struct fimc_pipeline *p = to_fimc_pipeline(fimc->vid_cap.ve.pipe);
76562306a36Sopenharmony_ci	struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR];
76662306a36Sopenharmony_ci	struct v4l2_subdev_format sfmt = {
76762306a36Sopenharmony_ci		.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE
76862306a36Sopenharmony_ci		       : V4L2_SUBDEV_FORMAT_TRY,
76962306a36Sopenharmony_ci	};
77062306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf = &sfmt.format;
77162306a36Sopenharmony_ci	struct media_entity *me;
77262306a36Sopenharmony_ci	struct fimc_fmt *ffmt;
77362306a36Sopenharmony_ci	struct media_pad *pad;
77462306a36Sopenharmony_ci	int ret, i = 1;
77562306a36Sopenharmony_ci	u32 fcc;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (WARN_ON(!sd || !tfmt))
77862306a36Sopenharmony_ci		return -EINVAL;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	sfmt.format = *tfmt;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	me = fimc_pipeline_get_head(&sd->entity);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	while (1) {
78562306a36Sopenharmony_ci		ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL,
78662306a36Sopenharmony_ci					FMT_FLAGS_CAM, i++);
78762306a36Sopenharmony_ci		if (ffmt == NULL) {
78862306a36Sopenharmony_ci			/*
78962306a36Sopenharmony_ci			 * Notify user-space if common pixel code for
79062306a36Sopenharmony_ci			 * host and sensor does not exist.
79162306a36Sopenharmony_ci			 */
79262306a36Sopenharmony_ci			return -EINVAL;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci		mf->code = tfmt->code = ffmt->mbus_code;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		/* set format on all pipeline subdevs */
79762306a36Sopenharmony_ci		while (me != &fimc->vid_cap.subdev.entity) {
79862306a36Sopenharmony_ci			sd = media_entity_to_v4l2_subdev(me);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci			sfmt.pad = 0;
80162306a36Sopenharmony_ci			ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
80262306a36Sopenharmony_ci			if (ret)
80362306a36Sopenharmony_ci				return ret;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci			if (me->pads[0].flags & MEDIA_PAD_FL_SINK) {
80662306a36Sopenharmony_ci				sfmt.pad = me->num_pads - 1;
80762306a36Sopenharmony_ci				mf->code = tfmt->code;
80862306a36Sopenharmony_ci				ret = v4l2_subdev_call(sd, pad, set_fmt, NULL,
80962306a36Sopenharmony_ci									&sfmt);
81062306a36Sopenharmony_ci				if (ret)
81162306a36Sopenharmony_ci					return ret;
81262306a36Sopenharmony_ci			}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci			pad = media_pad_remote_pad_first(&me->pads[sfmt.pad]);
81562306a36Sopenharmony_ci			if (!pad)
81662306a36Sopenharmony_ci				return -EINVAL;
81762306a36Sopenharmony_ci			me = pad->entity;
81862306a36Sopenharmony_ci		}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		if (mf->code != tfmt->code)
82162306a36Sopenharmony_ci			continue;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci		fcc = ffmt->fourcc;
82462306a36Sopenharmony_ci		tfmt->width  = mf->width;
82562306a36Sopenharmony_ci		tfmt->height = mf->height;
82662306a36Sopenharmony_ci		ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height,
82762306a36Sopenharmony_ci					NULL, &fcc, FIMC_SD_PAD_SINK_CAM);
82862306a36Sopenharmony_ci		ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height,
82962306a36Sopenharmony_ci					NULL, &fcc, FIMC_SD_PAD_SOURCE);
83062306a36Sopenharmony_ci		if (ffmt && ffmt->mbus_code)
83162306a36Sopenharmony_ci			mf->code = ffmt->mbus_code;
83262306a36Sopenharmony_ci		if (mf->width != tfmt->width || mf->height != tfmt->height)
83362306a36Sopenharmony_ci			continue;
83462306a36Sopenharmony_ci		tfmt->code = mf->code;
83562306a36Sopenharmony_ci		break;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (fmt_id && ffmt)
83962306a36Sopenharmony_ci		*fmt_id = ffmt;
84062306a36Sopenharmony_ci	*tfmt = *mf;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	return 0;
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/**
84662306a36Sopenharmony_ci * fimc_get_sensor_frame_desc - query the sensor for media bus frame parameters
84762306a36Sopenharmony_ci * @sensor: pointer to the sensor subdev
84862306a36Sopenharmony_ci * @plane_fmt: provides plane sizes corresponding to the frame layout entries
84962306a36Sopenharmony_ci * @num_planes: number of planes
85062306a36Sopenharmony_ci * @try: true to set the frame parameters, false to query only
85162306a36Sopenharmony_ci *
85262306a36Sopenharmony_ci * This function is used by this driver only for compressed/blob data formats.
85362306a36Sopenharmony_ci */
85462306a36Sopenharmony_cistatic int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor,
85562306a36Sopenharmony_ci				      struct v4l2_plane_pix_format *plane_fmt,
85662306a36Sopenharmony_ci				      unsigned int num_planes, bool try)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct v4l2_mbus_frame_desc fd = { };
85962306a36Sopenharmony_ci	int i, ret;
86062306a36Sopenharmony_ci	int pad;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	for (i = 0; i < num_planes; i++)
86362306a36Sopenharmony_ci		fd.entry[i].length = plane_fmt[i].sizeimage;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	pad = sensor->entity.num_pads - 1;
86662306a36Sopenharmony_ci	if (try)
86762306a36Sopenharmony_ci		ret = v4l2_subdev_call(sensor, pad, set_frame_desc, pad, &fd);
86862306a36Sopenharmony_ci	else
86962306a36Sopenharmony_ci		ret = v4l2_subdev_call(sensor, pad, get_frame_desc, pad, &fd);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (ret < 0)
87262306a36Sopenharmony_ci		return ret;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (num_planes != fd.num_entries)
87562306a36Sopenharmony_ci		return -EINVAL;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	for (i = 0; i < num_planes; i++)
87862306a36Sopenharmony_ci		plane_fmt[i].sizeimage = fd.entry[i].length;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (fd.entry[0].length > FIMC_MAX_JPEG_BUF_SIZE) {
88162306a36Sopenharmony_ci		v4l2_err(sensor->v4l2_dev,  "Unsupported buffer size: %u\n",
88262306a36Sopenharmony_ci			 fd.entry[0].length);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		return -EINVAL;
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	return 0;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic int fimc_cap_g_fmt_mplane(struct file *file, void *fh,
89162306a36Sopenharmony_ci				 struct v4l2_format *f)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	__fimc_get_format(&fimc->vid_cap.ctx->d_frame, f);
89662306a36Sopenharmony_ci	return 0;
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/*
90062306a36Sopenharmony_ci * Try or set format on the fimc.X.capture video node and additionally
90162306a36Sopenharmony_ci * on the whole pipeline if @try is false.
90262306a36Sopenharmony_ci * Locking: the caller must _not_ hold the graph mutex.
90362306a36Sopenharmony_ci */
90462306a36Sopenharmony_cistatic int __video_try_or_set_format(struct fimc_dev *fimc,
90562306a36Sopenharmony_ci				     struct v4l2_format *f, bool try,
90662306a36Sopenharmony_ci				     struct fimc_fmt **inp_fmt,
90762306a36Sopenharmony_ci				     struct fimc_fmt **out_fmt)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
91062306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
91162306a36Sopenharmony_ci	struct exynos_video_entity *ve = &vc->ve;
91262306a36Sopenharmony_ci	struct fimc_ctx *ctx = vc->ctx;
91362306a36Sopenharmony_ci	unsigned int width = 0, height = 0;
91462306a36Sopenharmony_ci	int ret = 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Pre-configure format at the camera input interface, for JPEG only */
91762306a36Sopenharmony_ci	if (fimc_jpeg_fourcc(pix->pixelformat)) {
91862306a36Sopenharmony_ci		fimc_capture_try_format(ctx, &pix->width, &pix->height,
91962306a36Sopenharmony_ci					NULL, &pix->pixelformat,
92062306a36Sopenharmony_ci					FIMC_SD_PAD_SINK_CAM);
92162306a36Sopenharmony_ci		if (try) {
92262306a36Sopenharmony_ci			width = pix->width;
92362306a36Sopenharmony_ci			height = pix->height;
92462306a36Sopenharmony_ci		} else {
92562306a36Sopenharmony_ci			ctx->s_frame.f_width = pix->width;
92662306a36Sopenharmony_ci			ctx->s_frame.f_height = pix->height;
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* Try the format at the scaler and the DMA output */
93162306a36Sopenharmony_ci	*out_fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
93262306a36Sopenharmony_ci					  NULL, &pix->pixelformat,
93362306a36Sopenharmony_ci					  FIMC_SD_PAD_SOURCE);
93462306a36Sopenharmony_ci	if (*out_fmt == NULL)
93562306a36Sopenharmony_ci		return -EINVAL;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* Restore image width/height for JPEG (no resizing supported). */
93862306a36Sopenharmony_ci	if (try && fimc_jpeg_fourcc(pix->pixelformat)) {
93962306a36Sopenharmony_ci		pix->width = width;
94062306a36Sopenharmony_ci		pix->height = height;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* Try to match format at the host and the sensor */
94462306a36Sopenharmony_ci	if (!vc->user_subdev_api) {
94562306a36Sopenharmony_ci		struct v4l2_mbus_framefmt mbus_fmt;
94662306a36Sopenharmony_ci		struct v4l2_mbus_framefmt *mf;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		mf = try ? &mbus_fmt : &fimc->vid_cap.ci_fmt;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci		mf->code = (*out_fmt)->mbus_code;
95162306a36Sopenharmony_ci		mf->width = pix->width;
95262306a36Sopenharmony_ci		mf->height = pix->height;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		fimc_md_graph_lock(ve);
95562306a36Sopenharmony_ci		ret = fimc_pipeline_try_format(ctx, mf, inp_fmt, try);
95662306a36Sopenharmony_ci		fimc_md_graph_unlock(ve);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci		if (ret < 0)
95962306a36Sopenharmony_ci			return ret;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		pix->width = mf->width;
96262306a36Sopenharmony_ci		pix->height = mf->height;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	fimc_adjust_mplane_format(*out_fmt, pix->width, pix->height, pix);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if ((*out_fmt)->flags & FMT_FLAGS_COMPRESSED) {
96862306a36Sopenharmony_ci		struct v4l2_subdev *sensor;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		fimc_md_graph_lock(ve);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		sensor = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR);
97362306a36Sopenharmony_ci		if (sensor)
97462306a36Sopenharmony_ci			fimc_get_sensor_frame_desc(sensor, pix->plane_fmt,
97562306a36Sopenharmony_ci						   (*out_fmt)->memplanes, try);
97662306a36Sopenharmony_ci		else
97762306a36Sopenharmony_ci			ret = -EPIPE;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		fimc_md_graph_unlock(ve);
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	return ret;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_cistatic int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
98662306a36Sopenharmony_ci				   struct v4l2_format *f)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
98962306a36Sopenharmony_ci	struct fimc_fmt *out_fmt = NULL, *inp_fmt = NULL;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	return __video_try_or_set_format(fimc, f, true, &inp_fmt, &out_fmt);
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx,
99562306a36Sopenharmony_ci					enum fimc_color_fmt color)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	bool jpeg = fimc_fmt_is_user_defined(color);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	ctx->scaler.enabled = !jpeg;
100062306a36Sopenharmony_ci	fimc_ctrls_activate(ctx, !jpeg);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (jpeg)
100362306a36Sopenharmony_ci		set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
100462306a36Sopenharmony_ci	else
100562306a36Sopenharmony_ci		clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic int __fimc_capture_set_format(struct fimc_dev *fimc,
100962306a36Sopenharmony_ci				     struct v4l2_format *f)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
101262306a36Sopenharmony_ci	struct fimc_ctx *ctx = vc->ctx;
101362306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
101462306a36Sopenharmony_ci	struct fimc_frame *ff = &ctx->d_frame;
101562306a36Sopenharmony_ci	struct fimc_fmt *inp_fmt = NULL;
101662306a36Sopenharmony_ci	int ret, i;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	if (vb2_is_busy(&fimc->vid_cap.vbq))
101962306a36Sopenharmony_ci		return -EBUSY;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	ret = __video_try_or_set_format(fimc, f, false, &inp_fmt, &ff->fmt);
102262306a36Sopenharmony_ci	if (ret < 0)
102362306a36Sopenharmony_ci		return ret;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/* Update RGB Alpha control state and value range */
102662306a36Sopenharmony_ci	fimc_alpha_ctrl_update(ctx);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	for (i = 0; i < ff->fmt->memplanes; i++) {
102962306a36Sopenharmony_ci		ff->bytesperline[i] = pix->plane_fmt[i].bytesperline;
103062306a36Sopenharmony_ci		ff->payload[i] = pix->plane_fmt[i].sizeimage;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	set_frame_bounds(ff, pix->width, pix->height);
103462306a36Sopenharmony_ci	/* Reset the composition rectangle if not yet configured */
103562306a36Sopenharmony_ci	if (!(ctx->state & FIMC_COMPOSE))
103662306a36Sopenharmony_ci		set_frame_crop(ff, 0, 0, pix->width, pix->height);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	fimc_capture_mark_jpeg_xfer(ctx, ff->fmt->color);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* Reset cropping and set format at the camera interface input */
104162306a36Sopenharmony_ci	if (!vc->user_subdev_api) {
104262306a36Sopenharmony_ci		ctx->s_frame.fmt = inp_fmt;
104362306a36Sopenharmony_ci		set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
104462306a36Sopenharmony_ci		set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	return ret;
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cistatic int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
105162306a36Sopenharmony_ci				 struct v4l2_format *f)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	return __fimc_capture_set_format(fimc, f);
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_cistatic int fimc_cap_enum_input(struct file *file, void *priv,
105962306a36Sopenharmony_ci			       struct v4l2_input *i)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
106262306a36Sopenharmony_ci	struct exynos_video_entity *ve = &fimc->vid_cap.ve;
106362306a36Sopenharmony_ci	struct v4l2_subdev *sd;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (i->index != 0)
106662306a36Sopenharmony_ci		return -EINVAL;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	i->type = V4L2_INPUT_TYPE_CAMERA;
106962306a36Sopenharmony_ci	fimc_md_graph_lock(ve);
107062306a36Sopenharmony_ci	sd = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR);
107162306a36Sopenharmony_ci	fimc_md_graph_unlock(ve);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (sd)
107462306a36Sopenharmony_ci		strscpy(i->name, sd->name, sizeof(i->name));
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return 0;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic int fimc_cap_s_input(struct file *file, void *priv, unsigned int i)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	return i == 0 ? i : -EINVAL;
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	*i = 0;
108762306a36Sopenharmony_ci	return 0;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci/**
109162306a36Sopenharmony_ci * fimc_pipeline_validate - check for formats inconsistencies
109262306a36Sopenharmony_ci *                          between source and sink pad of each link
109362306a36Sopenharmony_ci * @fimc:	the FIMC device this context applies to
109462306a36Sopenharmony_ci *
109562306a36Sopenharmony_ci * Return 0 if all formats match or -EPIPE otherwise.
109662306a36Sopenharmony_ci */
109762306a36Sopenharmony_cistatic int fimc_pipeline_validate(struct fimc_dev *fimc)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct v4l2_subdev_format sink_fmt = {
110062306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
110162306a36Sopenharmony_ci	};
110262306a36Sopenharmony_ci	struct v4l2_subdev_format src_fmt = {
110362306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
110462306a36Sopenharmony_ci	};
110562306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
110662306a36Sopenharmony_ci	struct v4l2_subdev *sd = &vc->subdev;
110762306a36Sopenharmony_ci	struct fimc_pipeline *p = to_fimc_pipeline(vc->ve.pipe);
110862306a36Sopenharmony_ci	struct media_pad *sink_pad, *src_pad;
110962306a36Sopenharmony_ci	int i, ret;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	while (1) {
111262306a36Sopenharmony_ci		/*
111362306a36Sopenharmony_ci		 * Find current entity sink pad and any remote sink pad linked
111462306a36Sopenharmony_ci		 * to it. We stop if there is no sink pad in current entity or
111562306a36Sopenharmony_ci		 * it is not linked to any other remote entity.
111662306a36Sopenharmony_ci		 */
111762306a36Sopenharmony_ci		src_pad = NULL;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		for (i = 0; i < sd->entity.num_pads; i++) {
112062306a36Sopenharmony_ci			struct media_pad *p = &sd->entity.pads[i];
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci			if (p->flags & MEDIA_PAD_FL_SINK) {
112362306a36Sopenharmony_ci				sink_pad = p;
112462306a36Sopenharmony_ci				src_pad = media_pad_remote_pad_first(sink_pad);
112562306a36Sopenharmony_ci				if (src_pad)
112662306a36Sopenharmony_ci					break;
112762306a36Sopenharmony_ci			}
112862306a36Sopenharmony_ci		}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci		if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity))
113162306a36Sopenharmony_ci			break;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		/* Don't call FIMC subdev operation to avoid nested locking */
113462306a36Sopenharmony_ci		if (sd == &vc->subdev) {
113562306a36Sopenharmony_ci			struct fimc_frame *ff = &vc->ctx->s_frame;
113662306a36Sopenharmony_ci			sink_fmt.format.width = ff->f_width;
113762306a36Sopenharmony_ci			sink_fmt.format.height = ff->f_height;
113862306a36Sopenharmony_ci			sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
113962306a36Sopenharmony_ci		} else {
114062306a36Sopenharmony_ci			sink_fmt.pad = sink_pad->index;
114162306a36Sopenharmony_ci			ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
114262306a36Sopenharmony_ci			if (ret < 0 && ret != -ENOIOCTLCMD)
114362306a36Sopenharmony_ci				return -EPIPE;
114462306a36Sopenharmony_ci		}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		/* Retrieve format at the source pad */
114762306a36Sopenharmony_ci		sd = media_entity_to_v4l2_subdev(src_pad->entity);
114862306a36Sopenharmony_ci		src_fmt.pad = src_pad->index;
114962306a36Sopenharmony_ci		ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
115062306a36Sopenharmony_ci		if (ret < 0 && ret != -ENOIOCTLCMD)
115162306a36Sopenharmony_ci			return -EPIPE;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		if (src_fmt.format.width != sink_fmt.format.width ||
115462306a36Sopenharmony_ci		    src_fmt.format.height != sink_fmt.format.height ||
115562306a36Sopenharmony_ci		    src_fmt.format.code != sink_fmt.format.code)
115662306a36Sopenharmony_ci			return -EPIPE;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci		if (sd == p->subdevs[IDX_SENSOR] &&
115962306a36Sopenharmony_ci		    fimc_user_defined_mbus_fmt(src_fmt.format.code)) {
116062306a36Sopenharmony_ci			struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES];
116162306a36Sopenharmony_ci			struct fimc_frame *frame = &vc->ctx->d_frame;
116262306a36Sopenharmony_ci			unsigned int i;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci			ret = fimc_get_sensor_frame_desc(sd, plane_fmt,
116562306a36Sopenharmony_ci							 frame->fmt->memplanes,
116662306a36Sopenharmony_ci							 false);
116762306a36Sopenharmony_ci			if (ret < 0)
116862306a36Sopenharmony_ci				return -EPIPE;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci			for (i = 0; i < frame->fmt->memplanes; i++)
117162306a36Sopenharmony_ci				if (frame->payload[i] < plane_fmt[i].sizeimage)
117262306a36Sopenharmony_ci					return -EPIPE;
117362306a36Sopenharmony_ci		}
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci	return 0;
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic int fimc_cap_streamon(struct file *file, void *priv,
117962306a36Sopenharmony_ci			     enum v4l2_buf_type type)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
118262306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
118362306a36Sopenharmony_ci	struct fimc_source_info *si = NULL;
118462306a36Sopenharmony_ci	struct v4l2_subdev *sd;
118562306a36Sopenharmony_ci	int ret;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (fimc_capture_active(fimc))
118862306a36Sopenharmony_ci		return -EBUSY;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	ret = video_device_pipeline_start(&vc->ve.vdev, &vc->ve.pipe->mp);
119162306a36Sopenharmony_ci	if (ret < 0)
119262306a36Sopenharmony_ci		return ret;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	sd = __fimc_md_get_subdev(vc->ve.pipe, IDX_SENSOR);
119562306a36Sopenharmony_ci	if (sd)
119662306a36Sopenharmony_ci		si = v4l2_get_subdev_hostdata(sd);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (si == NULL) {
119962306a36Sopenharmony_ci		ret = -EPIPE;
120062306a36Sopenharmony_ci		goto err_p_stop;
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci	/*
120362306a36Sopenharmony_ci	 * Save configuration data related to currently attached image
120462306a36Sopenharmony_ci	 * sensor or other data source, e.g. FIMC-IS.
120562306a36Sopenharmony_ci	 */
120662306a36Sopenharmony_ci	vc->source_config = *si;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (vc->input == GRP_ID_FIMC_IS)
120962306a36Sopenharmony_ci		vc->source_config.fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (vc->user_subdev_api) {
121262306a36Sopenharmony_ci		ret = fimc_pipeline_validate(fimc);
121362306a36Sopenharmony_ci		if (ret < 0)
121462306a36Sopenharmony_ci			goto err_p_stop;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	ret = vb2_ioctl_streamon(file, priv, type);
121862306a36Sopenharmony_ci	if (!ret) {
121962306a36Sopenharmony_ci		vc->streaming = true;
122062306a36Sopenharmony_ci		return ret;
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cierr_p_stop:
122462306a36Sopenharmony_ci	video_device_pipeline_stop(&vc->ve.vdev);
122562306a36Sopenharmony_ci	return ret;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_cistatic int fimc_cap_streamoff(struct file *file, void *priv,
122962306a36Sopenharmony_ci			    enum v4l2_buf_type type)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
123262306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
123362306a36Sopenharmony_ci	int ret;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	ret = vb2_ioctl_streamoff(file, priv, type);
123662306a36Sopenharmony_ci	if (ret < 0)
123762306a36Sopenharmony_ci		return ret;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	if (vc->streaming) {
124062306a36Sopenharmony_ci		video_device_pipeline_stop(&vc->ve.vdev);
124162306a36Sopenharmony_ci		vc->streaming = false;
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	return 0;
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic int fimc_cap_reqbufs(struct file *file, void *priv,
124862306a36Sopenharmony_ci			    struct v4l2_requestbuffers *reqbufs)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
125162306a36Sopenharmony_ci	int ret;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	ret = vb2_ioctl_reqbufs(file, priv, reqbufs);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	if (!ret)
125662306a36Sopenharmony_ci		fimc->vid_cap.reqbufs_count = reqbufs->count;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return ret;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic int fimc_cap_g_selection(struct file *file, void *fh,
126262306a36Sopenharmony_ci				struct v4l2_selection *s)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
126562306a36Sopenharmony_ci	struct fimc_ctx *ctx = fimc->vid_cap.ctx;
126662306a36Sopenharmony_ci	struct fimc_frame *f = &ctx->s_frame;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
126962306a36Sopenharmony_ci		return -EINVAL;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	switch (s->target) {
127262306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
127362306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
127462306a36Sopenharmony_ci		f = &ctx->d_frame;
127562306a36Sopenharmony_ci		fallthrough;
127662306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
127762306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
127862306a36Sopenharmony_ci		s->r.left = 0;
127962306a36Sopenharmony_ci		s->r.top = 0;
128062306a36Sopenharmony_ci		s->r.width = f->o_width;
128162306a36Sopenharmony_ci		s->r.height = f->o_height;
128262306a36Sopenharmony_ci		return 0;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
128562306a36Sopenharmony_ci		f = &ctx->d_frame;
128662306a36Sopenharmony_ci		fallthrough;
128762306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
128862306a36Sopenharmony_ci		s->r.left = f->offs_h;
128962306a36Sopenharmony_ci		s->r.top = f->offs_v;
129062306a36Sopenharmony_ci		s->r.width = f->width;
129162306a36Sopenharmony_ci		s->r.height = f->height;
129262306a36Sopenharmony_ci		return 0;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	return -EINVAL;
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic int fimc_cap_s_selection(struct file *file, void *fh,
129962306a36Sopenharmony_ci				struct v4l2_selection *s)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct fimc_dev *fimc = video_drvdata(file);
130262306a36Sopenharmony_ci	struct fimc_ctx *ctx = fimc->vid_cap.ctx;
130362306a36Sopenharmony_ci	struct v4l2_rect rect = s->r;
130462306a36Sopenharmony_ci	struct fimc_frame *f;
130562306a36Sopenharmony_ci	unsigned long flags;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
130862306a36Sopenharmony_ci		return -EINVAL;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	if (s->target == V4L2_SEL_TGT_COMPOSE)
131162306a36Sopenharmony_ci		f = &ctx->d_frame;
131262306a36Sopenharmony_ci	else if (s->target == V4L2_SEL_TGT_CROP)
131362306a36Sopenharmony_ci		f = &ctx->s_frame;
131462306a36Sopenharmony_ci	else
131562306a36Sopenharmony_ci		return -EINVAL;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	fimc_capture_try_selection(ctx, &rect, s->target);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	if (s->flags & V4L2_SEL_FLAG_LE &&
132062306a36Sopenharmony_ci	    !v4l2_rect_enclosed(&rect, &s->r))
132162306a36Sopenharmony_ci		return -ERANGE;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (s->flags & V4L2_SEL_FLAG_GE &&
132462306a36Sopenharmony_ci	    !v4l2_rect_enclosed(&s->r, &rect))
132562306a36Sopenharmony_ci		return -ERANGE;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	s->r = rect;
132862306a36Sopenharmony_ci	spin_lock_irqsave(&fimc->slock, flags);
132962306a36Sopenharmony_ci	set_frame_crop(f, s->r.left, s->r.top, s->r.width,
133062306a36Sopenharmony_ci		       s->r.height);
133162306a36Sopenharmony_ci	spin_unlock_irqrestore(&fimc->slock, flags);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
133462306a36Sopenharmony_ci	return 0;
133562306a36Sopenharmony_ci}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
133862306a36Sopenharmony_ci	.vidioc_querycap		= fimc_cap_querycap,
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap	= fimc_cap_enum_fmt,
134162306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap_mplane	= fimc_cap_try_fmt_mplane,
134262306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap_mplane	= fimc_cap_s_fmt_mplane,
134362306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap_mplane	= fimc_cap_g_fmt_mplane,
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	.vidioc_reqbufs			= fimc_cap_reqbufs,
134662306a36Sopenharmony_ci	.vidioc_querybuf		= vb2_ioctl_querybuf,
134762306a36Sopenharmony_ci	.vidioc_qbuf			= vb2_ioctl_qbuf,
134862306a36Sopenharmony_ci	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
134962306a36Sopenharmony_ci	.vidioc_expbuf			= vb2_ioctl_expbuf,
135062306a36Sopenharmony_ci	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
135162306a36Sopenharmony_ci	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	.vidioc_streamon		= fimc_cap_streamon,
135462306a36Sopenharmony_ci	.vidioc_streamoff		= fimc_cap_streamoff,
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	.vidioc_g_selection		= fimc_cap_g_selection,
135762306a36Sopenharmony_ci	.vidioc_s_selection		= fimc_cap_s_selection,
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	.vidioc_enum_input		= fimc_cap_enum_input,
136062306a36Sopenharmony_ci	.vidioc_s_input			= fimc_cap_s_input,
136162306a36Sopenharmony_ci	.vidioc_g_input			= fimc_cap_g_input,
136262306a36Sopenharmony_ci};
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci/* Capture subdev media entity operations */
136562306a36Sopenharmony_cistatic int fimc_link_setup(struct media_entity *entity,
136662306a36Sopenharmony_ci			   const struct media_pad *local,
136762306a36Sopenharmony_ci			   const struct media_pad *remote, u32 flags)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
137062306a36Sopenharmony_ci	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
137162306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
137262306a36Sopenharmony_ci	struct v4l2_subdev *sensor;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (!is_media_entity_v4l2_subdev(remote->entity))
137562306a36Sopenharmony_ci		return -EINVAL;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	if (WARN_ON(fimc == NULL))
137862306a36Sopenharmony_ci		return 0;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	dbg("%s --> %s, flags: 0x%x. input: 0x%x",
138162306a36Sopenharmony_ci	    local->entity->name, remote->entity->name, flags,
138262306a36Sopenharmony_ci	    fimc->vid_cap.input);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
138562306a36Sopenharmony_ci		fimc->vid_cap.input = 0;
138662306a36Sopenharmony_ci		return 0;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	if (vc->input != 0)
139062306a36Sopenharmony_ci		return -EBUSY;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	vc->input = sd->grp_id;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if (vc->user_subdev_api)
139562306a36Sopenharmony_ci		return 0;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/* Inherit V4L2 controls from the image sensor subdev. */
139862306a36Sopenharmony_ci	sensor = fimc_find_remote_sensor(&vc->subdev.entity);
139962306a36Sopenharmony_ci	if (sensor == NULL)
140062306a36Sopenharmony_ci		return 0;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	return v4l2_ctrl_add_handler(&vc->ctx->ctrls.handler,
140362306a36Sopenharmony_ci				     sensor->ctrl_handler, NULL, true);
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_cistatic const struct media_entity_operations fimc_sd_media_ops = {
140762306a36Sopenharmony_ci	.link_setup = fimc_link_setup,
140862306a36Sopenharmony_ci};
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci/**
141162306a36Sopenharmony_ci * fimc_sensor_notify - v4l2_device notification from a sensor subdev
141262306a36Sopenharmony_ci * @sd: pointer to a subdev generating the notification
141362306a36Sopenharmony_ci * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY
141462306a36Sopenharmony_ci * @arg: pointer to an u32 type integer that stores the frame payload value
141562306a36Sopenharmony_ci *
141662306a36Sopenharmony_ci * The End Of Frame notification sent by sensor subdev in its still capture
141762306a36Sopenharmony_ci * mode. If there is only a single VSYNC generated by the sensor at the
141862306a36Sopenharmony_ci * beginning of a frame transmission, FIMC does not issue the LastIrq
141962306a36Sopenharmony_ci * (end of frame) interrupt. And this notification is used to complete the
142062306a36Sopenharmony_ci * frame capture and returning a buffer to user-space. Subdev drivers should
142162306a36Sopenharmony_ci * call this notification from their last 'End of frame capture' interrupt.
142262306a36Sopenharmony_ci */
142362306a36Sopenharmony_civoid fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
142462306a36Sopenharmony_ci			void *arg)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct fimc_source_info	*si;
142762306a36Sopenharmony_ci	struct fimc_vid_buffer *buf;
142862306a36Sopenharmony_ci	struct fimc_md *fmd;
142962306a36Sopenharmony_ci	struct fimc_dev *fimc;
143062306a36Sopenharmony_ci	unsigned long flags;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	if (sd == NULL)
143362306a36Sopenharmony_ci		return;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	si = v4l2_get_subdev_hostdata(sd);
143662306a36Sopenharmony_ci	fmd = entity_to_fimc_mdev(&sd->entity);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	spin_lock_irqsave(&fmd->slock, flags);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	fimc = si ? source_to_sensor_info(si)->host : NULL;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
144362306a36Sopenharmony_ci	    test_bit(ST_CAPT_PEND, &fimc->state)) {
144462306a36Sopenharmony_ci		unsigned long irq_flags;
144562306a36Sopenharmony_ci		spin_lock_irqsave(&fimc->slock, irq_flags);
144662306a36Sopenharmony_ci		if (!list_empty(&fimc->vid_cap.active_buf_q)) {
144762306a36Sopenharmony_ci			buf = list_entry(fimc->vid_cap.active_buf_q.next,
144862306a36Sopenharmony_ci					 struct fimc_vid_buffer, list);
144962306a36Sopenharmony_ci			vb2_set_plane_payload(&buf->vb.vb2_buf, 0,
145062306a36Sopenharmony_ci					      *((u32 *)arg));
145162306a36Sopenharmony_ci		}
145262306a36Sopenharmony_ci		fimc_capture_irq_handler(fimc, 1);
145362306a36Sopenharmony_ci		fimc_deactivate_capture(fimc);
145462306a36Sopenharmony_ci		spin_unlock_irqrestore(&fimc->slock, irq_flags);
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci	spin_unlock_irqrestore(&fmd->slock, flags);
145762306a36Sopenharmony_ci}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_cistatic int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
146062306a36Sopenharmony_ci				      struct v4l2_subdev_state *sd_state,
146162306a36Sopenharmony_ci				      struct v4l2_subdev_mbus_code_enum *code)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	struct fimc_fmt *fmt;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index);
146662306a36Sopenharmony_ci	if (!fmt)
146762306a36Sopenharmony_ci		return -EINVAL;
146862306a36Sopenharmony_ci	code->code = fmt->mbus_code;
146962306a36Sopenharmony_ci	return 0;
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_cistatic int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
147362306a36Sopenharmony_ci			       struct v4l2_subdev_state *sd_state,
147462306a36Sopenharmony_ci			       struct v4l2_subdev_format *fmt)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
147762306a36Sopenharmony_ci	struct fimc_ctx *ctx = fimc->vid_cap.ctx;
147862306a36Sopenharmony_ci	struct fimc_frame *ff = &ctx->s_frame;
147962306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
148262306a36Sopenharmony_ci		mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
148362306a36Sopenharmony_ci		fmt->format = *mf;
148462306a36Sopenharmony_ci		return 0;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	mf = &fmt->format;
148862306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	switch (fmt->pad) {
149162306a36Sopenharmony_ci	case FIMC_SD_PAD_SOURCE:
149262306a36Sopenharmony_ci		if (!WARN_ON(ff->fmt == NULL))
149362306a36Sopenharmony_ci			mf->code = ff->fmt->mbus_code;
149462306a36Sopenharmony_ci		/* Sink pads crop rectangle size */
149562306a36Sopenharmony_ci		mf->width = ff->width;
149662306a36Sopenharmony_ci		mf->height = ff->height;
149762306a36Sopenharmony_ci		break;
149862306a36Sopenharmony_ci	case FIMC_SD_PAD_SINK_FIFO:
149962306a36Sopenharmony_ci		*mf = fimc->vid_cap.wb_fmt;
150062306a36Sopenharmony_ci		break;
150162306a36Sopenharmony_ci	case FIMC_SD_PAD_SINK_CAM:
150262306a36Sopenharmony_ci	default:
150362306a36Sopenharmony_ci		*mf = fimc->vid_cap.ci_fmt;
150462306a36Sopenharmony_ci		break;
150562306a36Sopenharmony_ci	}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
150862306a36Sopenharmony_ci	mf->colorspace = V4L2_COLORSPACE_JPEG;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	return 0;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
151462306a36Sopenharmony_ci			       struct v4l2_subdev_state *sd_state,
151562306a36Sopenharmony_ci			       struct v4l2_subdev_format *fmt)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
151862306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mf = &fmt->format;
151962306a36Sopenharmony_ci	struct fimc_vid_cap *vc = &fimc->vid_cap;
152062306a36Sopenharmony_ci	struct fimc_ctx *ctx = vc->ctx;
152162306a36Sopenharmony_ci	struct fimc_frame *ff;
152262306a36Sopenharmony_ci	struct fimc_fmt *ffmt;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	dbg("pad%d: code: 0x%x, %dx%d",
152562306a36Sopenharmony_ci	    fmt->pad, mf->code, mf->width, mf->height);
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	if (fmt->pad == FIMC_SD_PAD_SOURCE && vb2_is_busy(&vc->vbq))
152862306a36Sopenharmony_ci		return -EBUSY;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
153162306a36Sopenharmony_ci	ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height,
153262306a36Sopenharmony_ci				       &mf->code, NULL, fmt->pad);
153362306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
153462306a36Sopenharmony_ci	mf->colorspace = V4L2_COLORSPACE_JPEG;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
153762306a36Sopenharmony_ci		mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
153862306a36Sopenharmony_ci		*mf = fmt->format;
153962306a36Sopenharmony_ci		return 0;
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci	/* There must be a bug in the driver if this happens */
154262306a36Sopenharmony_ci	if (WARN_ON(ffmt == NULL))
154362306a36Sopenharmony_ci		return -EINVAL;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	/* Update RGB Alpha control state and value range */
154662306a36Sopenharmony_ci	fimc_alpha_ctrl_update(ctx);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	fimc_capture_mark_jpeg_xfer(ctx, ffmt->color);
154962306a36Sopenharmony_ci	if (fmt->pad == FIMC_SD_PAD_SOURCE) {
155062306a36Sopenharmony_ci		ff = &ctx->d_frame;
155162306a36Sopenharmony_ci		/* Sink pads crop rectangle size */
155262306a36Sopenharmony_ci		mf->width = ctx->s_frame.width;
155362306a36Sopenharmony_ci		mf->height = ctx->s_frame.height;
155462306a36Sopenharmony_ci	} else {
155562306a36Sopenharmony_ci		ff = &ctx->s_frame;
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
155962306a36Sopenharmony_ci	set_frame_bounds(ff, mf->width, mf->height);
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	if (fmt->pad == FIMC_SD_PAD_SINK_FIFO)
156262306a36Sopenharmony_ci		vc->wb_fmt = *mf;
156362306a36Sopenharmony_ci	else if (fmt->pad == FIMC_SD_PAD_SINK_CAM)
156462306a36Sopenharmony_ci		vc->ci_fmt = *mf;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	ff->fmt = ffmt;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	/* Reset the crop rectangle if required. */
156962306a36Sopenharmony_ci	if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE)))
157062306a36Sopenharmony_ci		set_frame_crop(ff, 0, 0, mf->width, mf->height);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (fmt->pad != FIMC_SD_PAD_SOURCE)
157362306a36Sopenharmony_ci		ctx->state &= ~FIMC_COMPOSE;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
157662306a36Sopenharmony_ci	return 0;
157762306a36Sopenharmony_ci}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_cistatic int fimc_subdev_get_selection(struct v4l2_subdev *sd,
158062306a36Sopenharmony_ci				     struct v4l2_subdev_state *sd_state,
158162306a36Sopenharmony_ci				     struct v4l2_subdev_selection *sel)
158262306a36Sopenharmony_ci{
158362306a36Sopenharmony_ci	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
158462306a36Sopenharmony_ci	struct fimc_ctx *ctx = fimc->vid_cap.ctx;
158562306a36Sopenharmony_ci	struct fimc_frame *f = &ctx->s_frame;
158662306a36Sopenharmony_ci	struct v4l2_rect *r = &sel->r;
158762306a36Sopenharmony_ci	struct v4l2_rect *try_sel;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	if (sel->pad == FIMC_SD_PAD_SOURCE)
159062306a36Sopenharmony_ci		return -EINVAL;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	switch (sel->target) {
159562306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
159662306a36Sopenharmony_ci		f = &ctx->d_frame;
159762306a36Sopenharmony_ci		fallthrough;
159862306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
159962306a36Sopenharmony_ci		r->width = f->o_width;
160062306a36Sopenharmony_ci		r->height = f->o_height;
160162306a36Sopenharmony_ci		r->left = 0;
160262306a36Sopenharmony_ci		r->top = 0;
160362306a36Sopenharmony_ci		mutex_unlock(&fimc->lock);
160462306a36Sopenharmony_ci		return 0;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
160762306a36Sopenharmony_ci		try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad);
160862306a36Sopenharmony_ci		break;
160962306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
161062306a36Sopenharmony_ci		try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad);
161162306a36Sopenharmony_ci		f = &ctx->d_frame;
161262306a36Sopenharmony_ci		break;
161362306a36Sopenharmony_ci	default:
161462306a36Sopenharmony_ci		mutex_unlock(&fimc->lock);
161562306a36Sopenharmony_ci		return -EINVAL;
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
161962306a36Sopenharmony_ci		sel->r = *try_sel;
162062306a36Sopenharmony_ci	} else {
162162306a36Sopenharmony_ci		r->left = f->offs_h;
162262306a36Sopenharmony_ci		r->top = f->offs_v;
162362306a36Sopenharmony_ci		r->width = f->width;
162462306a36Sopenharmony_ci		r->height = f->height;
162562306a36Sopenharmony_ci	}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
162862306a36Sopenharmony_ci	    sel->pad, r->left, r->top, r->width, r->height,
162962306a36Sopenharmony_ci	    f->f_width, f->f_height);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
163262306a36Sopenharmony_ci	return 0;
163362306a36Sopenharmony_ci}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_cistatic int fimc_subdev_set_selection(struct v4l2_subdev *sd,
163662306a36Sopenharmony_ci				     struct v4l2_subdev_state *sd_state,
163762306a36Sopenharmony_ci				     struct v4l2_subdev_selection *sel)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
164062306a36Sopenharmony_ci	struct fimc_ctx *ctx = fimc->vid_cap.ctx;
164162306a36Sopenharmony_ci	struct fimc_frame *f = &ctx->s_frame;
164262306a36Sopenharmony_ci	struct v4l2_rect *r = &sel->r;
164362306a36Sopenharmony_ci	struct v4l2_rect *try_sel;
164462306a36Sopenharmony_ci	unsigned long flags;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	if (sel->pad == FIMC_SD_PAD_SOURCE)
164762306a36Sopenharmony_ci		return -EINVAL;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
165062306a36Sopenharmony_ci	fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP);
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	switch (sel->target) {
165362306a36Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
165462306a36Sopenharmony_ci		try_sel = v4l2_subdev_get_try_crop(sd, sd_state, sel->pad);
165562306a36Sopenharmony_ci		break;
165662306a36Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE:
165762306a36Sopenharmony_ci		try_sel = v4l2_subdev_get_try_compose(sd, sd_state, sel->pad);
165862306a36Sopenharmony_ci		f = &ctx->d_frame;
165962306a36Sopenharmony_ci		break;
166062306a36Sopenharmony_ci	default:
166162306a36Sopenharmony_ci		mutex_unlock(&fimc->lock);
166262306a36Sopenharmony_ci		return -EINVAL;
166362306a36Sopenharmony_ci	}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
166662306a36Sopenharmony_ci		*try_sel = sel->r;
166762306a36Sopenharmony_ci	} else {
166862306a36Sopenharmony_ci		spin_lock_irqsave(&fimc->slock, flags);
166962306a36Sopenharmony_ci		set_frame_crop(f, r->left, r->top, r->width, r->height);
167062306a36Sopenharmony_ci		set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
167162306a36Sopenharmony_ci		if (sel->target == V4L2_SEL_TGT_COMPOSE)
167262306a36Sopenharmony_ci			ctx->state |= FIMC_COMPOSE;
167362306a36Sopenharmony_ci		spin_unlock_irqrestore(&fimc->slock, flags);
167462306a36Sopenharmony_ci	}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top,
167762306a36Sopenharmony_ci	    r->width, r->height);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
168062306a36Sopenharmony_ci	return 0;
168162306a36Sopenharmony_ci}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = {
168462306a36Sopenharmony_ci	.enum_mbus_code = fimc_subdev_enum_mbus_code,
168562306a36Sopenharmony_ci	.get_selection = fimc_subdev_get_selection,
168662306a36Sopenharmony_ci	.set_selection = fimc_subdev_set_selection,
168762306a36Sopenharmony_ci	.get_fmt = fimc_subdev_get_fmt,
168862306a36Sopenharmony_ci	.set_fmt = fimc_subdev_set_fmt,
168962306a36Sopenharmony_ci};
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_cistatic const struct v4l2_subdev_ops fimc_subdev_ops = {
169262306a36Sopenharmony_ci	.pad = &fimc_subdev_pad_ops,
169362306a36Sopenharmony_ci};
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci/* Set default format at the sensor and host interface */
169662306a36Sopenharmony_cistatic int fimc_capture_set_default_format(struct fimc_dev *fimc)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	struct v4l2_format fmt = {
169962306a36Sopenharmony_ci		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
170062306a36Sopenharmony_ci		.fmt.pix_mp = {
170162306a36Sopenharmony_ci			.width		= FIMC_DEFAULT_WIDTH,
170262306a36Sopenharmony_ci			.height		= FIMC_DEFAULT_HEIGHT,
170362306a36Sopenharmony_ci			.pixelformat	= V4L2_PIX_FMT_YUYV,
170462306a36Sopenharmony_ci			.field		= V4L2_FIELD_NONE,
170562306a36Sopenharmony_ci			.colorspace	= V4L2_COLORSPACE_JPEG,
170662306a36Sopenharmony_ci		},
170762306a36Sopenharmony_ci	};
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	return __fimc_capture_set_format(fimc, &fmt);
171062306a36Sopenharmony_ci}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci/* fimc->lock must be already initialized */
171362306a36Sopenharmony_cistatic int fimc_register_capture_device(struct fimc_dev *fimc,
171462306a36Sopenharmony_ci				 struct v4l2_device *v4l2_dev)
171562306a36Sopenharmony_ci{
171662306a36Sopenharmony_ci	struct video_device *vfd = &fimc->vid_cap.ve.vdev;
171762306a36Sopenharmony_ci	struct vb2_queue *q = &fimc->vid_cap.vbq;
171862306a36Sopenharmony_ci	struct fimc_ctx *ctx;
171962306a36Sopenharmony_ci	struct fimc_vid_cap *vid_cap;
172062306a36Sopenharmony_ci	struct fimc_fmt *fmt;
172162306a36Sopenharmony_ci	int ret = -ENOMEM;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
172462306a36Sopenharmony_ci	if (!ctx)
172562306a36Sopenharmony_ci		return -ENOMEM;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	ctx->fimc_dev	 = fimc;
172862306a36Sopenharmony_ci	ctx->in_path	 = FIMC_IO_CAMERA;
172962306a36Sopenharmony_ci	ctx->out_path	 = FIMC_IO_DMA;
173062306a36Sopenharmony_ci	ctx->state	 = FIMC_CTX_CAP;
173162306a36Sopenharmony_ci	ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
173262306a36Sopenharmony_ci	ctx->d_frame.fmt = ctx->s_frame.fmt;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	memset(vfd, 0, sizeof(*vfd));
173562306a36Sopenharmony_ci	snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.capture", fimc->id);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	vfd->fops	= &fimc_capture_fops;
173862306a36Sopenharmony_ci	vfd->ioctl_ops	= &fimc_capture_ioctl_ops;
173962306a36Sopenharmony_ci	vfd->v4l2_dev	= v4l2_dev;
174062306a36Sopenharmony_ci	vfd->minor	= -1;
174162306a36Sopenharmony_ci	vfd->release	= video_device_release_empty;
174262306a36Sopenharmony_ci	vfd->queue	= q;
174362306a36Sopenharmony_ci	vfd->lock	= &fimc->lock;
174462306a36Sopenharmony_ci	vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	video_set_drvdata(vfd, fimc);
174762306a36Sopenharmony_ci	vid_cap = &fimc->vid_cap;
174862306a36Sopenharmony_ci	vid_cap->active_buf_cnt = 0;
174962306a36Sopenharmony_ci	vid_cap->reqbufs_count = 0;
175062306a36Sopenharmony_ci	vid_cap->ctx = ctx;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	INIT_LIST_HEAD(&vid_cap->pending_buf_q);
175362306a36Sopenharmony_ci	INIT_LIST_HEAD(&vid_cap->active_buf_q);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	memset(q, 0, sizeof(*q));
175662306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
175762306a36Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
175862306a36Sopenharmony_ci	q->drv_priv = ctx;
175962306a36Sopenharmony_ci	q->ops = &fimc_capture_qops;
176062306a36Sopenharmony_ci	q->mem_ops = &vb2_dma_contig_memops;
176162306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct fimc_vid_buffer);
176262306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
176362306a36Sopenharmony_ci	q->lock = &fimc->lock;
176462306a36Sopenharmony_ci	q->dev = &fimc->pdev->dev;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	ret = vb2_queue_init(q);
176762306a36Sopenharmony_ci	if (ret)
176862306a36Sopenharmony_ci		goto err_free_ctx;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	/* Default format configuration */
177162306a36Sopenharmony_ci	fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
177262306a36Sopenharmony_ci	vid_cap->ci_fmt.width = FIMC_DEFAULT_WIDTH;
177362306a36Sopenharmony_ci	vid_cap->ci_fmt.height = FIMC_DEFAULT_HEIGHT;
177462306a36Sopenharmony_ci	vid_cap->ci_fmt.code = fmt->mbus_code;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	ctx->s_frame.width = FIMC_DEFAULT_WIDTH;
177762306a36Sopenharmony_ci	ctx->s_frame.height = FIMC_DEFAULT_HEIGHT;
177862306a36Sopenharmony_ci	ctx->s_frame.fmt = fmt;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_WRITEBACK, 0);
178162306a36Sopenharmony_ci	vid_cap->wb_fmt = vid_cap->ci_fmt;
178262306a36Sopenharmony_ci	vid_cap->wb_fmt.code = fmt->mbus_code;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
178562306a36Sopenharmony_ci	vfd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
178662306a36Sopenharmony_ci	ret = media_entity_pads_init(&vfd->entity, 1, &vid_cap->vd_pad);
178762306a36Sopenharmony_ci	if (ret)
178862306a36Sopenharmony_ci		goto err_free_ctx;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	ret = fimc_ctrls_create(ctx);
179162306a36Sopenharmony_ci	if (ret)
179262306a36Sopenharmony_ci		goto err_me_cleanup;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
179562306a36Sopenharmony_ci	if (ret)
179662306a36Sopenharmony_ci		goto err_ctrl_free;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
179962306a36Sopenharmony_ci		  vfd->name, video_device_node_name(vfd));
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	vfd->ctrl_handler = &ctx->ctrls.handler;
180262306a36Sopenharmony_ci	return 0;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cierr_ctrl_free:
180562306a36Sopenharmony_ci	fimc_ctrls_delete(ctx);
180662306a36Sopenharmony_cierr_me_cleanup:
180762306a36Sopenharmony_ci	media_entity_cleanup(&vfd->entity);
180862306a36Sopenharmony_cierr_free_ctx:
180962306a36Sopenharmony_ci	kfree(ctx);
181062306a36Sopenharmony_ci	return ret;
181162306a36Sopenharmony_ci}
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_cistatic int fimc_capture_subdev_registered(struct v4l2_subdev *sd)
181462306a36Sopenharmony_ci{
181562306a36Sopenharmony_ci	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
181662306a36Sopenharmony_ci	int ret;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	if (fimc == NULL)
181962306a36Sopenharmony_ci		return -ENXIO;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	ret = fimc_register_m2m_device(fimc, sd->v4l2_dev);
182262306a36Sopenharmony_ci	if (ret)
182362306a36Sopenharmony_ci		return ret;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	fimc->vid_cap.ve.pipe = v4l2_get_subdev_hostdata(sd);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	ret = fimc_register_capture_device(fimc, sd->v4l2_dev);
182862306a36Sopenharmony_ci	if (ret) {
182962306a36Sopenharmony_ci		fimc_unregister_m2m_device(fimc);
183062306a36Sopenharmony_ci		fimc->vid_cap.ve.pipe = NULL;
183162306a36Sopenharmony_ci	}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	return ret;
183462306a36Sopenharmony_ci}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_cistatic void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd)
183762306a36Sopenharmony_ci{
183862306a36Sopenharmony_ci	struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
183962306a36Sopenharmony_ci	struct video_device *vdev;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	if (fimc == NULL)
184262306a36Sopenharmony_ci		return;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	mutex_lock(&fimc->lock);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	fimc_unregister_m2m_device(fimc);
184762306a36Sopenharmony_ci	vdev = &fimc->vid_cap.ve.vdev;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	if (video_is_registered(vdev)) {
185062306a36Sopenharmony_ci		video_unregister_device(vdev);
185162306a36Sopenharmony_ci		media_entity_cleanup(&vdev->entity);
185262306a36Sopenharmony_ci		fimc_ctrls_delete(fimc->vid_cap.ctx);
185362306a36Sopenharmony_ci		fimc->vid_cap.ve.pipe = NULL;
185462306a36Sopenharmony_ci	}
185562306a36Sopenharmony_ci	kfree(fimc->vid_cap.ctx);
185662306a36Sopenharmony_ci	fimc->vid_cap.ctx = NULL;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	mutex_unlock(&fimc->lock);
185962306a36Sopenharmony_ci}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_cistatic const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = {
186262306a36Sopenharmony_ci	.registered = fimc_capture_subdev_registered,
186362306a36Sopenharmony_ci	.unregistered = fimc_capture_subdev_unregistered,
186462306a36Sopenharmony_ci};
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ciint fimc_initialize_capture_subdev(struct fimc_dev *fimc)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
186962306a36Sopenharmony_ci	int ret;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	v4l2_subdev_init(sd, &fimc_subdev_ops);
187262306a36Sopenharmony_ci	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
187362306a36Sopenharmony_ci	snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK;
187662306a36Sopenharmony_ci	fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK;
187762306a36Sopenharmony_ci	fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
187862306a36Sopenharmony_ci	ret = media_entity_pads_init(&sd->entity, FIMC_SD_PADS_NUM,
187962306a36Sopenharmony_ci				fimc->vid_cap.sd_pads);
188062306a36Sopenharmony_ci	if (ret)
188162306a36Sopenharmony_ci		return ret;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	sd->entity.ops = &fimc_sd_media_ops;
188462306a36Sopenharmony_ci	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
188562306a36Sopenharmony_ci	sd->internal_ops = &fimc_capture_sd_internal_ops;
188662306a36Sopenharmony_ci	v4l2_set_subdevdata(sd, fimc);
188762306a36Sopenharmony_ci	return 0;
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_civoid fimc_unregister_capture_subdev(struct fimc_dev *fimc)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
189562306a36Sopenharmony_ci	media_entity_cleanup(&sd->entity);
189662306a36Sopenharmony_ci	v4l2_set_subdevdata(sd, NULL);
189762306a36Sopenharmony_ci}
1898