162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017,2020 Intel Corporation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Based partially on Intel IPU4 driver written by
662306a36Sopenharmony_ci *  Sakari Ailus <sakari.ailus@linux.intel.com>
762306a36Sopenharmony_ci *  Samu Onkalo <samu.onkalo@intel.com>
862306a36Sopenharmony_ci *  Jouni Högander <jouni.hogander@intel.com>
962306a36Sopenharmony_ci *  Jouni Ukkonen <jouni.ukkonen@intel.com>
1062306a36Sopenharmony_ci *  Antti Laakso <antti.laakso@intel.com>
1162306a36Sopenharmony_ci * et al.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/bitops.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/iopoll.h>
1862306a36Sopenharmony_ci#include <linux/mm.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/pci.h>
2162306a36Sopenharmony_ci#include <linux/pfn.h>
2262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2362306a36Sopenharmony_ci#include <linux/property.h>
2462306a36Sopenharmony_ci#include <linux/vmalloc.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <media/ipu-bridge.h>
2762306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
2862306a36Sopenharmony_ci#include <media/v4l2-device.h>
2962306a36Sopenharmony_ci#include <media/v4l2-event.h>
3062306a36Sopenharmony_ci#include <media/v4l2-fwnode.h>
3162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
3262306a36Sopenharmony_ci#include <media/videobuf2-dma-sg.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "ipu3-cio2.h"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct ipu3_cio2_fmt {
3762306a36Sopenharmony_ci	u32 mbus_code;
3862306a36Sopenharmony_ci	u32 fourcc;
3962306a36Sopenharmony_ci	u8 mipicode;
4062306a36Sopenharmony_ci	u8 bpp;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * These are raw formats used in Intel's third generation of
4562306a36Sopenharmony_ci * Image Processing Unit known as IPU3.
4662306a36Sopenharmony_ci * 10bit raw bayer packed, 32 bytes for every 25 pixels,
4762306a36Sopenharmony_ci * last LSB 6 bits unused.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic const struct ipu3_cio2_fmt formats[] = {
5062306a36Sopenharmony_ci	{	/* put default entry at beginning */
5162306a36Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
5262306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SGRBG10,
5362306a36Sopenharmony_ci		.mipicode	= 0x2b,
5462306a36Sopenharmony_ci		.bpp		= 10,
5562306a36Sopenharmony_ci	}, {
5662306a36Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
5762306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SGBRG10,
5862306a36Sopenharmony_ci		.mipicode	= 0x2b,
5962306a36Sopenharmony_ci		.bpp		= 10,
6062306a36Sopenharmony_ci	}, {
6162306a36Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
6262306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SBGGR10,
6362306a36Sopenharmony_ci		.mipicode	= 0x2b,
6462306a36Sopenharmony_ci		.bpp		= 10,
6562306a36Sopenharmony_ci	}, {
6662306a36Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
6762306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SRGGB10,
6862306a36Sopenharmony_ci		.mipicode	= 0x2b,
6962306a36Sopenharmony_ci		.bpp		= 10,
7062306a36Sopenharmony_ci	}, {
7162306a36Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,
7262306a36Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_Y10,
7362306a36Sopenharmony_ci		.mipicode	= 0x2b,
7462306a36Sopenharmony_ci		.bpp		= 10,
7562306a36Sopenharmony_ci	},
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * cio2_find_format - lookup color format by fourcc or/and media bus code
8062306a36Sopenharmony_ci * @pixelformat: fourcc to match, ignored if null
8162306a36Sopenharmony_ci * @mbus_code: media bus code to match, ignored if null
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic const struct ipu3_cio2_fmt *cio2_find_format(const u32 *pixelformat,
8462306a36Sopenharmony_ci						    const u32 *mbus_code)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	unsigned int i;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++) {
8962306a36Sopenharmony_ci		if (pixelformat && *pixelformat != formats[i].fourcc)
9062306a36Sopenharmony_ci			continue;
9162306a36Sopenharmony_ci		if (mbus_code && *mbus_code != formats[i].mbus_code)
9262306a36Sopenharmony_ci			continue;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		return &formats[i];
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return NULL;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic inline u32 cio2_bytesperline(const unsigned int width)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	/*
10362306a36Sopenharmony_ci	 * 64 bytes for every 50 pixels, the line length
10462306a36Sopenharmony_ci	 * in bytes is multiple of 64 (line end alignment).
10562306a36Sopenharmony_ci	 */
10662306a36Sopenharmony_ci	return DIV_ROUND_UP(width, 50) * 64;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**************** FBPT operations ****************/
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void cio2_fbpt_exit_dummy(struct cio2_device *cio2)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (cio2->dummy_lop) {
11662306a36Sopenharmony_ci		dma_free_coherent(dev, PAGE_SIZE, cio2->dummy_lop,
11762306a36Sopenharmony_ci				  cio2->dummy_lop_bus_addr);
11862306a36Sopenharmony_ci		cio2->dummy_lop = NULL;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	if (cio2->dummy_page) {
12162306a36Sopenharmony_ci		dma_free_coherent(dev, PAGE_SIZE, cio2->dummy_page,
12262306a36Sopenharmony_ci				  cio2->dummy_page_bus_addr);
12362306a36Sopenharmony_ci		cio2->dummy_page = NULL;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int cio2_fbpt_init_dummy(struct cio2_device *cio2)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
13062306a36Sopenharmony_ci	unsigned int i;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	cio2->dummy_page = dma_alloc_coherent(dev, PAGE_SIZE,
13362306a36Sopenharmony_ci					      &cio2->dummy_page_bus_addr,
13462306a36Sopenharmony_ci					      GFP_KERNEL);
13562306a36Sopenharmony_ci	cio2->dummy_lop = dma_alloc_coherent(dev, PAGE_SIZE,
13662306a36Sopenharmony_ci					     &cio2->dummy_lop_bus_addr,
13762306a36Sopenharmony_ci					     GFP_KERNEL);
13862306a36Sopenharmony_ci	if (!cio2->dummy_page || !cio2->dummy_lop) {
13962306a36Sopenharmony_ci		cio2_fbpt_exit_dummy(cio2);
14062306a36Sopenharmony_ci		return -ENOMEM;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * List of Pointers(LOP) contains 1024x32b pointers to 4KB page each
14462306a36Sopenharmony_ci	 * Initialize each entry to dummy_page bus base address.
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	for (i = 0; i < CIO2_LOP_ENTRIES; i++)
14762306a36Sopenharmony_ci		cio2->dummy_lop[i] = PFN_DOWN(cio2->dummy_page_bus_addr);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return 0;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void cio2_fbpt_entry_enable(struct cio2_device *cio2,
15362306a36Sopenharmony_ci				   struct cio2_fbpt_entry entry[CIO2_MAX_LOPS])
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	/*
15662306a36Sopenharmony_ci	 * The CPU first initializes some fields in fbpt, then sets
15762306a36Sopenharmony_ci	 * the VALID bit, this barrier is to ensure that the DMA(device)
15862306a36Sopenharmony_ci	 * does not see the VALID bit enabled before other fields are
15962306a36Sopenharmony_ci	 * initialized; otherwise it could lead to havoc.
16062306a36Sopenharmony_ci	 */
16162306a36Sopenharmony_ci	dma_wmb();
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/*
16462306a36Sopenharmony_ci	 * Request interrupts for start and completion
16562306a36Sopenharmony_ci	 * Valid bit is applicable only to 1st entry
16662306a36Sopenharmony_ci	 */
16762306a36Sopenharmony_ci	entry[0].first_entry.ctrl = CIO2_FBPT_CTRL_VALID |
16862306a36Sopenharmony_ci		CIO2_FBPT_CTRL_IOC | CIO2_FBPT_CTRL_IOS;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* Initialize fpbt entries to point to dummy frame */
17262306a36Sopenharmony_cistatic void cio2_fbpt_entry_init_dummy(struct cio2_device *cio2,
17362306a36Sopenharmony_ci				       struct cio2_fbpt_entry
17462306a36Sopenharmony_ci				       entry[CIO2_MAX_LOPS])
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	unsigned int i;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	entry[0].first_entry.first_page_offset = 0;
17962306a36Sopenharmony_ci	entry[1].second_entry.num_of_pages = CIO2_LOP_ENTRIES * CIO2_MAX_LOPS;
18062306a36Sopenharmony_ci	entry[1].second_entry.last_page_available_bytes = PAGE_SIZE - 1;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	for (i = 0; i < CIO2_MAX_LOPS; i++)
18362306a36Sopenharmony_ci		entry[i].lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	cio2_fbpt_entry_enable(cio2, entry);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* Initialize fpbt entries to point to a given buffer */
18962306a36Sopenharmony_cistatic void cio2_fbpt_entry_init_buf(struct cio2_device *cio2,
19062306a36Sopenharmony_ci				     struct cio2_buffer *b,
19162306a36Sopenharmony_ci				     struct cio2_fbpt_entry
19262306a36Sopenharmony_ci				     entry[CIO2_MAX_LOPS])
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct vb2_buffer *vb = &b->vbb.vb2_buf;
19562306a36Sopenharmony_ci	unsigned int length = vb->planes[0].length;
19662306a36Sopenharmony_ci	int remaining, i;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	entry[0].first_entry.first_page_offset = b->offset;
19962306a36Sopenharmony_ci	remaining = length + entry[0].first_entry.first_page_offset;
20062306a36Sopenharmony_ci	entry[1].second_entry.num_of_pages = PFN_UP(remaining);
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * last_page_available_bytes has the offset of the last byte in the
20362306a36Sopenharmony_ci	 * last page which is still accessible by DMA. DMA cannot access
20462306a36Sopenharmony_ci	 * beyond this point. Valid range for this is from 0 to 4095.
20562306a36Sopenharmony_ci	 * 0 indicates 1st byte in the page is DMA accessible.
20662306a36Sopenharmony_ci	 * 4095 (PAGE_SIZE - 1) means every single byte in the last page
20762306a36Sopenharmony_ci	 * is available for DMA transfer.
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	remaining = offset_in_page(remaining) ?: PAGE_SIZE;
21062306a36Sopenharmony_ci	entry[1].second_entry.last_page_available_bytes = remaining - 1;
21162306a36Sopenharmony_ci	/* Fill FBPT */
21262306a36Sopenharmony_ci	remaining = length;
21362306a36Sopenharmony_ci	i = 0;
21462306a36Sopenharmony_ci	while (remaining > 0) {
21562306a36Sopenharmony_ci		entry->lop_page_addr = PFN_DOWN(b->lop_bus_addr[i]);
21662306a36Sopenharmony_ci		remaining -= CIO2_LOP_ENTRIES * PAGE_SIZE;
21762306a36Sopenharmony_ci		entry++;
21862306a36Sopenharmony_ci		i++;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/*
22262306a36Sopenharmony_ci	 * The first not meaningful FBPT entry should point to a valid LOP
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	entry->lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	cio2_fbpt_entry_enable(cio2, entry);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	q->fbpt = dma_alloc_coherent(dev, CIO2_FBPT_SIZE, &q->fbpt_bus_addr,
23462306a36Sopenharmony_ci				     GFP_KERNEL);
23562306a36Sopenharmony_ci	if (!q->fbpt)
23662306a36Sopenharmony_ci		return -ENOMEM;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/**************** CSI2 hardware setup ****************/
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci * The CSI2 receiver has several parameters affecting
25062306a36Sopenharmony_ci * the receiver timings. These depend on the MIPI bus frequency
25162306a36Sopenharmony_ci * F in Hz (sensor transmitter rate) as follows:
25262306a36Sopenharmony_ci *     register value = (A/1e9 + B * UI) / COUNT_ACC
25362306a36Sopenharmony_ci * where
25462306a36Sopenharmony_ci *      UI = 1 / (2 * F) in seconds
25562306a36Sopenharmony_ci *      COUNT_ACC = counter accuracy in seconds
25662306a36Sopenharmony_ci *      For IPU3 COUNT_ACC = 0.0625
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * A and B are coefficients from the table below,
25962306a36Sopenharmony_ci * depending whether the register minimum or maximum value is
26062306a36Sopenharmony_ci * calculated.
26162306a36Sopenharmony_ci *                                     Minimum     Maximum
26262306a36Sopenharmony_ci * Clock lane                          A     B     A     B
26362306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_clane     0     0    38     0
26462306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_clane    95    -8   300   -16
26562306a36Sopenharmony_ci * Data lanes
26662306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane0    0     0    35     4
26762306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane0   85    -2   145    -6
26862306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane1    0     0    35     4
26962306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane1   85    -2   145    -6
27062306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane2    0     0    35     4
27162306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane2   85    -2   145    -6
27262306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane3    0     0    35     4
27362306a36Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane3   85    -2   145    -6
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci * We use the minimum values of both A and B.
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/*
27962306a36Sopenharmony_ci * shift for keeping value range suitable for 32-bit integer arithmetic
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_ci#define LIMIT_SHIFT	8
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic s32 cio2_rx_timing(s32 a, s32 b, s64 freq, int def)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	const u32 accinv = 16; /* invert of counter resolution */
28662306a36Sopenharmony_ci	const u32 uiinv = 500000000; /* 1e9 / 2 */
28762306a36Sopenharmony_ci	s32 r;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	freq >>= LIMIT_SHIFT;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (WARN_ON(freq <= 0 || freq > S32_MAX))
29262306a36Sopenharmony_ci		return def;
29362306a36Sopenharmony_ci	/*
29462306a36Sopenharmony_ci	 * b could be 0, -2 or -8, so |accinv * b| is always
29562306a36Sopenharmony_ci	 * less than (1 << ds) and thus |r| < 500000000.
29662306a36Sopenharmony_ci	 */
29762306a36Sopenharmony_ci	r = accinv * b * (uiinv >> LIMIT_SHIFT);
29862306a36Sopenharmony_ci	r = r / (s32)freq;
29962306a36Sopenharmony_ci	/* max value of a is 95 */
30062306a36Sopenharmony_ci	r += accinv * a;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return r;
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/* Calculate the delay value for termination enable of clock lane HS Rx */
30662306a36Sopenharmony_cistatic int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q,
30762306a36Sopenharmony_ci				 struct cio2_csi2_timing *timing,
30862306a36Sopenharmony_ci				 unsigned int bpp, unsigned int lanes)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
31162306a36Sopenharmony_ci	s64 freq;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (!q->sensor)
31462306a36Sopenharmony_ci		return -ENODEV;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	freq = v4l2_get_link_freq(q->sensor->ctrl_handler, bpp, lanes * 2);
31762306a36Sopenharmony_ci	if (freq < 0) {
31862306a36Sopenharmony_ci		dev_err(dev, "error %lld, invalid link_freq\n", freq);
31962306a36Sopenharmony_ci		return freq;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	timing->clk_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_A,
32362306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_B,
32462306a36Sopenharmony_ci					    freq,
32562306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT);
32662306a36Sopenharmony_ci	timing->clk_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_A,
32762306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_B,
32862306a36Sopenharmony_ci					    freq,
32962306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT);
33062306a36Sopenharmony_ci	timing->dat_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_A,
33162306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_B,
33262306a36Sopenharmony_ci					    freq,
33362306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT);
33462306a36Sopenharmony_ci	timing->dat_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_A,
33562306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_B,
33662306a36Sopenharmony_ci					    freq,
33762306a36Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	dev_dbg(dev, "freq ct value is %d\n", timing->clk_termen);
34062306a36Sopenharmony_ci	dev_dbg(dev, "freq cs value is %d\n", timing->clk_settle);
34162306a36Sopenharmony_ci	dev_dbg(dev, "freq dt value is %d\n", timing->dat_termen);
34262306a36Sopenharmony_ci	dev_dbg(dev, "freq ds value is %d\n", timing->dat_settle);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci};
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	static const int NUM_VCS = 4;
35062306a36Sopenharmony_ci	static const int SID;	/* Stream id */
35162306a36Sopenharmony_ci	static const int ENTRY;
35262306a36Sopenharmony_ci	static const int FBPT_WIDTH = DIV_ROUND_UP(CIO2_MAX_LOPS,
35362306a36Sopenharmony_ci					CIO2_FBPT_SUBENTRY_UNIT);
35462306a36Sopenharmony_ci	const u32 num_buffers1 = CIO2_MAX_BUFFERS - 1;
35562306a36Sopenharmony_ci	const struct ipu3_cio2_fmt *fmt;
35662306a36Sopenharmony_ci	void __iomem *const base = cio2->base;
35762306a36Sopenharmony_ci	u8 lanes, csi2bus = q->csi2.port;
35862306a36Sopenharmony_ci	u8 sensor_vc = SENSOR_VIR_CH_DFLT;
35962306a36Sopenharmony_ci	struct cio2_csi2_timing timing = { 0 };
36062306a36Sopenharmony_ci	int i, r;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	fmt = cio2_find_format(NULL, &q->subdev_fmt.code);
36362306a36Sopenharmony_ci	if (!fmt)
36462306a36Sopenharmony_ci		return -EINVAL;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	lanes = q->csi2.lanes;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	r = cio2_csi2_calc_timing(cio2, q, &timing, fmt->bpp, lanes);
36962306a36Sopenharmony_ci	if (r)
37062306a36Sopenharmony_ci		return r;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	writel(timing.clk_termen, q->csi_rx_base +
37362306a36Sopenharmony_ci		CIO2_REG_CSIRX_DLY_CNT_TERMEN(CIO2_CSIRX_DLY_CNT_CLANE_IDX));
37462306a36Sopenharmony_ci	writel(timing.clk_settle, q->csi_rx_base +
37562306a36Sopenharmony_ci		CIO2_REG_CSIRX_DLY_CNT_SETTLE(CIO2_CSIRX_DLY_CNT_CLANE_IDX));
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	for (i = 0; i < lanes; i++) {
37862306a36Sopenharmony_ci		writel(timing.dat_termen, q->csi_rx_base +
37962306a36Sopenharmony_ci			CIO2_REG_CSIRX_DLY_CNT_TERMEN(i));
38062306a36Sopenharmony_ci		writel(timing.dat_settle, q->csi_rx_base +
38162306a36Sopenharmony_ci			CIO2_REG_CSIRX_DLY_CNT_SETTLE(i));
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	writel(CIO2_PBM_WMCTRL1_MIN_2CK |
38562306a36Sopenharmony_ci	       CIO2_PBM_WMCTRL1_MID1_2CK |
38662306a36Sopenharmony_ci	       CIO2_PBM_WMCTRL1_MID2_2CK, base + CIO2_REG_PBM_WMCTRL1);
38762306a36Sopenharmony_ci	writel(CIO2_PBM_WMCTRL2_HWM_2CK << CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT |
38862306a36Sopenharmony_ci	       CIO2_PBM_WMCTRL2_LWM_2CK << CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT |
38962306a36Sopenharmony_ci	       CIO2_PBM_WMCTRL2_OBFFWM_2CK <<
39062306a36Sopenharmony_ci	       CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT |
39162306a36Sopenharmony_ci	       CIO2_PBM_WMCTRL2_TRANSDYN << CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT |
39262306a36Sopenharmony_ci	       CIO2_PBM_WMCTRL2_OBFF_MEM_EN, base + CIO2_REG_PBM_WMCTRL2);
39362306a36Sopenharmony_ci	writel(CIO2_PBM_ARB_CTRL_LANES_DIV <<
39462306a36Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT |
39562306a36Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_LE_EN |
39662306a36Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN <<
39762306a36Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT |
39862306a36Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP <<
39962306a36Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT,
40062306a36Sopenharmony_ci	       base + CIO2_REG_PBM_ARB_CTRL);
40162306a36Sopenharmony_ci	writel(CIO2_CSIRX_STATUS_DLANE_HS_MASK,
40262306a36Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_HS);
40362306a36Sopenharmony_ci	writel(CIO2_CSIRX_STATUS_DLANE_LP_MASK,
40462306a36Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_LP);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	writel(CIO2_FB_HPLL_FREQ, base + CIO2_REG_FB_HPLL_FREQ);
40762306a36Sopenharmony_ci	writel(CIO2_ISCLK_RATIO, base + CIO2_REG_ISCLK_RATIO);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Configure MIPI backend */
41062306a36Sopenharmony_ci	for (i = 0; i < NUM_VCS; i++)
41162306a36Sopenharmony_ci		writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_SP_LUT_ENTRY(i));
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* There are 16 short packet LUT entry */
41462306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
41562306a36Sopenharmony_ci		writel(CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD,
41662306a36Sopenharmony_ci		       q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(i));
41762306a36Sopenharmony_ci	writel(CIO2_MIPIBE_GLOBAL_LUT_DISREGARD,
41862306a36Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_MIPIBE_GLOBAL_LUT_DISREGARD);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	writel(CIO2_INT_EN_EXT_IE_MASK, base + CIO2_REG_INT_EN_EXT_IE);
42162306a36Sopenharmony_ci	writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK);
42262306a36Sopenharmony_ci	writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE);
42362306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_EDGE);
42462306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_LEVEL_NOT_PULSE);
42562306a36Sopenharmony_ci	writel(CIO2_INT_EN_EXT_OE_MASK, base + CIO2_REG_INT_EN_EXT_OE);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	writel(CIO2_REG_INT_EN_IRQ | CIO2_INT_IOC(CIO2_DMA_CHAN) |
42862306a36Sopenharmony_ci	       CIO2_REG_INT_EN_IOS(CIO2_DMA_CHAN),
42962306a36Sopenharmony_ci	       base + CIO2_REG_INT_EN);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	writel((CIO2_PXM_PXF_FMT_CFG_BPP_10 | CIO2_PXM_PXF_FMT_CFG_PCK_64B)
43262306a36Sopenharmony_ci	       << CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT,
43362306a36Sopenharmony_ci	       base + CIO2_REG_PXM_PXF_FMT_CFG0(csi2bus));
43462306a36Sopenharmony_ci	writel(SID << CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT |
43562306a36Sopenharmony_ci	       sensor_vc << CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT |
43662306a36Sopenharmony_ci	       fmt->mipicode << CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT,
43762306a36Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(ENTRY));
43862306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_COMP_FORMAT(sensor_vc));
43962306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_FORCE_RAW8);
44062306a36Sopenharmony_ci	writel(0, base + CIO2_REG_PXM_SID2BID0(csi2bus));
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	writel(lanes, q->csi_rx_base + CIO2_REG_CSIRX_NOF_ENABLED_LANES);
44362306a36Sopenharmony_ci	writel(CIO2_CGC_PRIM_TGE |
44462306a36Sopenharmony_ci	       CIO2_CGC_SIDE_TGE |
44562306a36Sopenharmony_ci	       CIO2_CGC_XOSC_TGE |
44662306a36Sopenharmony_ci	       CIO2_CGC_D3I3_TGE |
44762306a36Sopenharmony_ci	       CIO2_CGC_CSI2_INTERFRAME_TGE |
44862306a36Sopenharmony_ci	       CIO2_CGC_CSI2_PORT_DCGE |
44962306a36Sopenharmony_ci	       CIO2_CGC_SIDE_DCGE |
45062306a36Sopenharmony_ci	       CIO2_CGC_PRIM_DCGE |
45162306a36Sopenharmony_ci	       CIO2_CGC_ROSC_DCGE |
45262306a36Sopenharmony_ci	       CIO2_CGC_XOSC_DCGE |
45362306a36Sopenharmony_ci	       CIO2_CGC_CLKGATE_HOLDOFF << CIO2_CGC_CLKGATE_HOLDOFF_SHIFT |
45462306a36Sopenharmony_ci	       CIO2_CGC_CSI_CLKGATE_HOLDOFF
45562306a36Sopenharmony_ci	       << CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT, base + CIO2_REG_CGC);
45662306a36Sopenharmony_ci	writel(CIO2_LTRCTRL_LTRDYNEN, base + CIO2_REG_LTRCTRL);
45762306a36Sopenharmony_ci	writel(CIO2_LTRVAL0_VAL << CIO2_LTRVAL02_VAL_SHIFT |
45862306a36Sopenharmony_ci	       CIO2_LTRVAL0_SCALE << CIO2_LTRVAL02_SCALE_SHIFT |
45962306a36Sopenharmony_ci	       CIO2_LTRVAL1_VAL << CIO2_LTRVAL13_VAL_SHIFT |
46062306a36Sopenharmony_ci	       CIO2_LTRVAL1_SCALE << CIO2_LTRVAL13_SCALE_SHIFT,
46162306a36Sopenharmony_ci	       base + CIO2_REG_LTRVAL01);
46262306a36Sopenharmony_ci	writel(CIO2_LTRVAL2_VAL << CIO2_LTRVAL02_VAL_SHIFT |
46362306a36Sopenharmony_ci	       CIO2_LTRVAL2_SCALE << CIO2_LTRVAL02_SCALE_SHIFT |
46462306a36Sopenharmony_ci	       CIO2_LTRVAL3_VAL << CIO2_LTRVAL13_VAL_SHIFT |
46562306a36Sopenharmony_ci	       CIO2_LTRVAL3_SCALE << CIO2_LTRVAL13_SCALE_SHIFT,
46662306a36Sopenharmony_ci	       base + CIO2_REG_LTRVAL23);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	for (i = 0; i < CIO2_NUM_DMA_CHAN; i++) {
46962306a36Sopenharmony_ci		writel(0, base + CIO2_REG_CDMABA(i));
47062306a36Sopenharmony_ci		writel(0, base + CIO2_REG_CDMAC0(i));
47162306a36Sopenharmony_ci		writel(0, base + CIO2_REG_CDMAC1(i));
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* Enable DMA */
47562306a36Sopenharmony_ci	writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(CIO2_DMA_CHAN));
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	writel(num_buffers1 << CIO2_CDMAC0_FBPT_LEN_SHIFT |
47862306a36Sopenharmony_ci	       FBPT_WIDTH << CIO2_CDMAC0_FBPT_WIDTH_SHIFT |
47962306a36Sopenharmony_ci	       CIO2_CDMAC0_DMA_INTR_ON_FE |
48062306a36Sopenharmony_ci	       CIO2_CDMAC0_FBPT_UPDATE_FIFO_FULL |
48162306a36Sopenharmony_ci	       CIO2_CDMAC0_DMA_EN |
48262306a36Sopenharmony_ci	       CIO2_CDMAC0_DMA_INTR_ON_FS |
48362306a36Sopenharmony_ci	       CIO2_CDMAC0_DMA_HALTED, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN));
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	writel(1 << CIO2_CDMAC1_LINENUMUPDATE_SHIFT,
48662306a36Sopenharmony_ci	       base + CIO2_REG_CDMAC1(CIO2_DMA_CHAN));
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	writel(0, base + CIO2_REG_PBM_FOPN_ABORT);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	writel(CIO2_PXM_FRF_CFG_CRC_TH << CIO2_PXM_FRF_CFG_CRC_TH_SHIFT |
49162306a36Sopenharmony_ci	       CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NR |
49262306a36Sopenharmony_ci	       CIO2_PXM_FRF_CFG_MSK_ECC_RE |
49362306a36Sopenharmony_ci	       CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NE,
49462306a36Sopenharmony_ci	       base + CIO2_REG_PXM_FRF_CFG(q->csi2.port));
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* Clear interrupts */
49762306a36Sopenharmony_ci	writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_CLEAR);
49862306a36Sopenharmony_ci	writel(~0, base + CIO2_REG_INT_STS_EXT_OE);
49962306a36Sopenharmony_ci	writel(~0, base + CIO2_REG_INT_STS_EXT_IE);
50062306a36Sopenharmony_ci	writel(~0, base + CIO2_REG_INT_STS);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* Enable devices, starting from the last device in the pipe */
50362306a36Sopenharmony_ci	writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE);
50462306a36Sopenharmony_ci	writel(1, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
51262306a36Sopenharmony_ci	void __iomem *const base = cio2->base;
51362306a36Sopenharmony_ci	unsigned int i;
51462306a36Sopenharmony_ci	u32 value;
51562306a36Sopenharmony_ci	int ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Disable CSI receiver and MIPI backend devices */
51862306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK);
51962306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE);
52062306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE);
52162306a36Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Halt DMA */
52462306a36Sopenharmony_ci	writel(0, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN));
52562306a36Sopenharmony_ci	ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN),
52662306a36Sopenharmony_ci				 value, value & CIO2_CDMAC0_DMA_HALTED,
52762306a36Sopenharmony_ci				 4000, 2000000);
52862306a36Sopenharmony_ci	if (ret)
52962306a36Sopenharmony_ci		dev_err(dev, "DMA %i can not be halted\n", CIO2_DMA_CHAN);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	for (i = 0; i < CIO2_NUM_PORTS; i++) {
53262306a36Sopenharmony_ci		writel(readl(base + CIO2_REG_PXM_FRF_CFG(i)) |
53362306a36Sopenharmony_ci		       CIO2_PXM_FRF_CFG_ABORT, base + CIO2_REG_PXM_FRF_CFG(i));
53462306a36Sopenharmony_ci		writel(readl(base + CIO2_REG_PBM_FOPN_ABORT) |
53562306a36Sopenharmony_ci		       CIO2_PBM_FOPN_ABORT(i), base + CIO2_REG_PBM_FOPN_ABORT);
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
54262306a36Sopenharmony_ci	struct cio2_queue *q = cio2->cur_queue;
54362306a36Sopenharmony_ci	struct cio2_fbpt_entry *entry;
54462306a36Sopenharmony_ci	u64 ns = ktime_get_ns();
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (dma_chan >= CIO2_QUEUES) {
54762306a36Sopenharmony_ci		dev_err(dev, "bad DMA channel %i\n", dma_chan);
54862306a36Sopenharmony_ci		return;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS];
55262306a36Sopenharmony_ci	if (entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID) {
55362306a36Sopenharmony_ci		dev_warn(dev, "no ready buffers found on DMA channel %u\n",
55462306a36Sopenharmony_ci			 dma_chan);
55562306a36Sopenharmony_ci		return;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* Find out which buffer(s) are ready */
55962306a36Sopenharmony_ci	do {
56062306a36Sopenharmony_ci		struct cio2_buffer *b;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		b = q->bufs[q->bufs_first];
56362306a36Sopenharmony_ci		if (b) {
56462306a36Sopenharmony_ci			unsigned int received = entry[1].second_entry.num_of_bytes;
56562306a36Sopenharmony_ci			unsigned long payload =
56662306a36Sopenharmony_ci				vb2_get_plane_payload(&b->vbb.vb2_buf, 0);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			q->bufs[q->bufs_first] = NULL;
56962306a36Sopenharmony_ci			atomic_dec(&q->bufs_queued);
57062306a36Sopenharmony_ci			dev_dbg(dev, "buffer %i done\n", b->vbb.vb2_buf.index);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci			b->vbb.vb2_buf.timestamp = ns;
57362306a36Sopenharmony_ci			b->vbb.field = V4L2_FIELD_NONE;
57462306a36Sopenharmony_ci			b->vbb.sequence = atomic_read(&q->frame_sequence);
57562306a36Sopenharmony_ci			if (payload != received)
57662306a36Sopenharmony_ci				dev_warn(dev,
57762306a36Sopenharmony_ci					 "payload length is %lu, received %u\n",
57862306a36Sopenharmony_ci					 payload, received);
57962306a36Sopenharmony_ci			vb2_buffer_done(&b->vbb.vb2_buf, VB2_BUF_STATE_DONE);
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci		atomic_inc(&q->frame_sequence);
58262306a36Sopenharmony_ci		cio2_fbpt_entry_init_dummy(cio2, entry);
58362306a36Sopenharmony_ci		q->bufs_first = (q->bufs_first + 1) % CIO2_MAX_BUFFERS;
58462306a36Sopenharmony_ci		entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS];
58562306a36Sopenharmony_ci	} while (!(entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID));
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic void cio2_queue_event_sof(struct cio2_device *cio2, struct cio2_queue *q)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	/*
59162306a36Sopenharmony_ci	 * For the user space camera control algorithms it is essential
59262306a36Sopenharmony_ci	 * to know when the reception of a frame has begun. That's often
59362306a36Sopenharmony_ci	 * the best timing information to get from the hardware.
59462306a36Sopenharmony_ci	 */
59562306a36Sopenharmony_ci	struct v4l2_event event = {
59662306a36Sopenharmony_ci		.type = V4L2_EVENT_FRAME_SYNC,
59762306a36Sopenharmony_ci		.u.frame_sync.frame_sequence = atomic_read(&q->frame_sequence),
59862306a36Sopenharmony_ci	};
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	v4l2_event_queue(q->subdev.devnode, &event);
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic const char *const cio2_irq_errs[] = {
60462306a36Sopenharmony_ci	"single packet header error corrected",
60562306a36Sopenharmony_ci	"multiple packet header errors detected",
60662306a36Sopenharmony_ci	"payload checksum (CRC) error",
60762306a36Sopenharmony_ci	"fifo overflow",
60862306a36Sopenharmony_ci	"reserved short packet data type detected",
60962306a36Sopenharmony_ci	"reserved long packet data type detected",
61062306a36Sopenharmony_ci	"incomplete long packet detected",
61162306a36Sopenharmony_ci	"frame sync error",
61262306a36Sopenharmony_ci	"line sync error",
61362306a36Sopenharmony_ci	"DPHY start of transmission error",
61462306a36Sopenharmony_ci	"DPHY synchronization error",
61562306a36Sopenharmony_ci	"escape mode error",
61662306a36Sopenharmony_ci	"escape mode trigger event",
61762306a36Sopenharmony_ci	"escape mode ultra-low power state for data lane(s)",
61862306a36Sopenharmony_ci	"escape mode ultra-low power state exit for clock lane",
61962306a36Sopenharmony_ci	"inter-frame short packet discarded",
62062306a36Sopenharmony_ci	"inter-frame long packet discarded",
62162306a36Sopenharmony_ci	"non-matching Long Packet stalled",
62262306a36Sopenharmony_ci};
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic void cio2_irq_log_irq_errs(struct device *dev, u8 port, u32 status)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	unsigned long csi2_status = status;
62762306a36Sopenharmony_ci	unsigned int i;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	for_each_set_bit(i, &csi2_status, ARRAY_SIZE(cio2_irq_errs))
63062306a36Sopenharmony_ci		dev_err(dev, "CSI-2 receiver port %i: %s\n",
63162306a36Sopenharmony_ci			port, cio2_irq_errs[i]);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (fls_long(csi2_status) >= ARRAY_SIZE(cio2_irq_errs))
63462306a36Sopenharmony_ci		dev_warn(dev, "unknown CSI2 error 0x%lx on port %i\n",
63562306a36Sopenharmony_ci			 csi2_status, port);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic const char *const cio2_port_errs[] = {
63962306a36Sopenharmony_ci	"ECC recoverable",
64062306a36Sopenharmony_ci	"DPHY not recoverable",
64162306a36Sopenharmony_ci	"ECC not recoverable",
64262306a36Sopenharmony_ci	"CRC error",
64362306a36Sopenharmony_ci	"INTERFRAMEDATA",
64462306a36Sopenharmony_ci	"PKT2SHORT",
64562306a36Sopenharmony_ci	"PKT2LONG",
64662306a36Sopenharmony_ci};
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void cio2_irq_log_port_errs(struct device *dev, u8 port, u32 status)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	unsigned long port_status = status;
65162306a36Sopenharmony_ci	unsigned int i;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for_each_set_bit(i, &port_status, ARRAY_SIZE(cio2_port_errs))
65462306a36Sopenharmony_ci		dev_err(dev, "port %i error %s\n", port, cio2_port_errs[i]);
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
66062306a36Sopenharmony_ci	void __iomem *const base = cio2->base;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (int_status & CIO2_INT_IOOE) {
66362306a36Sopenharmony_ci		/*
66462306a36Sopenharmony_ci		 * Interrupt on Output Error:
66562306a36Sopenharmony_ci		 * 1) SRAM is full and FS received, or
66662306a36Sopenharmony_ci		 * 2) An invalid bit detected by DMA.
66762306a36Sopenharmony_ci		 */
66862306a36Sopenharmony_ci		u32 oe_status, oe_clear;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci		oe_clear = readl(base + CIO2_REG_INT_STS_EXT_OE);
67162306a36Sopenharmony_ci		oe_status = oe_clear;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		if (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) {
67462306a36Sopenharmony_ci			dev_err(dev, "DMA output error: 0x%x\n",
67562306a36Sopenharmony_ci				(oe_status & CIO2_INT_EXT_OE_DMAOE_MASK)
67662306a36Sopenharmony_ci				>> CIO2_INT_EXT_OE_DMAOE_SHIFT);
67762306a36Sopenharmony_ci			oe_status &= ~CIO2_INT_EXT_OE_DMAOE_MASK;
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci		if (oe_status & CIO2_INT_EXT_OE_OES_MASK) {
68062306a36Sopenharmony_ci			dev_err(dev, "DMA output error on CSI2 buses: 0x%x\n",
68162306a36Sopenharmony_ci				(oe_status & CIO2_INT_EXT_OE_OES_MASK)
68262306a36Sopenharmony_ci				>> CIO2_INT_EXT_OE_OES_SHIFT);
68362306a36Sopenharmony_ci			oe_status &= ~CIO2_INT_EXT_OE_OES_MASK;
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci		writel(oe_clear, base + CIO2_REG_INT_STS_EXT_OE);
68662306a36Sopenharmony_ci		if (oe_status)
68762306a36Sopenharmony_ci			dev_warn(dev, "unknown interrupt 0x%x on OE\n",
68862306a36Sopenharmony_ci				 oe_status);
68962306a36Sopenharmony_ci		int_status &= ~CIO2_INT_IOOE;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (int_status & CIO2_INT_IOC_MASK) {
69362306a36Sopenharmony_ci		/* DMA IO done -- frame ready */
69462306a36Sopenharmony_ci		u32 clr = 0;
69562306a36Sopenharmony_ci		unsigned int d;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		for (d = 0; d < CIO2_NUM_DMA_CHAN; d++)
69862306a36Sopenharmony_ci			if (int_status & CIO2_INT_IOC(d)) {
69962306a36Sopenharmony_ci				clr |= CIO2_INT_IOC(d);
70062306a36Sopenharmony_ci				cio2_buffer_done(cio2, d);
70162306a36Sopenharmony_ci			}
70262306a36Sopenharmony_ci		int_status &= ~clr;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (int_status & CIO2_INT_IOS_IOLN_MASK) {
70662306a36Sopenharmony_ci		/* DMA IO starts or reached specified line */
70762306a36Sopenharmony_ci		u32 clr = 0;
70862306a36Sopenharmony_ci		unsigned int d;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		for (d = 0; d < CIO2_NUM_DMA_CHAN; d++)
71162306a36Sopenharmony_ci			if (int_status & CIO2_INT_IOS_IOLN(d)) {
71262306a36Sopenharmony_ci				clr |= CIO2_INT_IOS_IOLN(d);
71362306a36Sopenharmony_ci				if (d == CIO2_DMA_CHAN)
71462306a36Sopenharmony_ci					cio2_queue_event_sof(cio2,
71562306a36Sopenharmony_ci							     cio2->cur_queue);
71662306a36Sopenharmony_ci			}
71762306a36Sopenharmony_ci		int_status &= ~clr;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (int_status & (CIO2_INT_IOIE | CIO2_INT_IOIRQ)) {
72162306a36Sopenharmony_ci		/* CSI2 receiver (error) interrupt */
72262306a36Sopenharmony_ci		unsigned int port;
72362306a36Sopenharmony_ci		u32 ie_status;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci		ie_status = readl(base + CIO2_REG_INT_STS_EXT_IE);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		for (port = 0; port < CIO2_NUM_PORTS; port++) {
72862306a36Sopenharmony_ci			u32 port_status = (ie_status >> (port * 8)) & 0xff;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci			cio2_irq_log_port_errs(dev, port, port_status);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci			if (ie_status & CIO2_INT_EXT_IE_IRQ(port)) {
73362306a36Sopenharmony_ci				void __iomem *csi_rx_base =
73462306a36Sopenharmony_ci						base + CIO2_REG_PIPE_BASE(port);
73562306a36Sopenharmony_ci				u32 csi2_status;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci				csi2_status = readl(csi_rx_base +
73862306a36Sopenharmony_ci						CIO2_REG_IRQCTRL_STATUS);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci				cio2_irq_log_irq_errs(dev, port, csi2_status);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci				writel(csi2_status,
74362306a36Sopenharmony_ci				       csi_rx_base + CIO2_REG_IRQCTRL_CLEAR);
74462306a36Sopenharmony_ci			}
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		writel(ie_status, base + CIO2_REG_INT_STS_EXT_IE);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		int_status &= ~(CIO2_INT_IOIE | CIO2_INT_IOIRQ);
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (int_status)
75362306a36Sopenharmony_ci		dev_warn(dev, "unknown interrupt 0x%x on INT\n", int_status);
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic irqreturn_t cio2_irq(int irq, void *cio2_ptr)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct cio2_device *cio2 = cio2_ptr;
75962306a36Sopenharmony_ci	void __iomem *const base = cio2->base;
76062306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
76162306a36Sopenharmony_ci	u32 int_status;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	int_status = readl(base + CIO2_REG_INT_STS);
76462306a36Sopenharmony_ci	dev_dbg(dev, "isr enter - interrupt status 0x%x\n", int_status);
76562306a36Sopenharmony_ci	if (!int_status)
76662306a36Sopenharmony_ci		return IRQ_NONE;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	do {
76962306a36Sopenharmony_ci		writel(int_status, base + CIO2_REG_INT_STS);
77062306a36Sopenharmony_ci		cio2_irq_handle_once(cio2, int_status);
77162306a36Sopenharmony_ci		int_status = readl(base + CIO2_REG_INT_STS);
77262306a36Sopenharmony_ci		if (int_status)
77362306a36Sopenharmony_ci			dev_dbg(dev, "pending status 0x%x\n", int_status);
77462306a36Sopenharmony_ci	} while (int_status);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	return IRQ_HANDLED;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/**************** Videobuf2 interface ****************/
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic void cio2_vb2_return_all_buffers(struct cio2_queue *q,
78262306a36Sopenharmony_ci					enum vb2_buffer_state state)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	unsigned int i;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++) {
78762306a36Sopenharmony_ci		if (q->bufs[i]) {
78862306a36Sopenharmony_ci			atomic_dec(&q->bufs_queued);
78962306a36Sopenharmony_ci			vb2_buffer_done(&q->bufs[i]->vbb.vb2_buf,
79062306a36Sopenharmony_ci					state);
79162306a36Sopenharmony_ci			q->bufs[i] = NULL;
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic int cio2_vb2_queue_setup(struct vb2_queue *vq,
79762306a36Sopenharmony_ci				unsigned int *num_buffers,
79862306a36Sopenharmony_ci				unsigned int *num_planes,
79962306a36Sopenharmony_ci				unsigned int sizes[],
80062306a36Sopenharmony_ci				struct device *alloc_devs[])
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vq);
80362306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
80462306a36Sopenharmony_ci	struct cio2_queue *q = vb2q_to_cio2_queue(vq);
80562306a36Sopenharmony_ci	unsigned int i;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (*num_planes && *num_planes < q->format.num_planes)
80862306a36Sopenharmony_ci		return -EINVAL;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	for (i = 0; i < q->format.num_planes; ++i) {
81162306a36Sopenharmony_ci		if (*num_planes && sizes[i] < q->format.plane_fmt[i].sizeimage)
81262306a36Sopenharmony_ci			return -EINVAL;
81362306a36Sopenharmony_ci		sizes[i] = q->format.plane_fmt[i].sizeimage;
81462306a36Sopenharmony_ci		alloc_devs[i] = dev;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	*num_planes = q->format.num_planes;
81862306a36Sopenharmony_ci	*num_buffers = clamp_val(*num_buffers, 1, CIO2_MAX_BUFFERS);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* Initialize buffer queue */
82162306a36Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++) {
82262306a36Sopenharmony_ci		q->bufs[i] = NULL;
82362306a36Sopenharmony_ci		cio2_fbpt_entry_init_dummy(cio2, &q->fbpt[i * CIO2_MAX_LOPS]);
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci	atomic_set(&q->bufs_queued, 0);
82662306a36Sopenharmony_ci	q->bufs_first = 0;
82762306a36Sopenharmony_ci	q->bufs_next = 0;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	return 0;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/* Called after each buffer is allocated */
83362306a36Sopenharmony_cistatic int cio2_vb2_buf_init(struct vb2_buffer *vb)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue);
83662306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
83762306a36Sopenharmony_ci	struct cio2_buffer *b = to_cio2_buffer(vb);
83862306a36Sopenharmony_ci	unsigned int pages = PFN_UP(vb->planes[0].length);
83962306a36Sopenharmony_ci	unsigned int lops = DIV_ROUND_UP(pages + 1, CIO2_LOP_ENTRIES);
84062306a36Sopenharmony_ci	struct sg_table *sg;
84162306a36Sopenharmony_ci	struct sg_dma_page_iter sg_iter;
84262306a36Sopenharmony_ci	unsigned int i, j;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (lops <= 0 || lops > CIO2_MAX_LOPS) {
84562306a36Sopenharmony_ci		dev_err(dev, "%s: bad buffer size (%i)\n", __func__,
84662306a36Sopenharmony_ci			vb->planes[0].length);
84762306a36Sopenharmony_ci		return -ENOSPC;		/* Should never happen */
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	memset(b->lop, 0, sizeof(b->lop));
85162306a36Sopenharmony_ci	/* Allocate LOP table */
85262306a36Sopenharmony_ci	for (i = 0; i < lops; i++) {
85362306a36Sopenharmony_ci		b->lop[i] = dma_alloc_coherent(dev, PAGE_SIZE,
85462306a36Sopenharmony_ci					       &b->lop_bus_addr[i], GFP_KERNEL);
85562306a36Sopenharmony_ci		if (!b->lop[i])
85662306a36Sopenharmony_ci			goto fail;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* Fill LOP */
86062306a36Sopenharmony_ci	sg = vb2_dma_sg_plane_desc(vb, 0);
86162306a36Sopenharmony_ci	if (!sg)
86262306a36Sopenharmony_ci		return -ENOMEM;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	if (sg->nents && sg->sgl)
86562306a36Sopenharmony_ci		b->offset = sg->sgl->offset;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	i = j = 0;
86862306a36Sopenharmony_ci	for_each_sg_dma_page(sg->sgl, &sg_iter, sg->nents, 0) {
86962306a36Sopenharmony_ci		if (!pages--)
87062306a36Sopenharmony_ci			break;
87162306a36Sopenharmony_ci		b->lop[i][j] = PFN_DOWN(sg_page_iter_dma_address(&sg_iter));
87262306a36Sopenharmony_ci		j++;
87362306a36Sopenharmony_ci		if (j == CIO2_LOP_ENTRIES) {
87462306a36Sopenharmony_ci			i++;
87562306a36Sopenharmony_ci			j = 0;
87662306a36Sopenharmony_ci		}
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	b->lop[i][j] = PFN_DOWN(cio2->dummy_page_bus_addr);
88062306a36Sopenharmony_ci	return 0;
88162306a36Sopenharmony_cifail:
88262306a36Sopenharmony_ci	while (i--)
88362306a36Sopenharmony_ci		dma_free_coherent(dev, PAGE_SIZE, b->lop[i], b->lop_bus_addr[i]);
88462306a36Sopenharmony_ci	return -ENOMEM;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci/* Transfer buffer ownership to cio2 */
88862306a36Sopenharmony_cistatic void cio2_vb2_buf_queue(struct vb2_buffer *vb)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue);
89162306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
89262306a36Sopenharmony_ci	struct cio2_queue *q =
89362306a36Sopenharmony_ci		container_of(vb->vb2_queue, struct cio2_queue, vbq);
89462306a36Sopenharmony_ci	struct cio2_buffer *b = to_cio2_buffer(vb);
89562306a36Sopenharmony_ci	struct cio2_fbpt_entry *entry;
89662306a36Sopenharmony_ci	unsigned long flags;
89762306a36Sopenharmony_ci	unsigned int i, j, next = q->bufs_next;
89862306a36Sopenharmony_ci	int bufs_queued = atomic_inc_return(&q->bufs_queued);
89962306a36Sopenharmony_ci	u32 fbpt_rp;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	dev_dbg(dev, "queue buffer %d\n", vb->index);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/*
90462306a36Sopenharmony_ci	 * This code queues the buffer to the CIO2 DMA engine, which starts
90562306a36Sopenharmony_ci	 * running once streaming has started. It is possible that this code
90662306a36Sopenharmony_ci	 * gets pre-empted due to increased CPU load. Upon this, the driver
90762306a36Sopenharmony_ci	 * does not get an opportunity to queue new buffers to the CIO2 DMA
90862306a36Sopenharmony_ci	 * engine. When the DMA engine encounters an FBPT entry without the
90962306a36Sopenharmony_ci	 * VALID bit set, the DMA engine halts, which requires a restart of
91062306a36Sopenharmony_ci	 * the DMA engine and sensor, to continue streaming.
91162306a36Sopenharmony_ci	 * This is not desired and is highly unlikely given that there are
91262306a36Sopenharmony_ci	 * 32 FBPT entries that the DMA engine needs to process, to run into
91362306a36Sopenharmony_ci	 * an FBPT entry, without the VALID bit set. We try to mitigate this
91462306a36Sopenharmony_ci	 * by disabling interrupts for the duration of this queueing.
91562306a36Sopenharmony_ci	 */
91662306a36Sopenharmony_ci	local_irq_save(flags);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	fbpt_rp = (readl(cio2->base + CIO2_REG_CDMARI(CIO2_DMA_CHAN))
91962306a36Sopenharmony_ci		   >> CIO2_CDMARI_FBPT_RP_SHIFT)
92062306a36Sopenharmony_ci		   & CIO2_CDMARI_FBPT_RP_MASK;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/*
92362306a36Sopenharmony_ci	 * fbpt_rp is the fbpt entry that the dma is currently working
92462306a36Sopenharmony_ci	 * on, but since it could jump to next entry at any time,
92562306a36Sopenharmony_ci	 * assume that we might already be there.
92662306a36Sopenharmony_ci	 */
92762306a36Sopenharmony_ci	fbpt_rp = (fbpt_rp + 1) % CIO2_MAX_BUFFERS;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (bufs_queued <= 1 || fbpt_rp == next)
93062306a36Sopenharmony_ci		/* Buffers were drained */
93162306a36Sopenharmony_ci		next = (fbpt_rp + 1) % CIO2_MAX_BUFFERS;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++) {
93462306a36Sopenharmony_ci		/*
93562306a36Sopenharmony_ci		 * We have allocated CIO2_MAX_BUFFERS circularly for the
93662306a36Sopenharmony_ci		 * hw, the user has requested N buffer queue. The driver
93762306a36Sopenharmony_ci		 * ensures N <= CIO2_MAX_BUFFERS and guarantees that whenever
93862306a36Sopenharmony_ci		 * user queues a buffer, there necessarily is a free buffer.
93962306a36Sopenharmony_ci		 */
94062306a36Sopenharmony_ci		if (!q->bufs[next]) {
94162306a36Sopenharmony_ci			q->bufs[next] = b;
94262306a36Sopenharmony_ci			entry = &q->fbpt[next * CIO2_MAX_LOPS];
94362306a36Sopenharmony_ci			cio2_fbpt_entry_init_buf(cio2, b, entry);
94462306a36Sopenharmony_ci			local_irq_restore(flags);
94562306a36Sopenharmony_ci			q->bufs_next = (next + 1) % CIO2_MAX_BUFFERS;
94662306a36Sopenharmony_ci			for (j = 0; j < vb->num_planes; j++)
94762306a36Sopenharmony_ci				vb2_set_plane_payload(vb, j,
94862306a36Sopenharmony_ci					q->format.plane_fmt[j].sizeimage);
94962306a36Sopenharmony_ci			return;
95062306a36Sopenharmony_ci		}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci		dev_dbg(dev, "entry %i was full!\n", next);
95362306a36Sopenharmony_ci		next = (next + 1) % CIO2_MAX_BUFFERS;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	local_irq_restore(flags);
95762306a36Sopenharmony_ci	dev_err(dev, "error: all cio2 entries were full!\n");
95862306a36Sopenharmony_ci	atomic_dec(&q->bufs_queued);
95962306a36Sopenharmony_ci	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci/* Called when each buffer is freed */
96362306a36Sopenharmony_cistatic void cio2_vb2_buf_cleanup(struct vb2_buffer *vb)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue);
96662306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
96762306a36Sopenharmony_ci	struct cio2_buffer *b = to_cio2_buffer(vb);
96862306a36Sopenharmony_ci	unsigned int i;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	/* Free LOP table */
97162306a36Sopenharmony_ci	for (i = 0; i < CIO2_MAX_LOPS; i++) {
97262306a36Sopenharmony_ci		if (b->lop[i])
97362306a36Sopenharmony_ci			dma_free_coherent(dev, PAGE_SIZE,
97462306a36Sopenharmony_ci					  b->lop[i], b->lop_bus_addr[i]);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct cio2_queue *q = vb2q_to_cio2_queue(vq);
98162306a36Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vq);
98262306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
98362306a36Sopenharmony_ci	int r;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	cio2->cur_queue = q;
98662306a36Sopenharmony_ci	atomic_set(&q->frame_sequence, 0);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	r = pm_runtime_resume_and_get(dev);
98962306a36Sopenharmony_ci	if (r < 0) {
99062306a36Sopenharmony_ci		dev_info(dev, "failed to set power %d\n", r);
99162306a36Sopenharmony_ci		return r;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	r = video_device_pipeline_start(&q->vdev, &q->pipe);
99562306a36Sopenharmony_ci	if (r)
99662306a36Sopenharmony_ci		goto fail_pipeline;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	r = cio2_hw_init(cio2, q);
99962306a36Sopenharmony_ci	if (r)
100062306a36Sopenharmony_ci		goto fail_hw;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* Start streaming on sensor */
100362306a36Sopenharmony_ci	r = v4l2_subdev_call(q->sensor, video, s_stream, 1);
100462306a36Sopenharmony_ci	if (r)
100562306a36Sopenharmony_ci		goto fail_csi2_subdev;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	cio2->streaming = true;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	return 0;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cifail_csi2_subdev:
101262306a36Sopenharmony_ci	cio2_hw_exit(cio2, q);
101362306a36Sopenharmony_cifail_hw:
101462306a36Sopenharmony_ci	video_device_pipeline_stop(&q->vdev);
101562306a36Sopenharmony_cifail_pipeline:
101662306a36Sopenharmony_ci	dev_dbg(dev, "failed to start streaming (%d)\n", r);
101762306a36Sopenharmony_ci	cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_QUEUED);
101862306a36Sopenharmony_ci	pm_runtime_put(dev);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	return r;
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic void cio2_vb2_stop_streaming(struct vb2_queue *vq)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	struct cio2_queue *q = vb2q_to_cio2_queue(vq);
102662306a36Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vq);
102762306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (v4l2_subdev_call(q->sensor, video, s_stream, 0))
103062306a36Sopenharmony_ci		dev_err(dev, "failed to stop sensor streaming\n");
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	cio2_hw_exit(cio2, q);
103362306a36Sopenharmony_ci	synchronize_irq(cio2->pci_dev->irq);
103462306a36Sopenharmony_ci	cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR);
103562306a36Sopenharmony_ci	video_device_pipeline_stop(&q->vdev);
103662306a36Sopenharmony_ci	pm_runtime_put(dev);
103762306a36Sopenharmony_ci	cio2->streaming = false;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic const struct vb2_ops cio2_vb2_ops = {
104162306a36Sopenharmony_ci	.buf_init = cio2_vb2_buf_init,
104262306a36Sopenharmony_ci	.buf_queue = cio2_vb2_buf_queue,
104362306a36Sopenharmony_ci	.buf_cleanup = cio2_vb2_buf_cleanup,
104462306a36Sopenharmony_ci	.queue_setup = cio2_vb2_queue_setup,
104562306a36Sopenharmony_ci	.start_streaming = cio2_vb2_start_streaming,
104662306a36Sopenharmony_ci	.stop_streaming = cio2_vb2_stop_streaming,
104762306a36Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
104862306a36Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
104962306a36Sopenharmony_ci};
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci/**************** V4L2 interface ****************/
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic int cio2_v4l2_querycap(struct file *file, void *fh,
105462306a36Sopenharmony_ci			      struct v4l2_capability *cap)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	strscpy(cap->driver, CIO2_NAME, sizeof(cap->driver));
105762306a36Sopenharmony_ci	strscpy(cap->card, CIO2_DEVICE_NAME, sizeof(cap->card));
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return 0;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic int cio2_v4l2_enum_fmt(struct file *file, void *fh,
106362306a36Sopenharmony_ci			      struct v4l2_fmtdesc *f)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	if (f->index >= ARRAY_SIZE(formats))
106662306a36Sopenharmony_ci		return -EINVAL;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	f->pixelformat = formats[f->index].fourcc;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return 0;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci/* The format is validated in cio2_video_link_validate() */
107462306a36Sopenharmony_cistatic int cio2_v4l2_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct cio2_queue *q = file_to_cio2_queue(file);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	f->fmt.pix_mp = q->format;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return 0;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	const struct ipu3_cio2_fmt *fmt;
108662306a36Sopenharmony_ci	struct v4l2_pix_format_mplane *mpix = &f->fmt.pix_mp;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	fmt = cio2_find_format(&mpix->pixelformat, NULL);
108962306a36Sopenharmony_ci	if (!fmt)
109062306a36Sopenharmony_ci		fmt = &formats[0];
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* Only supports up to 4224x3136 */
109362306a36Sopenharmony_ci	if (mpix->width > CIO2_IMAGE_MAX_WIDTH)
109462306a36Sopenharmony_ci		mpix->width = CIO2_IMAGE_MAX_WIDTH;
109562306a36Sopenharmony_ci	if (mpix->height > CIO2_IMAGE_MAX_HEIGHT)
109662306a36Sopenharmony_ci		mpix->height = CIO2_IMAGE_MAX_HEIGHT;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	mpix->num_planes = 1;
109962306a36Sopenharmony_ci	mpix->pixelformat = fmt->fourcc;
110062306a36Sopenharmony_ci	mpix->colorspace = V4L2_COLORSPACE_RAW;
110162306a36Sopenharmony_ci	mpix->field = V4L2_FIELD_NONE;
110262306a36Sopenharmony_ci	mpix->plane_fmt[0].bytesperline = cio2_bytesperline(mpix->width);
110362306a36Sopenharmony_ci	mpix->plane_fmt[0].sizeimage = mpix->plane_fmt[0].bytesperline *
110462306a36Sopenharmony_ci							mpix->height;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	/* use default */
110762306a36Sopenharmony_ci	mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
110862306a36Sopenharmony_ci	mpix->quantization = V4L2_QUANTIZATION_DEFAULT;
110962306a36Sopenharmony_ci	mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	return 0;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic int cio2_v4l2_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct cio2_queue *q = file_to_cio2_queue(file);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	cio2_v4l2_try_fmt(file, fh, f);
111962306a36Sopenharmony_ci	q->format = f->fmt.pix_mp;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	return 0;
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic int
112562306a36Sopenharmony_cicio2_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	if (input->index > 0)
112862306a36Sopenharmony_ci		return -EINVAL;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	strscpy(input->name, "camera", sizeof(input->name));
113162306a36Sopenharmony_ci	input->type = V4L2_INPUT_TYPE_CAMERA;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	return 0;
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic int
113762306a36Sopenharmony_cicio2_video_g_input(struct file *file, void *fh, unsigned int *input)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	*input = 0;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	return 0;
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_cistatic int
114562306a36Sopenharmony_cicio2_video_s_input(struct file *file, void *fh, unsigned int input)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	return input == 0 ? 0 : -EINVAL;
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic const struct v4l2_file_operations cio2_v4l2_fops = {
115162306a36Sopenharmony_ci	.owner = THIS_MODULE,
115262306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
115362306a36Sopenharmony_ci	.open = v4l2_fh_open,
115462306a36Sopenharmony_ci	.release = vb2_fop_release,
115562306a36Sopenharmony_ci	.poll = vb2_fop_poll,
115662306a36Sopenharmony_ci	.mmap = vb2_fop_mmap,
115762306a36Sopenharmony_ci};
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops cio2_v4l2_ioctl_ops = {
116062306a36Sopenharmony_ci	.vidioc_querycap = cio2_v4l2_querycap,
116162306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = cio2_v4l2_enum_fmt,
116262306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap_mplane = cio2_v4l2_g_fmt,
116362306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap_mplane = cio2_v4l2_s_fmt,
116462306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap_mplane = cio2_v4l2_try_fmt,
116562306a36Sopenharmony_ci	.vidioc_reqbufs = vb2_ioctl_reqbufs,
116662306a36Sopenharmony_ci	.vidioc_create_bufs = vb2_ioctl_create_bufs,
116762306a36Sopenharmony_ci	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
116862306a36Sopenharmony_ci	.vidioc_querybuf = vb2_ioctl_querybuf,
116962306a36Sopenharmony_ci	.vidioc_qbuf = vb2_ioctl_qbuf,
117062306a36Sopenharmony_ci	.vidioc_dqbuf = vb2_ioctl_dqbuf,
117162306a36Sopenharmony_ci	.vidioc_streamon = vb2_ioctl_streamon,
117262306a36Sopenharmony_ci	.vidioc_streamoff = vb2_ioctl_streamoff,
117362306a36Sopenharmony_ci	.vidioc_expbuf = vb2_ioctl_expbuf,
117462306a36Sopenharmony_ci	.vidioc_enum_input = cio2_video_enum_input,
117562306a36Sopenharmony_ci	.vidioc_g_input	= cio2_video_g_input,
117662306a36Sopenharmony_ci	.vidioc_s_input	= cio2_video_s_input,
117762306a36Sopenharmony_ci};
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic int cio2_subdev_subscribe_event(struct v4l2_subdev *sd,
118062306a36Sopenharmony_ci				       struct v4l2_fh *fh,
118162306a36Sopenharmony_ci				       struct v4l2_event_subscription *sub)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	if (sub->type != V4L2_EVENT_FRAME_SYNC)
118462306a36Sopenharmony_ci		return -EINVAL;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* Line number. For now only zero accepted. */
118762306a36Sopenharmony_ci	if (sub->id != 0)
118862306a36Sopenharmony_ci		return -EINVAL;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return v4l2_event_subscribe(fh, sub, 0, NULL);
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
119662306a36Sopenharmony_ci	const struct v4l2_mbus_framefmt fmt_default = {
119762306a36Sopenharmony_ci		.width = 1936,
119862306a36Sopenharmony_ci		.height = 1096,
119962306a36Sopenharmony_ci		.code = formats[0].mbus_code,
120062306a36Sopenharmony_ci		.field = V4L2_FIELD_NONE,
120162306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_RAW,
120262306a36Sopenharmony_ci		.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
120362306a36Sopenharmony_ci		.quantization = V4L2_QUANTIZATION_DEFAULT,
120462306a36Sopenharmony_ci		.xfer_func = V4L2_XFER_FUNC_DEFAULT,
120562306a36Sopenharmony_ci	};
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	/* Initialize try_fmt */
120862306a36Sopenharmony_ci	format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SINK);
120962306a36Sopenharmony_ci	*format = fmt_default;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	/* same as sink */
121262306a36Sopenharmony_ci	format = v4l2_subdev_get_try_format(sd, fh->state, CIO2_PAD_SOURCE);
121362306a36Sopenharmony_ci	*format = fmt_default;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	return 0;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci/*
121962306a36Sopenharmony_ci * cio2_subdev_get_fmt - Handle get format by pads subdev method
122062306a36Sopenharmony_ci * @sd : pointer to v4l2 subdev structure
122162306a36Sopenharmony_ci * @cfg: V4L2 subdev pad config
122262306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
122362306a36Sopenharmony_ci * return -EINVAL or zero on success
122462306a36Sopenharmony_ci */
122562306a36Sopenharmony_cistatic int cio2_subdev_get_fmt(struct v4l2_subdev *sd,
122662306a36Sopenharmony_ci			       struct v4l2_subdev_state *sd_state,
122762306a36Sopenharmony_ci			       struct v4l2_subdev_format *fmt)
122862306a36Sopenharmony_ci{
122962306a36Sopenharmony_ci	struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	mutex_lock(&q->subdev_lock);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
123462306a36Sopenharmony_ci		fmt->format = *v4l2_subdev_get_try_format(sd, sd_state,
123562306a36Sopenharmony_ci							  fmt->pad);
123662306a36Sopenharmony_ci	else
123762306a36Sopenharmony_ci		fmt->format = q->subdev_fmt;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	mutex_unlock(&q->subdev_lock);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return 0;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci/*
124562306a36Sopenharmony_ci * cio2_subdev_set_fmt - Handle set format by pads subdev method
124662306a36Sopenharmony_ci * @sd : pointer to v4l2 subdev structure
124762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad config
124862306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
124962306a36Sopenharmony_ci * return -EINVAL or zero on success
125062306a36Sopenharmony_ci */
125162306a36Sopenharmony_cistatic int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
125262306a36Sopenharmony_ci			       struct v4l2_subdev_state *sd_state,
125362306a36Sopenharmony_ci			       struct v4l2_subdev_format *fmt)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
125662306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *mbus;
125762306a36Sopenharmony_ci	u32 mbus_code = fmt->format.code;
125862306a36Sopenharmony_ci	unsigned int i;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	/*
126162306a36Sopenharmony_ci	 * Only allow setting sink pad format;
126262306a36Sopenharmony_ci	 * source always propagates from sink
126362306a36Sopenharmony_ci	 */
126462306a36Sopenharmony_ci	if (fmt->pad == CIO2_PAD_SOURCE)
126562306a36Sopenharmony_ci		return cio2_subdev_get_fmt(sd, sd_state, fmt);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
126862306a36Sopenharmony_ci		mbus = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
126962306a36Sopenharmony_ci	else
127062306a36Sopenharmony_ci		mbus = &q->subdev_fmt;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	fmt->format.code = formats[0].mbus_code;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++) {
127562306a36Sopenharmony_ci		if (formats[i].mbus_code == mbus_code) {
127662306a36Sopenharmony_ci			fmt->format.code = mbus_code;
127762306a36Sopenharmony_ci			break;
127862306a36Sopenharmony_ci		}
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	fmt->format.width = min(fmt->format.width, CIO2_IMAGE_MAX_WIDTH);
128262306a36Sopenharmony_ci	fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_HEIGHT);
128362306a36Sopenharmony_ci	fmt->format.field = V4L2_FIELD_NONE;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	mutex_lock(&q->subdev_lock);
128662306a36Sopenharmony_ci	*mbus = fmt->format;
128762306a36Sopenharmony_ci	mutex_unlock(&q->subdev_lock);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	return 0;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cistatic int cio2_subdev_enum_mbus_code(struct v4l2_subdev *sd,
129362306a36Sopenharmony_ci				      struct v4l2_subdev_state *sd_state,
129462306a36Sopenharmony_ci				      struct v4l2_subdev_mbus_code_enum *code)
129562306a36Sopenharmony_ci{
129662306a36Sopenharmony_ci	if (code->index >= ARRAY_SIZE(formats))
129762306a36Sopenharmony_ci		return -EINVAL;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	code->code = formats[code->index].mbus_code;
130062306a36Sopenharmony_ci	return 0;
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_cistatic int cio2_subdev_link_validate_get_format(struct media_pad *pad,
130462306a36Sopenharmony_ci						struct v4l2_subdev_format *fmt)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	if (is_media_entity_v4l2_subdev(pad->entity)) {
130762306a36Sopenharmony_ci		struct v4l2_subdev *sd =
130862306a36Sopenharmony_ci			media_entity_to_v4l2_subdev(pad->entity);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci		memset(fmt, 0, sizeof(*fmt));
131162306a36Sopenharmony_ci		fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
131262306a36Sopenharmony_ci		fmt->pad = pad->index;
131362306a36Sopenharmony_ci		return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	return -EINVAL;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic int cio2_video_link_validate(struct media_link *link)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	struct media_entity *entity = link->sink->entity;
132262306a36Sopenharmony_ci	struct video_device *vd = media_entity_to_video_device(entity);
132362306a36Sopenharmony_ci	struct cio2_queue *q = container_of(vd, struct cio2_queue, vdev);
132462306a36Sopenharmony_ci	struct cio2_device *cio2 = video_get_drvdata(vd);
132562306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
132662306a36Sopenharmony_ci	struct v4l2_subdev_format source_fmt;
132762306a36Sopenharmony_ci	int ret;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (!media_pad_remote_pad_first(entity->pads)) {
133062306a36Sopenharmony_ci		dev_info(dev, "video node %s pad not connected\n", vd->name);
133162306a36Sopenharmony_ci		return -ENOTCONN;
133262306a36Sopenharmony_ci	}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	ret = cio2_subdev_link_validate_get_format(link->source, &source_fmt);
133562306a36Sopenharmony_ci	if (ret < 0)
133662306a36Sopenharmony_ci		return 0;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	if (source_fmt.format.width != q->format.width ||
133962306a36Sopenharmony_ci	    source_fmt.format.height != q->format.height) {
134062306a36Sopenharmony_ci		dev_err(dev, "Wrong width or height %ux%u (%ux%u expected)\n",
134162306a36Sopenharmony_ci			q->format.width, q->format.height,
134262306a36Sopenharmony_ci			source_fmt.format.width, source_fmt.format.height);
134362306a36Sopenharmony_ci		return -EINVAL;
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	if (!cio2_find_format(&q->format.pixelformat, &source_fmt.format.code))
134762306a36Sopenharmony_ci		return -EINVAL;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	return 0;
135062306a36Sopenharmony_ci}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops cio2_subdev_core_ops = {
135362306a36Sopenharmony_ci	.subscribe_event = cio2_subdev_subscribe_event,
135462306a36Sopenharmony_ci	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
135562306a36Sopenharmony_ci};
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_cistatic const struct v4l2_subdev_internal_ops cio2_subdev_internal_ops = {
135862306a36Sopenharmony_ci	.open = cio2_subdev_open,
135962306a36Sopenharmony_ci};
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops cio2_subdev_pad_ops = {
136262306a36Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate_default,
136362306a36Sopenharmony_ci	.get_fmt = cio2_subdev_get_fmt,
136462306a36Sopenharmony_ci	.set_fmt = cio2_subdev_set_fmt,
136562306a36Sopenharmony_ci	.enum_mbus_code = cio2_subdev_enum_mbus_code,
136662306a36Sopenharmony_ci};
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_cistatic const struct v4l2_subdev_ops cio2_subdev_ops = {
136962306a36Sopenharmony_ci	.core = &cio2_subdev_core_ops,
137062306a36Sopenharmony_ci	.pad = &cio2_subdev_pad_ops,
137162306a36Sopenharmony_ci};
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci/******* V4L2 sub-device asynchronous registration callbacks***********/
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistruct sensor_async_subdev {
137662306a36Sopenharmony_ci	struct v4l2_async_connection asd;
137762306a36Sopenharmony_ci	struct csi2_bus_info csi2;
137862306a36Sopenharmony_ci};
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci#define to_sensor_asd(__asd)	\
138162306a36Sopenharmony_ci	container_of_const(__asd, struct sensor_async_subdev, asd)
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci/* The .bound() notifier callback when a match is found */
138462306a36Sopenharmony_cistatic int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
138562306a36Sopenharmony_ci			       struct v4l2_subdev *sd,
138662306a36Sopenharmony_ci			       struct v4l2_async_connection *asd)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	struct cio2_device *cio2 = to_cio2_device(notifier);
138962306a36Sopenharmony_ci	struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
139062306a36Sopenharmony_ci	struct cio2_queue *q;
139162306a36Sopenharmony_ci	int ret;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	if (cio2->queue[s_asd->csi2.port].sensor)
139462306a36Sopenharmony_ci		return -EBUSY;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	ret = ipu_bridge_instantiate_vcm(sd->dev);
139762306a36Sopenharmony_ci	if (ret)
139862306a36Sopenharmony_ci		return ret;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	q = &cio2->queue[s_asd->csi2.port];
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	q->csi2 = s_asd->csi2;
140362306a36Sopenharmony_ci	q->sensor = sd;
140462306a36Sopenharmony_ci	q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	return 0;
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci/* The .unbind callback */
141062306a36Sopenharmony_cistatic void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
141162306a36Sopenharmony_ci				 struct v4l2_subdev *sd,
141262306a36Sopenharmony_ci				 struct v4l2_async_connection *asd)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	struct cio2_device *cio2 = to_cio2_device(notifier);
141562306a36Sopenharmony_ci	struct sensor_async_subdev *s_asd = to_sensor_asd(asd);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	cio2->queue[s_asd->csi2.port].sensor = NULL;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci/* .complete() is called after all subdevices have been located */
142162306a36Sopenharmony_cistatic int cio2_notifier_complete(struct v4l2_async_notifier *notifier)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	struct cio2_device *cio2 = to_cio2_device(notifier);
142462306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
142562306a36Sopenharmony_ci	struct sensor_async_subdev *s_asd;
142662306a36Sopenharmony_ci	struct v4l2_async_connection *asd;
142762306a36Sopenharmony_ci	struct cio2_queue *q;
142862306a36Sopenharmony_ci	int ret;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	list_for_each_entry(asd, &cio2->notifier.done_list, asc_entry) {
143162306a36Sopenharmony_ci		s_asd = to_sensor_asd(asd);
143262306a36Sopenharmony_ci		q = &cio2->queue[s_asd->csi2.port];
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		ret = media_entity_get_fwnode_pad(&q->sensor->entity,
143562306a36Sopenharmony_ci						  s_asd->asd.match.fwnode,
143662306a36Sopenharmony_ci						  MEDIA_PAD_FL_SOURCE);
143762306a36Sopenharmony_ci		if (ret < 0) {
143862306a36Sopenharmony_ci			dev_err(dev, "no pad for endpoint %pfw (%d)\n",
143962306a36Sopenharmony_ci				s_asd->asd.match.fwnode, ret);
144062306a36Sopenharmony_ci			return ret;
144162306a36Sopenharmony_ci		}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci		ret = media_create_pad_link(&q->sensor->entity, ret,
144462306a36Sopenharmony_ci					    &q->subdev.entity, CIO2_PAD_SINK,
144562306a36Sopenharmony_ci					    0);
144662306a36Sopenharmony_ci		if (ret) {
144762306a36Sopenharmony_ci			dev_err(dev, "failed to create link for %s (endpoint %pfw, error %d)\n",
144862306a36Sopenharmony_ci				q->sensor->name, s_asd->asd.match.fwnode, ret);
144962306a36Sopenharmony_ci			return ret;
145062306a36Sopenharmony_ci		}
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
145462306a36Sopenharmony_ci}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_cistatic const struct v4l2_async_notifier_operations cio2_async_ops = {
145762306a36Sopenharmony_ci	.bound = cio2_notifier_bound,
145862306a36Sopenharmony_ci	.unbind = cio2_notifier_unbind,
145962306a36Sopenharmony_ci	.complete = cio2_notifier_complete,
146062306a36Sopenharmony_ci};
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic int cio2_parse_firmware(struct cio2_device *cio2)
146362306a36Sopenharmony_ci{
146462306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
146562306a36Sopenharmony_ci	unsigned int i;
146662306a36Sopenharmony_ci	int ret;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	for (i = 0; i < CIO2_NUM_PORTS; i++) {
146962306a36Sopenharmony_ci		struct v4l2_fwnode_endpoint vep = {
147062306a36Sopenharmony_ci			.bus_type = V4L2_MBUS_CSI2_DPHY
147162306a36Sopenharmony_ci		};
147262306a36Sopenharmony_ci		struct sensor_async_subdev *s_asd;
147362306a36Sopenharmony_ci		struct fwnode_handle *ep;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci		ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0,
147662306a36Sopenharmony_ci						FWNODE_GRAPH_ENDPOINT_NEXT);
147762306a36Sopenharmony_ci		if (!ep)
147862306a36Sopenharmony_ci			continue;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci		ret = v4l2_fwnode_endpoint_parse(ep, &vep);
148162306a36Sopenharmony_ci		if (ret)
148262306a36Sopenharmony_ci			goto err_parse;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci		s_asd = v4l2_async_nf_add_fwnode_remote(&cio2->notifier, ep,
148562306a36Sopenharmony_ci							struct
148662306a36Sopenharmony_ci							sensor_async_subdev);
148762306a36Sopenharmony_ci		if (IS_ERR(s_asd)) {
148862306a36Sopenharmony_ci			ret = PTR_ERR(s_asd);
148962306a36Sopenharmony_ci			goto err_parse;
149062306a36Sopenharmony_ci		}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci		s_asd->csi2.port = vep.base.port;
149362306a36Sopenharmony_ci		s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci		fwnode_handle_put(ep);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci		continue;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_cierr_parse:
150062306a36Sopenharmony_ci		fwnode_handle_put(ep);
150162306a36Sopenharmony_ci		return ret;
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	/*
150562306a36Sopenharmony_ci	 * Proceed even without sensors connected to allow the device to
150662306a36Sopenharmony_ci	 * suspend.
150762306a36Sopenharmony_ci	 */
150862306a36Sopenharmony_ci	cio2->notifier.ops = &cio2_async_ops;
150962306a36Sopenharmony_ci	ret = v4l2_async_nf_register(&cio2->notifier);
151062306a36Sopenharmony_ci	if (ret)
151162306a36Sopenharmony_ci		dev_err(dev, "failed to register async notifier : %d\n", ret);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	return ret;
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci/**************** Queue initialization ****************/
151762306a36Sopenharmony_cistatic const struct media_entity_operations cio2_media_ops = {
151862306a36Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate,
151962306a36Sopenharmony_ci};
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_cistatic const struct media_entity_operations cio2_video_entity_ops = {
152262306a36Sopenharmony_ci	.link_validate = cio2_video_link_validate,
152362306a36Sopenharmony_ci};
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	static const u32 default_width = 1936;
152862306a36Sopenharmony_ci	static const u32 default_height = 1096;
152962306a36Sopenharmony_ci	const struct ipu3_cio2_fmt dflt_fmt = formats[0];
153062306a36Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
153162306a36Sopenharmony_ci	struct video_device *vdev = &q->vdev;
153262306a36Sopenharmony_ci	struct vb2_queue *vbq = &q->vbq;
153362306a36Sopenharmony_ci	struct v4l2_subdev *subdev = &q->subdev;
153462306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt;
153562306a36Sopenharmony_ci	int r;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* Initialize miscellaneous variables */
153862306a36Sopenharmony_ci	mutex_init(&q->lock);
153962306a36Sopenharmony_ci	mutex_init(&q->subdev_lock);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/* Initialize formats to default values */
154262306a36Sopenharmony_ci	fmt = &q->subdev_fmt;
154362306a36Sopenharmony_ci	fmt->width = default_width;
154462306a36Sopenharmony_ci	fmt->height = default_height;
154562306a36Sopenharmony_ci	fmt->code = dflt_fmt.mbus_code;
154662306a36Sopenharmony_ci	fmt->field = V4L2_FIELD_NONE;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	q->format.width = default_width;
154962306a36Sopenharmony_ci	q->format.height = default_height;
155062306a36Sopenharmony_ci	q->format.pixelformat = dflt_fmt.fourcc;
155162306a36Sopenharmony_ci	q->format.colorspace = V4L2_COLORSPACE_RAW;
155262306a36Sopenharmony_ci	q->format.field = V4L2_FIELD_NONE;
155362306a36Sopenharmony_ci	q->format.num_planes = 1;
155462306a36Sopenharmony_ci	q->format.plane_fmt[0].bytesperline =
155562306a36Sopenharmony_ci				cio2_bytesperline(q->format.width);
155662306a36Sopenharmony_ci	q->format.plane_fmt[0].sizeimage = q->format.plane_fmt[0].bytesperline *
155762306a36Sopenharmony_ci						q->format.height;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	/* Initialize fbpt */
156062306a36Sopenharmony_ci	r = cio2_fbpt_init(cio2, q);
156162306a36Sopenharmony_ci	if (r)
156262306a36Sopenharmony_ci		goto fail_fbpt;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	/* Initialize media entities */
156562306a36Sopenharmony_ci	q->subdev_pads[CIO2_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
156662306a36Sopenharmony_ci		MEDIA_PAD_FL_MUST_CONNECT;
156762306a36Sopenharmony_ci	q->subdev_pads[CIO2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
156862306a36Sopenharmony_ci	subdev->entity.ops = &cio2_media_ops;
156962306a36Sopenharmony_ci	subdev->internal_ops = &cio2_subdev_internal_ops;
157062306a36Sopenharmony_ci	r = media_entity_pads_init(&subdev->entity, CIO2_PADS, q->subdev_pads);
157162306a36Sopenharmony_ci	if (r) {
157262306a36Sopenharmony_ci		dev_err(dev, "failed initialize subdev media entity (%d)\n", r);
157362306a36Sopenharmony_ci		goto fail_subdev_media_entity;
157462306a36Sopenharmony_ci	}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	q->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
157762306a36Sopenharmony_ci	vdev->entity.ops = &cio2_video_entity_ops;
157862306a36Sopenharmony_ci	r = media_entity_pads_init(&vdev->entity, 1, &q->vdev_pad);
157962306a36Sopenharmony_ci	if (r) {
158062306a36Sopenharmony_ci		dev_err(dev, "failed initialize videodev media entity (%d)\n",
158162306a36Sopenharmony_ci			r);
158262306a36Sopenharmony_ci		goto fail_vdev_media_entity;
158362306a36Sopenharmony_ci	}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	/* Initialize subdev */
158662306a36Sopenharmony_ci	v4l2_subdev_init(subdev, &cio2_subdev_ops);
158762306a36Sopenharmony_ci	subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
158862306a36Sopenharmony_ci	subdev->owner = THIS_MODULE;
158962306a36Sopenharmony_ci	snprintf(subdev->name, sizeof(subdev->name),
159062306a36Sopenharmony_ci		 CIO2_ENTITY_NAME " %td", q - cio2->queue);
159162306a36Sopenharmony_ci	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
159262306a36Sopenharmony_ci	v4l2_set_subdevdata(subdev, cio2);
159362306a36Sopenharmony_ci	r = v4l2_device_register_subdev(&cio2->v4l2_dev, subdev);
159462306a36Sopenharmony_ci	if (r) {
159562306a36Sopenharmony_ci		dev_err(dev, "failed initialize subdev (%d)\n", r);
159662306a36Sopenharmony_ci		goto fail_subdev;
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/* Initialize vbq */
160062306a36Sopenharmony_ci	vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
160162306a36Sopenharmony_ci	vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF;
160262306a36Sopenharmony_ci	vbq->ops = &cio2_vb2_ops;
160362306a36Sopenharmony_ci	vbq->mem_ops = &vb2_dma_sg_memops;
160462306a36Sopenharmony_ci	vbq->buf_struct_size = sizeof(struct cio2_buffer);
160562306a36Sopenharmony_ci	vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
160662306a36Sopenharmony_ci	vbq->min_buffers_needed = 1;
160762306a36Sopenharmony_ci	vbq->drv_priv = cio2;
160862306a36Sopenharmony_ci	vbq->lock = &q->lock;
160962306a36Sopenharmony_ci	r = vb2_queue_init(vbq);
161062306a36Sopenharmony_ci	if (r) {
161162306a36Sopenharmony_ci		dev_err(dev, "failed to initialize videobuf2 queue (%d)\n", r);
161262306a36Sopenharmony_ci		goto fail_subdev;
161362306a36Sopenharmony_ci	}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	/* Initialize vdev */
161662306a36Sopenharmony_ci	snprintf(vdev->name, sizeof(vdev->name),
161762306a36Sopenharmony_ci		 "%s %td", CIO2_NAME, q - cio2->queue);
161862306a36Sopenharmony_ci	vdev->release = video_device_release_empty;
161962306a36Sopenharmony_ci	vdev->fops = &cio2_v4l2_fops;
162062306a36Sopenharmony_ci	vdev->ioctl_ops = &cio2_v4l2_ioctl_ops;
162162306a36Sopenharmony_ci	vdev->lock = &cio2->lock;
162262306a36Sopenharmony_ci	vdev->v4l2_dev = &cio2->v4l2_dev;
162362306a36Sopenharmony_ci	vdev->queue = &q->vbq;
162462306a36Sopenharmony_ci	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING;
162562306a36Sopenharmony_ci	video_set_drvdata(vdev, cio2);
162662306a36Sopenharmony_ci	r = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
162762306a36Sopenharmony_ci	if (r) {
162862306a36Sopenharmony_ci		dev_err(dev, "failed to register video device (%d)\n", r);
162962306a36Sopenharmony_ci		goto fail_vdev;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* Create link from CIO2 subdev to output node */
163362306a36Sopenharmony_ci	r = media_create_pad_link(
163462306a36Sopenharmony_ci		&subdev->entity, CIO2_PAD_SOURCE, &vdev->entity, 0,
163562306a36Sopenharmony_ci		MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
163662306a36Sopenharmony_ci	if (r)
163762306a36Sopenharmony_ci		goto fail_link;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	return 0;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_cifail_link:
164262306a36Sopenharmony_ci	vb2_video_unregister_device(&q->vdev);
164362306a36Sopenharmony_cifail_vdev:
164462306a36Sopenharmony_ci	v4l2_device_unregister_subdev(subdev);
164562306a36Sopenharmony_cifail_subdev:
164662306a36Sopenharmony_ci	media_entity_cleanup(&vdev->entity);
164762306a36Sopenharmony_cifail_vdev_media_entity:
164862306a36Sopenharmony_ci	media_entity_cleanup(&subdev->entity);
164962306a36Sopenharmony_cifail_subdev_media_entity:
165062306a36Sopenharmony_ci	cio2_fbpt_exit(q, dev);
165162306a36Sopenharmony_cifail_fbpt:
165262306a36Sopenharmony_ci	mutex_destroy(&q->subdev_lock);
165362306a36Sopenharmony_ci	mutex_destroy(&q->lock);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	return r;
165662306a36Sopenharmony_ci}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_cistatic void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
165962306a36Sopenharmony_ci{
166062306a36Sopenharmony_ci	vb2_video_unregister_device(&q->vdev);
166162306a36Sopenharmony_ci	media_entity_cleanup(&q->vdev.entity);
166262306a36Sopenharmony_ci	v4l2_device_unregister_subdev(&q->subdev);
166362306a36Sopenharmony_ci	media_entity_cleanup(&q->subdev.entity);
166462306a36Sopenharmony_ci	cio2_fbpt_exit(q, &cio2->pci_dev->dev);
166562306a36Sopenharmony_ci	mutex_destroy(&q->subdev_lock);
166662306a36Sopenharmony_ci	mutex_destroy(&q->lock);
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic int cio2_queues_init(struct cio2_device *cio2)
167062306a36Sopenharmony_ci{
167162306a36Sopenharmony_ci	int i, r;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	for (i = 0; i < CIO2_QUEUES; i++) {
167462306a36Sopenharmony_ci		r = cio2_queue_init(cio2, &cio2->queue[i]);
167562306a36Sopenharmony_ci		if (r)
167662306a36Sopenharmony_ci			break;
167762306a36Sopenharmony_ci	}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	if (i == CIO2_QUEUES)
168062306a36Sopenharmony_ci		return 0;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	for (i--; i >= 0; i--)
168362306a36Sopenharmony_ci		cio2_queue_exit(cio2, &cio2->queue[i]);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return r;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_cistatic void cio2_queues_exit(struct cio2_device *cio2)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	unsigned int i;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	for (i = 0; i < CIO2_QUEUES; i++)
169362306a36Sopenharmony_ci		cio2_queue_exit(cio2, &cio2->queue[i]);
169462306a36Sopenharmony_ci}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_cistatic int cio2_check_fwnode_graph(struct fwnode_handle *fwnode)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	struct fwnode_handle *endpoint;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(fwnode))
170162306a36Sopenharmony_ci		return -EINVAL;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
170462306a36Sopenharmony_ci	if (endpoint) {
170562306a36Sopenharmony_ci		fwnode_handle_put(endpoint);
170662306a36Sopenharmony_ci		return 0;
170762306a36Sopenharmony_ci	}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	return cio2_check_fwnode_graph(fwnode->secondary);
171062306a36Sopenharmony_ci}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci/**************** PCI interface ****************/
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_cistatic int cio2_pci_probe(struct pci_dev *pci_dev,
171562306a36Sopenharmony_ci			  const struct pci_device_id *id)
171662306a36Sopenharmony_ci{
171762306a36Sopenharmony_ci	struct device *dev = &pci_dev->dev;
171862306a36Sopenharmony_ci	struct fwnode_handle *fwnode = dev_fwnode(dev);
171962306a36Sopenharmony_ci	struct cio2_device *cio2;
172062306a36Sopenharmony_ci	int r;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	/*
172362306a36Sopenharmony_ci	 * On some platforms no connections to sensors are defined in firmware,
172462306a36Sopenharmony_ci	 * if the device has no endpoints then we can try to build those as
172562306a36Sopenharmony_ci	 * software_nodes parsed from SSDB.
172662306a36Sopenharmony_ci	 */
172762306a36Sopenharmony_ci	r = cio2_check_fwnode_graph(fwnode);
172862306a36Sopenharmony_ci	if (r) {
172962306a36Sopenharmony_ci		if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) {
173062306a36Sopenharmony_ci			dev_err(dev, "fwnode graph has no endpoints connected\n");
173162306a36Sopenharmony_ci			return -EINVAL;
173262306a36Sopenharmony_ci		}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci		r = ipu_bridge_init(dev, ipu_bridge_parse_ssdb);
173562306a36Sopenharmony_ci		if (r)
173662306a36Sopenharmony_ci			return r;
173762306a36Sopenharmony_ci	}
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	cio2 = devm_kzalloc(dev, sizeof(*cio2), GFP_KERNEL);
174062306a36Sopenharmony_ci	if (!cio2)
174162306a36Sopenharmony_ci		return -ENOMEM;
174262306a36Sopenharmony_ci	cio2->pci_dev = pci_dev;
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	r = pcim_enable_device(pci_dev);
174562306a36Sopenharmony_ci	if (r) {
174662306a36Sopenharmony_ci		dev_err(dev, "failed to enable device (%d)\n", r);
174762306a36Sopenharmony_ci		return r;
174862306a36Sopenharmony_ci	}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	dev_info(dev, "device 0x%x (rev: 0x%x)\n",
175162306a36Sopenharmony_ci		 pci_dev->device, pci_dev->revision);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	r = pcim_iomap_regions(pci_dev, 1 << CIO2_PCI_BAR, pci_name(pci_dev));
175462306a36Sopenharmony_ci	if (r) {
175562306a36Sopenharmony_ci		dev_err(dev, "failed to remap I/O memory (%d)\n", r);
175662306a36Sopenharmony_ci		return -ENODEV;
175762306a36Sopenharmony_ci	}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	cio2->base = pcim_iomap_table(pci_dev)[CIO2_PCI_BAR];
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, cio2);
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	pci_set_master(pci_dev);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	r = dma_set_mask(&pci_dev->dev, CIO2_DMA_MASK);
176662306a36Sopenharmony_ci	if (r) {
176762306a36Sopenharmony_ci		dev_err(dev, "failed to set DMA mask (%d)\n", r);
176862306a36Sopenharmony_ci		return -ENODEV;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	r = pci_enable_msi(pci_dev);
177262306a36Sopenharmony_ci	if (r) {
177362306a36Sopenharmony_ci		dev_err(dev, "failed to enable MSI (%d)\n", r);
177462306a36Sopenharmony_ci		return r;
177562306a36Sopenharmony_ci	}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	r = cio2_fbpt_init_dummy(cio2);
177862306a36Sopenharmony_ci	if (r)
177962306a36Sopenharmony_ci		return r;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	mutex_init(&cio2->lock);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	cio2->media_dev.dev = dev;
178462306a36Sopenharmony_ci	strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME,
178562306a36Sopenharmony_ci		sizeof(cio2->media_dev.model));
178662306a36Sopenharmony_ci	cio2->media_dev.hw_revision = 0;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	media_device_init(&cio2->media_dev);
178962306a36Sopenharmony_ci	r = media_device_register(&cio2->media_dev);
179062306a36Sopenharmony_ci	if (r < 0)
179162306a36Sopenharmony_ci		goto fail_mutex_destroy;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	cio2->v4l2_dev.mdev = &cio2->media_dev;
179462306a36Sopenharmony_ci	r = v4l2_device_register(dev, &cio2->v4l2_dev);
179562306a36Sopenharmony_ci	if (r) {
179662306a36Sopenharmony_ci		dev_err(dev, "failed to register V4L2 device (%d)\n", r);
179762306a36Sopenharmony_ci		goto fail_media_device_unregister;
179862306a36Sopenharmony_ci	}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	r = cio2_queues_init(cio2);
180162306a36Sopenharmony_ci	if (r)
180262306a36Sopenharmony_ci		goto fail_v4l2_device_unregister;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	/* Register notifier for subdevices we care */
180762306a36Sopenharmony_ci	r = cio2_parse_firmware(cio2);
180862306a36Sopenharmony_ci	if (r)
180962306a36Sopenharmony_ci		goto fail_clean_notifier;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED,
181262306a36Sopenharmony_ci			     CIO2_NAME, cio2);
181362306a36Sopenharmony_ci	if (r) {
181462306a36Sopenharmony_ci		dev_err(dev, "failed to request IRQ (%d)\n", r);
181562306a36Sopenharmony_ci		goto fail_clean_notifier;
181662306a36Sopenharmony_ci	}
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
181962306a36Sopenharmony_ci	pm_runtime_allow(dev);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	return 0;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_cifail_clean_notifier:
182462306a36Sopenharmony_ci	v4l2_async_nf_unregister(&cio2->notifier);
182562306a36Sopenharmony_ci	v4l2_async_nf_cleanup(&cio2->notifier);
182662306a36Sopenharmony_ci	cio2_queues_exit(cio2);
182762306a36Sopenharmony_cifail_v4l2_device_unregister:
182862306a36Sopenharmony_ci	v4l2_device_unregister(&cio2->v4l2_dev);
182962306a36Sopenharmony_cifail_media_device_unregister:
183062306a36Sopenharmony_ci	media_device_unregister(&cio2->media_dev);
183162306a36Sopenharmony_ci	media_device_cleanup(&cio2->media_dev);
183262306a36Sopenharmony_cifail_mutex_destroy:
183362306a36Sopenharmony_ci	mutex_destroy(&cio2->lock);
183462306a36Sopenharmony_ci	cio2_fbpt_exit_dummy(cio2);
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	return r;
183762306a36Sopenharmony_ci}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_cistatic void cio2_pci_remove(struct pci_dev *pci_dev)
184062306a36Sopenharmony_ci{
184162306a36Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	media_device_unregister(&cio2->media_dev);
184462306a36Sopenharmony_ci	v4l2_async_nf_unregister(&cio2->notifier);
184562306a36Sopenharmony_ci	v4l2_async_nf_cleanup(&cio2->notifier);
184662306a36Sopenharmony_ci	cio2_queues_exit(cio2);
184762306a36Sopenharmony_ci	cio2_fbpt_exit_dummy(cio2);
184862306a36Sopenharmony_ci	v4l2_device_unregister(&cio2->v4l2_dev);
184962306a36Sopenharmony_ci	media_device_cleanup(&cio2->media_dev);
185062306a36Sopenharmony_ci	mutex_destroy(&cio2->lock);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	pm_runtime_forbid(&pci_dev->dev);
185362306a36Sopenharmony_ci	pm_runtime_get_noresume(&pci_dev->dev);
185462306a36Sopenharmony_ci}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_cistatic int __maybe_unused cio2_runtime_suspend(struct device *dev)
185762306a36Sopenharmony_ci{
185862306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
185962306a36Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
186062306a36Sopenharmony_ci	void __iomem *const base = cio2->base;
186162306a36Sopenharmony_ci	u16 pm;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C);
186462306a36Sopenharmony_ci	dev_dbg(dev, "cio2 runtime suspend.\n");
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
186762306a36Sopenharmony_ci	pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
186862306a36Sopenharmony_ci	pm |= CIO2_PMCSR_D3;
186962306a36Sopenharmony_ci	pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	return 0;
187262306a36Sopenharmony_ci}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_cistatic int __maybe_unused cio2_runtime_resume(struct device *dev)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
187762306a36Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
187862306a36Sopenharmony_ci	void __iomem *const base = cio2->base;
187962306a36Sopenharmony_ci	u16 pm;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C);
188262306a36Sopenharmony_ci	dev_dbg(dev, "cio2 runtime resume.\n");
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
188562306a36Sopenharmony_ci	pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
188662306a36Sopenharmony_ci	pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	return 0;
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci/*
189262306a36Sopenharmony_ci * Helper function to advance all the elements of a circular buffer by "start"
189362306a36Sopenharmony_ci * positions
189462306a36Sopenharmony_ci */
189562306a36Sopenharmony_cistatic void arrange(void *ptr, size_t elem_size, size_t elems, size_t start)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	struct {
189862306a36Sopenharmony_ci		size_t begin, end;
189962306a36Sopenharmony_ci	} arr[2] = {
190062306a36Sopenharmony_ci		{ 0, start - 1 },
190162306a36Sopenharmony_ci		{ start, elems - 1 },
190262306a36Sopenharmony_ci	};
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci#define CHUNK_SIZE(a) ((a)->end - (a)->begin + 1)
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	/* Loop as long as we have out-of-place entries */
190762306a36Sopenharmony_ci	while (CHUNK_SIZE(&arr[0]) && CHUNK_SIZE(&arr[1])) {
190862306a36Sopenharmony_ci		size_t size0, i;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci		/*
191162306a36Sopenharmony_ci		 * Find the number of entries that can be arranged on this
191262306a36Sopenharmony_ci		 * iteration.
191362306a36Sopenharmony_ci		 */
191462306a36Sopenharmony_ci		size0 = min(CHUNK_SIZE(&arr[0]), CHUNK_SIZE(&arr[1]));
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci		/* Swap the entries in two parts of the array. */
191762306a36Sopenharmony_ci		for (i = 0; i < size0; i++) {
191862306a36Sopenharmony_ci			u8 *d = ptr + elem_size * (arr[1].begin + i);
191962306a36Sopenharmony_ci			u8 *s = ptr + elem_size * (arr[0].begin + i);
192062306a36Sopenharmony_ci			size_t j;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci			for (j = 0; j < elem_size; j++)
192362306a36Sopenharmony_ci				swap(d[j], s[j]);
192462306a36Sopenharmony_ci		}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci		if (CHUNK_SIZE(&arr[0]) > CHUNK_SIZE(&arr[1])) {
192762306a36Sopenharmony_ci			/* The end of the first array remains unarranged. */
192862306a36Sopenharmony_ci			arr[0].begin += size0;
192962306a36Sopenharmony_ci		} else {
193062306a36Sopenharmony_ci			/*
193162306a36Sopenharmony_ci			 * The first array is fully arranged so we proceed
193262306a36Sopenharmony_ci			 * handling the next one.
193362306a36Sopenharmony_ci			 */
193462306a36Sopenharmony_ci			arr[0].begin = arr[1].begin;
193562306a36Sopenharmony_ci			arr[0].end = arr[1].begin + size0 - 1;
193662306a36Sopenharmony_ci			arr[1].begin += size0;
193762306a36Sopenharmony_ci		}
193862306a36Sopenharmony_ci	}
193962306a36Sopenharmony_ci}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_cistatic void cio2_fbpt_rearrange(struct cio2_device *cio2, struct cio2_queue *q)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	unsigned int i, j;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	for (i = 0, j = q->bufs_first; i < CIO2_MAX_BUFFERS;
194662306a36Sopenharmony_ci		i++, j = (j + 1) % CIO2_MAX_BUFFERS)
194762306a36Sopenharmony_ci		if (q->bufs[j])
194862306a36Sopenharmony_ci			break;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	if (i == CIO2_MAX_BUFFERS)
195162306a36Sopenharmony_ci		return;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	if (j) {
195462306a36Sopenharmony_ci		arrange(q->fbpt, sizeof(struct cio2_fbpt_entry) * CIO2_MAX_LOPS,
195562306a36Sopenharmony_ci			CIO2_MAX_BUFFERS, j);
195662306a36Sopenharmony_ci		arrange(q->bufs, sizeof(struct cio2_buffer *),
195762306a36Sopenharmony_ci			CIO2_MAX_BUFFERS, j);
195862306a36Sopenharmony_ci	}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	/*
196162306a36Sopenharmony_ci	 * DMA clears the valid bit when accessing the buffer.
196262306a36Sopenharmony_ci	 * When stopping stream in suspend callback, some of the buffers
196362306a36Sopenharmony_ci	 * may be in invalid state. After resume, when DMA meets the invalid
196462306a36Sopenharmony_ci	 * buffer, it will halt and stop receiving new data.
196562306a36Sopenharmony_ci	 * To avoid DMA halting, set the valid bit for all buffers in FBPT.
196662306a36Sopenharmony_ci	 */
196762306a36Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++)
196862306a36Sopenharmony_ci		cio2_fbpt_entry_enable(cio2, q->fbpt + i * CIO2_MAX_LOPS);
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_cistatic int __maybe_unused cio2_suspend(struct device *dev)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
197462306a36Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
197562306a36Sopenharmony_ci	struct cio2_queue *q = cio2->cur_queue;
197662306a36Sopenharmony_ci	int r;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	dev_dbg(dev, "cio2 suspend\n");
197962306a36Sopenharmony_ci	if (!cio2->streaming)
198062306a36Sopenharmony_ci		return 0;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	/* Stop stream */
198362306a36Sopenharmony_ci	r = v4l2_subdev_call(q->sensor, video, s_stream, 0);
198462306a36Sopenharmony_ci	if (r) {
198562306a36Sopenharmony_ci		dev_err(dev, "failed to stop sensor streaming\n");
198662306a36Sopenharmony_ci		return r;
198762306a36Sopenharmony_ci	}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	cio2_hw_exit(cio2, q);
199062306a36Sopenharmony_ci	synchronize_irq(pci_dev->irq);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	pm_runtime_force_suspend(dev);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	/*
199562306a36Sopenharmony_ci	 * Upon resume, hw starts to process the fbpt entries from beginning,
199662306a36Sopenharmony_ci	 * so relocate the queued buffs to the fbpt head before suspend.
199762306a36Sopenharmony_ci	 */
199862306a36Sopenharmony_ci	cio2_fbpt_rearrange(cio2, q);
199962306a36Sopenharmony_ci	q->bufs_first = 0;
200062306a36Sopenharmony_ci	q->bufs_next = 0;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	return 0;
200362306a36Sopenharmony_ci}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cistatic int __maybe_unused cio2_resume(struct device *dev)
200662306a36Sopenharmony_ci{
200762306a36Sopenharmony_ci	struct cio2_device *cio2 = dev_get_drvdata(dev);
200862306a36Sopenharmony_ci	struct cio2_queue *q = cio2->cur_queue;
200962306a36Sopenharmony_ci	int r;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	dev_dbg(dev, "cio2 resume\n");
201262306a36Sopenharmony_ci	if (!cio2->streaming)
201362306a36Sopenharmony_ci		return 0;
201462306a36Sopenharmony_ci	/* Start stream */
201562306a36Sopenharmony_ci	r = pm_runtime_force_resume(dev);
201662306a36Sopenharmony_ci	if (r < 0) {
201762306a36Sopenharmony_ci		dev_err(dev, "failed to set power %d\n", r);
201862306a36Sopenharmony_ci		return r;
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	r = cio2_hw_init(cio2, q);
202262306a36Sopenharmony_ci	if (r) {
202362306a36Sopenharmony_ci		dev_err(dev, "fail to init cio2 hw\n");
202462306a36Sopenharmony_ci		return r;
202562306a36Sopenharmony_ci	}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	r = v4l2_subdev_call(q->sensor, video, s_stream, 1);
202862306a36Sopenharmony_ci	if (r) {
202962306a36Sopenharmony_ci		dev_err(dev, "fail to start sensor streaming\n");
203062306a36Sopenharmony_ci		cio2_hw_exit(cio2, q);
203162306a36Sopenharmony_ci	}
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	return r;
203462306a36Sopenharmony_ci}
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_cistatic const struct dev_pm_ops cio2_pm_ops = {
203762306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(&cio2_runtime_suspend, &cio2_runtime_resume, NULL)
203862306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(&cio2_suspend, &cio2_resume)
203962306a36Sopenharmony_ci};
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_cistatic const struct pci_device_id cio2_pci_id_table[] = {
204262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) },
204362306a36Sopenharmony_ci	{ }
204462306a36Sopenharmony_ci};
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cio2_pci_id_table);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_cistatic struct pci_driver cio2_pci_driver = {
204962306a36Sopenharmony_ci	.name = CIO2_NAME,
205062306a36Sopenharmony_ci	.id_table = cio2_pci_id_table,
205162306a36Sopenharmony_ci	.probe = cio2_pci_probe,
205262306a36Sopenharmony_ci	.remove = cio2_pci_remove,
205362306a36Sopenharmony_ci	.driver = {
205462306a36Sopenharmony_ci		.pm = &cio2_pm_ops,
205562306a36Sopenharmony_ci	},
205662306a36Sopenharmony_ci};
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_cimodule_pci_driver(cio2_pci_driver);
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ciMODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
206162306a36Sopenharmony_ciMODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
206262306a36Sopenharmony_ciMODULE_AUTHOR("Jian Xu Zheng");
206362306a36Sopenharmony_ciMODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
206462306a36Sopenharmony_ciMODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
206562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
206662306a36Sopenharmony_ciMODULE_DESCRIPTION("IPU3 CIO2 driver");
206762306a36Sopenharmony_ciMODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
2068