xref: /linux/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c (revision c771600c6af14749609b49565ffb4cac2959710d)
16edb685aSTomi Valkeinen // SPDX-License-Identifier: GPL-2.0-only
26edb685aSTomi Valkeinen /*
36edb685aSTomi Valkeinen  * RP1 Camera Front End Driver
46edb685aSTomi Valkeinen  *
56edb685aSTomi Valkeinen  * Copyright (c) 2021-2024 Raspberry Pi Ltd.
66edb685aSTomi Valkeinen  * Copyright (c) 2023-2024 Ideas on Board Oy
76edb685aSTomi Valkeinen  */
86edb685aSTomi Valkeinen 
96edb685aSTomi Valkeinen #include <linux/clk.h>
106edb685aSTomi Valkeinen #include <linux/debugfs.h>
116edb685aSTomi Valkeinen #include <linux/delay.h>
126edb685aSTomi Valkeinen #include <linux/device.h>
136edb685aSTomi Valkeinen #include <linux/dma-mapping.h>
146edb685aSTomi Valkeinen #include <linux/err.h>
156edb685aSTomi Valkeinen #include <linux/fwnode.h>
166edb685aSTomi Valkeinen #include <linux/init.h>
176edb685aSTomi Valkeinen #include <linux/interrupt.h>
186edb685aSTomi Valkeinen #include <linux/io.h>
196edb685aSTomi Valkeinen #include <linux/lcm.h>
206edb685aSTomi Valkeinen #include <linux/math.h>
216edb685aSTomi Valkeinen #include <linux/module.h>
226edb685aSTomi Valkeinen #include <linux/platform_device.h>
236edb685aSTomi Valkeinen #include <linux/pm_runtime.h>
246edb685aSTomi Valkeinen #include <linux/property.h>
256edb685aSTomi Valkeinen #include <linux/seq_file.h>
266edb685aSTomi Valkeinen #include <linux/slab.h>
276edb685aSTomi Valkeinen #include <linux/uaccess.h>
286edb685aSTomi Valkeinen #include <linux/videodev2.h>
296edb685aSTomi Valkeinen 
306edb685aSTomi Valkeinen #include <media/v4l2-async.h>
316edb685aSTomi Valkeinen #include <media/v4l2-common.h>
326edb685aSTomi Valkeinen #include <media/v4l2-ctrls.h>
336edb685aSTomi Valkeinen #include <media/v4l2-dev.h>
346edb685aSTomi Valkeinen #include <media/v4l2-device.h>
356edb685aSTomi Valkeinen #include <media/v4l2-event.h>
366edb685aSTomi Valkeinen #include <media/v4l2-fwnode.h>
376edb685aSTomi Valkeinen #include <media/v4l2-ioctl.h>
386edb685aSTomi Valkeinen #include <media/v4l2-mc.h>
396edb685aSTomi Valkeinen #include <media/videobuf2-dma-contig.h>
406edb685aSTomi Valkeinen 
416edb685aSTomi Valkeinen #include <linux/media/raspberrypi/pisp_fe_config.h>
426edb685aSTomi Valkeinen #include <linux/media/raspberrypi/pisp_fe_statistics.h>
436edb685aSTomi Valkeinen 
446edb685aSTomi Valkeinen #include "cfe-fmts.h"
456edb685aSTomi Valkeinen #include "cfe.h"
466edb685aSTomi Valkeinen #include "csi2.h"
476edb685aSTomi Valkeinen #include "pisp-fe.h"
486edb685aSTomi Valkeinen 
496edb685aSTomi Valkeinen #define CREATE_TRACE_POINTS
506edb685aSTomi Valkeinen #include "cfe-trace.h"
516edb685aSTomi Valkeinen 
526edb685aSTomi Valkeinen #define CFE_MODULE_NAME	"rp1-cfe"
536edb685aSTomi Valkeinen #define CFE_VERSION	"1.0"
546edb685aSTomi Valkeinen 
556edb685aSTomi Valkeinen #define cfe_dbg(cfe, fmt, arg...) dev_dbg(&(cfe)->pdev->dev, fmt, ##arg)
566edb685aSTomi Valkeinen #define cfe_info(cfe, fmt, arg...) dev_info(&(cfe)->pdev->dev, fmt, ##arg)
576edb685aSTomi Valkeinen #define cfe_err(cfe, fmt, arg...) dev_err(&(cfe)->pdev->dev, fmt, ##arg)
586edb685aSTomi Valkeinen 
596edb685aSTomi Valkeinen /* MIPICFG registers */
606edb685aSTomi Valkeinen #define MIPICFG_CFG		0x004
616edb685aSTomi Valkeinen #define MIPICFG_INTR		0x028
626edb685aSTomi Valkeinen #define MIPICFG_INTE		0x02c
636edb685aSTomi Valkeinen #define MIPICFG_INTF		0x030
646edb685aSTomi Valkeinen #define MIPICFG_INTS		0x034
656edb685aSTomi Valkeinen 
666edb685aSTomi Valkeinen #define MIPICFG_CFG_SEL_CSI	BIT(0)
676edb685aSTomi Valkeinen 
686edb685aSTomi Valkeinen #define MIPICFG_INT_CSI_DMA	BIT(0)
696edb685aSTomi Valkeinen #define MIPICFG_INT_CSI_HOST	BIT(2)
706edb685aSTomi Valkeinen #define MIPICFG_INT_PISP_FE	BIT(4)
716edb685aSTomi Valkeinen 
726edb685aSTomi Valkeinen #define BPL_ALIGNMENT 16
736edb685aSTomi Valkeinen #define MAX_BYTESPERLINE 0xffffff00
746edb685aSTomi Valkeinen #define MAX_BUFFER_SIZE  0xffffff00
756edb685aSTomi Valkeinen /*
766edb685aSTomi Valkeinen  * Max width is therefore determined by the max stride divided by the number of
776edb685aSTomi Valkeinen  * bits per pixel.
786edb685aSTomi Valkeinen  *
796edb685aSTomi Valkeinen  * However, to avoid overflow issues let's use a 16k maximum. This lets us
806edb685aSTomi Valkeinen  * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful
816edb685aSTomi Valkeinen  * review and adjustment of the code is needed so that it will deal with
826edb685aSTomi Valkeinen  * overflows correctly.
836edb685aSTomi Valkeinen  */
846edb685aSTomi Valkeinen #define MAX_WIDTH 16384
856edb685aSTomi Valkeinen #define MAX_HEIGHT MAX_WIDTH
866edb685aSTomi Valkeinen /* Define a nominal minimum image size */
876edb685aSTomi Valkeinen #define MIN_WIDTH 16
886edb685aSTomi Valkeinen #define MIN_HEIGHT 16
896edb685aSTomi Valkeinen 
906edb685aSTomi Valkeinen #define MIN_META_WIDTH 4
916edb685aSTomi Valkeinen #define MIN_META_HEIGHT 1
926edb685aSTomi Valkeinen 
936edb685aSTomi Valkeinen const struct v4l2_mbus_framefmt cfe_default_format = {
946edb685aSTomi Valkeinen 	.width = 640,
956edb685aSTomi Valkeinen 	.height = 480,
966edb685aSTomi Valkeinen 	.code = MEDIA_BUS_FMT_SRGGB10_1X10,
976edb685aSTomi Valkeinen 	.field = V4L2_FIELD_NONE,
986edb685aSTomi Valkeinen 	.colorspace = V4L2_COLORSPACE_RAW,
996edb685aSTomi Valkeinen 	.ycbcr_enc = V4L2_YCBCR_ENC_601,
1006edb685aSTomi Valkeinen 	.quantization = V4L2_QUANTIZATION_FULL_RANGE,
1016edb685aSTomi Valkeinen 	.xfer_func = V4L2_XFER_FUNC_NONE,
1026edb685aSTomi Valkeinen };
1036edb685aSTomi Valkeinen 
1046edb685aSTomi Valkeinen enum node_ids {
1056edb685aSTomi Valkeinen 	/* CSI2 HW output nodes first. */
1066edb685aSTomi Valkeinen 	CSI2_CH0,
1076edb685aSTomi Valkeinen 	CSI2_CH1,
1086edb685aSTomi Valkeinen 	CSI2_CH2,
1096edb685aSTomi Valkeinen 	CSI2_CH3,
1106edb685aSTomi Valkeinen 	/* FE only nodes from here on. */
1116edb685aSTomi Valkeinen 	FE_OUT0,
1126edb685aSTomi Valkeinen 	FE_OUT1,
1136edb685aSTomi Valkeinen 	FE_STATS,
1146edb685aSTomi Valkeinen 	FE_CONFIG,
1156edb685aSTomi Valkeinen 	NUM_NODES
1166edb685aSTomi Valkeinen };
1176edb685aSTomi Valkeinen 
1186edb685aSTomi Valkeinen struct node_description {
1196edb685aSTomi Valkeinen 	enum node_ids id;
1206edb685aSTomi Valkeinen 	const char *name;
1216edb685aSTomi Valkeinen 	unsigned int caps;
1226edb685aSTomi Valkeinen 	unsigned int pad_flags;
1236edb685aSTomi Valkeinen 	unsigned int link_pad;
1246edb685aSTomi Valkeinen };
1256edb685aSTomi Valkeinen 
1266edb685aSTomi Valkeinen /* Must match the ordering of enum ids */
1276edb685aSTomi Valkeinen static const struct node_description node_desc[NUM_NODES] = {
1286edb685aSTomi Valkeinen 	[CSI2_CH0] = {
1296edb685aSTomi Valkeinen 		.name = "csi2-ch0",
1306edb685aSTomi Valkeinen 		.caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
1316edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
1326edb685aSTomi Valkeinen 		.link_pad = CSI2_PAD_FIRST_SOURCE + 0
1336edb685aSTomi Valkeinen 	},
1346edb685aSTomi Valkeinen 	/*
1356edb685aSTomi Valkeinen 	 * At the moment the main userspace component (libcamera) doesn't
1366edb685aSTomi Valkeinen 	 * support metadata with video nodes that support both video and
1376edb685aSTomi Valkeinen 	 * metadata. So for the time being this node is set to only support
1386edb685aSTomi Valkeinen 	 * V4L2_CAP_META_CAPTURE.
1396edb685aSTomi Valkeinen 	 */
1406edb685aSTomi Valkeinen 	[CSI2_CH1] = {
1416edb685aSTomi Valkeinen 		.name = "csi2-ch1",
1426edb685aSTomi Valkeinen 		.caps = V4L2_CAP_META_CAPTURE,
1436edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
1446edb685aSTomi Valkeinen 		.link_pad = CSI2_PAD_FIRST_SOURCE + 1
1456edb685aSTomi Valkeinen 	},
1466edb685aSTomi Valkeinen 	[CSI2_CH2] = {
1476edb685aSTomi Valkeinen 		.name = "csi2-ch2",
1486edb685aSTomi Valkeinen 		.caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
1496edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
1506edb685aSTomi Valkeinen 		.link_pad = CSI2_PAD_FIRST_SOURCE + 2
1516edb685aSTomi Valkeinen 	},
1526edb685aSTomi Valkeinen 	[CSI2_CH3] = {
1536edb685aSTomi Valkeinen 		.name = "csi2-ch3",
1546edb685aSTomi Valkeinen 		.caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
1556edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
1566edb685aSTomi Valkeinen 		.link_pad = CSI2_PAD_FIRST_SOURCE + 3
1576edb685aSTomi Valkeinen 	},
1586edb685aSTomi Valkeinen 	[FE_OUT0] = {
1596edb685aSTomi Valkeinen 		.name = "fe-image0",
1606edb685aSTomi Valkeinen 		.caps = V4L2_CAP_VIDEO_CAPTURE,
1616edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
1626edb685aSTomi Valkeinen 		.link_pad = FE_OUTPUT0_PAD
1636edb685aSTomi Valkeinen 	},
1646edb685aSTomi Valkeinen 	[FE_OUT1] = {
1656edb685aSTomi Valkeinen 		.name = "fe-image1",
1666edb685aSTomi Valkeinen 		.caps = V4L2_CAP_VIDEO_CAPTURE,
1676edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
1686edb685aSTomi Valkeinen 		.link_pad = FE_OUTPUT1_PAD
1696edb685aSTomi Valkeinen 	},
1706edb685aSTomi Valkeinen 	[FE_STATS] = {
1716edb685aSTomi Valkeinen 		.name = "fe-stats",
1726edb685aSTomi Valkeinen 		.caps = V4L2_CAP_META_CAPTURE,
1736edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
1746edb685aSTomi Valkeinen 		.link_pad = FE_STATS_PAD
1756edb685aSTomi Valkeinen 	},
1766edb685aSTomi Valkeinen 	[FE_CONFIG] = {
1776edb685aSTomi Valkeinen 		.name = "fe-config",
1786edb685aSTomi Valkeinen 		.caps = V4L2_CAP_META_OUTPUT,
1796edb685aSTomi Valkeinen 		.pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT,
1806edb685aSTomi Valkeinen 		.link_pad = FE_CONFIG_PAD
1816edb685aSTomi Valkeinen 	},
1826edb685aSTomi Valkeinen };
1836edb685aSTomi Valkeinen 
1846edb685aSTomi Valkeinen #define is_fe_node(node) (((node)->id) >= FE_OUT0)
1856edb685aSTomi Valkeinen #define is_csi2_node(node) (!is_fe_node(node))
1866edb685aSTomi Valkeinen 
1876edb685aSTomi Valkeinen #define node_supports_image_output(node) \
1886edb685aSTomi Valkeinen 	(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE)
1896edb685aSTomi Valkeinen #define node_supports_meta_output(node) \
1906edb685aSTomi Valkeinen 	(node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE)
1916edb685aSTomi Valkeinen #define node_supports_image_input(node) \
1926edb685aSTomi Valkeinen 	(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT)
1936edb685aSTomi Valkeinen #define node_supports_meta_input(node) \
1946edb685aSTomi Valkeinen 	(node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT)
1956edb685aSTomi Valkeinen #define node_supports_image(node) \
1966edb685aSTomi Valkeinen 	(node_supports_image_output(node) || node_supports_image_input(node))
1976edb685aSTomi Valkeinen #define node_supports_meta(node) \
1986edb685aSTomi Valkeinen 	(node_supports_meta_output(node) || node_supports_meta_input(node))
1996edb685aSTomi Valkeinen 
2006edb685aSTomi Valkeinen #define is_image_output_node(node) \
2016edb685aSTomi Valkeinen 	((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
2026edb685aSTomi Valkeinen #define is_image_input_node(node) \
2036edb685aSTomi Valkeinen 	((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
2046edb685aSTomi Valkeinen #define is_image_node(node) \
2056edb685aSTomi Valkeinen 	(is_image_output_node(node) || is_image_input_node(node))
2066edb685aSTomi Valkeinen #define is_meta_output_node(node) \
2076edb685aSTomi Valkeinen 	((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE)
2086edb685aSTomi Valkeinen #define is_meta_input_node(node) \
2096edb685aSTomi Valkeinen 	((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT)
2106edb685aSTomi Valkeinen #define is_meta_node(node) \
2116edb685aSTomi Valkeinen 	(is_meta_output_node(node) || is_meta_input_node(node))
2126edb685aSTomi Valkeinen 
2136edb685aSTomi Valkeinen /* To track state across all nodes. */
2146edb685aSTomi Valkeinen #define NODE_REGISTERED		BIT(0)
2156edb685aSTomi Valkeinen #define NODE_ENABLED		BIT(1)
2166edb685aSTomi Valkeinen #define NODE_STREAMING		BIT(2)
2176edb685aSTomi Valkeinen #define FS_INT			BIT(3)
2186edb685aSTomi Valkeinen #define FE_INT			BIT(4)
2196edb685aSTomi Valkeinen #define NUM_STATES		5
2206edb685aSTomi Valkeinen 
2216edb685aSTomi Valkeinen struct cfe_buffer {
2226edb685aSTomi Valkeinen 	struct vb2_v4l2_buffer vb;
2236edb685aSTomi Valkeinen 	struct list_head list;
2246edb685aSTomi Valkeinen };
2256edb685aSTomi Valkeinen 
2266edb685aSTomi Valkeinen struct cfe_config_buffer {
2276edb685aSTomi Valkeinen 	struct cfe_buffer buf;
2286edb685aSTomi Valkeinen 	struct pisp_fe_config config;
2296edb685aSTomi Valkeinen };
2306edb685aSTomi Valkeinen 
2316edb685aSTomi Valkeinen static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb)
2326edb685aSTomi Valkeinen {
2336edb685aSTomi Valkeinen 	return container_of(vb, struct cfe_buffer, vb.vb2_buf);
2346edb685aSTomi Valkeinen }
2356edb685aSTomi Valkeinen 
2366edb685aSTomi Valkeinen static inline
2376edb685aSTomi Valkeinen struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf)
2386edb685aSTomi Valkeinen {
2396edb685aSTomi Valkeinen 	return container_of(buf, struct cfe_config_buffer, buf);
2406edb685aSTomi Valkeinen }
2416edb685aSTomi Valkeinen 
2426edb685aSTomi Valkeinen struct cfe_node {
2436edb685aSTomi Valkeinen 	/* Node id */
2446edb685aSTomi Valkeinen 	enum node_ids id;
2456edb685aSTomi Valkeinen 	/* Pointer pointing to current v4l2_buffer */
2466edb685aSTomi Valkeinen 	struct cfe_buffer *cur_frm;
2476edb685aSTomi Valkeinen 	/* Pointer pointing to next v4l2_buffer */
2486edb685aSTomi Valkeinen 	struct cfe_buffer *next_frm;
2496edb685aSTomi Valkeinen 	/* Used to store current pixel format */
2506edb685aSTomi Valkeinen 	struct v4l2_format vid_fmt;
2516edb685aSTomi Valkeinen 	/* Used to store current meta format */
2526edb685aSTomi Valkeinen 	struct v4l2_format meta_fmt;
2536edb685aSTomi Valkeinen 	/* Buffer queue used in video-buf */
2546edb685aSTomi Valkeinen 	struct vb2_queue buffer_queue;
2556edb685aSTomi Valkeinen 	/* Queue of filled frames */
2566edb685aSTomi Valkeinen 	struct list_head dma_queue;
2576edb685aSTomi Valkeinen 	/* lock used to access this structure */
2586edb685aSTomi Valkeinen 	struct mutex lock;
2596edb685aSTomi Valkeinen 	/* Identifies video device for this channel */
2606edb685aSTomi Valkeinen 	struct video_device video_dev;
2616edb685aSTomi Valkeinen 	/* Pointer to the parent handle */
2626edb685aSTomi Valkeinen 	struct cfe_device *cfe;
2636edb685aSTomi Valkeinen 	/* Media pad for this node */
2646edb685aSTomi Valkeinen 	struct media_pad pad;
2656edb685aSTomi Valkeinen 	/* Frame-start counter */
2666edb685aSTomi Valkeinen 	unsigned int fs_count;
2676edb685aSTomi Valkeinen 	/* Timestamp of the current buffer */
2686edb685aSTomi Valkeinen 	u64 ts;
2696edb685aSTomi Valkeinen };
2706edb685aSTomi Valkeinen 
2716edb685aSTomi Valkeinen struct cfe_device {
2726edb685aSTomi Valkeinen 	struct dentry *debugfs;
2736edb685aSTomi Valkeinen 	struct kref kref;
2746edb685aSTomi Valkeinen 
2756edb685aSTomi Valkeinen 	/* peripheral base address */
2766edb685aSTomi Valkeinen 	void __iomem *mipi_cfg_base;
2776edb685aSTomi Valkeinen 
2786edb685aSTomi Valkeinen 	struct clk *clk;
2796edb685aSTomi Valkeinen 
2806edb685aSTomi Valkeinen 	/* V4l2 device */
2816edb685aSTomi Valkeinen 	struct v4l2_device v4l2_dev;
2826edb685aSTomi Valkeinen 	struct media_device mdev;
2836edb685aSTomi Valkeinen 	struct media_pipeline pipe;
2846edb685aSTomi Valkeinen 
2856edb685aSTomi Valkeinen 	/* IRQ lock for node state and DMA queues */
2866edb685aSTomi Valkeinen 	spinlock_t state_lock;
2876edb685aSTomi Valkeinen 	bool job_ready;
2886edb685aSTomi Valkeinen 	bool job_queued;
2896edb685aSTomi Valkeinen 
2906edb685aSTomi Valkeinen 	/* parent device */
2916edb685aSTomi Valkeinen 	struct platform_device *pdev;
2926edb685aSTomi Valkeinen 	/* subdevice async Notifier */
2936edb685aSTomi Valkeinen 	struct v4l2_async_notifier notifier;
2946edb685aSTomi Valkeinen 
2956edb685aSTomi Valkeinen 	/* Source sub device */
2966edb685aSTomi Valkeinen 	struct v4l2_subdev *source_sd;
2976edb685aSTomi Valkeinen 	/* Source subdev's pad */
2986edb685aSTomi Valkeinen 	u32 source_pad;
2996edb685aSTomi Valkeinen 
3006edb685aSTomi Valkeinen 	struct cfe_node node[NUM_NODES];
3016edb685aSTomi Valkeinen 	DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES);
3026edb685aSTomi Valkeinen 
3036edb685aSTomi Valkeinen 	struct csi2_device csi2;
3046edb685aSTomi Valkeinen 	struct pisp_fe_device fe;
3056edb685aSTomi Valkeinen 
3066edb685aSTomi Valkeinen 	int fe_csi2_channel;
3076edb685aSTomi Valkeinen 
3086edb685aSTomi Valkeinen 	/* Mask of enabled streams */
3096edb685aSTomi Valkeinen 	u64 streams_mask;
3106edb685aSTomi Valkeinen };
3116edb685aSTomi Valkeinen 
3126edb685aSTomi Valkeinen static inline bool is_fe_enabled(struct cfe_device *cfe)
3136edb685aSTomi Valkeinen {
3146edb685aSTomi Valkeinen 	return cfe->fe_csi2_channel != -1;
3156edb685aSTomi Valkeinen }
3166edb685aSTomi Valkeinen 
3176edb685aSTomi Valkeinen static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev)
3186edb685aSTomi Valkeinen {
3196edb685aSTomi Valkeinen 	return container_of(v4l2_dev, struct cfe_device, v4l2_dev);
3206edb685aSTomi Valkeinen }
3216edb685aSTomi Valkeinen 
3226edb685aSTomi Valkeinen static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset)
3236edb685aSTomi Valkeinen {
3246edb685aSTomi Valkeinen 	return readl(cfe->mipi_cfg_base + offset);
3256edb685aSTomi Valkeinen }
3266edb685aSTomi Valkeinen 
3276edb685aSTomi Valkeinen static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val)
3286edb685aSTomi Valkeinen {
3296edb685aSTomi Valkeinen 	writel(val, cfe->mipi_cfg_base + offset);
3306edb685aSTomi Valkeinen }
3316edb685aSTomi Valkeinen 
3326edb685aSTomi Valkeinen static bool check_state(struct cfe_device *cfe, unsigned long state,
3336edb685aSTomi Valkeinen 			unsigned int node_id)
3346edb685aSTomi Valkeinen {
3356edb685aSTomi Valkeinen 	unsigned long bit;
3366edb685aSTomi Valkeinen 
3376edb685aSTomi Valkeinen 	for_each_set_bit(bit, &state, sizeof(state)) {
3386edb685aSTomi Valkeinen 		if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags))
3396edb685aSTomi Valkeinen 			return false;
3406edb685aSTomi Valkeinen 	}
3416edb685aSTomi Valkeinen 
3426edb685aSTomi Valkeinen 	return true;
3436edb685aSTomi Valkeinen }
3446edb685aSTomi Valkeinen 
3456edb685aSTomi Valkeinen static void set_state(struct cfe_device *cfe, unsigned long state,
3466edb685aSTomi Valkeinen 		      unsigned int node_id)
3476edb685aSTomi Valkeinen {
3486edb685aSTomi Valkeinen 	unsigned long bit;
3496edb685aSTomi Valkeinen 
3506edb685aSTomi Valkeinen 	for_each_set_bit(bit, &state, sizeof(state))
3516edb685aSTomi Valkeinen 		set_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
3526edb685aSTomi Valkeinen }
3536edb685aSTomi Valkeinen 
3546edb685aSTomi Valkeinen static void clear_state(struct cfe_device *cfe, unsigned long state,
3556edb685aSTomi Valkeinen 			unsigned int node_id)
3566edb685aSTomi Valkeinen {
3576edb685aSTomi Valkeinen 	unsigned long bit;
3586edb685aSTomi Valkeinen 
3596edb685aSTomi Valkeinen 	for_each_set_bit(bit, &state, sizeof(state))
3606edb685aSTomi Valkeinen 		clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
3616edb685aSTomi Valkeinen }
3626edb685aSTomi Valkeinen 
3636edb685aSTomi Valkeinen static bool test_any_node(struct cfe_device *cfe, unsigned long cond)
3646edb685aSTomi Valkeinen {
3656edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
3666edb685aSTomi Valkeinen 		if (check_state(cfe, cond, i))
3676edb685aSTomi Valkeinen 			return true;
3686edb685aSTomi Valkeinen 	}
3696edb685aSTomi Valkeinen 
3706edb685aSTomi Valkeinen 	return false;
3716edb685aSTomi Valkeinen }
3726edb685aSTomi Valkeinen 
3736edb685aSTomi Valkeinen static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond,
3746edb685aSTomi Valkeinen 			   unsigned long cond)
3756edb685aSTomi Valkeinen {
3766edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
3776edb685aSTomi Valkeinen 		if (check_state(cfe, precond, i)) {
3786edb685aSTomi Valkeinen 			if (!check_state(cfe, cond, i))
3796edb685aSTomi Valkeinen 				return false;
3806edb685aSTomi Valkeinen 		}
3816edb685aSTomi Valkeinen 	}
3826edb685aSTomi Valkeinen 
3836edb685aSTomi Valkeinen 	return true;
3846edb685aSTomi Valkeinen }
3856edb685aSTomi Valkeinen 
3866edb685aSTomi Valkeinen static int mipi_cfg_regs_show(struct seq_file *s, void *data)
3876edb685aSTomi Valkeinen {
3886edb685aSTomi Valkeinen 	struct cfe_device *cfe = s->private;
3896edb685aSTomi Valkeinen 	int ret;
3906edb685aSTomi Valkeinen 
3916edb685aSTomi Valkeinen 	ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
3926edb685aSTomi Valkeinen 	if (ret)
3936edb685aSTomi Valkeinen 		return ret;
3946edb685aSTomi Valkeinen 
3956edb685aSTomi Valkeinen #define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg))
3966edb685aSTomi Valkeinen 	DUMP(MIPICFG_CFG);
3976edb685aSTomi Valkeinen 	DUMP(MIPICFG_INTR);
3986edb685aSTomi Valkeinen 	DUMP(MIPICFG_INTE);
3996edb685aSTomi Valkeinen 	DUMP(MIPICFG_INTF);
4006edb685aSTomi Valkeinen 	DUMP(MIPICFG_INTS);
4016edb685aSTomi Valkeinen #undef DUMP
4026edb685aSTomi Valkeinen 
4036edb685aSTomi Valkeinen 	pm_runtime_put(&cfe->pdev->dev);
4046edb685aSTomi Valkeinen 
4056edb685aSTomi Valkeinen 	return 0;
4066edb685aSTomi Valkeinen }
4076edb685aSTomi Valkeinen 
4086edb685aSTomi Valkeinen DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs);
4096edb685aSTomi Valkeinen 
4106edb685aSTomi Valkeinen /* Format setup functions */
4116edb685aSTomi Valkeinen const struct cfe_fmt *find_format_by_code(u32 code)
4126edb685aSTomi Valkeinen {
4136edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) {
4146edb685aSTomi Valkeinen 		if (formats[i].code == code)
4156edb685aSTomi Valkeinen 			return &formats[i];
4166edb685aSTomi Valkeinen 	}
4176edb685aSTomi Valkeinen 
4186edb685aSTomi Valkeinen 	return NULL;
4196edb685aSTomi Valkeinen }
4206edb685aSTomi Valkeinen 
4216edb685aSTomi Valkeinen const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
4226edb685aSTomi Valkeinen {
4236edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) {
4246edb685aSTomi Valkeinen 		if (formats[i].fourcc == pixelformat)
4256edb685aSTomi Valkeinen 			return &formats[i];
4266edb685aSTomi Valkeinen 	}
4276edb685aSTomi Valkeinen 
4286edb685aSTomi Valkeinen 	return NULL;
4296edb685aSTomi Valkeinen }
4306edb685aSTomi Valkeinen 
4316edb685aSTomi Valkeinen static const struct cfe_fmt *find_format_by_code_and_fourcc(u32 code,
4326edb685aSTomi Valkeinen 							    u32 fourcc)
4336edb685aSTomi Valkeinen {
4346edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) {
4356edb685aSTomi Valkeinen 		if (formats[i].code == code && formats[i].fourcc == fourcc)
4366edb685aSTomi Valkeinen 			return &formats[i];
4376edb685aSTomi Valkeinen 	}
4386edb685aSTomi Valkeinen 
4396edb685aSTomi Valkeinen 	return NULL;
4406edb685aSTomi Valkeinen }
4416edb685aSTomi Valkeinen 
4426edb685aSTomi Valkeinen /*
4436edb685aSTomi Valkeinen  * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap
4446edb685aSTomi Valkeinen  * possible.
4456edb685aSTomi Valkeinen  */
4466edb685aSTomi Valkeinen u32 cfe_find_16bit_code(u32 code)
4476edb685aSTomi Valkeinen {
4486edb685aSTomi Valkeinen 	const struct cfe_fmt *cfe_fmt;
4496edb685aSTomi Valkeinen 
4506edb685aSTomi Valkeinen 	cfe_fmt = find_format_by_code(code);
4516edb685aSTomi Valkeinen 
4526edb685aSTomi Valkeinen 	if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT])
4536edb685aSTomi Valkeinen 		return 0;
4546edb685aSTomi Valkeinen 
4556edb685aSTomi Valkeinen 	cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]);
4566edb685aSTomi Valkeinen 	if (!cfe_fmt)
4576edb685aSTomi Valkeinen 		return 0;
4586edb685aSTomi Valkeinen 
4596edb685aSTomi Valkeinen 	return cfe_fmt->code;
4606edb685aSTomi Valkeinen }
4616edb685aSTomi Valkeinen 
4626edb685aSTomi Valkeinen /*
4636edb685aSTomi Valkeinen  * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap
4646edb685aSTomi Valkeinen  * possible.
4656edb685aSTomi Valkeinen  */
4666edb685aSTomi Valkeinen u32 cfe_find_compressed_code(u32 code)
4676edb685aSTomi Valkeinen {
4686edb685aSTomi Valkeinen 	const struct cfe_fmt *cfe_fmt;
4696edb685aSTomi Valkeinen 
4706edb685aSTomi Valkeinen 	cfe_fmt = find_format_by_code(code);
4716edb685aSTomi Valkeinen 
4726edb685aSTomi Valkeinen 	if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED])
4736edb685aSTomi Valkeinen 		return 0;
4746edb685aSTomi Valkeinen 
4756edb685aSTomi Valkeinen 	cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]);
4766edb685aSTomi Valkeinen 	if (!cfe_fmt)
4776edb685aSTomi Valkeinen 		return 0;
4786edb685aSTomi Valkeinen 
4796edb685aSTomi Valkeinen 	return cfe_fmt->code;
4806edb685aSTomi Valkeinen }
4816edb685aSTomi Valkeinen 
4826edb685aSTomi Valkeinen static void cfe_calc_vid_format_size_bpl(struct cfe_device *cfe,
4836edb685aSTomi Valkeinen 					 const struct cfe_fmt *fmt,
4846edb685aSTomi Valkeinen 					 struct v4l2_format *f)
4856edb685aSTomi Valkeinen {
4866edb685aSTomi Valkeinen 	unsigned int min_bytesperline;
4876edb685aSTomi Valkeinen 
4886edb685aSTomi Valkeinen 	v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
4896edb685aSTomi Valkeinen 			      &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, 0);
4906edb685aSTomi Valkeinen 
4916edb685aSTomi Valkeinen 	min_bytesperline =
4926edb685aSTomi Valkeinen 		ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT);
4936edb685aSTomi Valkeinen 
4946edb685aSTomi Valkeinen 	if (f->fmt.pix.bytesperline > min_bytesperline &&
4956edb685aSTomi Valkeinen 	    f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
4966edb685aSTomi Valkeinen 		f->fmt.pix.bytesperline =
4976edb685aSTomi Valkeinen 			ALIGN(f->fmt.pix.bytesperline, BPL_ALIGNMENT);
4986edb685aSTomi Valkeinen 	else
4996edb685aSTomi Valkeinen 		f->fmt.pix.bytesperline = min_bytesperline;
5006edb685aSTomi Valkeinen 
5016edb685aSTomi Valkeinen 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
5026edb685aSTomi Valkeinen 
5036edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u img_size:%u\n", __func__,
5046edb685aSTomi Valkeinen 		&f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height,
5056edb685aSTomi Valkeinen 		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
5066edb685aSTomi Valkeinen }
5076edb685aSTomi Valkeinen 
5086edb685aSTomi Valkeinen static void cfe_calc_meta_format_size_bpl(struct cfe_device *cfe,
5096edb685aSTomi Valkeinen 					  const struct cfe_fmt *fmt,
5106edb685aSTomi Valkeinen 					  struct v4l2_format *f)
5116edb685aSTomi Valkeinen {
5126edb685aSTomi Valkeinen 	v4l_bound_align_image(&f->fmt.meta.width, MIN_META_WIDTH, MAX_WIDTH, 2,
5136edb685aSTomi Valkeinen 			      &f->fmt.meta.height, MIN_META_HEIGHT, MAX_HEIGHT,
5146edb685aSTomi Valkeinen 			      0, 0);
5156edb685aSTomi Valkeinen 
5166edb685aSTomi Valkeinen 	f->fmt.meta.bytesperline = (f->fmt.meta.width * fmt->depth) >> 3;
5176edb685aSTomi Valkeinen 	f->fmt.meta.buffersize = f->fmt.meta.height * f->fmt.pix.bytesperline;
5186edb685aSTomi Valkeinen 
5196edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u buf_size:%u\n", __func__,
5206edb685aSTomi Valkeinen 		&f->fmt.meta.dataformat, f->fmt.meta.width, f->fmt.meta.height,
5216edb685aSTomi Valkeinen 		f->fmt.meta.bytesperline, f->fmt.meta.buffersize);
5226edb685aSTomi Valkeinen }
5236edb685aSTomi Valkeinen 
5246edb685aSTomi Valkeinen static void cfe_schedule_next_csi2_job(struct cfe_device *cfe)
5256edb685aSTomi Valkeinen {
5266edb685aSTomi Valkeinen 	struct cfe_buffer *buf;
5276edb685aSTomi Valkeinen 	dma_addr_t addr;
5286edb685aSTomi Valkeinen 
5296edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
5306edb685aSTomi Valkeinen 		struct cfe_node *node = &cfe->node[i];
5316edb685aSTomi Valkeinen 		unsigned int stride, size;
5326edb685aSTomi Valkeinen 
5336edb685aSTomi Valkeinen 		if (!check_state(cfe, NODE_STREAMING, i))
5346edb685aSTomi Valkeinen 			continue;
5356edb685aSTomi Valkeinen 
5366edb685aSTomi Valkeinen 		buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
5376edb685aSTomi Valkeinen 				       list);
5386edb685aSTomi Valkeinen 		node->next_frm = buf;
5396edb685aSTomi Valkeinen 		list_del(&buf->list);
5406edb685aSTomi Valkeinen 
5416edb685aSTomi Valkeinen 		trace_cfe_csi2_schedule(node->id, &buf->vb.vb2_buf);
5426edb685aSTomi Valkeinen 
5436edb685aSTomi Valkeinen 		if (is_meta_node(node)) {
5446edb685aSTomi Valkeinen 			size = node->meta_fmt.fmt.meta.buffersize;
5456edb685aSTomi Valkeinen 			/* We use CSI2_CH_CTRL_PACK_BYTES, so stride == 0 */
5466edb685aSTomi Valkeinen 			stride = 0;
5476edb685aSTomi Valkeinen 		} else {
5486edb685aSTomi Valkeinen 			size = node->vid_fmt.fmt.pix.sizeimage;
5496edb685aSTomi Valkeinen 			stride = node->vid_fmt.fmt.pix.bytesperline;
5506edb685aSTomi Valkeinen 		}
5516edb685aSTomi Valkeinen 
5526edb685aSTomi Valkeinen 		addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
5536edb685aSTomi Valkeinen 		csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size);
5546edb685aSTomi Valkeinen 	}
5556edb685aSTomi Valkeinen }
5566edb685aSTomi Valkeinen 
5576edb685aSTomi Valkeinen static void cfe_schedule_next_pisp_job(struct cfe_device *cfe)
5586edb685aSTomi Valkeinen {
5596edb685aSTomi Valkeinen 	struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 };
5606edb685aSTomi Valkeinen 	struct cfe_config_buffer *config_buf;
5616edb685aSTomi Valkeinen 	struct cfe_buffer *buf;
5626edb685aSTomi Valkeinen 
5636edb685aSTomi Valkeinen 	for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) {
5646edb685aSTomi Valkeinen 		struct cfe_node *node = &cfe->node[i];
5656edb685aSTomi Valkeinen 
5666edb685aSTomi Valkeinen 		if (!check_state(cfe, NODE_STREAMING, i))
5676edb685aSTomi Valkeinen 			continue;
5686edb685aSTomi Valkeinen 
5696edb685aSTomi Valkeinen 		buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
5706edb685aSTomi Valkeinen 				       list);
5716edb685aSTomi Valkeinen 
5726edb685aSTomi Valkeinen 		trace_cfe_fe_schedule(node->id, &buf->vb.vb2_buf);
5736edb685aSTomi Valkeinen 
5746edb685aSTomi Valkeinen 		node->next_frm = buf;
5756edb685aSTomi Valkeinen 		vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf;
5766edb685aSTomi Valkeinen 		list_del(&buf->list);
5776edb685aSTomi Valkeinen 	}
5786edb685aSTomi Valkeinen 
5796edb685aSTomi Valkeinen 	config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm);
5806edb685aSTomi Valkeinen 	pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config);
5816edb685aSTomi Valkeinen }
5826edb685aSTomi Valkeinen 
5836edb685aSTomi Valkeinen static bool cfe_check_job_ready(struct cfe_device *cfe)
5846edb685aSTomi Valkeinen {
5856edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
5866edb685aSTomi Valkeinen 		struct cfe_node *node = &cfe->node[i];
5876edb685aSTomi Valkeinen 
5886edb685aSTomi Valkeinen 		if (!check_state(cfe, NODE_ENABLED, i))
5896edb685aSTomi Valkeinen 			continue;
5906edb685aSTomi Valkeinen 
5916edb685aSTomi Valkeinen 		if (list_empty(&node->dma_queue))
5926edb685aSTomi Valkeinen 			return false;
5936edb685aSTomi Valkeinen 	}
5946edb685aSTomi Valkeinen 
5956edb685aSTomi Valkeinen 	return true;
5966edb685aSTomi Valkeinen }
5976edb685aSTomi Valkeinen 
5986edb685aSTomi Valkeinen static void cfe_prepare_next_job(struct cfe_device *cfe)
5996edb685aSTomi Valkeinen {
6006edb685aSTomi Valkeinen 	trace_cfe_prepare_next_job(is_fe_enabled(cfe));
6016edb685aSTomi Valkeinen 
6026edb685aSTomi Valkeinen 	cfe->job_queued = true;
6036edb685aSTomi Valkeinen 	cfe_schedule_next_csi2_job(cfe);
6046edb685aSTomi Valkeinen 	if (is_fe_enabled(cfe))
6056edb685aSTomi Valkeinen 		cfe_schedule_next_pisp_job(cfe);
6066edb685aSTomi Valkeinen 
6076edb685aSTomi Valkeinen 	/* Flag if another job is ready after this. */
6086edb685aSTomi Valkeinen 	cfe->job_ready = cfe_check_job_ready(cfe);
6096edb685aSTomi Valkeinen }
6106edb685aSTomi Valkeinen 
6116edb685aSTomi Valkeinen static void cfe_process_buffer_complete(struct cfe_node *node,
6126edb685aSTomi Valkeinen 					enum vb2_buffer_state state)
6136edb685aSTomi Valkeinen {
6146edb685aSTomi Valkeinen 	trace_cfe_buffer_complete(node->id, &node->cur_frm->vb);
6156edb685aSTomi Valkeinen 
6166edb685aSTomi Valkeinen 	node->cur_frm->vb.sequence = node->fs_count - 1;
6176edb685aSTomi Valkeinen 	vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
6186edb685aSTomi Valkeinen }
6196edb685aSTomi Valkeinen 
6206edb685aSTomi Valkeinen static void cfe_queue_event_sof(struct cfe_node *node)
6216edb685aSTomi Valkeinen {
6226edb685aSTomi Valkeinen 	struct v4l2_event event = {
6236edb685aSTomi Valkeinen 		.type = V4L2_EVENT_FRAME_SYNC,
6246edb685aSTomi Valkeinen 		.u.frame_sync.frame_sequence = node->fs_count - 1,
6256edb685aSTomi Valkeinen 	};
6266edb685aSTomi Valkeinen 
6276edb685aSTomi Valkeinen 	v4l2_event_queue(&node->video_dev, &event);
6286edb685aSTomi Valkeinen }
6296edb685aSTomi Valkeinen 
6306edb685aSTomi Valkeinen static void cfe_sof_isr(struct cfe_node *node)
6316edb685aSTomi Valkeinen {
6326edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
6336edb685aSTomi Valkeinen 	bool matching_fs = true;
6346edb685aSTomi Valkeinen 
6356edb685aSTomi Valkeinen 	trace_cfe_frame_start(node->id, node->fs_count);
6366edb685aSTomi Valkeinen 
6376edb685aSTomi Valkeinen 	/*
6386edb685aSTomi Valkeinen 	 * If the sensor is producing unexpected frame event ordering over a
6396edb685aSTomi Valkeinen 	 * sustained period of time, guard against the possibility of coming
6406edb685aSTomi Valkeinen 	 * here and orphaning the cur_frm if it's not been dequeued already.
6416edb685aSTomi Valkeinen 	 * Unfortunately, there is not enough hardware state to tell if this
6426edb685aSTomi Valkeinen 	 * may have occurred.
6436edb685aSTomi Valkeinen 	 */
644fba1aff8SColin Ian King 	if (WARN(node->cur_frm, "%s: [%s] Orphaned frame at seq %u\n",
6456edb685aSTomi Valkeinen 		 __func__, node_desc[node->id].name, node->fs_count))
6466edb685aSTomi Valkeinen 		cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR);
6476edb685aSTomi Valkeinen 
6486edb685aSTomi Valkeinen 	node->cur_frm = node->next_frm;
6496edb685aSTomi Valkeinen 	node->next_frm = NULL;
6506edb685aSTomi Valkeinen 	node->fs_count++;
6516edb685aSTomi Valkeinen 
6526edb685aSTomi Valkeinen 	node->ts = ktime_get_ns();
6536edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
6546edb685aSTomi Valkeinen 		if (!check_state(cfe, NODE_STREAMING, i) || i == node->id)
6556edb685aSTomi Valkeinen 			continue;
6566edb685aSTomi Valkeinen 		/*
6576edb685aSTomi Valkeinen 		 * This checks if any other node has seen a FS. If yes, use the
6586edb685aSTomi Valkeinen 		 * same timestamp, eventually across all node buffers.
6596edb685aSTomi Valkeinen 		 */
6606edb685aSTomi Valkeinen 		if (cfe->node[i].fs_count >= node->fs_count)
6616edb685aSTomi Valkeinen 			node->ts = cfe->node[i].ts;
6626edb685aSTomi Valkeinen 		/*
6636edb685aSTomi Valkeinen 		 * This checks if all other node have seen a matching FS. If
6646edb685aSTomi Valkeinen 		 * yes, we can flag another job to be queued.
6656edb685aSTomi Valkeinen 		 */
6666edb685aSTomi Valkeinen 		if (matching_fs && cfe->node[i].fs_count != node->fs_count)
6676edb685aSTomi Valkeinen 			matching_fs = false;
6686edb685aSTomi Valkeinen 	}
6696edb685aSTomi Valkeinen 
6706edb685aSTomi Valkeinen 	if (matching_fs)
6716edb685aSTomi Valkeinen 		cfe->job_queued = false;
6726edb685aSTomi Valkeinen 
6736edb685aSTomi Valkeinen 	if (node->cur_frm)
6746edb685aSTomi Valkeinen 		node->cur_frm->vb.vb2_buf.timestamp = node->ts;
6756edb685aSTomi Valkeinen 
6766edb685aSTomi Valkeinen 	set_state(cfe, FS_INT, node->id);
6776edb685aSTomi Valkeinen 	clear_state(cfe, FE_INT, node->id);
6786edb685aSTomi Valkeinen 
6796edb685aSTomi Valkeinen 	if (is_image_output_node(node))
6806edb685aSTomi Valkeinen 		cfe_queue_event_sof(node);
6816edb685aSTomi Valkeinen }
6826edb685aSTomi Valkeinen 
6836edb685aSTomi Valkeinen static void cfe_eof_isr(struct cfe_node *node)
6846edb685aSTomi Valkeinen {
6856edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
6866edb685aSTomi Valkeinen 
6876edb685aSTomi Valkeinen 	trace_cfe_frame_end(node->id, node->fs_count - 1);
6886edb685aSTomi Valkeinen 
6896edb685aSTomi Valkeinen 	if (node->cur_frm)
6906edb685aSTomi Valkeinen 		cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE);
6916edb685aSTomi Valkeinen 
6926edb685aSTomi Valkeinen 	node->cur_frm = NULL;
6936edb685aSTomi Valkeinen 	set_state(cfe, FE_INT, node->id);
6946edb685aSTomi Valkeinen 	clear_state(cfe, FS_INT, node->id);
6956edb685aSTomi Valkeinen }
6966edb685aSTomi Valkeinen 
6976edb685aSTomi Valkeinen static irqreturn_t cfe_isr(int irq, void *dev)
6986edb685aSTomi Valkeinen {
6996edb685aSTomi Valkeinen 	struct cfe_device *cfe = dev;
7006edb685aSTomi Valkeinen 	bool sof[NUM_NODES] = { 0 }, eof[NUM_NODES] = { 0 };
7016edb685aSTomi Valkeinen 	u32 sts;
7026edb685aSTomi Valkeinen 
7036edb685aSTomi Valkeinen 	sts = cfg_reg_read(cfe, MIPICFG_INTS);
7046edb685aSTomi Valkeinen 
7056edb685aSTomi Valkeinen 	if (sts & MIPICFG_INT_CSI_DMA)
7066edb685aSTomi Valkeinen 		csi2_isr(&cfe->csi2, sof, eof);
7076edb685aSTomi Valkeinen 
7086edb685aSTomi Valkeinen 	if (sts & MIPICFG_INT_PISP_FE)
7096edb685aSTomi Valkeinen 		pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS,
7106edb685aSTomi Valkeinen 			    eof + CSI2_NUM_CHANNELS);
7116edb685aSTomi Valkeinen 
7126edb685aSTomi Valkeinen 	spin_lock(&cfe->state_lock);
7136edb685aSTomi Valkeinen 
7146edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
7156edb685aSTomi Valkeinen 		struct cfe_node *node = &cfe->node[i];
7166edb685aSTomi Valkeinen 
7176edb685aSTomi Valkeinen 		/*
7186edb685aSTomi Valkeinen 		 * The check_state(NODE_STREAMING) is to ensure we do not loop
7196edb685aSTomi Valkeinen 		 * over the CSI2_CHx nodes when the FE is active since they
7206edb685aSTomi Valkeinen 		 * generate interrupts even though the node is not streaming.
7216edb685aSTomi Valkeinen 		 */
7226edb685aSTomi Valkeinen 		if (!check_state(cfe, NODE_STREAMING, i) || !(sof[i] || eof[i]))
7236edb685aSTomi Valkeinen 			continue;
7246edb685aSTomi Valkeinen 
7256edb685aSTomi Valkeinen 		/*
7266edb685aSTomi Valkeinen 		 * There are 3 cases where we could get FS + FE_ACK at
7276edb685aSTomi Valkeinen 		 * the same time:
7286edb685aSTomi Valkeinen 		 * 1) FE of the current frame, and FS of the next frame.
7296edb685aSTomi Valkeinen 		 * 2) FS + FE of the same frame.
7306edb685aSTomi Valkeinen 		 * 3) FE of the current frame, and FS + FE of the next
7316edb685aSTomi Valkeinen 		 *    frame. To handle this, see the sof handler below.
7326edb685aSTomi Valkeinen 		 *
7336edb685aSTomi Valkeinen 		 * (1) is handled implicitly by the ordering of the FE and FS
7346edb685aSTomi Valkeinen 		 * handlers below.
7356edb685aSTomi Valkeinen 		 */
7366edb685aSTomi Valkeinen 		if (eof[i]) {
7376edb685aSTomi Valkeinen 			/*
7386edb685aSTomi Valkeinen 			 * The condition below tests for (2). Run the FS handler
7396edb685aSTomi Valkeinen 			 * first before the FE handler, both for the current
7406edb685aSTomi Valkeinen 			 * frame.
7416edb685aSTomi Valkeinen 			 */
7426edb685aSTomi Valkeinen 			if (sof[i] && !check_state(cfe, FS_INT, i)) {
7436edb685aSTomi Valkeinen 				cfe_sof_isr(node);
7446edb685aSTomi Valkeinen 				sof[i] = false;
7456edb685aSTomi Valkeinen 			}
7466edb685aSTomi Valkeinen 
7476edb685aSTomi Valkeinen 			cfe_eof_isr(node);
7486edb685aSTomi Valkeinen 		}
7496edb685aSTomi Valkeinen 
7506edb685aSTomi Valkeinen 		if (sof[i]) {
7516edb685aSTomi Valkeinen 			/*
7526edb685aSTomi Valkeinen 			 * The condition below tests for (3). In such cases, we
7536edb685aSTomi Valkeinen 			 * come in here with FS flag set in the node state from
7546edb685aSTomi Valkeinen 			 * the previous frame since it only gets cleared in
7556edb685aSTomi Valkeinen 			 * cfe_eof_isr(). Handle the FE for the previous
7566edb685aSTomi Valkeinen 			 * frame first before the FS handler for the current
7576edb685aSTomi Valkeinen 			 * frame.
7586edb685aSTomi Valkeinen 			 */
7596edb685aSTomi Valkeinen 			if (check_state(cfe, FS_INT, node->id) &&
7606edb685aSTomi Valkeinen 			    !check_state(cfe, FE_INT, node->id)) {
7616edb685aSTomi Valkeinen 				cfe_dbg(cfe, "%s: [%s] Handling missing previous FE interrupt\n",
7626edb685aSTomi Valkeinen 					__func__, node_desc[node->id].name);
7636edb685aSTomi Valkeinen 				cfe_eof_isr(node);
7646edb685aSTomi Valkeinen 			}
7656edb685aSTomi Valkeinen 
7666edb685aSTomi Valkeinen 			cfe_sof_isr(node);
7676edb685aSTomi Valkeinen 		}
7686edb685aSTomi Valkeinen 
7696edb685aSTomi Valkeinen 		if (!cfe->job_queued && cfe->job_ready)
7706edb685aSTomi Valkeinen 			cfe_prepare_next_job(cfe);
7716edb685aSTomi Valkeinen 	}
7726edb685aSTomi Valkeinen 
7736edb685aSTomi Valkeinen 	spin_unlock(&cfe->state_lock);
7746edb685aSTomi Valkeinen 
7756edb685aSTomi Valkeinen 	return IRQ_HANDLED;
7766edb685aSTomi Valkeinen }
7776edb685aSTomi Valkeinen 
7786edb685aSTomi Valkeinen /*
7796edb685aSTomi Valkeinen  * Stream helpers
7806edb685aSTomi Valkeinen  */
7816edb685aSTomi Valkeinen 
7826edb685aSTomi Valkeinen static int cfe_get_vc_dt_fallback(struct cfe_device *cfe, u8 *vc, u8 *dt)
7836edb685aSTomi Valkeinen {
7846edb685aSTomi Valkeinen 	struct v4l2_subdev_state *state;
7856edb685aSTomi Valkeinen 	struct v4l2_mbus_framefmt *fmt;
7866edb685aSTomi Valkeinen 	const struct cfe_fmt *cfe_fmt;
7876edb685aSTomi Valkeinen 
7886edb685aSTomi Valkeinen 	state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
7896edb685aSTomi Valkeinen 
7906edb685aSTomi Valkeinen 	fmt = v4l2_subdev_state_get_format(state, CSI2_PAD_SINK, 0);
7916edb685aSTomi Valkeinen 	if (!fmt)
7926edb685aSTomi Valkeinen 		return -EINVAL;
7936edb685aSTomi Valkeinen 
7946edb685aSTomi Valkeinen 	cfe_fmt = find_format_by_code(fmt->code);
7956edb685aSTomi Valkeinen 	if (!cfe_fmt)
7966edb685aSTomi Valkeinen 		return -EINVAL;
7976edb685aSTomi Valkeinen 
7986edb685aSTomi Valkeinen 	*vc = 0;
7996edb685aSTomi Valkeinen 	*dt = cfe_fmt->csi_dt;
8006edb685aSTomi Valkeinen 
8016edb685aSTomi Valkeinen 	return 0;
8026edb685aSTomi Valkeinen }
8036edb685aSTomi Valkeinen 
8046edb685aSTomi Valkeinen static int cfe_get_vc_dt(struct cfe_device *cfe, unsigned int channel, u8 *vc,
8056edb685aSTomi Valkeinen 			 u8 *dt)
8066edb685aSTomi Valkeinen {
8076edb685aSTomi Valkeinen 	struct v4l2_mbus_frame_desc remote_desc;
8086edb685aSTomi Valkeinen 	struct v4l2_subdev_state *state;
8096edb685aSTomi Valkeinen 	u32 sink_stream;
8106edb685aSTomi Valkeinen 	unsigned int i;
8116edb685aSTomi Valkeinen 	int ret;
8126edb685aSTomi Valkeinen 
8136edb685aSTomi Valkeinen 	state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
8146edb685aSTomi Valkeinen 
8156edb685aSTomi Valkeinen 	ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
8166edb685aSTomi Valkeinen 		CSI2_PAD_FIRST_SOURCE + channel, 0, NULL, &sink_stream);
8176edb685aSTomi Valkeinen 	if (ret)
8186edb685aSTomi Valkeinen 		return ret;
8196edb685aSTomi Valkeinen 
8206edb685aSTomi Valkeinen 	ret = v4l2_subdev_call(cfe->source_sd, pad, get_frame_desc,
8216edb685aSTomi Valkeinen 			       cfe->source_pad, &remote_desc);
8226edb685aSTomi Valkeinen 	if (ret == -ENOIOCTLCMD) {
8236edb685aSTomi Valkeinen 		cfe_dbg(cfe, "source does not support get_frame_desc, use fallback\n");
8246edb685aSTomi Valkeinen 		return cfe_get_vc_dt_fallback(cfe, vc, dt);
8256edb685aSTomi Valkeinen 	} else if (ret) {
8266edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to get frame descriptor\n");
8276edb685aSTomi Valkeinen 		return ret;
8286edb685aSTomi Valkeinen 	}
8296edb685aSTomi Valkeinen 
8306edb685aSTomi Valkeinen 	if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
8316edb685aSTomi Valkeinen 		cfe_err(cfe, "Frame descriptor does not describe CSI-2 link");
8326edb685aSTomi Valkeinen 		return -EINVAL;
8336edb685aSTomi Valkeinen 	}
8346edb685aSTomi Valkeinen 
8356edb685aSTomi Valkeinen 	for (i = 0; i < remote_desc.num_entries; i++) {
8366edb685aSTomi Valkeinen 		if (remote_desc.entry[i].stream == sink_stream)
8376edb685aSTomi Valkeinen 			break;
8386edb685aSTomi Valkeinen 	}
8396edb685aSTomi Valkeinen 
8406edb685aSTomi Valkeinen 	if (i == remote_desc.num_entries) {
8416edb685aSTomi Valkeinen 		cfe_err(cfe, "Stream %u not found in remote frame desc\n",
8426edb685aSTomi Valkeinen 			sink_stream);
8436edb685aSTomi Valkeinen 		return -EINVAL;
8446edb685aSTomi Valkeinen 	}
8456edb685aSTomi Valkeinen 
8466edb685aSTomi Valkeinen 	*vc = remote_desc.entry[i].bus.csi2.vc;
8476edb685aSTomi Valkeinen 	*dt = remote_desc.entry[i].bus.csi2.dt;
8486edb685aSTomi Valkeinen 
8496edb685aSTomi Valkeinen 	return 0;
8506edb685aSTomi Valkeinen }
8516edb685aSTomi Valkeinen 
8526edb685aSTomi Valkeinen static int cfe_start_channel(struct cfe_node *node)
8536edb685aSTomi Valkeinen {
8546edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
8556edb685aSTomi Valkeinen 	struct v4l2_subdev_state *state;
8566edb685aSTomi Valkeinen 	struct v4l2_mbus_framefmt *source_fmt;
8576edb685aSTomi Valkeinen 	const struct cfe_fmt *fmt;
8586edb685aSTomi Valkeinen 	unsigned long flags;
8596edb685aSTomi Valkeinen 	bool start_fe;
8606edb685aSTomi Valkeinen 	int ret;
8616edb685aSTomi Valkeinen 
8626edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
8636edb685aSTomi Valkeinen 
8646edb685aSTomi Valkeinen 	start_fe = is_fe_enabled(cfe) &&
8656edb685aSTomi Valkeinen 		   test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
8666edb685aSTomi Valkeinen 
8676edb685aSTomi Valkeinen 	state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
8686edb685aSTomi Valkeinen 
8696edb685aSTomi Valkeinen 	if (start_fe) {
8706edb685aSTomi Valkeinen 		unsigned int width, height;
8716edb685aSTomi Valkeinen 		u8 vc, dt;
8726edb685aSTomi Valkeinen 
8736edb685aSTomi Valkeinen 		cfe_dbg(cfe, "%s: %s using csi2 channel %d\n", __func__,
8746edb685aSTomi Valkeinen 			node_desc[FE_OUT0].name, cfe->fe_csi2_channel);
8756edb685aSTomi Valkeinen 
8766edb685aSTomi Valkeinen 		ret = cfe_get_vc_dt(cfe, cfe->fe_csi2_channel, &vc, &dt);
8776edb685aSTomi Valkeinen 		if (ret)
8786edb685aSTomi Valkeinen 			return ret;
8796edb685aSTomi Valkeinen 
8806edb685aSTomi Valkeinen 		source_fmt = v4l2_subdev_state_get_format(state,
8816edb685aSTomi Valkeinen 			node_desc[cfe->fe_csi2_channel].link_pad);
8826edb685aSTomi Valkeinen 		fmt = find_format_by_code(source_fmt->code);
8836edb685aSTomi Valkeinen 
8846edb685aSTomi Valkeinen 		width = source_fmt->width;
8856edb685aSTomi Valkeinen 		height = source_fmt->height;
8866edb685aSTomi Valkeinen 
8876edb685aSTomi Valkeinen 		/* Must have a valid CSI2 datatype. */
8886edb685aSTomi Valkeinen 		WARN_ON(!fmt->csi_dt);
8896edb685aSTomi Valkeinen 
8906edb685aSTomi Valkeinen 		/*
8916edb685aSTomi Valkeinen 		 * Start the associated CSI2 Channel as well.
8926edb685aSTomi Valkeinen 		 *
8936edb685aSTomi Valkeinen 		 * Must write to the ADDR register to latch the ctrl values
8946edb685aSTomi Valkeinen 		 * even if we are connected to the front end. Once running,
8956edb685aSTomi Valkeinen 		 * this is handled by the CSI2 AUTO_ARM mode.
8966edb685aSTomi Valkeinen 		 */
8976edb685aSTomi Valkeinen 		csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel,
8986edb685aSTomi Valkeinen 				   CSI2_MODE_FE_STREAMING,
8996edb685aSTomi Valkeinen 				   true, false, width, height, vc, dt);
9006edb685aSTomi Valkeinen 		csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1);
9016edb685aSTomi Valkeinen 		pisp_fe_start(&cfe->fe);
9026edb685aSTomi Valkeinen 	}
9036edb685aSTomi Valkeinen 
9046edb685aSTomi Valkeinen 	if (is_csi2_node(node)) {
9056edb685aSTomi Valkeinen 		unsigned int width = 0, height = 0;
9066edb685aSTomi Valkeinen 		u8 vc, dt;
9076edb685aSTomi Valkeinen 
9086edb685aSTomi Valkeinen 		ret = cfe_get_vc_dt(cfe, node->id, &vc, &dt);
9096edb685aSTomi Valkeinen 		if (ret) {
9106edb685aSTomi Valkeinen 			if (start_fe) {
9116edb685aSTomi Valkeinen 				csi2_stop_channel(&cfe->csi2,
9126edb685aSTomi Valkeinen 						  cfe->fe_csi2_channel);
9136edb685aSTomi Valkeinen 				pisp_fe_stop(&cfe->fe);
9146edb685aSTomi Valkeinen 			}
9156edb685aSTomi Valkeinen 
9166edb685aSTomi Valkeinen 			return ret;
9176edb685aSTomi Valkeinen 		}
9186edb685aSTomi Valkeinen 
9196edb685aSTomi Valkeinen 		u32 mode = CSI2_MODE_NORMAL;
9206edb685aSTomi Valkeinen 
9216edb685aSTomi Valkeinen 		source_fmt = v4l2_subdev_state_get_format(state,
9226edb685aSTomi Valkeinen 			node_desc[node->id].link_pad);
9236edb685aSTomi Valkeinen 		fmt = find_format_by_code(source_fmt->code);
9246edb685aSTomi Valkeinen 
9256edb685aSTomi Valkeinen 		/* Must have a valid CSI2 datatype. */
9266edb685aSTomi Valkeinen 		WARN_ON(!fmt->csi_dt);
9276edb685aSTomi Valkeinen 
9286edb685aSTomi Valkeinen 		if (is_image_output_node(node)) {
9296edb685aSTomi Valkeinen 			u32  pixfmt;
9306edb685aSTomi Valkeinen 
9316edb685aSTomi Valkeinen 			width = source_fmt->width;
9326edb685aSTomi Valkeinen 			height = source_fmt->height;
9336edb685aSTomi Valkeinen 
9346edb685aSTomi Valkeinen 			pixfmt = node->vid_fmt.fmt.pix.pixelformat;
9356edb685aSTomi Valkeinen 
9366edb685aSTomi Valkeinen 			if (pixfmt == fmt->remap[CFE_REMAP_16BIT]) {
9376edb685aSTomi Valkeinen 				mode = CSI2_MODE_REMAP;
9386edb685aSTomi Valkeinen 			} else if (pixfmt == fmt->remap[CFE_REMAP_COMPRESSED]) {
9396edb685aSTomi Valkeinen 				mode = CSI2_MODE_COMPRESSED;
9406edb685aSTomi Valkeinen 				csi2_set_compression(&cfe->csi2, node->id,
9416edb685aSTomi Valkeinen 						     CSI2_COMPRESSION_DELTA, 0,
9426edb685aSTomi Valkeinen 						     0);
9436edb685aSTomi Valkeinen 			}
9446edb685aSTomi Valkeinen 		}
9456edb685aSTomi Valkeinen 		/* Unconditionally start this CSI2 channel. */
9466edb685aSTomi Valkeinen 		csi2_start_channel(&cfe->csi2, node->id,
9476edb685aSTomi Valkeinen 				   mode,
9486edb685aSTomi Valkeinen 				   /* Auto arm */
9496edb685aSTomi Valkeinen 				   false,
9506edb685aSTomi Valkeinen 				   /* Pack bytes */
9516edb685aSTomi Valkeinen 				   is_meta_node(node) ? true : false,
9526edb685aSTomi Valkeinen 				   width, height, vc, dt);
9536edb685aSTomi Valkeinen 	}
9546edb685aSTomi Valkeinen 
9556edb685aSTomi Valkeinen 	spin_lock_irqsave(&cfe->state_lock, flags);
9566edb685aSTomi Valkeinen 	if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING))
9576edb685aSTomi Valkeinen 		cfe_prepare_next_job(cfe);
9586edb685aSTomi Valkeinen 	spin_unlock_irqrestore(&cfe->state_lock, flags);
9596edb685aSTomi Valkeinen 
9606edb685aSTomi Valkeinen 	return 0;
9616edb685aSTomi Valkeinen }
9626edb685aSTomi Valkeinen 
9636edb685aSTomi Valkeinen static void cfe_stop_channel(struct cfe_node *node, bool fe_stop)
9646edb685aSTomi Valkeinen {
9656edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
9666edb685aSTomi Valkeinen 
9676edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s] fe_stop %u\n", __func__,
9686edb685aSTomi Valkeinen 		node_desc[node->id].name, fe_stop);
9696edb685aSTomi Valkeinen 
9706edb685aSTomi Valkeinen 	if (fe_stop) {
9716edb685aSTomi Valkeinen 		csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel);
9726edb685aSTomi Valkeinen 		pisp_fe_stop(&cfe->fe);
9736edb685aSTomi Valkeinen 	}
9746edb685aSTomi Valkeinen 
9756edb685aSTomi Valkeinen 	if (is_csi2_node(node))
9766edb685aSTomi Valkeinen 		csi2_stop_channel(&cfe->csi2, node->id);
9776edb685aSTomi Valkeinen }
9786edb685aSTomi Valkeinen 
9796edb685aSTomi Valkeinen static void cfe_return_buffers(struct cfe_node *node,
9806edb685aSTomi Valkeinen 			       enum vb2_buffer_state state)
9816edb685aSTomi Valkeinen {
9826edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
9836edb685aSTomi Valkeinen 	struct cfe_buffer *buf, *tmp;
9846edb685aSTomi Valkeinen 	unsigned long flags;
9856edb685aSTomi Valkeinen 
9866edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
9876edb685aSTomi Valkeinen 
9886edb685aSTomi Valkeinen 	spin_lock_irqsave(&cfe->state_lock, flags);
9896edb685aSTomi Valkeinen 	list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) {
9906edb685aSTomi Valkeinen 		list_del(&buf->list);
9916edb685aSTomi Valkeinen 		trace_cfe_return_buffer(node->id, buf->vb.vb2_buf.index, 2);
9926edb685aSTomi Valkeinen 		vb2_buffer_done(&buf->vb.vb2_buf, state);
9936edb685aSTomi Valkeinen 	}
9946edb685aSTomi Valkeinen 
9956edb685aSTomi Valkeinen 	if (node->cur_frm) {
9966edb685aSTomi Valkeinen 		trace_cfe_return_buffer(node->id,
9976edb685aSTomi Valkeinen 					node->cur_frm->vb.vb2_buf.index, 0);
9986edb685aSTomi Valkeinen 		vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
9996edb685aSTomi Valkeinen 	}
10006edb685aSTomi Valkeinen 	if (node->next_frm && node->cur_frm != node->next_frm) {
10016edb685aSTomi Valkeinen 		trace_cfe_return_buffer(node->id,
10026edb685aSTomi Valkeinen 					node->next_frm->vb.vb2_buf.index, 1);
10036edb685aSTomi Valkeinen 		vb2_buffer_done(&node->next_frm->vb.vb2_buf, state);
10046edb685aSTomi Valkeinen 	}
10056edb685aSTomi Valkeinen 
10066edb685aSTomi Valkeinen 	node->cur_frm = NULL;
10076edb685aSTomi Valkeinen 	node->next_frm = NULL;
10086edb685aSTomi Valkeinen 	spin_unlock_irqrestore(&cfe->state_lock, flags);
10096edb685aSTomi Valkeinen }
10106edb685aSTomi Valkeinen 
10116edb685aSTomi Valkeinen /*
10126edb685aSTomi Valkeinen  * vb2 ops
10136edb685aSTomi Valkeinen  */
10146edb685aSTomi Valkeinen 
10156edb685aSTomi Valkeinen static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
10166edb685aSTomi Valkeinen 			   unsigned int *nplanes, unsigned int sizes[],
10176edb685aSTomi Valkeinen 			   struct device *alloc_devs[])
10186edb685aSTomi Valkeinen {
10196edb685aSTomi Valkeinen 	struct cfe_node *node = vb2_get_drv_priv(vq);
10206edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
10216edb685aSTomi Valkeinen 	unsigned int size = is_image_node(node) ?
10226edb685aSTomi Valkeinen 				    node->vid_fmt.fmt.pix.sizeimage :
10236edb685aSTomi Valkeinen 				    node->meta_fmt.fmt.meta.buffersize;
10246edb685aSTomi Valkeinen 
10256edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
10266edb685aSTomi Valkeinen 		node->buffer_queue.type);
10276edb685aSTomi Valkeinen 
10286edb685aSTomi Valkeinen 	if (vq->max_num_buffers + *nbuffers < 3)
10296edb685aSTomi Valkeinen 		*nbuffers = 3 - vq->max_num_buffers;
10306edb685aSTomi Valkeinen 
10316edb685aSTomi Valkeinen 	if (*nplanes) {
10326edb685aSTomi Valkeinen 		if (sizes[0] < size) {
10336edb685aSTomi Valkeinen 			cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size);
10346edb685aSTomi Valkeinen 			return -EINVAL;
10356edb685aSTomi Valkeinen 		}
10366edb685aSTomi Valkeinen 		size = sizes[0];
10376edb685aSTomi Valkeinen 	}
10386edb685aSTomi Valkeinen 
10396edb685aSTomi Valkeinen 	*nplanes = 1;
10406edb685aSTomi Valkeinen 	sizes[0] = size;
10416edb685aSTomi Valkeinen 
10426edb685aSTomi Valkeinen 	return 0;
10436edb685aSTomi Valkeinen }
10446edb685aSTomi Valkeinen 
10456edb685aSTomi Valkeinen static int cfe_buffer_prepare(struct vb2_buffer *vb)
10466edb685aSTomi Valkeinen {
10476edb685aSTomi Valkeinen 	struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
10486edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
10496edb685aSTomi Valkeinen 	struct cfe_buffer *buf = to_cfe_buffer(vb);
10506edb685aSTomi Valkeinen 	unsigned long size;
10516edb685aSTomi Valkeinen 
10526edb685aSTomi Valkeinen 	trace_cfe_buffer_prepare(node->id, vb);
10536edb685aSTomi Valkeinen 
10546edb685aSTomi Valkeinen 	size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage :
10556edb685aSTomi Valkeinen 				     node->meta_fmt.fmt.meta.buffersize;
10566edb685aSTomi Valkeinen 	if (vb2_plane_size(vb, 0) < size) {
10576edb685aSTomi Valkeinen 		cfe_err(cfe, "data will not fit into plane (%lu < %lu)\n",
10586edb685aSTomi Valkeinen 			vb2_plane_size(vb, 0), size);
10596edb685aSTomi Valkeinen 		return -EINVAL;
10606edb685aSTomi Valkeinen 	}
10616edb685aSTomi Valkeinen 
10626edb685aSTomi Valkeinen 	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
10636edb685aSTomi Valkeinen 
10646edb685aSTomi Valkeinen 	if (node->id == FE_CONFIG) {
10656edb685aSTomi Valkeinen 		struct cfe_config_buffer *b = to_cfe_config_buffer(buf);
10666edb685aSTomi Valkeinen 		void *addr = vb2_plane_vaddr(vb, 0);
10676edb685aSTomi Valkeinen 
10686edb685aSTomi Valkeinen 		memcpy(&b->config, addr, sizeof(struct pisp_fe_config));
10696edb685aSTomi Valkeinen 		return pisp_fe_validate_config(&cfe->fe, &b->config,
10706edb685aSTomi Valkeinen 					       &cfe->node[FE_OUT0].vid_fmt,
10716edb685aSTomi Valkeinen 					       &cfe->node[FE_OUT1].vid_fmt);
10726edb685aSTomi Valkeinen 	}
10736edb685aSTomi Valkeinen 
10746edb685aSTomi Valkeinen 	return 0;
10756edb685aSTomi Valkeinen }
10766edb685aSTomi Valkeinen 
10776edb685aSTomi Valkeinen static void cfe_buffer_queue(struct vb2_buffer *vb)
10786edb685aSTomi Valkeinen {
10796edb685aSTomi Valkeinen 	struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
10806edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
10816edb685aSTomi Valkeinen 	struct cfe_buffer *buf = to_cfe_buffer(vb);
10826edb685aSTomi Valkeinen 	unsigned long flags;
10836edb685aSTomi Valkeinen 	bool schedule_now;
10846edb685aSTomi Valkeinen 
10856edb685aSTomi Valkeinen 	spin_lock_irqsave(&cfe->state_lock, flags);
10866edb685aSTomi Valkeinen 
10876edb685aSTomi Valkeinen 	list_add_tail(&buf->list, &node->dma_queue);
10886edb685aSTomi Valkeinen 
10896edb685aSTomi Valkeinen 	if (!cfe->job_ready)
10906edb685aSTomi Valkeinen 		cfe->job_ready = cfe_check_job_ready(cfe);
10916edb685aSTomi Valkeinen 
10926edb685aSTomi Valkeinen 	schedule_now = !cfe->job_queued && cfe->job_ready &&
10936edb685aSTomi Valkeinen 		       test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
10946edb685aSTomi Valkeinen 
10956edb685aSTomi Valkeinen 	trace_cfe_buffer_queue(node->id, vb, schedule_now);
10966edb685aSTomi Valkeinen 
10976edb685aSTomi Valkeinen 	if (schedule_now)
10986edb685aSTomi Valkeinen 		cfe_prepare_next_job(cfe);
10996edb685aSTomi Valkeinen 
11006edb685aSTomi Valkeinen 	spin_unlock_irqrestore(&cfe->state_lock, flags);
11016edb685aSTomi Valkeinen }
11026edb685aSTomi Valkeinen 
11036edb685aSTomi Valkeinen static s64 cfe_get_source_link_freq(struct cfe_device *cfe)
11046edb685aSTomi Valkeinen {
11056edb685aSTomi Valkeinen 	struct v4l2_subdev_state *state;
11066edb685aSTomi Valkeinen 	s64 link_freq;
11076edb685aSTomi Valkeinen 	u32 bpp;
11086edb685aSTomi Valkeinen 
11096edb685aSTomi Valkeinen 	state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
11106edb685aSTomi Valkeinen 
11116edb685aSTomi Valkeinen 	/*
11126edb685aSTomi Valkeinen 	 * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back
11136edb685aSTomi Valkeinen 	 * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available.
11146edb685aSTomi Valkeinen 	 *
11156edb685aSTomi Valkeinen 	 * With multistream input there is no single pixel rate, and thus we
11166edb685aSTomi Valkeinen 	 * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which
11176edb685aSTomi Valkeinen 	 * causes v4l2_get_link_freq() to return an error if it falls back to
11186edb685aSTomi Valkeinen 	 * V4L2_CID_PIXEL_RATE.
11196edb685aSTomi Valkeinen 	 */
11206edb685aSTomi Valkeinen 
11216edb685aSTomi Valkeinen 	if (state->routing.num_routes == 1) {
11226edb685aSTomi Valkeinen 		struct v4l2_subdev_route *route = &state->routing.routes[0];
11236edb685aSTomi Valkeinen 		struct v4l2_mbus_framefmt *source_fmt;
11246edb685aSTomi Valkeinen 		const struct cfe_fmt *fmt;
11256edb685aSTomi Valkeinen 
11266edb685aSTomi Valkeinen 		source_fmt = v4l2_subdev_state_get_format(state,
11276edb685aSTomi Valkeinen 							  route->sink_pad,
11286edb685aSTomi Valkeinen 							  route->sink_stream);
11296edb685aSTomi Valkeinen 
11306edb685aSTomi Valkeinen 		fmt = find_format_by_code(source_fmt->code);
11316edb685aSTomi Valkeinen 		if (!fmt)
11326edb685aSTomi Valkeinen 			return -EINVAL;
11336edb685aSTomi Valkeinen 
11346edb685aSTomi Valkeinen 		bpp = fmt->depth;
11356edb685aSTomi Valkeinen 	} else {
11366edb685aSTomi Valkeinen 		bpp = 0;
11376edb685aSTomi Valkeinen 	}
11386edb685aSTomi Valkeinen 
11396edb685aSTomi Valkeinen 	link_freq = v4l2_get_link_freq(cfe->source_sd->ctrl_handler, bpp,
11406edb685aSTomi Valkeinen 				       2 * cfe->csi2.dphy.active_lanes);
11416edb685aSTomi Valkeinen 	if (link_freq < 0)
11426edb685aSTomi Valkeinen 		cfe_err(cfe, "failed to get link freq for subdev '%s'\n",
11436edb685aSTomi Valkeinen 			cfe->source_sd->name);
11446edb685aSTomi Valkeinen 
11456edb685aSTomi Valkeinen 	return link_freq;
11466edb685aSTomi Valkeinen }
11476edb685aSTomi Valkeinen 
11486edb685aSTomi Valkeinen static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count)
11496edb685aSTomi Valkeinen {
11506edb685aSTomi Valkeinen 	struct v4l2_mbus_config mbus_config = { 0 };
11516edb685aSTomi Valkeinen 	struct cfe_node *node = vb2_get_drv_priv(vq);
11526edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
11536edb685aSTomi Valkeinen 	struct v4l2_subdev_state *state;
11546edb685aSTomi Valkeinen 	struct v4l2_subdev_route *route;
11556edb685aSTomi Valkeinen 	s64 link_freq;
11566edb685aSTomi Valkeinen 	int ret;
11576edb685aSTomi Valkeinen 
11586edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
11596edb685aSTomi Valkeinen 
11606edb685aSTomi Valkeinen 	if (!check_state(cfe, NODE_ENABLED, node->id)) {
11616edb685aSTomi Valkeinen 		cfe_err(cfe, "%s node link is not enabled.\n",
11626edb685aSTomi Valkeinen 			node_desc[node->id].name);
11636edb685aSTomi Valkeinen 		ret = -EINVAL;
11646edb685aSTomi Valkeinen 		goto err_streaming;
11656edb685aSTomi Valkeinen 	}
11666edb685aSTomi Valkeinen 
11676edb685aSTomi Valkeinen 	ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
11686edb685aSTomi Valkeinen 	if (ret < 0) {
11696edb685aSTomi Valkeinen 		cfe_err(cfe, "pm_runtime_resume_and_get failed\n");
11706edb685aSTomi Valkeinen 		goto err_streaming;
11716edb685aSTomi Valkeinen 	}
11726edb685aSTomi Valkeinen 
11736edb685aSTomi Valkeinen 	/* When using the Frontend, we must enable the FE_CONFIG node. */
11746edb685aSTomi Valkeinen 	if (is_fe_enabled(cfe) &&
11756edb685aSTomi Valkeinen 	    !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) {
11766edb685aSTomi Valkeinen 		cfe_err(cfe, "FE enabled, but FE_CONFIG node is not\n");
11776edb685aSTomi Valkeinen 		ret = -EINVAL;
11786edb685aSTomi Valkeinen 		goto err_pm_put;
11796edb685aSTomi Valkeinen 	}
11806edb685aSTomi Valkeinen 
11816edb685aSTomi Valkeinen 	ret = media_pipeline_start(&node->pad, &cfe->pipe);
11826edb685aSTomi Valkeinen 	if (ret < 0) {
11836edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to start media pipeline: %d\n", ret);
11846edb685aSTomi Valkeinen 		goto err_pm_put;
11856edb685aSTomi Valkeinen 	}
11866edb685aSTomi Valkeinen 
11876edb685aSTomi Valkeinen 	state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
11886edb685aSTomi Valkeinen 
11896edb685aSTomi Valkeinen 	clear_state(cfe, FS_INT | FE_INT, node->id);
11906edb685aSTomi Valkeinen 	set_state(cfe, NODE_STREAMING, node->id);
11916edb685aSTomi Valkeinen 	node->fs_count = 0;
11926edb685aSTomi Valkeinen 
11936edb685aSTomi Valkeinen 	ret = cfe_start_channel(node);
11946edb685aSTomi Valkeinen 	if (ret)
11956edb685aSTomi Valkeinen 		goto err_unlock_state;
11966edb685aSTomi Valkeinen 
11976edb685aSTomi Valkeinen 	if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
11986edb685aSTomi Valkeinen 		cfe_dbg(cfe, "Streaming on hold, as all nodes are not set to streaming yet\n");
11996edb685aSTomi Valkeinen 		v4l2_subdev_unlock_state(state);
12006edb685aSTomi Valkeinen 		return 0;
12016edb685aSTomi Valkeinen 	}
12026edb685aSTomi Valkeinen 
12036edb685aSTomi Valkeinen 	cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI);
12046edb685aSTomi Valkeinen 	cfg_reg_write(cfe, MIPICFG_INTE,
12056edb685aSTomi Valkeinen 		      MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE);
12066edb685aSTomi Valkeinen 
12076edb685aSTomi Valkeinen 	ret = v4l2_subdev_call(cfe->source_sd, pad, get_mbus_config, 0,
12086edb685aSTomi Valkeinen 			       &mbus_config);
12096edb685aSTomi Valkeinen 	if (ret < 0 && ret != -ENOIOCTLCMD) {
12106edb685aSTomi Valkeinen 		cfe_err(cfe, "g_mbus_config failed\n");
12116edb685aSTomi Valkeinen 		goto err_clear_inte;
12126edb685aSTomi Valkeinen 	}
12136edb685aSTomi Valkeinen 
12146edb685aSTomi Valkeinen 	cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
12156edb685aSTomi Valkeinen 	if (!cfe->csi2.dphy.active_lanes)
12166edb685aSTomi Valkeinen 		cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes;
12176edb685aSTomi Valkeinen 	if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) {
12186edb685aSTomi Valkeinen 		cfe_err(cfe, "Device has requested %u data lanes, which is >%u configured in DT\n",
12196edb685aSTomi Valkeinen 			cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes);
12206edb685aSTomi Valkeinen 		ret = -EINVAL;
12216edb685aSTomi Valkeinen 		goto err_clear_inte;
12226edb685aSTomi Valkeinen 	}
12236edb685aSTomi Valkeinen 
12246edb685aSTomi Valkeinen 	link_freq = cfe_get_source_link_freq(cfe);
12256edb685aSTomi Valkeinen 	if (link_freq < 0)
12266edb685aSTomi Valkeinen 		goto err_clear_inte;
12276edb685aSTomi Valkeinen 
12286edb685aSTomi Valkeinen 	cfe->csi2.dphy.dphy_rate = div_s64(link_freq * 2, 1000000);
12296edb685aSTomi Valkeinen 	csi2_open_rx(&cfe->csi2);
12306edb685aSTomi Valkeinen 
12316edb685aSTomi Valkeinen 	cfe->streams_mask = 0;
12326edb685aSTomi Valkeinen 
12336edb685aSTomi Valkeinen 	for_each_active_route(&state->routing, route)
12346edb685aSTomi Valkeinen 		cfe->streams_mask |= BIT_ULL(route->sink_stream);
12356edb685aSTomi Valkeinen 
12366edb685aSTomi Valkeinen 	ret = v4l2_subdev_enable_streams(cfe->source_sd, cfe->source_pad,
12376edb685aSTomi Valkeinen 					 cfe->streams_mask);
12386edb685aSTomi Valkeinen 	if (ret) {
12396edb685aSTomi Valkeinen 		cfe_err(cfe, "stream on failed in subdev\n");
12406edb685aSTomi Valkeinen 		goto err_disable_cfe;
12416edb685aSTomi Valkeinen 	}
12426edb685aSTomi Valkeinen 
12436edb685aSTomi Valkeinen 	cfe_dbg(cfe, "Streaming enabled\n");
12446edb685aSTomi Valkeinen 
12456edb685aSTomi Valkeinen 	v4l2_subdev_unlock_state(state);
12466edb685aSTomi Valkeinen 
12476edb685aSTomi Valkeinen 	return 0;
12486edb685aSTomi Valkeinen 
12496edb685aSTomi Valkeinen err_disable_cfe:
12506edb685aSTomi Valkeinen 	csi2_close_rx(&cfe->csi2);
12516edb685aSTomi Valkeinen err_clear_inte:
12526edb685aSTomi Valkeinen 	cfg_reg_write(cfe, MIPICFG_INTE, 0);
12536edb685aSTomi Valkeinen 
12546edb685aSTomi Valkeinen 	cfe_stop_channel(node,
12556edb685aSTomi Valkeinen 			 is_fe_enabled(cfe) && test_all_nodes(cfe, NODE_ENABLED,
12566edb685aSTomi Valkeinen 							      NODE_STREAMING));
12576edb685aSTomi Valkeinen err_unlock_state:
12586edb685aSTomi Valkeinen 	v4l2_subdev_unlock_state(state);
12596edb685aSTomi Valkeinen 	media_pipeline_stop(&node->pad);
12606edb685aSTomi Valkeinen err_pm_put:
12616edb685aSTomi Valkeinen 	pm_runtime_put(&cfe->pdev->dev);
12626edb685aSTomi Valkeinen err_streaming:
12636edb685aSTomi Valkeinen 	cfe_return_buffers(node, VB2_BUF_STATE_QUEUED);
12646edb685aSTomi Valkeinen 	clear_state(cfe, NODE_STREAMING, node->id);
12656edb685aSTomi Valkeinen 
12666edb685aSTomi Valkeinen 	return ret;
12676edb685aSTomi Valkeinen }
12686edb685aSTomi Valkeinen 
12696edb685aSTomi Valkeinen static void cfe_stop_streaming(struct vb2_queue *vq)
12706edb685aSTomi Valkeinen {
12716edb685aSTomi Valkeinen 	struct cfe_node *node = vb2_get_drv_priv(vq);
12726edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
12736edb685aSTomi Valkeinen 	unsigned long flags;
12746edb685aSTomi Valkeinen 	bool fe_stop;
12756edb685aSTomi Valkeinen 
12766edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
12776edb685aSTomi Valkeinen 
12786edb685aSTomi Valkeinen 	spin_lock_irqsave(&cfe->state_lock, flags);
12796edb685aSTomi Valkeinen 	fe_stop = is_fe_enabled(cfe) &&
12806edb685aSTomi Valkeinen 		  test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
12816edb685aSTomi Valkeinen 
12826edb685aSTomi Valkeinen 	cfe->job_ready = false;
12836edb685aSTomi Valkeinen 	clear_state(cfe, NODE_STREAMING, node->id);
12846edb685aSTomi Valkeinen 	spin_unlock_irqrestore(&cfe->state_lock, flags);
12856edb685aSTomi Valkeinen 
12866edb685aSTomi Valkeinen 	cfe_stop_channel(node, fe_stop);
12876edb685aSTomi Valkeinen 
12886edb685aSTomi Valkeinen 	if (!test_any_node(cfe, NODE_STREAMING)) {
12896edb685aSTomi Valkeinen 		struct v4l2_subdev_state *state;
12906edb685aSTomi Valkeinen 		int ret;
12916edb685aSTomi Valkeinen 
12926edb685aSTomi Valkeinen 		state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
12936edb685aSTomi Valkeinen 
12946edb685aSTomi Valkeinen 		ret = v4l2_subdev_disable_streams(cfe->source_sd,
12956edb685aSTomi Valkeinen 						  cfe->source_pad,
12966edb685aSTomi Valkeinen 						  cfe->streams_mask);
12976edb685aSTomi Valkeinen 		if (ret)
12986edb685aSTomi Valkeinen 			cfe_err(cfe, "stream disable failed in subdev\n");
12996edb685aSTomi Valkeinen 
13006edb685aSTomi Valkeinen 		v4l2_subdev_unlock_state(state);
13016edb685aSTomi Valkeinen 
13026edb685aSTomi Valkeinen 		csi2_close_rx(&cfe->csi2);
13036edb685aSTomi Valkeinen 
13046edb685aSTomi Valkeinen 		cfg_reg_write(cfe, MIPICFG_INTE, 0);
13056edb685aSTomi Valkeinen 
13066edb685aSTomi Valkeinen 		cfe_dbg(cfe, "%s: Streaming disabled\n", __func__);
13076edb685aSTomi Valkeinen 	}
13086edb685aSTomi Valkeinen 
13096edb685aSTomi Valkeinen 	media_pipeline_stop(&node->pad);
13106edb685aSTomi Valkeinen 
13116edb685aSTomi Valkeinen 	/* Clear all queued buffers for the node */
13126edb685aSTomi Valkeinen 	cfe_return_buffers(node, VB2_BUF_STATE_ERROR);
13136edb685aSTomi Valkeinen 
13146edb685aSTomi Valkeinen 	pm_runtime_put(&cfe->pdev->dev);
13156edb685aSTomi Valkeinen }
13166edb685aSTomi Valkeinen 
13176edb685aSTomi Valkeinen static const struct vb2_ops cfe_video_qops = {
13186edb685aSTomi Valkeinen 	.wait_prepare = vb2_ops_wait_prepare,
13196edb685aSTomi Valkeinen 	.wait_finish = vb2_ops_wait_finish,
13206edb685aSTomi Valkeinen 	.queue_setup = cfe_queue_setup,
13216edb685aSTomi Valkeinen 	.buf_prepare = cfe_buffer_prepare,
13226edb685aSTomi Valkeinen 	.buf_queue = cfe_buffer_queue,
13236edb685aSTomi Valkeinen 	.start_streaming = cfe_start_streaming,
13246edb685aSTomi Valkeinen 	.stop_streaming = cfe_stop_streaming,
13256edb685aSTomi Valkeinen };
13266edb685aSTomi Valkeinen 
13276edb685aSTomi Valkeinen /*
13286edb685aSTomi Valkeinen  * v4l2 ioctl ops
13296edb685aSTomi Valkeinen  */
13306edb685aSTomi Valkeinen 
13316edb685aSTomi Valkeinen static int cfe_querycap(struct file *file, void *priv,
13326edb685aSTomi Valkeinen 			struct v4l2_capability *cap)
13336edb685aSTomi Valkeinen {
13346edb685aSTomi Valkeinen 	strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver));
13356edb685aSTomi Valkeinen 	strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card));
13366edb685aSTomi Valkeinen 
13376edb685aSTomi Valkeinen 	cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE |
13386edb685aSTomi Valkeinen 			     V4L2_CAP_META_OUTPUT;
13396edb685aSTomi Valkeinen 
13406edb685aSTomi Valkeinen 	return 0;
13416edb685aSTomi Valkeinen }
13426edb685aSTomi Valkeinen 
13436edb685aSTomi Valkeinen static int cfe_enum_fmt_vid_cap(struct file *file, void *priv,
13446edb685aSTomi Valkeinen 				struct v4l2_fmtdesc *f)
13456edb685aSTomi Valkeinen {
13466edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
13476edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
13486edb685aSTomi Valkeinen 	unsigned int i, j;
13496edb685aSTomi Valkeinen 
13506edb685aSTomi Valkeinen 	if (!node_supports_image_output(node))
13516edb685aSTomi Valkeinen 		return -EINVAL;
13526edb685aSTomi Valkeinen 
13536edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
13546edb685aSTomi Valkeinen 
13556edb685aSTomi Valkeinen 	for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) {
13566edb685aSTomi Valkeinen 		if (f->mbus_code && formats[i].code != f->mbus_code)
13576edb685aSTomi Valkeinen 			continue;
13586edb685aSTomi Valkeinen 
13596edb685aSTomi Valkeinen 		if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT ||
13606edb685aSTomi Valkeinen 		    formats[i].flags & CFE_FORMAT_FLAG_META_CAP)
13616edb685aSTomi Valkeinen 			continue;
13626edb685aSTomi Valkeinen 
13636edb685aSTomi Valkeinen 		if (is_fe_node(node) &&
13646edb685aSTomi Valkeinen 		    !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT))
13656edb685aSTomi Valkeinen 			continue;
13666edb685aSTomi Valkeinen 
13676edb685aSTomi Valkeinen 		if (j == f->index) {
13686edb685aSTomi Valkeinen 			f->pixelformat = formats[i].fourcc;
13696edb685aSTomi Valkeinen 			f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
13706edb685aSTomi Valkeinen 			return 0;
13716edb685aSTomi Valkeinen 		}
13726edb685aSTomi Valkeinen 		j++;
13736edb685aSTomi Valkeinen 	}
13746edb685aSTomi Valkeinen 
13756edb685aSTomi Valkeinen 	return -EINVAL;
13766edb685aSTomi Valkeinen }
13776edb685aSTomi Valkeinen 
13786edb685aSTomi Valkeinen static int cfe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
13796edb685aSTomi Valkeinen {
13806edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
13816edb685aSTomi Valkeinen 
13826edb685aSTomi Valkeinen 	if (!node_supports_image(node))
13836edb685aSTomi Valkeinen 		return -EINVAL;
13846edb685aSTomi Valkeinen 
13856edb685aSTomi Valkeinen 	*f = node->vid_fmt;
13866edb685aSTomi Valkeinen 
13876edb685aSTomi Valkeinen 	return 0;
13886edb685aSTomi Valkeinen }
13896edb685aSTomi Valkeinen 
13906edb685aSTomi Valkeinen static int cfe_validate_fmt_vid_cap(struct cfe_node *node,
13916edb685aSTomi Valkeinen 				    struct v4l2_format *f)
13926edb685aSTomi Valkeinen {
13936edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
13946edb685aSTomi Valkeinen 	const struct cfe_fmt *fmt;
13956edb685aSTomi Valkeinen 
13966edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 pix %p4cc\n", __func__,
13976edb685aSTomi Valkeinen 		node_desc[node->id].name, f->fmt.pix.width, f->fmt.pix.height,
13986edb685aSTomi Valkeinen 		&f->fmt.pix.pixelformat);
13996edb685aSTomi Valkeinen 
14006edb685aSTomi Valkeinen 	if (!node_supports_image_output(node))
14016edb685aSTomi Valkeinen 		return -EINVAL;
14026edb685aSTomi Valkeinen 
14036edb685aSTomi Valkeinen 	/*
14046edb685aSTomi Valkeinen 	 * Default to a format that works for both CSI2 and FE.
14056edb685aSTomi Valkeinen 	 */
14066edb685aSTomi Valkeinen 	fmt = find_format_by_pix(f->fmt.pix.pixelformat);
14076edb685aSTomi Valkeinen 	if (!fmt)
14086edb685aSTomi Valkeinen 		fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
14096edb685aSTomi Valkeinen 
14106edb685aSTomi Valkeinen 	f->fmt.pix.pixelformat = fmt->fourcc;
14116edb685aSTomi Valkeinen 
14126edb685aSTomi Valkeinen 	if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) {
14136edb685aSTomi Valkeinen 		f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT];
14146edb685aSTomi Valkeinen 		fmt = find_format_by_pix(f->fmt.pix.pixelformat);
14156edb685aSTomi Valkeinen 	}
14166edb685aSTomi Valkeinen 
14176edb685aSTomi Valkeinen 	f->fmt.pix.field = V4L2_FIELD_NONE;
14186edb685aSTomi Valkeinen 
14196edb685aSTomi Valkeinen 	cfe_calc_vid_format_size_bpl(cfe, fmt, f);
14206edb685aSTomi Valkeinen 
14216edb685aSTomi Valkeinen 	return 0;
14226edb685aSTomi Valkeinen }
14236edb685aSTomi Valkeinen 
14246edb685aSTomi Valkeinen static int cfe_s_fmt_vid_cap(struct file *file, void *priv,
14256edb685aSTomi Valkeinen 			     struct v4l2_format *f)
14266edb685aSTomi Valkeinen {
14276edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
14286edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
14296edb685aSTomi Valkeinen 	struct vb2_queue *q = &node->buffer_queue;
14306edb685aSTomi Valkeinen 	int ret;
14316edb685aSTomi Valkeinen 
14326edb685aSTomi Valkeinen 	if (vb2_is_busy(q))
14336edb685aSTomi Valkeinen 		return -EBUSY;
14346edb685aSTomi Valkeinen 
14356edb685aSTomi Valkeinen 	ret = cfe_validate_fmt_vid_cap(node, f);
14366edb685aSTomi Valkeinen 	if (ret)
14376edb685aSTomi Valkeinen 		return ret;
14386edb685aSTomi Valkeinen 
14396edb685aSTomi Valkeinen 	node->vid_fmt = *f;
14406edb685aSTomi Valkeinen 
14416edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: Set %ux%u, V4L2 pix %p4cc\n", __func__,
14426edb685aSTomi Valkeinen 		node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height,
14436edb685aSTomi Valkeinen 		&node->vid_fmt.fmt.pix.pixelformat);
14446edb685aSTomi Valkeinen 
14456edb685aSTomi Valkeinen 	return 0;
14466edb685aSTomi Valkeinen }
14476edb685aSTomi Valkeinen 
14486edb685aSTomi Valkeinen static int cfe_try_fmt_vid_cap(struct file *file, void *priv,
14496edb685aSTomi Valkeinen 			       struct v4l2_format *f)
14506edb685aSTomi Valkeinen {
14516edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
14526edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
14536edb685aSTomi Valkeinen 
14546edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
14556edb685aSTomi Valkeinen 
14566edb685aSTomi Valkeinen 	return cfe_validate_fmt_vid_cap(node, f);
14576edb685aSTomi Valkeinen }
14586edb685aSTomi Valkeinen 
14596edb685aSTomi Valkeinen static int cfe_enum_fmt_meta(struct file *file, void *priv,
14606edb685aSTomi Valkeinen 			     struct v4l2_fmtdesc *f)
14616edb685aSTomi Valkeinen {
14626edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
14636edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
14646edb685aSTomi Valkeinen 
14656edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
14666edb685aSTomi Valkeinen 
14676edb685aSTomi Valkeinen 	if (!node_supports_meta(node))
14686edb685aSTomi Valkeinen 		return -EINVAL;
14696edb685aSTomi Valkeinen 
14706edb685aSTomi Valkeinen 	switch (node->id) {
14716edb685aSTomi Valkeinen 	case CSI2_CH0...CSI2_CH3:
14726edb685aSTomi Valkeinen 		f->flags = V4L2_FMT_FLAG_META_LINE_BASED;
14736edb685aSTomi Valkeinen 
14746edb685aSTomi Valkeinen 		switch (f->index) {
14756edb685aSTomi Valkeinen 		case 0:
14766edb685aSTomi Valkeinen 			f->pixelformat = V4L2_META_FMT_GENERIC_8;
14776edb685aSTomi Valkeinen 			return 0;
14786edb685aSTomi Valkeinen 		case 1:
14796edb685aSTomi Valkeinen 			f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_10;
14806edb685aSTomi Valkeinen 			return 0;
14816edb685aSTomi Valkeinen 		case 2:
14826edb685aSTomi Valkeinen 			f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_12;
14836edb685aSTomi Valkeinen 			return 0;
14846edb685aSTomi Valkeinen 		default:
14856edb685aSTomi Valkeinen 			return -EINVAL;
14866edb685aSTomi Valkeinen 		}
14876edb685aSTomi Valkeinen 	default:
14886edb685aSTomi Valkeinen 		break;
14896edb685aSTomi Valkeinen 	}
14906edb685aSTomi Valkeinen 
14916edb685aSTomi Valkeinen 	if (f->index != 0)
14926edb685aSTomi Valkeinen 		return -EINVAL;
14936edb685aSTomi Valkeinen 
14946edb685aSTomi Valkeinen 	switch (node->id) {
14956edb685aSTomi Valkeinen 	case FE_STATS:
14966edb685aSTomi Valkeinen 		f->pixelformat = V4L2_META_FMT_RPI_FE_STATS;
14976edb685aSTomi Valkeinen 		return 0;
14986edb685aSTomi Valkeinen 	case FE_CONFIG:
14996edb685aSTomi Valkeinen 		f->pixelformat = V4L2_META_FMT_RPI_FE_CFG;
15006edb685aSTomi Valkeinen 		return 0;
15016edb685aSTomi Valkeinen 	default:
15026edb685aSTomi Valkeinen 		return -EINVAL;
15036edb685aSTomi Valkeinen 	}
15046edb685aSTomi Valkeinen }
15056edb685aSTomi Valkeinen 
15066edb685aSTomi Valkeinen static int cfe_validate_fmt_meta(struct cfe_node *node, struct v4l2_format *f)
15076edb685aSTomi Valkeinen {
15086edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
15096edb685aSTomi Valkeinen 	const struct cfe_fmt *fmt;
15106edb685aSTomi Valkeinen 
15116edb685aSTomi Valkeinen 	switch (node->id) {
15126edb685aSTomi Valkeinen 	case CSI2_CH0...CSI2_CH3:
15136edb685aSTomi Valkeinen 		cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 meta %p4cc\n", __func__,
15146edb685aSTomi Valkeinen 			node_desc[node->id].name, f->fmt.meta.width,
15156edb685aSTomi Valkeinen 			f->fmt.meta.height, &f->fmt.meta.dataformat);
15166edb685aSTomi Valkeinen 		break;
15176edb685aSTomi Valkeinen 	case FE_STATS:
15186edb685aSTomi Valkeinen 	case FE_CONFIG:
15196edb685aSTomi Valkeinen 		cfe_dbg(cfe, "%s: [%s] %u bytes, V4L2 meta %p4cc\n", __func__,
15206edb685aSTomi Valkeinen 			node_desc[node->id].name, f->fmt.meta.buffersize,
15216edb685aSTomi Valkeinen 			&f->fmt.meta.dataformat);
15226edb685aSTomi Valkeinen 		break;
15236edb685aSTomi Valkeinen 	default:
15246edb685aSTomi Valkeinen 		return -EINVAL;
15256edb685aSTomi Valkeinen 	}
15266edb685aSTomi Valkeinen 
15276edb685aSTomi Valkeinen 	if (!node_supports_meta(node))
15286edb685aSTomi Valkeinen 		return -EINVAL;
15296edb685aSTomi Valkeinen 
15306edb685aSTomi Valkeinen 	switch (node->id) {
15316edb685aSTomi Valkeinen 	case CSI2_CH0...CSI2_CH3:
15326edb685aSTomi Valkeinen 		fmt = find_format_by_pix(f->fmt.meta.dataformat);
15336edb685aSTomi Valkeinen 		if (!fmt || !(fmt->flags & CFE_FORMAT_FLAG_META_CAP))
15346edb685aSTomi Valkeinen 			fmt = find_format_by_pix(V4L2_META_FMT_GENERIC_CSI2_10);
15356edb685aSTomi Valkeinen 
15366edb685aSTomi Valkeinen 		f->fmt.meta.dataformat = fmt->fourcc;
15376edb685aSTomi Valkeinen 
15386edb685aSTomi Valkeinen 		cfe_calc_meta_format_size_bpl(cfe, fmt, f);
15396edb685aSTomi Valkeinen 
15406edb685aSTomi Valkeinen 		return 0;
15416edb685aSTomi Valkeinen 	case FE_STATS:
15426edb685aSTomi Valkeinen 		f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS;
15436edb685aSTomi Valkeinen 		f->fmt.meta.buffersize = sizeof(struct pisp_statistics);
15446edb685aSTomi Valkeinen 		return 0;
15456edb685aSTomi Valkeinen 	case FE_CONFIG:
15466edb685aSTomi Valkeinen 		f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG;
15476edb685aSTomi Valkeinen 		f->fmt.meta.buffersize = sizeof(struct pisp_fe_config);
15486edb685aSTomi Valkeinen 		return 0;
15496edb685aSTomi Valkeinen 	default:
15506edb685aSTomi Valkeinen 		return -EINVAL;
15516edb685aSTomi Valkeinen 	}
15526edb685aSTomi Valkeinen }
15536edb685aSTomi Valkeinen 
15546edb685aSTomi Valkeinen static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
15556edb685aSTomi Valkeinen {
15566edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
15576edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
15586edb685aSTomi Valkeinen 
15596edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
15606edb685aSTomi Valkeinen 
15616edb685aSTomi Valkeinen 	if (!node_supports_meta(node))
15626edb685aSTomi Valkeinen 		return -EINVAL;
15636edb685aSTomi Valkeinen 
15646edb685aSTomi Valkeinen 	*f = node->meta_fmt;
15656edb685aSTomi Valkeinen 
15666edb685aSTomi Valkeinen 	return 0;
15676edb685aSTomi Valkeinen }
15686edb685aSTomi Valkeinen 
15696edb685aSTomi Valkeinen static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
15706edb685aSTomi Valkeinen {
15716edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
15726edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
15736edb685aSTomi Valkeinen 	struct vb2_queue *q = &node->buffer_queue;
15746edb685aSTomi Valkeinen 	int ret;
15756edb685aSTomi Valkeinen 
15766edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
15776edb685aSTomi Valkeinen 
15786edb685aSTomi Valkeinen 	if (vb2_is_busy(q))
15796edb685aSTomi Valkeinen 		return -EBUSY;
15806edb685aSTomi Valkeinen 
15816edb685aSTomi Valkeinen 	if (!node_supports_meta(node))
15826edb685aSTomi Valkeinen 		return -EINVAL;
15836edb685aSTomi Valkeinen 
15846edb685aSTomi Valkeinen 	ret = cfe_validate_fmt_meta(node, f);
15856edb685aSTomi Valkeinen 	if (ret)
15866edb685aSTomi Valkeinen 		return ret;
15876edb685aSTomi Valkeinen 
15886edb685aSTomi Valkeinen 	node->meta_fmt = *f;
15896edb685aSTomi Valkeinen 
15906edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: Set %p4cc\n", __func__,
15916edb685aSTomi Valkeinen 		&node->meta_fmt.fmt.meta.dataformat);
15926edb685aSTomi Valkeinen 
15936edb685aSTomi Valkeinen 	return 0;
15946edb685aSTomi Valkeinen }
15956edb685aSTomi Valkeinen 
15966edb685aSTomi Valkeinen static int cfe_try_fmt_meta(struct file *file, void *priv,
15976edb685aSTomi Valkeinen 			    struct v4l2_format *f)
15986edb685aSTomi Valkeinen {
15996edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
16006edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
16016edb685aSTomi Valkeinen 
16026edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
16036edb685aSTomi Valkeinen 	return cfe_validate_fmt_meta(node, f);
16046edb685aSTomi Valkeinen }
16056edb685aSTomi Valkeinen 
16066edb685aSTomi Valkeinen static int cfe_enum_framesizes(struct file *file, void *priv,
16076edb685aSTomi Valkeinen 			       struct v4l2_frmsizeenum *fsize)
16086edb685aSTomi Valkeinen {
16096edb685aSTomi Valkeinen 	struct cfe_node *node = video_drvdata(file);
16106edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
16116edb685aSTomi Valkeinen 	const struct cfe_fmt *fmt;
16126edb685aSTomi Valkeinen 
16136edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s [%s]\n", __func__, node_desc[node->id].name);
16146edb685aSTomi Valkeinen 
16156edb685aSTomi Valkeinen 	if (fsize->index > 0)
16166edb685aSTomi Valkeinen 		return -EINVAL;
16176edb685aSTomi Valkeinen 
16186edb685aSTomi Valkeinen 	/* check for valid format */
16196edb685aSTomi Valkeinen 	fmt = find_format_by_pix(fsize->pixel_format);
16206edb685aSTomi Valkeinen 	if (!fmt) {
16216edb685aSTomi Valkeinen 		cfe_dbg(cfe, "Invalid pixel code: %x\n", fsize->pixel_format);
16226edb685aSTomi Valkeinen 		return -EINVAL;
16236edb685aSTomi Valkeinen 	}
16246edb685aSTomi Valkeinen 
16256edb685aSTomi Valkeinen 	/* TODO: Do we have limits on the step_width? */
16266edb685aSTomi Valkeinen 
16276edb685aSTomi Valkeinen 	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
16286edb685aSTomi Valkeinen 	fsize->stepwise.min_width = MIN_WIDTH;
16296edb685aSTomi Valkeinen 	fsize->stepwise.max_width = MAX_WIDTH;
16306edb685aSTomi Valkeinen 	fsize->stepwise.step_width = 2;
16316edb685aSTomi Valkeinen 	fsize->stepwise.min_height = MIN_HEIGHT;
16326edb685aSTomi Valkeinen 	fsize->stepwise.max_height = MAX_HEIGHT;
16336edb685aSTomi Valkeinen 	fsize->stepwise.step_height = 1;
16346edb685aSTomi Valkeinen 
16356edb685aSTomi Valkeinen 	return 0;
16366edb685aSTomi Valkeinen }
16376edb685aSTomi Valkeinen 
16386edb685aSTomi Valkeinen static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv,
16396edb685aSTomi Valkeinen 				 struct v4l2_requestbuffers *p)
16406edb685aSTomi Valkeinen {
16416edb685aSTomi Valkeinen 	struct video_device *vdev = video_devdata(file);
16426edb685aSTomi Valkeinen 	struct cfe_node *node = video_get_drvdata(vdev);
16436edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
16446edb685aSTomi Valkeinen 	int ret;
16456edb685aSTomi Valkeinen 
16466edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
16476edb685aSTomi Valkeinen 		p->type);
16486edb685aSTomi Valkeinen 
16496edb685aSTomi Valkeinen 	if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
16506edb685aSTomi Valkeinen 	    p->type != V4L2_BUF_TYPE_META_CAPTURE &&
16516edb685aSTomi Valkeinen 	    p->type != V4L2_BUF_TYPE_META_OUTPUT)
16526edb685aSTomi Valkeinen 		return -EINVAL;
16536edb685aSTomi Valkeinen 
16546edb685aSTomi Valkeinen 	ret = vb2_queue_change_type(vdev->queue, p->type);
16556edb685aSTomi Valkeinen 	if (ret)
16566edb685aSTomi Valkeinen 		return ret;
16576edb685aSTomi Valkeinen 
16586edb685aSTomi Valkeinen 	return vb2_ioctl_reqbufs(file, priv, p);
16596edb685aSTomi Valkeinen }
16606edb685aSTomi Valkeinen 
16616edb685aSTomi Valkeinen static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv,
16626edb685aSTomi Valkeinen 				     struct v4l2_create_buffers *p)
16636edb685aSTomi Valkeinen {
16646edb685aSTomi Valkeinen 	struct video_device *vdev = video_devdata(file);
16656edb685aSTomi Valkeinen 	struct cfe_node *node = video_get_drvdata(vdev);
16666edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
16676edb685aSTomi Valkeinen 	int ret;
16686edb685aSTomi Valkeinen 
16696edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
16706edb685aSTomi Valkeinen 		p->format.type);
16716edb685aSTomi Valkeinen 
16726edb685aSTomi Valkeinen 	if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
16736edb685aSTomi Valkeinen 	    p->format.type != V4L2_BUF_TYPE_META_CAPTURE &&
16746edb685aSTomi Valkeinen 	    p->format.type != V4L2_BUF_TYPE_META_OUTPUT)
16756edb685aSTomi Valkeinen 		return -EINVAL;
16766edb685aSTomi Valkeinen 
16776edb685aSTomi Valkeinen 	ret = vb2_queue_change_type(vdev->queue, p->format.type);
16786edb685aSTomi Valkeinen 	if (ret)
16796edb685aSTomi Valkeinen 		return ret;
16806edb685aSTomi Valkeinen 
16816edb685aSTomi Valkeinen 	return vb2_ioctl_create_bufs(file, priv, p);
16826edb685aSTomi Valkeinen }
16836edb685aSTomi Valkeinen 
16846edb685aSTomi Valkeinen static int cfe_subscribe_event(struct v4l2_fh *fh,
16856edb685aSTomi Valkeinen 			       const struct v4l2_event_subscription *sub)
16866edb685aSTomi Valkeinen {
16876edb685aSTomi Valkeinen 	struct cfe_node *node = video_get_drvdata(fh->vdev);
16886edb685aSTomi Valkeinen 
16896edb685aSTomi Valkeinen 	switch (sub->type) {
16906edb685aSTomi Valkeinen 	case V4L2_EVENT_FRAME_SYNC:
16916edb685aSTomi Valkeinen 		if (!node_supports_image_output(node))
16926edb685aSTomi Valkeinen 			break;
16936edb685aSTomi Valkeinen 
16946edb685aSTomi Valkeinen 		return v4l2_event_subscribe(fh, sub, 2, NULL);
16956edb685aSTomi Valkeinen 	case V4L2_EVENT_SOURCE_CHANGE:
16966edb685aSTomi Valkeinen 		if (!node_supports_image_output(node) &&
16976edb685aSTomi Valkeinen 		    !node_supports_meta_output(node))
16986edb685aSTomi Valkeinen 			break;
16996edb685aSTomi Valkeinen 
17006edb685aSTomi Valkeinen 		return v4l2_event_subscribe(fh, sub, 4, NULL);
17016edb685aSTomi Valkeinen 	}
17026edb685aSTomi Valkeinen 
17036edb685aSTomi Valkeinen 	return v4l2_ctrl_subscribe_event(fh, sub);
17046edb685aSTomi Valkeinen }
17056edb685aSTomi Valkeinen 
17066edb685aSTomi Valkeinen static const struct v4l2_ioctl_ops cfe_ioctl_ops = {
17076edb685aSTomi Valkeinen 	.vidioc_querycap = cfe_querycap,
17086edb685aSTomi Valkeinen 	.vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap,
17096edb685aSTomi Valkeinen 	.vidioc_g_fmt_vid_cap = cfe_g_fmt,
17106edb685aSTomi Valkeinen 	.vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap,
17116edb685aSTomi Valkeinen 	.vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap,
17126edb685aSTomi Valkeinen 
17136edb685aSTomi Valkeinen 	.vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta,
17146edb685aSTomi Valkeinen 	.vidioc_g_fmt_meta_cap = cfe_g_fmt_meta,
17156edb685aSTomi Valkeinen 	.vidioc_s_fmt_meta_cap = cfe_s_fmt_meta,
17166edb685aSTomi Valkeinen 	.vidioc_try_fmt_meta_cap = cfe_try_fmt_meta,
17176edb685aSTomi Valkeinen 
17186edb685aSTomi Valkeinen 	.vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta,
17196edb685aSTomi Valkeinen 	.vidioc_g_fmt_meta_out = cfe_g_fmt_meta,
17206edb685aSTomi Valkeinen 	.vidioc_s_fmt_meta_out = cfe_s_fmt_meta,
17216edb685aSTomi Valkeinen 	.vidioc_try_fmt_meta_out = cfe_try_fmt_meta,
17226edb685aSTomi Valkeinen 
17236edb685aSTomi Valkeinen 	.vidioc_enum_framesizes = cfe_enum_framesizes,
17246edb685aSTomi Valkeinen 
17256edb685aSTomi Valkeinen 	.vidioc_reqbufs = cfe_vb2_ioctl_reqbufs,
17266edb685aSTomi Valkeinen 	.vidioc_create_bufs = cfe_vb2_ioctl_create_bufs,
17276edb685aSTomi Valkeinen 	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
17286edb685aSTomi Valkeinen 	.vidioc_querybuf = vb2_ioctl_querybuf,
17296edb685aSTomi Valkeinen 	.vidioc_qbuf = vb2_ioctl_qbuf,
17306edb685aSTomi Valkeinen 	.vidioc_dqbuf = vb2_ioctl_dqbuf,
17316edb685aSTomi Valkeinen 	.vidioc_expbuf = vb2_ioctl_expbuf,
17326edb685aSTomi Valkeinen 	.vidioc_streamon = vb2_ioctl_streamon,
17336edb685aSTomi Valkeinen 	.vidioc_streamoff = vb2_ioctl_streamoff,
17346edb685aSTomi Valkeinen 
17356edb685aSTomi Valkeinen 	.vidioc_subscribe_event = cfe_subscribe_event,
17366edb685aSTomi Valkeinen 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
17376edb685aSTomi Valkeinen };
17386edb685aSTomi Valkeinen 
17396edb685aSTomi Valkeinen static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification,
17406edb685aSTomi Valkeinen 		       void *arg)
17416edb685aSTomi Valkeinen {
17426edb685aSTomi Valkeinen 	struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev);
17436edb685aSTomi Valkeinen 
17446edb685aSTomi Valkeinen 	switch (notification) {
17456edb685aSTomi Valkeinen 	case V4L2_DEVICE_NOTIFY_EVENT:
17466edb685aSTomi Valkeinen 		for (unsigned int i = 0; i < NUM_NODES; i++) {
17476edb685aSTomi Valkeinen 			struct cfe_node *node = &cfe->node[i];
17486edb685aSTomi Valkeinen 
17496edb685aSTomi Valkeinen 			if (check_state(cfe, NODE_REGISTERED, i))
17506edb685aSTomi Valkeinen 				continue;
17516edb685aSTomi Valkeinen 
17526edb685aSTomi Valkeinen 			v4l2_event_queue(&node->video_dev, arg);
17536edb685aSTomi Valkeinen 		}
17546edb685aSTomi Valkeinen 		break;
17556edb685aSTomi Valkeinen 	default:
17566edb685aSTomi Valkeinen 		break;
17576edb685aSTomi Valkeinen 	}
17586edb685aSTomi Valkeinen }
17596edb685aSTomi Valkeinen 
17606edb685aSTomi Valkeinen /* cfe capture driver file operations */
17616edb685aSTomi Valkeinen static const struct v4l2_file_operations cfe_fops = {
17626edb685aSTomi Valkeinen 	.owner = THIS_MODULE,
17636edb685aSTomi Valkeinen 	.open = v4l2_fh_open,
17646edb685aSTomi Valkeinen 	.release = vb2_fop_release,
17656edb685aSTomi Valkeinen 	.poll = vb2_fop_poll,
17666edb685aSTomi Valkeinen 	.unlocked_ioctl = video_ioctl2,
17676edb685aSTomi Valkeinen 	.mmap = vb2_fop_mmap,
17686edb685aSTomi Valkeinen };
17696edb685aSTomi Valkeinen 
17706edb685aSTomi Valkeinen static int cfe_video_link_validate(struct media_link *link)
17716edb685aSTomi Valkeinen {
17726edb685aSTomi Valkeinen 	struct video_device *vd = container_of(link->sink->entity,
17736edb685aSTomi Valkeinen 					       struct video_device, entity);
17746edb685aSTomi Valkeinen 	struct cfe_node *node = container_of(vd, struct cfe_node, video_dev);
17756edb685aSTomi Valkeinen 	struct cfe_device *cfe = node->cfe;
17766edb685aSTomi Valkeinen 	struct v4l2_mbus_framefmt *source_fmt;
17776edb685aSTomi Valkeinen 	struct v4l2_subdev_state *state;
17786edb685aSTomi Valkeinen 	struct v4l2_subdev *source_sd;
17796edb685aSTomi Valkeinen 	int ret = 0;
17806edb685aSTomi Valkeinen 
17816edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__,
17826edb685aSTomi Valkeinen 		node_desc[node->id].name,
17836edb685aSTomi Valkeinen 		link->source->entity->name, link->source->index,
17846edb685aSTomi Valkeinen 		link->sink->entity->name, link->sink->index);
17856edb685aSTomi Valkeinen 
17866edb685aSTomi Valkeinen 	if (!media_entity_remote_source_pad_unique(link->sink->entity)) {
17876edb685aSTomi Valkeinen 		cfe_err(cfe, "video node %s pad not connected\n", vd->name);
17886edb685aSTomi Valkeinen 		return -ENOTCONN;
17896edb685aSTomi Valkeinen 	}
17906edb685aSTomi Valkeinen 
17916edb685aSTomi Valkeinen 	source_sd = media_entity_to_v4l2_subdev(link->source->entity);
17926edb685aSTomi Valkeinen 
17936edb685aSTomi Valkeinen 	state = v4l2_subdev_lock_and_get_active_state(source_sd);
17946edb685aSTomi Valkeinen 
17956edb685aSTomi Valkeinen 	source_fmt = v4l2_subdev_state_get_format(state, link->source->index);
17966edb685aSTomi Valkeinen 	if (!source_fmt) {
17976edb685aSTomi Valkeinen 		ret = -EINVAL;
17986edb685aSTomi Valkeinen 		goto out;
17996edb685aSTomi Valkeinen 	}
18006edb685aSTomi Valkeinen 
18016edb685aSTomi Valkeinen 	if (is_image_output_node(node)) {
18026edb685aSTomi Valkeinen 		struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix;
18036edb685aSTomi Valkeinen 		const struct cfe_fmt *fmt;
18046edb685aSTomi Valkeinen 
18056edb685aSTomi Valkeinen 		if (source_fmt->width != pix_fmt->width ||
18066edb685aSTomi Valkeinen 		    source_fmt->height != pix_fmt->height) {
18076edb685aSTomi Valkeinen 			cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n",
18086edb685aSTomi Valkeinen 				pix_fmt->width, pix_fmt->height,
18096edb685aSTomi Valkeinen 				source_fmt->width, source_fmt->height);
18106edb685aSTomi Valkeinen 			ret = -EINVAL;
18116edb685aSTomi Valkeinen 			goto out;
18126edb685aSTomi Valkeinen 		}
18136edb685aSTomi Valkeinen 
18146edb685aSTomi Valkeinen 		fmt = find_format_by_code_and_fourcc(source_fmt->code,
18156edb685aSTomi Valkeinen 						     pix_fmt->pixelformat);
18166edb685aSTomi Valkeinen 		if (!fmt) {
18176edb685aSTomi Valkeinen 			cfe_err(cfe, "Format mismatch!\n");
18186edb685aSTomi Valkeinen 			ret = -EINVAL;
18196edb685aSTomi Valkeinen 			goto out;
18206edb685aSTomi Valkeinen 		}
18216edb685aSTomi Valkeinen 	} else if (is_csi2_node(node) && is_meta_output_node(node)) {
18226edb685aSTomi Valkeinen 		struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta;
18236edb685aSTomi Valkeinen 		const struct cfe_fmt *fmt;
18246edb685aSTomi Valkeinen 
18256edb685aSTomi Valkeinen 		if (source_fmt->width != meta_fmt->width ||
18266edb685aSTomi Valkeinen 		    source_fmt->height != meta_fmt->height) {
18276edb685aSTomi Valkeinen 			cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n",
18286edb685aSTomi Valkeinen 				meta_fmt->width, meta_fmt->height,
18296edb685aSTomi Valkeinen 				source_fmt->width, source_fmt->height);
18306edb685aSTomi Valkeinen 			ret = -EINVAL;
18316edb685aSTomi Valkeinen 			goto out;
18326edb685aSTomi Valkeinen 		}
18336edb685aSTomi Valkeinen 
18346edb685aSTomi Valkeinen 		fmt = find_format_by_code_and_fourcc(source_fmt->code,
18356edb685aSTomi Valkeinen 						     meta_fmt->dataformat);
18366edb685aSTomi Valkeinen 		if (!fmt) {
18376edb685aSTomi Valkeinen 			cfe_err(cfe, "Format mismatch!\n");
18386edb685aSTomi Valkeinen 			ret = -EINVAL;
18396edb685aSTomi Valkeinen 			goto out;
18406edb685aSTomi Valkeinen 		}
18416edb685aSTomi Valkeinen 	}
18426edb685aSTomi Valkeinen 
18436edb685aSTomi Valkeinen out:
18446edb685aSTomi Valkeinen 	v4l2_subdev_unlock_state(state);
18456edb685aSTomi Valkeinen 
18466edb685aSTomi Valkeinen 	return ret;
18476edb685aSTomi Valkeinen }
18486edb685aSTomi Valkeinen 
18496edb685aSTomi Valkeinen static const struct media_entity_operations cfe_media_entity_ops = {
18506edb685aSTomi Valkeinen 	.link_validate = cfe_video_link_validate,
18516edb685aSTomi Valkeinen };
18526edb685aSTomi Valkeinen 
18536edb685aSTomi Valkeinen static int cfe_video_link_notify(struct media_link *link, u32 flags,
18546edb685aSTomi Valkeinen 				 unsigned int notification)
18556edb685aSTomi Valkeinen {
18566edb685aSTomi Valkeinen 	struct media_device *mdev = link->graph_obj.mdev;
18576edb685aSTomi Valkeinen 	struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev);
18586edb685aSTomi Valkeinen 	struct media_entity *fe = &cfe->fe.sd.entity;
18596edb685aSTomi Valkeinen 	struct media_entity *csi2 = &cfe->csi2.sd.entity;
18606edb685aSTomi Valkeinen 	unsigned long lock_flags;
18616edb685aSTomi Valkeinen 
18626edb685aSTomi Valkeinen 	if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH)
18636edb685aSTomi Valkeinen 		return 0;
18646edb685aSTomi Valkeinen 
18656edb685aSTomi Valkeinen 	cfe_dbg(cfe, "%s: %s[%u] -> %s[%u] 0x%x", __func__,
18666edb685aSTomi Valkeinen 		link->source->entity->name, link->source->index,
18676edb685aSTomi Valkeinen 		link->sink->entity->name, link->sink->index, flags);
18686edb685aSTomi Valkeinen 
18696edb685aSTomi Valkeinen 	spin_lock_irqsave(&cfe->state_lock, lock_flags);
18706edb685aSTomi Valkeinen 
18716edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
18726edb685aSTomi Valkeinen 		if (link->sink->entity != &cfe->node[i].video_dev.entity &&
18736edb685aSTomi Valkeinen 		    link->source->entity != &cfe->node[i].video_dev.entity)
18746edb685aSTomi Valkeinen 			continue;
18756edb685aSTomi Valkeinen 
18766edb685aSTomi Valkeinen 		if (link->flags & MEDIA_LNK_FL_ENABLED)
18776edb685aSTomi Valkeinen 			set_state(cfe, NODE_ENABLED, i);
18786edb685aSTomi Valkeinen 		else
18796edb685aSTomi Valkeinen 			clear_state(cfe, NODE_ENABLED, i);
18806edb685aSTomi Valkeinen 
18816edb685aSTomi Valkeinen 		break;
18826edb685aSTomi Valkeinen 	}
18836edb685aSTomi Valkeinen 
18846edb685aSTomi Valkeinen 	spin_unlock_irqrestore(&cfe->state_lock, lock_flags);
18856edb685aSTomi Valkeinen 
18866edb685aSTomi Valkeinen 	if (link->source->entity != csi2)
18876edb685aSTomi Valkeinen 		return 0;
18886edb685aSTomi Valkeinen 	if (link->sink->entity != fe)
18896edb685aSTomi Valkeinen 		return 0;
18906edb685aSTomi Valkeinen 	if (link->sink->index != 0)
18916edb685aSTomi Valkeinen 		return 0;
18926edb685aSTomi Valkeinen 
18936edb685aSTomi Valkeinen 	cfe->fe_csi2_channel = -1;
18946edb685aSTomi Valkeinen 	if (link->flags & MEDIA_LNK_FL_ENABLED) {
18956edb685aSTomi Valkeinen 		if (link->source->index == node_desc[CSI2_CH0].link_pad)
18966edb685aSTomi Valkeinen 			cfe->fe_csi2_channel = CSI2_CH0;
18976edb685aSTomi Valkeinen 		else if (link->source->index == node_desc[CSI2_CH1].link_pad)
18986edb685aSTomi Valkeinen 			cfe->fe_csi2_channel = CSI2_CH1;
18996edb685aSTomi Valkeinen 		else if (link->source->index == node_desc[CSI2_CH2].link_pad)
19006edb685aSTomi Valkeinen 			cfe->fe_csi2_channel = CSI2_CH2;
19016edb685aSTomi Valkeinen 		else if (link->source->index == node_desc[CSI2_CH3].link_pad)
19026edb685aSTomi Valkeinen 			cfe->fe_csi2_channel = CSI2_CH3;
19036edb685aSTomi Valkeinen 	}
19046edb685aSTomi Valkeinen 
19056edb685aSTomi Valkeinen 	if (is_fe_enabled(cfe))
19066edb685aSTomi Valkeinen 		cfe_dbg(cfe, "%s: Found CSI2:%d -> FE:0 link\n", __func__,
19076edb685aSTomi Valkeinen 			cfe->fe_csi2_channel);
19086edb685aSTomi Valkeinen 	else
19096edb685aSTomi Valkeinen 		cfe_dbg(cfe, "%s: Unable to find CSI2:x -> FE:0 link\n",
19106edb685aSTomi Valkeinen 			__func__);
19116edb685aSTomi Valkeinen 
19126edb685aSTomi Valkeinen 	return 0;
19136edb685aSTomi Valkeinen }
19146edb685aSTomi Valkeinen 
19156edb685aSTomi Valkeinen static const struct media_device_ops cfe_media_device_ops = {
19166edb685aSTomi Valkeinen 	.link_notify = cfe_video_link_notify,
19176edb685aSTomi Valkeinen };
19186edb685aSTomi Valkeinen 
19196edb685aSTomi Valkeinen static void cfe_release(struct kref *kref)
19206edb685aSTomi Valkeinen {
19216edb685aSTomi Valkeinen 	struct cfe_device *cfe = container_of(kref, struct cfe_device, kref);
19226edb685aSTomi Valkeinen 
19236edb685aSTomi Valkeinen 	media_device_cleanup(&cfe->mdev);
19246edb685aSTomi Valkeinen 
19256edb685aSTomi Valkeinen 	kfree(cfe);
19266edb685aSTomi Valkeinen }
19276edb685aSTomi Valkeinen 
19286edb685aSTomi Valkeinen static void cfe_put(struct cfe_device *cfe)
19296edb685aSTomi Valkeinen {
19306edb685aSTomi Valkeinen 	kref_put(&cfe->kref, cfe_release);
19316edb685aSTomi Valkeinen }
19326edb685aSTomi Valkeinen 
19336edb685aSTomi Valkeinen static void cfe_get(struct cfe_device *cfe)
19346edb685aSTomi Valkeinen {
19356edb685aSTomi Valkeinen 	kref_get(&cfe->kref);
19366edb685aSTomi Valkeinen }
19376edb685aSTomi Valkeinen 
19386edb685aSTomi Valkeinen static void cfe_node_release(struct video_device *vdev)
19396edb685aSTomi Valkeinen {
19406edb685aSTomi Valkeinen 	struct cfe_node *node = video_get_drvdata(vdev);
19416edb685aSTomi Valkeinen 
19426edb685aSTomi Valkeinen 	cfe_put(node->cfe);
19436edb685aSTomi Valkeinen }
19446edb685aSTomi Valkeinen 
19456edb685aSTomi Valkeinen static int cfe_register_node(struct cfe_device *cfe, int id)
19466edb685aSTomi Valkeinen {
19476edb685aSTomi Valkeinen 	struct video_device *vdev;
19486edb685aSTomi Valkeinen 	const struct cfe_fmt *fmt;
19496edb685aSTomi Valkeinen 	struct vb2_queue *q;
19506edb685aSTomi Valkeinen 	struct cfe_node *node = &cfe->node[id];
19516edb685aSTomi Valkeinen 	int ret;
19526edb685aSTomi Valkeinen 
19536edb685aSTomi Valkeinen 	node->cfe = cfe;
19546edb685aSTomi Valkeinen 	node->id = id;
19556edb685aSTomi Valkeinen 
19566edb685aSTomi Valkeinen 	if (node_supports_image(node)) {
19576edb685aSTomi Valkeinen 		if (node_supports_image_output(node))
19586edb685aSTomi Valkeinen 			node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
19596edb685aSTomi Valkeinen 		else
19606edb685aSTomi Valkeinen 			node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
19616edb685aSTomi Valkeinen 
19626edb685aSTomi Valkeinen 		fmt = find_format_by_code(cfe_default_format.code);
19636edb685aSTomi Valkeinen 		if (!fmt) {
19646edb685aSTomi Valkeinen 			cfe_err(cfe, "Failed to find format code\n");
19656edb685aSTomi Valkeinen 			return -EINVAL;
19666edb685aSTomi Valkeinen 		}
19676edb685aSTomi Valkeinen 
19686edb685aSTomi Valkeinen 		node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc;
19696edb685aSTomi Valkeinen 		v4l2_fill_pix_format(&node->vid_fmt.fmt.pix,
19706edb685aSTomi Valkeinen 				     &cfe_default_format);
19716edb685aSTomi Valkeinen 
19726edb685aSTomi Valkeinen 		ret = cfe_validate_fmt_vid_cap(node, &node->vid_fmt);
19736edb685aSTomi Valkeinen 		if (ret)
19746edb685aSTomi Valkeinen 			return ret;
19756edb685aSTomi Valkeinen 	}
19766edb685aSTomi Valkeinen 
19776edb685aSTomi Valkeinen 	if (node_supports_meta(node)) {
19786edb685aSTomi Valkeinen 		if (node_supports_meta_output(node))
19796edb685aSTomi Valkeinen 			node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
19806edb685aSTomi Valkeinen 		else
19816edb685aSTomi Valkeinen 			node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT;
19826edb685aSTomi Valkeinen 
19836edb685aSTomi Valkeinen 		ret = cfe_validate_fmt_meta(node, &node->meta_fmt);
19846edb685aSTomi Valkeinen 		if (ret)
19856edb685aSTomi Valkeinen 			return ret;
19866edb685aSTomi Valkeinen 	}
19876edb685aSTomi Valkeinen 
19886edb685aSTomi Valkeinen 	mutex_init(&node->lock);
19896edb685aSTomi Valkeinen 
19906edb685aSTomi Valkeinen 	q = &node->buffer_queue;
19916edb685aSTomi Valkeinen 	q->type = node_supports_image(node) ? node->vid_fmt.type :
19926edb685aSTomi Valkeinen 					      node->meta_fmt.type;
19936edb685aSTomi Valkeinen 	q->io_modes = VB2_MMAP | VB2_DMABUF;
19946edb685aSTomi Valkeinen 	q->drv_priv = node;
19956edb685aSTomi Valkeinen 	q->ops = &cfe_video_qops;
19966edb685aSTomi Valkeinen 	q->mem_ops = &vb2_dma_contig_memops;
19976edb685aSTomi Valkeinen 	q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer)
19986edb685aSTomi Valkeinen 					     : sizeof(struct cfe_buffer);
19996edb685aSTomi Valkeinen 	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
20006edb685aSTomi Valkeinen 	q->lock = &node->lock;
20016edb685aSTomi Valkeinen 	q->min_queued_buffers = 1;
20026edb685aSTomi Valkeinen 	q->dev = &cfe->pdev->dev;
20036edb685aSTomi Valkeinen 
20046edb685aSTomi Valkeinen 	ret = vb2_queue_init(q);
20056edb685aSTomi Valkeinen 	if (ret) {
20066edb685aSTomi Valkeinen 		cfe_err(cfe, "vb2_queue_init() failed\n");
20076edb685aSTomi Valkeinen 		return ret;
20086edb685aSTomi Valkeinen 	}
20096edb685aSTomi Valkeinen 
20106edb685aSTomi Valkeinen 	INIT_LIST_HEAD(&node->dma_queue);
20116edb685aSTomi Valkeinen 
20126edb685aSTomi Valkeinen 	vdev = &node->video_dev;
20136edb685aSTomi Valkeinen 	vdev->release = cfe_node_release;
20146edb685aSTomi Valkeinen 	vdev->fops = &cfe_fops;
20156edb685aSTomi Valkeinen 	vdev->ioctl_ops = &cfe_ioctl_ops;
20166edb685aSTomi Valkeinen 	vdev->entity.ops = &cfe_media_entity_ops;
20176edb685aSTomi Valkeinen 	vdev->v4l2_dev = &cfe->v4l2_dev;
20186edb685aSTomi Valkeinen 	vdev->vfl_dir = (node_supports_image_output(node) ||
20196edb685aSTomi Valkeinen 			 node_supports_meta_output(node)) ?
20206edb685aSTomi Valkeinen 				VFL_DIR_RX :
20216edb685aSTomi Valkeinen 				VFL_DIR_TX;
20226edb685aSTomi Valkeinen 	vdev->queue = q;
20236edb685aSTomi Valkeinen 	vdev->lock = &node->lock;
20246edb685aSTomi Valkeinen 	vdev->device_caps = node_desc[id].caps;
20256edb685aSTomi Valkeinen 	vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
20266edb685aSTomi Valkeinen 
20276edb685aSTomi Valkeinen 	/* Define the device names */
20286edb685aSTomi Valkeinen 	snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME,
20296edb685aSTomi Valkeinen 		 node_desc[id].name);
20306edb685aSTomi Valkeinen 
20316edb685aSTomi Valkeinen 	video_set_drvdata(vdev, node);
20326edb685aSTomi Valkeinen 	node->pad.flags = node_desc[id].pad_flags;
20336edb685aSTomi Valkeinen 	media_entity_pads_init(&vdev->entity, 1, &node->pad);
20346edb685aSTomi Valkeinen 
20356edb685aSTomi Valkeinen 	if (!node_supports_image(node)) {
20366edb685aSTomi Valkeinen 		v4l2_disable_ioctl(&node->video_dev,
20376edb685aSTomi Valkeinen 				   VIDIOC_ENUM_FRAMEINTERVALS);
20386edb685aSTomi Valkeinen 		v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES);
20396edb685aSTomi Valkeinen 	}
20406edb685aSTomi Valkeinen 
20416edb685aSTomi Valkeinen 	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
20426edb685aSTomi Valkeinen 	if (ret) {
20436edb685aSTomi Valkeinen 		cfe_err(cfe, "Unable to register video device %s\n",
20446edb685aSTomi Valkeinen 			vdev->name);
20456edb685aSTomi Valkeinen 		return ret;
20466edb685aSTomi Valkeinen 	}
20476edb685aSTomi Valkeinen 
20486edb685aSTomi Valkeinen 	cfe_info(cfe, "Registered [%s] node id %d as /dev/video%u\n",
20496edb685aSTomi Valkeinen 		 vdev->name, id, vdev->num);
20506edb685aSTomi Valkeinen 
20516edb685aSTomi Valkeinen 	/*
20526edb685aSTomi Valkeinen 	 * Acquire a reference to cfe, which will be released when the video
20536edb685aSTomi Valkeinen 	 * device will be unregistered and userspace will have closed all open
20546edb685aSTomi Valkeinen 	 * file handles.
20556edb685aSTomi Valkeinen 	 */
20566edb685aSTomi Valkeinen 	cfe_get(cfe);
20576edb685aSTomi Valkeinen 	set_state(cfe, NODE_REGISTERED, id);
20586edb685aSTomi Valkeinen 
20596edb685aSTomi Valkeinen 	return 0;
20606edb685aSTomi Valkeinen }
20616edb685aSTomi Valkeinen 
20626edb685aSTomi Valkeinen static void cfe_unregister_nodes(struct cfe_device *cfe)
20636edb685aSTomi Valkeinen {
20646edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
20656edb685aSTomi Valkeinen 		struct cfe_node *node = &cfe->node[i];
20666edb685aSTomi Valkeinen 
20676edb685aSTomi Valkeinen 		if (check_state(cfe, NODE_REGISTERED, i)) {
20686edb685aSTomi Valkeinen 			clear_state(cfe, NODE_REGISTERED, i);
20696edb685aSTomi Valkeinen 			video_unregister_device(&node->video_dev);
20706edb685aSTomi Valkeinen 		}
20716edb685aSTomi Valkeinen 	}
20726edb685aSTomi Valkeinen }
20736edb685aSTomi Valkeinen 
20746edb685aSTomi Valkeinen static int cfe_link_node_pads(struct cfe_device *cfe)
20756edb685aSTomi Valkeinen {
20766edb685aSTomi Valkeinen 	struct media_pad *remote_pad;
20776edb685aSTomi Valkeinen 	int ret;
20786edb685aSTomi Valkeinen 
20796edb685aSTomi Valkeinen 	/* Source -> CSI2 */
20806edb685aSTomi Valkeinen 
20816edb685aSTomi Valkeinen 	ret = v4l2_create_fwnode_links_to_pad(cfe->source_sd,
20826edb685aSTomi Valkeinen 					      &cfe->csi2.pad[CSI2_PAD_SINK],
20836edb685aSTomi Valkeinen 					      MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
20846edb685aSTomi Valkeinen 
20856edb685aSTomi Valkeinen 	if (ret) {
20866edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to create links to the source: %d\n", ret);
20876edb685aSTomi Valkeinen 		return ret;
20886edb685aSTomi Valkeinen 	}
20896edb685aSTomi Valkeinen 
20906edb685aSTomi Valkeinen 	remote_pad = media_pad_remote_pad_unique(&cfe->csi2.pad[CSI2_PAD_SINK]);
20916edb685aSTomi Valkeinen 	if (IS_ERR(remote_pad)) {
20926edb685aSTomi Valkeinen 		ret = PTR_ERR(remote_pad);
20936edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to get unique remote source pad: %d\n",
20946edb685aSTomi Valkeinen 			ret);
20956edb685aSTomi Valkeinen 		return ret;
20966edb685aSTomi Valkeinen 	}
20976edb685aSTomi Valkeinen 
20986edb685aSTomi Valkeinen 	cfe->source_pad = remote_pad->index;
20996edb685aSTomi Valkeinen 
21006edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
21016edb685aSTomi Valkeinen 		struct cfe_node *node = &cfe->node[i];
21026edb685aSTomi Valkeinen 
21036edb685aSTomi Valkeinen 		if (!check_state(cfe, NODE_REGISTERED, i))
21046edb685aSTomi Valkeinen 			continue;
21056edb685aSTomi Valkeinen 
21066edb685aSTomi Valkeinen 		/* CSI2 channel # -> /dev/video# */
21076edb685aSTomi Valkeinen 		ret = media_create_pad_link(&cfe->csi2.sd.entity,
21086edb685aSTomi Valkeinen 					    node_desc[i].link_pad,
21096edb685aSTomi Valkeinen 					    &node->video_dev.entity, 0, 0);
21106edb685aSTomi Valkeinen 		if (ret)
21116edb685aSTomi Valkeinen 			return ret;
21126edb685aSTomi Valkeinen 
21136edb685aSTomi Valkeinen 		if (node_supports_image(node)) {
21146edb685aSTomi Valkeinen 			/* CSI2 channel # -> FE Input */
21156edb685aSTomi Valkeinen 			ret = media_create_pad_link(&cfe->csi2.sd.entity,
21166edb685aSTomi Valkeinen 						    node_desc[i].link_pad,
21176edb685aSTomi Valkeinen 						    &cfe->fe.sd.entity,
21186edb685aSTomi Valkeinen 						    FE_STREAM_PAD, 0);
21196edb685aSTomi Valkeinen 			if (ret)
21206edb685aSTomi Valkeinen 				return ret;
21216edb685aSTomi Valkeinen 		}
21226edb685aSTomi Valkeinen 	}
21236edb685aSTomi Valkeinen 
21246edb685aSTomi Valkeinen 	for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) {
21256edb685aSTomi Valkeinen 		struct cfe_node *node = &cfe->node[i];
21266edb685aSTomi Valkeinen 		struct media_entity *src, *dst;
21276edb685aSTomi Valkeinen 		unsigned int src_pad, dst_pad;
21286edb685aSTomi Valkeinen 
21296edb685aSTomi Valkeinen 		if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) {
21306edb685aSTomi Valkeinen 			/* FE -> /dev/video# */
21316edb685aSTomi Valkeinen 			src = &cfe->fe.sd.entity;
21326edb685aSTomi Valkeinen 			src_pad = node_desc[i].link_pad;
21336edb685aSTomi Valkeinen 			dst = &node->video_dev.entity;
21346edb685aSTomi Valkeinen 			dst_pad = 0;
21356edb685aSTomi Valkeinen 		} else {
21366edb685aSTomi Valkeinen 			/* /dev/video# -> FE */
21376edb685aSTomi Valkeinen 			dst = &cfe->fe.sd.entity;
21386edb685aSTomi Valkeinen 			dst_pad = node_desc[i].link_pad;
21396edb685aSTomi Valkeinen 			src = &node->video_dev.entity;
21406edb685aSTomi Valkeinen 			src_pad = 0;
21416edb685aSTomi Valkeinen 		}
21426edb685aSTomi Valkeinen 
21436edb685aSTomi Valkeinen 		ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0);
21446edb685aSTomi Valkeinen 		if (ret)
21456edb685aSTomi Valkeinen 			return ret;
21466edb685aSTomi Valkeinen 	}
21476edb685aSTomi Valkeinen 
21486edb685aSTomi Valkeinen 	return 0;
21496edb685aSTomi Valkeinen }
21506edb685aSTomi Valkeinen 
21516edb685aSTomi Valkeinen static int cfe_probe_complete(struct cfe_device *cfe)
21526edb685aSTomi Valkeinen {
21536edb685aSTomi Valkeinen 	int ret;
21546edb685aSTomi Valkeinen 
21556edb685aSTomi Valkeinen 	cfe->v4l2_dev.notify = cfe_notify;
21566edb685aSTomi Valkeinen 
21576edb685aSTomi Valkeinen 	for (unsigned int i = 0; i < NUM_NODES; i++) {
21586edb685aSTomi Valkeinen 		ret = cfe_register_node(cfe, i);
21596edb685aSTomi Valkeinen 		if (ret) {
21606edb685aSTomi Valkeinen 			cfe_err(cfe, "Unable to register video node %u.\n", i);
21616edb685aSTomi Valkeinen 			goto unregister;
21626edb685aSTomi Valkeinen 		}
21636edb685aSTomi Valkeinen 	}
21646edb685aSTomi Valkeinen 
21656edb685aSTomi Valkeinen 	ret = cfe_link_node_pads(cfe);
21666edb685aSTomi Valkeinen 	if (ret) {
21676edb685aSTomi Valkeinen 		cfe_err(cfe, "Unable to link node pads.\n");
21686edb685aSTomi Valkeinen 		goto unregister;
21696edb685aSTomi Valkeinen 	}
21706edb685aSTomi Valkeinen 
21716edb685aSTomi Valkeinen 	ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev);
21726edb685aSTomi Valkeinen 	if (ret) {
21736edb685aSTomi Valkeinen 		cfe_err(cfe, "Unable to register subdev nodes.\n");
21746edb685aSTomi Valkeinen 		goto unregister;
21756edb685aSTomi Valkeinen 	}
21766edb685aSTomi Valkeinen 
21776edb685aSTomi Valkeinen 	return 0;
21786edb685aSTomi Valkeinen 
21796edb685aSTomi Valkeinen unregister:
21806edb685aSTomi Valkeinen 	cfe_unregister_nodes(cfe);
21816edb685aSTomi Valkeinen 	return ret;
21826edb685aSTomi Valkeinen }
21836edb685aSTomi Valkeinen 
21846edb685aSTomi Valkeinen static int cfe_async_bound(struct v4l2_async_notifier *notifier,
21856edb685aSTomi Valkeinen 			   struct v4l2_subdev *subdev,
21866edb685aSTomi Valkeinen 			   struct v4l2_async_connection *asd)
21876edb685aSTomi Valkeinen {
21886edb685aSTomi Valkeinen 	struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
21896edb685aSTomi Valkeinen 
21906edb685aSTomi Valkeinen 	if (cfe->source_sd) {
21916edb685aSTomi Valkeinen 		cfe_err(cfe, "Rejecting subdev %s (Already set!!)",
21926edb685aSTomi Valkeinen 			subdev->name);
21936edb685aSTomi Valkeinen 		return 0;
21946edb685aSTomi Valkeinen 	}
21956edb685aSTomi Valkeinen 
21966edb685aSTomi Valkeinen 	cfe->source_sd = subdev;
21976edb685aSTomi Valkeinen 
21986edb685aSTomi Valkeinen 	cfe_dbg(cfe, "Using source %s for capture\n", subdev->name);
21996edb685aSTomi Valkeinen 
22006edb685aSTomi Valkeinen 	return 0;
22016edb685aSTomi Valkeinen }
22026edb685aSTomi Valkeinen 
22036edb685aSTomi Valkeinen static int cfe_async_complete(struct v4l2_async_notifier *notifier)
22046edb685aSTomi Valkeinen {
22056edb685aSTomi Valkeinen 	struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
22066edb685aSTomi Valkeinen 
22076edb685aSTomi Valkeinen 	return cfe_probe_complete(cfe);
22086edb685aSTomi Valkeinen }
22096edb685aSTomi Valkeinen 
22106edb685aSTomi Valkeinen static const struct v4l2_async_notifier_operations cfe_async_ops = {
22116edb685aSTomi Valkeinen 	.bound = cfe_async_bound,
22126edb685aSTomi Valkeinen 	.complete = cfe_async_complete,
22136edb685aSTomi Valkeinen };
22146edb685aSTomi Valkeinen 
22156edb685aSTomi Valkeinen static int cfe_register_async_nf(struct cfe_device *cfe)
22166edb685aSTomi Valkeinen {
22176edb685aSTomi Valkeinen 	struct platform_device *pdev = cfe->pdev;
22186edb685aSTomi Valkeinen 	struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
22196edb685aSTomi Valkeinen 	struct fwnode_handle *local_ep_fwnode;
22206edb685aSTomi Valkeinen 	struct v4l2_async_connection *asd;
22216edb685aSTomi Valkeinen 	int ret;
22226edb685aSTomi Valkeinen 
22236edb685aSTomi Valkeinen 	local_ep_fwnode = fwnode_graph_get_endpoint_by_id(pdev->dev.fwnode, 0,
22246edb685aSTomi Valkeinen 							  0, 0);
22256edb685aSTomi Valkeinen 	if (!local_ep_fwnode) {
22266edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to find local endpoint fwnode\n");
22276edb685aSTomi Valkeinen 		return -ENODEV;
22286edb685aSTomi Valkeinen 	}
22296edb685aSTomi Valkeinen 
22306edb685aSTomi Valkeinen 	/* Parse the local endpoint and validate its configuration. */
22316edb685aSTomi Valkeinen 	ret = v4l2_fwnode_endpoint_parse(local_ep_fwnode, &ep);
22326edb685aSTomi Valkeinen 	if (ret) {
22336edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to find remote endpoint fwnode\n");
22346edb685aSTomi Valkeinen 		goto err_put_local_fwnode;
22356edb685aSTomi Valkeinen 	}
22366edb685aSTomi Valkeinen 
22376edb685aSTomi Valkeinen 	for (unsigned int lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes;
22386edb685aSTomi Valkeinen 	     lane++) {
22396edb685aSTomi Valkeinen 		if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) {
22406edb685aSTomi Valkeinen 			cfe_err(cfe, "Data lanes reordering not supported\n");
22416edb685aSTomi Valkeinen 			ret = -EINVAL;
22426edb685aSTomi Valkeinen 			goto err_put_local_fwnode;
22436edb685aSTomi Valkeinen 		}
22446edb685aSTomi Valkeinen 	}
22456edb685aSTomi Valkeinen 
22466edb685aSTomi Valkeinen 	cfe->csi2.dphy.max_lanes = ep.bus.mipi_csi2.num_data_lanes;
22476edb685aSTomi Valkeinen 	cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags;
22486edb685aSTomi Valkeinen 
22496edb685aSTomi Valkeinen 	/* Initialize and register the async notifier. */
22506edb685aSTomi Valkeinen 	v4l2_async_nf_init(&cfe->notifier, &cfe->v4l2_dev);
22516edb685aSTomi Valkeinen 	cfe->notifier.ops = &cfe_async_ops;
22526edb685aSTomi Valkeinen 
22536edb685aSTomi Valkeinen 	asd = v4l2_async_nf_add_fwnode_remote(&cfe->notifier, local_ep_fwnode,
22546edb685aSTomi Valkeinen 					      struct v4l2_async_connection);
22556edb685aSTomi Valkeinen 	if (IS_ERR(asd)) {
22566edb685aSTomi Valkeinen 		ret = PTR_ERR(asd);
22576edb685aSTomi Valkeinen 		cfe_err(cfe, "Error adding subdevice: %d\n", ret);
22586edb685aSTomi Valkeinen 		goto err_put_local_fwnode;
22596edb685aSTomi Valkeinen 	}
22606edb685aSTomi Valkeinen 
22616edb685aSTomi Valkeinen 	ret = v4l2_async_nf_register(&cfe->notifier);
22626edb685aSTomi Valkeinen 	if (ret) {
22636edb685aSTomi Valkeinen 		cfe_err(cfe, "Error registering async notifier: %d\n", ret);
22646edb685aSTomi Valkeinen 		goto err_nf_cleanup;
22656edb685aSTomi Valkeinen 	}
22666edb685aSTomi Valkeinen 
22676edb685aSTomi Valkeinen 	fwnode_handle_put(local_ep_fwnode);
22686edb685aSTomi Valkeinen 
22696edb685aSTomi Valkeinen 	return 0;
22706edb685aSTomi Valkeinen 
22716edb685aSTomi Valkeinen err_nf_cleanup:
22726edb685aSTomi Valkeinen 	v4l2_async_nf_cleanup(&cfe->notifier);
22736edb685aSTomi Valkeinen err_put_local_fwnode:
22746edb685aSTomi Valkeinen 	fwnode_handle_put(local_ep_fwnode);
22756edb685aSTomi Valkeinen 
22766edb685aSTomi Valkeinen 	return ret;
22776edb685aSTomi Valkeinen }
22786edb685aSTomi Valkeinen 
22796edb685aSTomi Valkeinen static int cfe_probe(struct platform_device *pdev)
22806edb685aSTomi Valkeinen {
22816edb685aSTomi Valkeinen 	struct cfe_device *cfe;
22826edb685aSTomi Valkeinen 	char debugfs_name[32];
22836edb685aSTomi Valkeinen 	int ret;
22846edb685aSTomi Valkeinen 
22856edb685aSTomi Valkeinen 	cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);
22866edb685aSTomi Valkeinen 	if (!cfe)
22876edb685aSTomi Valkeinen 		return -ENOMEM;
22886edb685aSTomi Valkeinen 
22896edb685aSTomi Valkeinen 	platform_set_drvdata(pdev, cfe);
22906edb685aSTomi Valkeinen 
22916edb685aSTomi Valkeinen 	kref_init(&cfe->kref);
22926edb685aSTomi Valkeinen 	cfe->pdev = pdev;
22936edb685aSTomi Valkeinen 	cfe->fe_csi2_channel = -1;
22946edb685aSTomi Valkeinen 	spin_lock_init(&cfe->state_lock);
22956edb685aSTomi Valkeinen 
22966edb685aSTomi Valkeinen 	cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0);
22976edb685aSTomi Valkeinen 	if (IS_ERR(cfe->csi2.base)) {
22986edb685aSTomi Valkeinen 		dev_err(&pdev->dev, "Failed to get dma io block\n");
22996edb685aSTomi Valkeinen 		ret = PTR_ERR(cfe->csi2.base);
23006edb685aSTomi Valkeinen 		goto err_cfe_put;
23016edb685aSTomi Valkeinen 	}
23026edb685aSTomi Valkeinen 
23036edb685aSTomi Valkeinen 	cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1);
23046edb685aSTomi Valkeinen 	if (IS_ERR(cfe->csi2.dphy.base)) {
23056edb685aSTomi Valkeinen 		dev_err(&pdev->dev, "Failed to get host io block\n");
23066edb685aSTomi Valkeinen 		ret = PTR_ERR(cfe->csi2.dphy.base);
23076edb685aSTomi Valkeinen 		goto err_cfe_put;
23086edb685aSTomi Valkeinen 	}
23096edb685aSTomi Valkeinen 
23106edb685aSTomi Valkeinen 	cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2);
23116edb685aSTomi Valkeinen 	if (IS_ERR(cfe->mipi_cfg_base)) {
23126edb685aSTomi Valkeinen 		dev_err(&pdev->dev, "Failed to get mipi cfg io block\n");
23136edb685aSTomi Valkeinen 		ret = PTR_ERR(cfe->mipi_cfg_base);
23146edb685aSTomi Valkeinen 		goto err_cfe_put;
23156edb685aSTomi Valkeinen 	}
23166edb685aSTomi Valkeinen 
23176edb685aSTomi Valkeinen 	cfe->fe.base = devm_platform_ioremap_resource(pdev, 3);
23186edb685aSTomi Valkeinen 	if (IS_ERR(cfe->fe.base)) {
23196edb685aSTomi Valkeinen 		dev_err(&pdev->dev, "Failed to get pisp fe io block\n");
23206edb685aSTomi Valkeinen 		ret = PTR_ERR(cfe->fe.base);
23216edb685aSTomi Valkeinen 		goto err_cfe_put;
23226edb685aSTomi Valkeinen 	}
23236edb685aSTomi Valkeinen 
23246edb685aSTomi Valkeinen 	ret = platform_get_irq(pdev, 0);
23256edb685aSTomi Valkeinen 	if (ret <= 0) {
23266edb685aSTomi Valkeinen 		ret = -EINVAL;
23276edb685aSTomi Valkeinen 		goto err_cfe_put;
23286edb685aSTomi Valkeinen 	}
23296edb685aSTomi Valkeinen 
23306edb685aSTomi Valkeinen 	ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe);
23316edb685aSTomi Valkeinen 	if (ret) {
23326edb685aSTomi Valkeinen 		dev_err(&pdev->dev, "Unable to request interrupt\n");
23336edb685aSTomi Valkeinen 		ret = -EINVAL;
23346edb685aSTomi Valkeinen 		goto err_cfe_put;
23356edb685aSTomi Valkeinen 	}
23366edb685aSTomi Valkeinen 
23376edb685aSTomi Valkeinen 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
23386edb685aSTomi Valkeinen 	if (ret) {
23396edb685aSTomi Valkeinen 		dev_err(&pdev->dev, "DMA enable failed\n");
23406edb685aSTomi Valkeinen 		goto err_cfe_put;
23416edb685aSTomi Valkeinen 	}
23426edb685aSTomi Valkeinen 
2343*be7de823STomi Valkeinen 	ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, UINT_MAX);
2344*be7de823STomi Valkeinen 	if (ret)
2345*be7de823STomi Valkeinen 		goto err_cfe_put;
2346*be7de823STomi Valkeinen 
23476edb685aSTomi Valkeinen 	/* TODO: Enable clock only when running. */
23486edb685aSTomi Valkeinen 	cfe->clk = devm_clk_get(&pdev->dev, NULL);
23498e9a03bdSDan Carpenter 	if (IS_ERR(cfe->clk)) {
23508e9a03bdSDan Carpenter 		ret = dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk),
23516edb685aSTomi Valkeinen 				    "clock not found\n");
23528e9a03bdSDan Carpenter 		goto err_cfe_put;
23538e9a03bdSDan Carpenter 	}
23546edb685aSTomi Valkeinen 
23556edb685aSTomi Valkeinen 	cfe->mdev.dev = &pdev->dev;
23566edb685aSTomi Valkeinen 	cfe->mdev.ops = &cfe_media_device_ops;
23576edb685aSTomi Valkeinen 	strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model));
23586edb685aSTomi Valkeinen 	strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial));
23596edb685aSTomi Valkeinen 	snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s",
23606edb685aSTomi Valkeinen 		 dev_name(&pdev->dev));
23616edb685aSTomi Valkeinen 
23626edb685aSTomi Valkeinen 	media_device_init(&cfe->mdev);
23636edb685aSTomi Valkeinen 
23646edb685aSTomi Valkeinen 	cfe->v4l2_dev.mdev = &cfe->mdev;
23656edb685aSTomi Valkeinen 
23666edb685aSTomi Valkeinen 	ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev);
23676edb685aSTomi Valkeinen 	if (ret) {
23686edb685aSTomi Valkeinen 		cfe_err(cfe, "Unable to register v4l2 device.\n");
23696edb685aSTomi Valkeinen 		goto err_cfe_put;
23706edb685aSTomi Valkeinen 	}
23716edb685aSTomi Valkeinen 
23726edb685aSTomi Valkeinen 	snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s",
23736edb685aSTomi Valkeinen 		 dev_name(&pdev->dev));
23746edb685aSTomi Valkeinen 	cfe->debugfs = debugfs_create_dir(debugfs_name, NULL);
23756edb685aSTomi Valkeinen 	debugfs_create_file("regs", 0440, cfe->debugfs, cfe,
23766edb685aSTomi Valkeinen 			    &mipi_cfg_regs_fops);
23776edb685aSTomi Valkeinen 
23786edb685aSTomi Valkeinen 	/* Enable the block power domain */
23796edb685aSTomi Valkeinen 	pm_runtime_enable(&pdev->dev);
23806edb685aSTomi Valkeinen 
23816edb685aSTomi Valkeinen 	ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
23826edb685aSTomi Valkeinen 	if (ret)
23836edb685aSTomi Valkeinen 		goto err_runtime_disable;
23846edb685aSTomi Valkeinen 
23856edb685aSTomi Valkeinen 	cfe->csi2.v4l2_dev = &cfe->v4l2_dev;
23866edb685aSTomi Valkeinen 	ret = csi2_init(&cfe->csi2, cfe->debugfs);
23876edb685aSTomi Valkeinen 	if (ret) {
23886edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to init csi2 (%d)\n", ret);
23896edb685aSTomi Valkeinen 		goto err_runtime_put;
23906edb685aSTomi Valkeinen 	}
23916edb685aSTomi Valkeinen 
23926edb685aSTomi Valkeinen 	cfe->fe.v4l2_dev = &cfe->v4l2_dev;
23936edb685aSTomi Valkeinen 	ret = pisp_fe_init(&cfe->fe, cfe->debugfs);
23946edb685aSTomi Valkeinen 	if (ret) {
23956edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to init pisp fe (%d)\n", ret);
23966edb685aSTomi Valkeinen 		goto err_csi2_uninit;
23976edb685aSTomi Valkeinen 	}
23986edb685aSTomi Valkeinen 
23996edb685aSTomi Valkeinen 	cfe->mdev.hw_revision = cfe->fe.hw_revision;
24006edb685aSTomi Valkeinen 	ret = media_device_register(&cfe->mdev);
24016edb685aSTomi Valkeinen 	if (ret < 0) {
24026edb685aSTomi Valkeinen 		cfe_err(cfe, "Unable to register media-controller device.\n");
24036edb685aSTomi Valkeinen 		goto err_pisp_fe_uninit;
24046edb685aSTomi Valkeinen 	}
24056edb685aSTomi Valkeinen 
24066edb685aSTomi Valkeinen 	ret = cfe_register_async_nf(cfe);
24076edb685aSTomi Valkeinen 	if (ret) {
24086edb685aSTomi Valkeinen 		cfe_err(cfe, "Failed to connect subdevs\n");
24096edb685aSTomi Valkeinen 		goto err_media_unregister;
24106edb685aSTomi Valkeinen 	}
24116edb685aSTomi Valkeinen 
24126edb685aSTomi Valkeinen 	pm_runtime_put(&cfe->pdev->dev);
24136edb685aSTomi Valkeinen 
24146edb685aSTomi Valkeinen 	return 0;
24156edb685aSTomi Valkeinen 
24166edb685aSTomi Valkeinen err_media_unregister:
24176edb685aSTomi Valkeinen 	media_device_unregister(&cfe->mdev);
24186edb685aSTomi Valkeinen err_pisp_fe_uninit:
24196edb685aSTomi Valkeinen 	pisp_fe_uninit(&cfe->fe);
24206edb685aSTomi Valkeinen err_csi2_uninit:
24216edb685aSTomi Valkeinen 	csi2_uninit(&cfe->csi2);
24226edb685aSTomi Valkeinen err_runtime_put:
24236edb685aSTomi Valkeinen 	pm_runtime_put(&cfe->pdev->dev);
24246edb685aSTomi Valkeinen err_runtime_disable:
24256edb685aSTomi Valkeinen 	pm_runtime_disable(&pdev->dev);
24266edb685aSTomi Valkeinen 	debugfs_remove(cfe->debugfs);
24276edb685aSTomi Valkeinen 	v4l2_device_unregister(&cfe->v4l2_dev);
24286edb685aSTomi Valkeinen err_cfe_put:
24296edb685aSTomi Valkeinen 	cfe_put(cfe);
24306edb685aSTomi Valkeinen 
24316edb685aSTomi Valkeinen 	return ret;
24326edb685aSTomi Valkeinen }
24336edb685aSTomi Valkeinen 
24346edb685aSTomi Valkeinen static void cfe_remove(struct platform_device *pdev)
24356edb685aSTomi Valkeinen {
24366edb685aSTomi Valkeinen 	struct cfe_device *cfe = platform_get_drvdata(pdev);
24376edb685aSTomi Valkeinen 
24386edb685aSTomi Valkeinen 	debugfs_remove(cfe->debugfs);
24396edb685aSTomi Valkeinen 
24406edb685aSTomi Valkeinen 	v4l2_async_nf_unregister(&cfe->notifier);
24416edb685aSTomi Valkeinen 	v4l2_async_nf_cleanup(&cfe->notifier);
24426edb685aSTomi Valkeinen 
24436edb685aSTomi Valkeinen 	media_device_unregister(&cfe->mdev);
24446edb685aSTomi Valkeinen 	cfe_unregister_nodes(cfe);
24456edb685aSTomi Valkeinen 
24466edb685aSTomi Valkeinen 	pisp_fe_uninit(&cfe->fe);
24476edb685aSTomi Valkeinen 	csi2_uninit(&cfe->csi2);
24486edb685aSTomi Valkeinen 
24496edb685aSTomi Valkeinen 	pm_runtime_disable(&pdev->dev);
24506edb685aSTomi Valkeinen 
24516edb685aSTomi Valkeinen 	v4l2_device_unregister(&cfe->v4l2_dev);
24526edb685aSTomi Valkeinen 
24536edb685aSTomi Valkeinen 	cfe_put(cfe);
24546edb685aSTomi Valkeinen }
24556edb685aSTomi Valkeinen 
24566edb685aSTomi Valkeinen static int cfe_runtime_suspend(struct device *dev)
24576edb685aSTomi Valkeinen {
24586edb685aSTomi Valkeinen 	struct platform_device *pdev = to_platform_device(dev);
24596edb685aSTomi Valkeinen 	struct cfe_device *cfe = platform_get_drvdata(pdev);
24606edb685aSTomi Valkeinen 
24616edb685aSTomi Valkeinen 	clk_disable_unprepare(cfe->clk);
24626edb685aSTomi Valkeinen 
24636edb685aSTomi Valkeinen 	return 0;
24646edb685aSTomi Valkeinen }
24656edb685aSTomi Valkeinen 
24666edb685aSTomi Valkeinen static int cfe_runtime_resume(struct device *dev)
24676edb685aSTomi Valkeinen {
24686edb685aSTomi Valkeinen 	struct platform_device *pdev = to_platform_device(dev);
24696edb685aSTomi Valkeinen 	struct cfe_device *cfe = platform_get_drvdata(pdev);
24706edb685aSTomi Valkeinen 	int ret;
24716edb685aSTomi Valkeinen 
24726edb685aSTomi Valkeinen 	ret = clk_prepare_enable(cfe->clk);
24736edb685aSTomi Valkeinen 	if (ret) {
24746edb685aSTomi Valkeinen 		dev_err(dev, "Unable to enable clock\n");
24756edb685aSTomi Valkeinen 		return ret;
24766edb685aSTomi Valkeinen 	}
24776edb685aSTomi Valkeinen 
24786edb685aSTomi Valkeinen 	return 0;
24796edb685aSTomi Valkeinen }
24806edb685aSTomi Valkeinen 
24816edb685aSTomi Valkeinen static const struct dev_pm_ops cfe_pm_ops = {
24826edb685aSTomi Valkeinen 	SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL)
24836edb685aSTomi Valkeinen 	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
24846edb685aSTomi Valkeinen 				     pm_runtime_force_resume)
24856edb685aSTomi Valkeinen };
24866edb685aSTomi Valkeinen 
24876edb685aSTomi Valkeinen static const struct of_device_id cfe_of_match[] = {
24886edb685aSTomi Valkeinen 	{ .compatible = "raspberrypi,rp1-cfe" },
24896edb685aSTomi Valkeinen 	{ /* sentinel */ },
24906edb685aSTomi Valkeinen };
24916edb685aSTomi Valkeinen MODULE_DEVICE_TABLE(of, cfe_of_match);
24926edb685aSTomi Valkeinen 
24936edb685aSTomi Valkeinen static struct platform_driver cfe_driver = {
24946edb685aSTomi Valkeinen 	.probe		= cfe_probe,
24956edb685aSTomi Valkeinen 	.remove		= cfe_remove,
24966edb685aSTomi Valkeinen 	.driver = {
24976edb685aSTomi Valkeinen 		.name	= CFE_MODULE_NAME,
24986edb685aSTomi Valkeinen 		.of_match_table = cfe_of_match,
24996edb685aSTomi Valkeinen 		.pm = &cfe_pm_ops,
25006edb685aSTomi Valkeinen 	},
25016edb685aSTomi Valkeinen };
25026edb685aSTomi Valkeinen 
25036edb685aSTomi Valkeinen module_platform_driver(cfe_driver);
25046edb685aSTomi Valkeinen 
25056edb685aSTomi Valkeinen MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
25066edb685aSTomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
25076edb685aSTomi Valkeinen MODULE_DESCRIPTION("Raspberry Pi RP1 Camera Front End driver");
25086edb685aSTomi Valkeinen MODULE_LICENSE("GPL");
25096edb685aSTomi Valkeinen MODULE_VERSION(CFE_VERSION);
2510