18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017,2020 Intel Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Based partially on Intel IPU4 driver written by
68c2ecf20Sopenharmony_ci *  Sakari Ailus <sakari.ailus@linux.intel.com>
78c2ecf20Sopenharmony_ci *  Samu Onkalo <samu.onkalo@intel.com>
88c2ecf20Sopenharmony_ci *  Jouni Högander <jouni.hogander@intel.com>
98c2ecf20Sopenharmony_ci *  Jouni Ukkonen <jouni.ukkonen@intel.com>
108c2ecf20Sopenharmony_ci *  Antti Laakso <antti.laakso@intel.com>
118c2ecf20Sopenharmony_ci * et al.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/pfn.h>
208c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
218c2ecf20Sopenharmony_ci#include <linux/property.h>
228c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
238c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
248c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
258c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
268c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h>
278c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
288c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-sg.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "ipu3-cio2.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct ipu3_cio2_fmt {
338c2ecf20Sopenharmony_ci	u32 mbus_code;
348c2ecf20Sopenharmony_ci	u32 fourcc;
358c2ecf20Sopenharmony_ci	u8 mipicode;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * These are raw formats used in Intel's third generation of
408c2ecf20Sopenharmony_ci * Image Processing Unit known as IPU3.
418c2ecf20Sopenharmony_ci * 10bit raw bayer packed, 32 bytes for every 25 pixels,
428c2ecf20Sopenharmony_ci * last LSB 6 bits unused.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_cistatic const struct ipu3_cio2_fmt formats[] = {
458c2ecf20Sopenharmony_ci	{	/* put default entry at beginning */
468c2ecf20Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
478c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SGRBG10,
488c2ecf20Sopenharmony_ci		.mipicode	= 0x2b,
498c2ecf20Sopenharmony_ci	}, {
508c2ecf20Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
518c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SGBRG10,
528c2ecf20Sopenharmony_ci		.mipicode	= 0x2b,
538c2ecf20Sopenharmony_ci	}, {
548c2ecf20Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
558c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SBGGR10,
568c2ecf20Sopenharmony_ci		.mipicode	= 0x2b,
578c2ecf20Sopenharmony_ci	}, {
588c2ecf20Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
598c2ecf20Sopenharmony_ci		.fourcc		= V4L2_PIX_FMT_IPU3_SRGGB10,
608c2ecf20Sopenharmony_ci		.mipicode	= 0x2b,
618c2ecf20Sopenharmony_ci	},
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * cio2_find_format - lookup color format by fourcc or/and media bus code
668c2ecf20Sopenharmony_ci * @pixelformat: fourcc to match, ignored if null
678c2ecf20Sopenharmony_ci * @mbus_code: media bus code to match, ignored if null
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic const struct ipu3_cio2_fmt *cio2_find_format(const u32 *pixelformat,
708c2ecf20Sopenharmony_ci						    const u32 *mbus_code)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	unsigned int i;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++) {
758c2ecf20Sopenharmony_ci		if (pixelformat && *pixelformat != formats[i].fourcc)
768c2ecf20Sopenharmony_ci			continue;
778c2ecf20Sopenharmony_ci		if (mbus_code && *mbus_code != formats[i].mbus_code)
788c2ecf20Sopenharmony_ci			continue;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		return &formats[i];
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return NULL;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic inline u32 cio2_bytesperline(const unsigned int width)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	/*
898c2ecf20Sopenharmony_ci	 * 64 bytes for every 50 pixels, the line length
908c2ecf20Sopenharmony_ci	 * in bytes is multiple of 64 (line end alignment).
918c2ecf20Sopenharmony_ci	 */
928c2ecf20Sopenharmony_ci	return DIV_ROUND_UP(width, 50) * 64;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/**************** FBPT operations ****************/
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void cio2_fbpt_exit_dummy(struct cio2_device *cio2)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	if (cio2->dummy_lop) {
1008c2ecf20Sopenharmony_ci		dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE,
1018c2ecf20Sopenharmony_ci				  cio2->dummy_lop, cio2->dummy_lop_bus_addr);
1028c2ecf20Sopenharmony_ci		cio2->dummy_lop = NULL;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	if (cio2->dummy_page) {
1058c2ecf20Sopenharmony_ci		dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE,
1068c2ecf20Sopenharmony_ci				  cio2->dummy_page, cio2->dummy_page_bus_addr);
1078c2ecf20Sopenharmony_ci		cio2->dummy_page = NULL;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int cio2_fbpt_init_dummy(struct cio2_device *cio2)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	unsigned int i;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	cio2->dummy_page = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE,
1168c2ecf20Sopenharmony_ci					      &cio2->dummy_page_bus_addr,
1178c2ecf20Sopenharmony_ci					      GFP_KERNEL);
1188c2ecf20Sopenharmony_ci	cio2->dummy_lop = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE,
1198c2ecf20Sopenharmony_ci					     &cio2->dummy_lop_bus_addr,
1208c2ecf20Sopenharmony_ci					     GFP_KERNEL);
1218c2ecf20Sopenharmony_ci	if (!cio2->dummy_page || !cio2->dummy_lop) {
1228c2ecf20Sopenharmony_ci		cio2_fbpt_exit_dummy(cio2);
1238c2ecf20Sopenharmony_ci		return -ENOMEM;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * List of Pointers(LOP) contains 1024x32b pointers to 4KB page each
1278c2ecf20Sopenharmony_ci	 * Initialize each entry to dummy_page bus base address.
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_LOP_ENTRIES; i++)
1308c2ecf20Sopenharmony_ci		cio2->dummy_lop[i] = PFN_DOWN(cio2->dummy_page_bus_addr);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void cio2_fbpt_entry_enable(struct cio2_device *cio2,
1368c2ecf20Sopenharmony_ci				   struct cio2_fbpt_entry entry[CIO2_MAX_LOPS])
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	/*
1398c2ecf20Sopenharmony_ci	 * The CPU first initializes some fields in fbpt, then sets
1408c2ecf20Sopenharmony_ci	 * the VALID bit, this barrier is to ensure that the DMA(device)
1418c2ecf20Sopenharmony_ci	 * does not see the VALID bit enabled before other fields are
1428c2ecf20Sopenharmony_ci	 * initialized; otherwise it could lead to havoc.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	dma_wmb();
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/*
1478c2ecf20Sopenharmony_ci	 * Request interrupts for start and completion
1488c2ecf20Sopenharmony_ci	 * Valid bit is applicable only to 1st entry
1498c2ecf20Sopenharmony_ci	 */
1508c2ecf20Sopenharmony_ci	entry[0].first_entry.ctrl = CIO2_FBPT_CTRL_VALID |
1518c2ecf20Sopenharmony_ci		CIO2_FBPT_CTRL_IOC | CIO2_FBPT_CTRL_IOS;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/* Initialize fpbt entries to point to dummy frame */
1558c2ecf20Sopenharmony_cistatic void cio2_fbpt_entry_init_dummy(struct cio2_device *cio2,
1568c2ecf20Sopenharmony_ci				       struct cio2_fbpt_entry
1578c2ecf20Sopenharmony_ci				       entry[CIO2_MAX_LOPS])
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	unsigned int i;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	entry[0].first_entry.first_page_offset = 0;
1628c2ecf20Sopenharmony_ci	entry[1].second_entry.num_of_pages = CIO2_LOP_ENTRIES * CIO2_MAX_LOPS;
1638c2ecf20Sopenharmony_ci	entry[1].second_entry.last_page_available_bytes = PAGE_SIZE - 1;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_MAX_LOPS; i++)
1668c2ecf20Sopenharmony_ci		entry[i].lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	cio2_fbpt_entry_enable(cio2, entry);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* Initialize fpbt entries to point to a given buffer */
1728c2ecf20Sopenharmony_cistatic void cio2_fbpt_entry_init_buf(struct cio2_device *cio2,
1738c2ecf20Sopenharmony_ci				     struct cio2_buffer *b,
1748c2ecf20Sopenharmony_ci				     struct cio2_fbpt_entry
1758c2ecf20Sopenharmony_ci				     entry[CIO2_MAX_LOPS])
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct vb2_buffer *vb = &b->vbb.vb2_buf;
1788c2ecf20Sopenharmony_ci	unsigned int length = vb->planes[0].length;
1798c2ecf20Sopenharmony_ci	int remaining, i;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	entry[0].first_entry.first_page_offset = b->offset;
1828c2ecf20Sopenharmony_ci	remaining = length + entry[0].first_entry.first_page_offset;
1838c2ecf20Sopenharmony_ci	entry[1].second_entry.num_of_pages = PFN_UP(remaining);
1848c2ecf20Sopenharmony_ci	/*
1858c2ecf20Sopenharmony_ci	 * last_page_available_bytes has the offset of the last byte in the
1868c2ecf20Sopenharmony_ci	 * last page which is still accessible by DMA. DMA cannot access
1878c2ecf20Sopenharmony_ci	 * beyond this point. Valid range for this is from 0 to 4095.
1888c2ecf20Sopenharmony_ci	 * 0 indicates 1st byte in the page is DMA accessible.
1898c2ecf20Sopenharmony_ci	 * 4095 (PAGE_SIZE - 1) means every single byte in the last page
1908c2ecf20Sopenharmony_ci	 * is available for DMA transfer.
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	entry[1].second_entry.last_page_available_bytes =
1938c2ecf20Sopenharmony_ci			(remaining & ~PAGE_MASK) ?
1948c2ecf20Sopenharmony_ci				(remaining & ~PAGE_MASK) - 1 : PAGE_SIZE - 1;
1958c2ecf20Sopenharmony_ci	/* Fill FBPT */
1968c2ecf20Sopenharmony_ci	remaining = length;
1978c2ecf20Sopenharmony_ci	i = 0;
1988c2ecf20Sopenharmony_ci	while (remaining > 0) {
1998c2ecf20Sopenharmony_ci		entry->lop_page_addr = PFN_DOWN(b->lop_bus_addr[i]);
2008c2ecf20Sopenharmony_ci		remaining -= CIO2_LOP_ENTRIES * PAGE_SIZE;
2018c2ecf20Sopenharmony_ci		entry++;
2028c2ecf20Sopenharmony_ci		i++;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/*
2068c2ecf20Sopenharmony_ci	 * The first not meaningful FBPT entry should point to a valid LOP
2078c2ecf20Sopenharmony_ci	 */
2088c2ecf20Sopenharmony_ci	entry->lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	cio2_fbpt_entry_enable(cio2, entry);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int cio2_fbpt_init(struct cio2_device *cio2, struct cio2_queue *q)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	q->fbpt = dma_alloc_coherent(dev, CIO2_FBPT_SIZE, &q->fbpt_bus_addr,
2188c2ecf20Sopenharmony_ci				     GFP_KERNEL);
2198c2ecf20Sopenharmony_ci	if (!q->fbpt)
2208c2ecf20Sopenharmony_ci		return -ENOMEM;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void cio2_fbpt_exit(struct cio2_queue *q, struct device *dev)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	dma_free_coherent(dev, CIO2_FBPT_SIZE, q->fbpt, q->fbpt_bus_addr);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/**************** CSI2 hardware setup ****************/
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/*
2338c2ecf20Sopenharmony_ci * The CSI2 receiver has several parameters affecting
2348c2ecf20Sopenharmony_ci * the receiver timings. These depend on the MIPI bus frequency
2358c2ecf20Sopenharmony_ci * F in Hz (sensor transmitter rate) as follows:
2368c2ecf20Sopenharmony_ci *     register value = (A/1e9 + B * UI) / COUNT_ACC
2378c2ecf20Sopenharmony_ci * where
2388c2ecf20Sopenharmony_ci *      UI = 1 / (2 * F) in seconds
2398c2ecf20Sopenharmony_ci *      COUNT_ACC = counter accuracy in seconds
2408c2ecf20Sopenharmony_ci *      For IPU3 COUNT_ACC = 0.0625
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * A and B are coefficients from the table below,
2438c2ecf20Sopenharmony_ci * depending whether the register minimum or maximum value is
2448c2ecf20Sopenharmony_ci * calculated.
2458c2ecf20Sopenharmony_ci *                                     Minimum     Maximum
2468c2ecf20Sopenharmony_ci * Clock lane                          A     B     A     B
2478c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_clane     0     0    38     0
2488c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_clane    95    -8   300   -16
2498c2ecf20Sopenharmony_ci * Data lanes
2508c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane0    0     0    35     4
2518c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane0   85    -2   145    -6
2528c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane1    0     0    35     4
2538c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane1   85    -2   145    -6
2548c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane2    0     0    35     4
2558c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane2   85    -2   145    -6
2568c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_termen_dlane3    0     0    35     4
2578c2ecf20Sopenharmony_ci * reg_rx_csi_dly_cnt_settle_dlane3   85    -2   145    -6
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * We use the minimum values of both A and B.
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/*
2638c2ecf20Sopenharmony_ci * shift for keeping value range suitable for 32-bit integer arithmetic
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_ci#define LIMIT_SHIFT	8
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic s32 cio2_rx_timing(s32 a, s32 b, s64 freq, int def)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	const u32 accinv = 16; /* invert of counter resolution */
2708c2ecf20Sopenharmony_ci	const u32 uiinv = 500000000; /* 1e9 / 2 */
2718c2ecf20Sopenharmony_ci	s32 r;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	freq >>= LIMIT_SHIFT;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (WARN_ON(freq <= 0 || freq > S32_MAX))
2768c2ecf20Sopenharmony_ci		return def;
2778c2ecf20Sopenharmony_ci	/*
2788c2ecf20Sopenharmony_ci	 * b could be 0, -2 or -8, so |accinv * b| is always
2798c2ecf20Sopenharmony_ci	 * less than (1 << ds) and thus |r| < 500000000.
2808c2ecf20Sopenharmony_ci	 */
2818c2ecf20Sopenharmony_ci	r = accinv * b * (uiinv >> LIMIT_SHIFT);
2828c2ecf20Sopenharmony_ci	r = r / (s32)freq;
2838c2ecf20Sopenharmony_ci	/* max value of a is 95 */
2848c2ecf20Sopenharmony_ci	r += accinv * a;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return r;
2878c2ecf20Sopenharmony_ci};
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/* Calculate the the delay value for termination enable of clock lane HS Rx */
2908c2ecf20Sopenharmony_cistatic int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q,
2918c2ecf20Sopenharmony_ci				 struct cio2_csi2_timing *timing)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
2948c2ecf20Sopenharmony_ci	struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ };
2958c2ecf20Sopenharmony_ci	struct v4l2_ctrl *link_freq;
2968c2ecf20Sopenharmony_ci	s64 freq;
2978c2ecf20Sopenharmony_ci	int r;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (!q->sensor)
3008c2ecf20Sopenharmony_ci		return -ENODEV;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	link_freq = v4l2_ctrl_find(q->sensor->ctrl_handler, V4L2_CID_LINK_FREQ);
3038c2ecf20Sopenharmony_ci	if (!link_freq) {
3048c2ecf20Sopenharmony_ci		dev_err(dev, "failed to find LINK_FREQ\n");
3058c2ecf20Sopenharmony_ci		return -EPIPE;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	qm.index = v4l2_ctrl_g_ctrl(link_freq);
3098c2ecf20Sopenharmony_ci	r = v4l2_querymenu(q->sensor->ctrl_handler, &qm);
3108c2ecf20Sopenharmony_ci	if (r) {
3118c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get menu item\n");
3128c2ecf20Sopenharmony_ci		return r;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (!qm.value) {
3168c2ecf20Sopenharmony_ci		dev_err(dev, "error invalid link_freq\n");
3178c2ecf20Sopenharmony_ci		return -EINVAL;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	freq = qm.value;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	timing->clk_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_A,
3228c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_B,
3238c2ecf20Sopenharmony_ci					    freq,
3248c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT);
3258c2ecf20Sopenharmony_ci	timing->clk_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_A,
3268c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_CLANE_B,
3278c2ecf20Sopenharmony_ci					    freq,
3288c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT);
3298c2ecf20Sopenharmony_ci	timing->dat_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_A,
3308c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_DLANE_B,
3318c2ecf20Sopenharmony_ci					    freq,
3328c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT);
3338c2ecf20Sopenharmony_ci	timing->dat_settle = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_A,
3348c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_DLANE_B,
3358c2ecf20Sopenharmony_ci					    freq,
3368c2ecf20Sopenharmony_ci					    CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	dev_dbg(dev, "freq ct value is %d\n", timing->clk_termen);
3398c2ecf20Sopenharmony_ci	dev_dbg(dev, "freq cs value is %d\n", timing->clk_settle);
3408c2ecf20Sopenharmony_ci	dev_dbg(dev, "freq dt value is %d\n", timing->dat_termen);
3418c2ecf20Sopenharmony_ci	dev_dbg(dev, "freq ds value is %d\n", timing->dat_settle);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci};
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	static const int NUM_VCS = 4;
3498c2ecf20Sopenharmony_ci	static const int SID;	/* Stream id */
3508c2ecf20Sopenharmony_ci	static const int ENTRY;
3518c2ecf20Sopenharmony_ci	static const int FBPT_WIDTH = DIV_ROUND_UP(CIO2_MAX_LOPS,
3528c2ecf20Sopenharmony_ci					CIO2_FBPT_SUBENTRY_UNIT);
3538c2ecf20Sopenharmony_ci	const u32 num_buffers1 = CIO2_MAX_BUFFERS - 1;
3548c2ecf20Sopenharmony_ci	const struct ipu3_cio2_fmt *fmt;
3558c2ecf20Sopenharmony_ci	void __iomem *const base = cio2->base;
3568c2ecf20Sopenharmony_ci	u8 lanes, csi2bus = q->csi2.port;
3578c2ecf20Sopenharmony_ci	u8 sensor_vc = SENSOR_VIR_CH_DFLT;
3588c2ecf20Sopenharmony_ci	struct cio2_csi2_timing timing = { 0 };
3598c2ecf20Sopenharmony_ci	int i, r;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	fmt = cio2_find_format(NULL, &q->subdev_fmt.code);
3628c2ecf20Sopenharmony_ci	if (!fmt)
3638c2ecf20Sopenharmony_ci		return -EINVAL;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	lanes = q->csi2.lanes;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	r = cio2_csi2_calc_timing(cio2, q, &timing);
3688c2ecf20Sopenharmony_ci	if (r)
3698c2ecf20Sopenharmony_ci		return r;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	writel(timing.clk_termen, q->csi_rx_base +
3728c2ecf20Sopenharmony_ci		CIO2_REG_CSIRX_DLY_CNT_TERMEN(CIO2_CSIRX_DLY_CNT_CLANE_IDX));
3738c2ecf20Sopenharmony_ci	writel(timing.clk_settle, q->csi_rx_base +
3748c2ecf20Sopenharmony_ci		CIO2_REG_CSIRX_DLY_CNT_SETTLE(CIO2_CSIRX_DLY_CNT_CLANE_IDX));
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (i = 0; i < lanes; i++) {
3778c2ecf20Sopenharmony_ci		writel(timing.dat_termen, q->csi_rx_base +
3788c2ecf20Sopenharmony_ci			CIO2_REG_CSIRX_DLY_CNT_TERMEN(i));
3798c2ecf20Sopenharmony_ci		writel(timing.dat_settle, q->csi_rx_base +
3808c2ecf20Sopenharmony_ci			CIO2_REG_CSIRX_DLY_CNT_SETTLE(i));
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	writel(CIO2_PBM_WMCTRL1_MIN_2CK |
3848c2ecf20Sopenharmony_ci	       CIO2_PBM_WMCTRL1_MID1_2CK |
3858c2ecf20Sopenharmony_ci	       CIO2_PBM_WMCTRL1_MID2_2CK, base + CIO2_REG_PBM_WMCTRL1);
3868c2ecf20Sopenharmony_ci	writel(CIO2_PBM_WMCTRL2_HWM_2CK << CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT |
3878c2ecf20Sopenharmony_ci	       CIO2_PBM_WMCTRL2_LWM_2CK << CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT |
3888c2ecf20Sopenharmony_ci	       CIO2_PBM_WMCTRL2_OBFFWM_2CK <<
3898c2ecf20Sopenharmony_ci	       CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT |
3908c2ecf20Sopenharmony_ci	       CIO2_PBM_WMCTRL2_TRANSDYN << CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT |
3918c2ecf20Sopenharmony_ci	       CIO2_PBM_WMCTRL2_OBFF_MEM_EN, base + CIO2_REG_PBM_WMCTRL2);
3928c2ecf20Sopenharmony_ci	writel(CIO2_PBM_ARB_CTRL_LANES_DIV <<
3938c2ecf20Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT |
3948c2ecf20Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_LE_EN |
3958c2ecf20Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN <<
3968c2ecf20Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT |
3978c2ecf20Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP <<
3988c2ecf20Sopenharmony_ci	       CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT,
3998c2ecf20Sopenharmony_ci	       base + CIO2_REG_PBM_ARB_CTRL);
4008c2ecf20Sopenharmony_ci	writel(CIO2_CSIRX_STATUS_DLANE_HS_MASK,
4018c2ecf20Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_HS);
4028c2ecf20Sopenharmony_ci	writel(CIO2_CSIRX_STATUS_DLANE_LP_MASK,
4038c2ecf20Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_CSIRX_STATUS_DLANE_LP);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	writel(CIO2_FB_HPLL_FREQ, base + CIO2_REG_FB_HPLL_FREQ);
4068c2ecf20Sopenharmony_ci	writel(CIO2_ISCLK_RATIO, base + CIO2_REG_ISCLK_RATIO);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* Configure MIPI backend */
4098c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_VCS; i++)
4108c2ecf20Sopenharmony_ci		writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_SP_LUT_ENTRY(i));
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* There are 16 short packet LUT entry */
4138c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
4148c2ecf20Sopenharmony_ci		writel(CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD,
4158c2ecf20Sopenharmony_ci		       q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(i));
4168c2ecf20Sopenharmony_ci	writel(CIO2_MIPIBE_GLOBAL_LUT_DISREGARD,
4178c2ecf20Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_MIPIBE_GLOBAL_LUT_DISREGARD);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	writel(CIO2_INT_EN_EXT_IE_MASK, base + CIO2_REG_INT_EN_EXT_IE);
4208c2ecf20Sopenharmony_ci	writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK);
4218c2ecf20Sopenharmony_ci	writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE);
4228c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_EDGE);
4238c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_LEVEL_NOT_PULSE);
4248c2ecf20Sopenharmony_ci	writel(CIO2_INT_EN_EXT_OE_MASK, base + CIO2_REG_INT_EN_EXT_OE);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	writel(CIO2_REG_INT_EN_IRQ | CIO2_INT_IOC(CIO2_DMA_CHAN) |
4278c2ecf20Sopenharmony_ci	       CIO2_REG_INT_EN_IOS(CIO2_DMA_CHAN),
4288c2ecf20Sopenharmony_ci	       base + CIO2_REG_INT_EN);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	writel((CIO2_PXM_PXF_FMT_CFG_BPP_10 | CIO2_PXM_PXF_FMT_CFG_PCK_64B)
4318c2ecf20Sopenharmony_ci	       << CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT,
4328c2ecf20Sopenharmony_ci	       base + CIO2_REG_PXM_PXF_FMT_CFG0(csi2bus));
4338c2ecf20Sopenharmony_ci	writel(SID << CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT |
4348c2ecf20Sopenharmony_ci	       sensor_vc << CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT |
4358c2ecf20Sopenharmony_ci	       fmt->mipicode << CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT,
4368c2ecf20Sopenharmony_ci	       q->csi_rx_base + CIO2_REG_MIPIBE_LP_LUT_ENTRY(ENTRY));
4378c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_COMP_FORMAT(sensor_vc));
4388c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_FORCE_RAW8);
4398c2ecf20Sopenharmony_ci	writel(0, base + CIO2_REG_PXM_SID2BID0(csi2bus));
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	writel(lanes, q->csi_rx_base + CIO2_REG_CSIRX_NOF_ENABLED_LANES);
4428c2ecf20Sopenharmony_ci	writel(CIO2_CGC_PRIM_TGE |
4438c2ecf20Sopenharmony_ci	       CIO2_CGC_SIDE_TGE |
4448c2ecf20Sopenharmony_ci	       CIO2_CGC_XOSC_TGE |
4458c2ecf20Sopenharmony_ci	       CIO2_CGC_D3I3_TGE |
4468c2ecf20Sopenharmony_ci	       CIO2_CGC_CSI2_INTERFRAME_TGE |
4478c2ecf20Sopenharmony_ci	       CIO2_CGC_CSI2_PORT_DCGE |
4488c2ecf20Sopenharmony_ci	       CIO2_CGC_SIDE_DCGE |
4498c2ecf20Sopenharmony_ci	       CIO2_CGC_PRIM_DCGE |
4508c2ecf20Sopenharmony_ci	       CIO2_CGC_ROSC_DCGE |
4518c2ecf20Sopenharmony_ci	       CIO2_CGC_XOSC_DCGE |
4528c2ecf20Sopenharmony_ci	       CIO2_CGC_CLKGATE_HOLDOFF << CIO2_CGC_CLKGATE_HOLDOFF_SHIFT |
4538c2ecf20Sopenharmony_ci	       CIO2_CGC_CSI_CLKGATE_HOLDOFF
4548c2ecf20Sopenharmony_ci	       << CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT, base + CIO2_REG_CGC);
4558c2ecf20Sopenharmony_ci	writel(CIO2_LTRCTRL_LTRDYNEN, base + CIO2_REG_LTRCTRL);
4568c2ecf20Sopenharmony_ci	writel(CIO2_LTRVAL0_VAL << CIO2_LTRVAL02_VAL_SHIFT |
4578c2ecf20Sopenharmony_ci	       CIO2_LTRVAL0_SCALE << CIO2_LTRVAL02_SCALE_SHIFT |
4588c2ecf20Sopenharmony_ci	       CIO2_LTRVAL1_VAL << CIO2_LTRVAL13_VAL_SHIFT |
4598c2ecf20Sopenharmony_ci	       CIO2_LTRVAL1_SCALE << CIO2_LTRVAL13_SCALE_SHIFT,
4608c2ecf20Sopenharmony_ci	       base + CIO2_REG_LTRVAL01);
4618c2ecf20Sopenharmony_ci	writel(CIO2_LTRVAL2_VAL << CIO2_LTRVAL02_VAL_SHIFT |
4628c2ecf20Sopenharmony_ci	       CIO2_LTRVAL2_SCALE << CIO2_LTRVAL02_SCALE_SHIFT |
4638c2ecf20Sopenharmony_ci	       CIO2_LTRVAL3_VAL << CIO2_LTRVAL13_VAL_SHIFT |
4648c2ecf20Sopenharmony_ci	       CIO2_LTRVAL3_SCALE << CIO2_LTRVAL13_SCALE_SHIFT,
4658c2ecf20Sopenharmony_ci	       base + CIO2_REG_LTRVAL23);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_NUM_DMA_CHAN; i++) {
4688c2ecf20Sopenharmony_ci		writel(0, base + CIO2_REG_CDMABA(i));
4698c2ecf20Sopenharmony_ci		writel(0, base + CIO2_REG_CDMAC0(i));
4708c2ecf20Sopenharmony_ci		writel(0, base + CIO2_REG_CDMAC1(i));
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Enable DMA */
4748c2ecf20Sopenharmony_ci	writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(CIO2_DMA_CHAN));
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	writel(num_buffers1 << CIO2_CDMAC0_FBPT_LEN_SHIFT |
4778c2ecf20Sopenharmony_ci	       FBPT_WIDTH << CIO2_CDMAC0_FBPT_WIDTH_SHIFT |
4788c2ecf20Sopenharmony_ci	       CIO2_CDMAC0_DMA_INTR_ON_FE |
4798c2ecf20Sopenharmony_ci	       CIO2_CDMAC0_FBPT_UPDATE_FIFO_FULL |
4808c2ecf20Sopenharmony_ci	       CIO2_CDMAC0_DMA_EN |
4818c2ecf20Sopenharmony_ci	       CIO2_CDMAC0_DMA_INTR_ON_FS |
4828c2ecf20Sopenharmony_ci	       CIO2_CDMAC0_DMA_HALTED, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN));
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	writel(1 << CIO2_CDMAC1_LINENUMUPDATE_SHIFT,
4858c2ecf20Sopenharmony_ci	       base + CIO2_REG_CDMAC1(CIO2_DMA_CHAN));
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	writel(0, base + CIO2_REG_PBM_FOPN_ABORT);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	writel(CIO2_PXM_FRF_CFG_CRC_TH << CIO2_PXM_FRF_CFG_CRC_TH_SHIFT |
4908c2ecf20Sopenharmony_ci	       CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NR |
4918c2ecf20Sopenharmony_ci	       CIO2_PXM_FRF_CFG_MSK_ECC_RE |
4928c2ecf20Sopenharmony_ci	       CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NE,
4938c2ecf20Sopenharmony_ci	       base + CIO2_REG_PXM_FRF_CFG(q->csi2.port));
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* Clear interrupts */
4968c2ecf20Sopenharmony_ci	writel(CIO2_IRQCTRL_MASK, q->csi_rx_base + CIO2_REG_IRQCTRL_CLEAR);
4978c2ecf20Sopenharmony_ci	writel(~0, base + CIO2_REG_INT_STS_EXT_OE);
4988c2ecf20Sopenharmony_ci	writel(~0, base + CIO2_REG_INT_STS_EXT_IE);
4998c2ecf20Sopenharmony_ci	writel(~0, base + CIO2_REG_INT_STS);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	/* Enable devices, starting from the last device in the pipe */
5028c2ecf20Sopenharmony_ci	writel(1, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE);
5038c2ecf20Sopenharmony_ci	writel(1, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return 0;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	void __iomem *const base = cio2->base;
5118c2ecf20Sopenharmony_ci	unsigned int i;
5128c2ecf20Sopenharmony_ci	u32 value;
5138c2ecf20Sopenharmony_ci	int ret;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/* Disable CSI receiver and MIPI backend devices */
5168c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK);
5178c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_ENABLE);
5188c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_CSIRX_ENABLE);
5198c2ecf20Sopenharmony_ci	writel(0, q->csi_rx_base + CIO2_REG_MIPIBE_ENABLE);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* Halt DMA */
5228c2ecf20Sopenharmony_ci	writel(0, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN));
5238c2ecf20Sopenharmony_ci	ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN),
5248c2ecf20Sopenharmony_ci				 value, value & CIO2_CDMAC0_DMA_HALTED,
5258c2ecf20Sopenharmony_ci				 4000, 2000000);
5268c2ecf20Sopenharmony_ci	if (ret)
5278c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
5288c2ecf20Sopenharmony_ci			"DMA %i can not be halted\n", CIO2_DMA_CHAN);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_NUM_PORTS; i++) {
5318c2ecf20Sopenharmony_ci		writel(readl(base + CIO2_REG_PXM_FRF_CFG(i)) |
5328c2ecf20Sopenharmony_ci		       CIO2_PXM_FRF_CFG_ABORT, base + CIO2_REG_PXM_FRF_CFG(i));
5338c2ecf20Sopenharmony_ci		writel(readl(base + CIO2_REG_PBM_FOPN_ABORT) |
5348c2ecf20Sopenharmony_ci		       CIO2_PBM_FOPN_ABORT(i), base + CIO2_REG_PBM_FOPN_ABORT);
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
5418c2ecf20Sopenharmony_ci	struct cio2_queue *q = cio2->cur_queue;
5428c2ecf20Sopenharmony_ci	struct cio2_fbpt_entry *entry;
5438c2ecf20Sopenharmony_ci	u64 ns = ktime_get_ns();
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (dma_chan >= CIO2_QUEUES) {
5468c2ecf20Sopenharmony_ci		dev_err(dev, "bad DMA channel %i\n", dma_chan);
5478c2ecf20Sopenharmony_ci		return;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS];
5518c2ecf20Sopenharmony_ci	if (entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID) {
5528c2ecf20Sopenharmony_ci		dev_warn(&cio2->pci_dev->dev,
5538c2ecf20Sopenharmony_ci			 "no ready buffers found on DMA channel %u\n",
5548c2ecf20Sopenharmony_ci			 dma_chan);
5558c2ecf20Sopenharmony_ci		return;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* Find out which buffer(s) are ready */
5598c2ecf20Sopenharmony_ci	do {
5608c2ecf20Sopenharmony_ci		struct cio2_buffer *b;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		b = q->bufs[q->bufs_first];
5638c2ecf20Sopenharmony_ci		if (b) {
5648c2ecf20Sopenharmony_ci			unsigned int bytes = entry[1].second_entry.num_of_bytes;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci			q->bufs[q->bufs_first] = NULL;
5678c2ecf20Sopenharmony_ci			atomic_dec(&q->bufs_queued);
5688c2ecf20Sopenharmony_ci			dev_dbg(&cio2->pci_dev->dev,
5698c2ecf20Sopenharmony_ci				"buffer %i done\n", b->vbb.vb2_buf.index);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci			b->vbb.vb2_buf.timestamp = ns;
5728c2ecf20Sopenharmony_ci			b->vbb.field = V4L2_FIELD_NONE;
5738c2ecf20Sopenharmony_ci			b->vbb.sequence = atomic_read(&q->frame_sequence);
5748c2ecf20Sopenharmony_ci			if (b->vbb.vb2_buf.planes[0].length != bytes)
5758c2ecf20Sopenharmony_ci				dev_warn(dev, "buffer length is %d received %d\n",
5768c2ecf20Sopenharmony_ci					 b->vbb.vb2_buf.planes[0].length,
5778c2ecf20Sopenharmony_ci					 bytes);
5788c2ecf20Sopenharmony_ci			vb2_buffer_done(&b->vbb.vb2_buf, VB2_BUF_STATE_DONE);
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci		atomic_inc(&q->frame_sequence);
5818c2ecf20Sopenharmony_ci		cio2_fbpt_entry_init_dummy(cio2, entry);
5828c2ecf20Sopenharmony_ci		q->bufs_first = (q->bufs_first + 1) % CIO2_MAX_BUFFERS;
5838c2ecf20Sopenharmony_ci		entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS];
5848c2ecf20Sopenharmony_ci	} while (!(entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID));
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic void cio2_queue_event_sof(struct cio2_device *cio2, struct cio2_queue *q)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	/*
5908c2ecf20Sopenharmony_ci	 * For the user space camera control algorithms it is essential
5918c2ecf20Sopenharmony_ci	 * to know when the reception of a frame has begun. That's often
5928c2ecf20Sopenharmony_ci	 * the best timing information to get from the hardware.
5938c2ecf20Sopenharmony_ci	 */
5948c2ecf20Sopenharmony_ci	struct v4l2_event event = {
5958c2ecf20Sopenharmony_ci		.type = V4L2_EVENT_FRAME_SYNC,
5968c2ecf20Sopenharmony_ci		.u.frame_sync.frame_sequence = atomic_read(&q->frame_sequence),
5978c2ecf20Sopenharmony_ci	};
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	v4l2_event_queue(q->subdev.devnode, &event);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic const char *const cio2_irq_errs[] = {
6038c2ecf20Sopenharmony_ci	"single packet header error corrected",
6048c2ecf20Sopenharmony_ci	"multiple packet header errors detected",
6058c2ecf20Sopenharmony_ci	"payload checksum (CRC) error",
6068c2ecf20Sopenharmony_ci	"fifo overflow",
6078c2ecf20Sopenharmony_ci	"reserved short packet data type detected",
6088c2ecf20Sopenharmony_ci	"reserved long packet data type detected",
6098c2ecf20Sopenharmony_ci	"incomplete long packet detected",
6108c2ecf20Sopenharmony_ci	"frame sync error",
6118c2ecf20Sopenharmony_ci	"line sync error",
6128c2ecf20Sopenharmony_ci	"DPHY start of transmission error",
6138c2ecf20Sopenharmony_ci	"DPHY synchronization error",
6148c2ecf20Sopenharmony_ci	"escape mode error",
6158c2ecf20Sopenharmony_ci	"escape mode trigger event",
6168c2ecf20Sopenharmony_ci	"escape mode ultra-low power state for data lane(s)",
6178c2ecf20Sopenharmony_ci	"escape mode ultra-low power state exit for clock lane",
6188c2ecf20Sopenharmony_ci	"inter-frame short packet discarded",
6198c2ecf20Sopenharmony_ci	"inter-frame long packet discarded",
6208c2ecf20Sopenharmony_ci	"non-matching Long Packet stalled",
6218c2ecf20Sopenharmony_ci};
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic const char *const cio2_port_errs[] = {
6248c2ecf20Sopenharmony_ci	"ECC recoverable",
6258c2ecf20Sopenharmony_ci	"DPHY not recoverable",
6268c2ecf20Sopenharmony_ci	"ECC not recoverable",
6278c2ecf20Sopenharmony_ci	"CRC error",
6288c2ecf20Sopenharmony_ci	"INTERFRAMEDATA",
6298c2ecf20Sopenharmony_ci	"PKT2SHORT",
6308c2ecf20Sopenharmony_ci	"PKT2LONG",
6318c2ecf20Sopenharmony_ci};
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cistatic void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	void __iomem *const base = cio2->base;
6368c2ecf20Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (int_status & CIO2_INT_IOOE) {
6398c2ecf20Sopenharmony_ci		/*
6408c2ecf20Sopenharmony_ci		 * Interrupt on Output Error:
6418c2ecf20Sopenharmony_ci		 * 1) SRAM is full and FS received, or
6428c2ecf20Sopenharmony_ci		 * 2) An invalid bit detected by DMA.
6438c2ecf20Sopenharmony_ci		 */
6448c2ecf20Sopenharmony_ci		u32 oe_status, oe_clear;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		oe_clear = readl(base + CIO2_REG_INT_STS_EXT_OE);
6478c2ecf20Sopenharmony_ci		oe_status = oe_clear;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		if (oe_status & CIO2_INT_EXT_OE_DMAOE_MASK) {
6508c2ecf20Sopenharmony_ci			dev_err(dev, "DMA output error: 0x%x\n",
6518c2ecf20Sopenharmony_ci				(oe_status & CIO2_INT_EXT_OE_DMAOE_MASK)
6528c2ecf20Sopenharmony_ci				>> CIO2_INT_EXT_OE_DMAOE_SHIFT);
6538c2ecf20Sopenharmony_ci			oe_status &= ~CIO2_INT_EXT_OE_DMAOE_MASK;
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci		if (oe_status & CIO2_INT_EXT_OE_OES_MASK) {
6568c2ecf20Sopenharmony_ci			dev_err(dev, "DMA output error on CSI2 buses: 0x%x\n",
6578c2ecf20Sopenharmony_ci				(oe_status & CIO2_INT_EXT_OE_OES_MASK)
6588c2ecf20Sopenharmony_ci				>> CIO2_INT_EXT_OE_OES_SHIFT);
6598c2ecf20Sopenharmony_ci			oe_status &= ~CIO2_INT_EXT_OE_OES_MASK;
6608c2ecf20Sopenharmony_ci		}
6618c2ecf20Sopenharmony_ci		writel(oe_clear, base + CIO2_REG_INT_STS_EXT_OE);
6628c2ecf20Sopenharmony_ci		if (oe_status)
6638c2ecf20Sopenharmony_ci			dev_warn(dev, "unknown interrupt 0x%x on OE\n",
6648c2ecf20Sopenharmony_ci				 oe_status);
6658c2ecf20Sopenharmony_ci		int_status &= ~CIO2_INT_IOOE;
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	if (int_status & CIO2_INT_IOC_MASK) {
6698c2ecf20Sopenharmony_ci		/* DMA IO done -- frame ready */
6708c2ecf20Sopenharmony_ci		u32 clr = 0;
6718c2ecf20Sopenharmony_ci		unsigned int d;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		for (d = 0; d < CIO2_NUM_DMA_CHAN; d++)
6748c2ecf20Sopenharmony_ci			if (int_status & CIO2_INT_IOC(d)) {
6758c2ecf20Sopenharmony_ci				clr |= CIO2_INT_IOC(d);
6768c2ecf20Sopenharmony_ci				cio2_buffer_done(cio2, d);
6778c2ecf20Sopenharmony_ci			}
6788c2ecf20Sopenharmony_ci		int_status &= ~clr;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (int_status & CIO2_INT_IOS_IOLN_MASK) {
6828c2ecf20Sopenharmony_ci		/* DMA IO starts or reached specified line */
6838c2ecf20Sopenharmony_ci		u32 clr = 0;
6848c2ecf20Sopenharmony_ci		unsigned int d;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		for (d = 0; d < CIO2_NUM_DMA_CHAN; d++)
6878c2ecf20Sopenharmony_ci			if (int_status & CIO2_INT_IOS_IOLN(d)) {
6888c2ecf20Sopenharmony_ci				clr |= CIO2_INT_IOS_IOLN(d);
6898c2ecf20Sopenharmony_ci				if (d == CIO2_DMA_CHAN)
6908c2ecf20Sopenharmony_ci					cio2_queue_event_sof(cio2,
6918c2ecf20Sopenharmony_ci							     cio2->cur_queue);
6928c2ecf20Sopenharmony_ci			}
6938c2ecf20Sopenharmony_ci		int_status &= ~clr;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	if (int_status & (CIO2_INT_IOIE | CIO2_INT_IOIRQ)) {
6978c2ecf20Sopenharmony_ci		/* CSI2 receiver (error) interrupt */
6988c2ecf20Sopenharmony_ci		u32 ie_status, ie_clear;
6998c2ecf20Sopenharmony_ci		unsigned int port;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci		ie_clear = readl(base + CIO2_REG_INT_STS_EXT_IE);
7028c2ecf20Sopenharmony_ci		ie_status = ie_clear;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci		for (port = 0; port < CIO2_NUM_PORTS; port++) {
7058c2ecf20Sopenharmony_ci			u32 port_status = (ie_status >> (port * 8)) & 0xff;
7068c2ecf20Sopenharmony_ci			u32 err_mask = BIT_MASK(ARRAY_SIZE(cio2_port_errs)) - 1;
7078c2ecf20Sopenharmony_ci			void __iomem *const csi_rx_base =
7088c2ecf20Sopenharmony_ci						base + CIO2_REG_PIPE_BASE(port);
7098c2ecf20Sopenharmony_ci			unsigned int i;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci			while (port_status & err_mask) {
7128c2ecf20Sopenharmony_ci				i = ffs(port_status) - 1;
7138c2ecf20Sopenharmony_ci				dev_err(dev, "port %i error %s\n",
7148c2ecf20Sopenharmony_ci					port, cio2_port_errs[i]);
7158c2ecf20Sopenharmony_ci				ie_status &= ~BIT(port * 8 + i);
7168c2ecf20Sopenharmony_ci				port_status &= ~BIT(i);
7178c2ecf20Sopenharmony_ci			}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci			if (ie_status & CIO2_INT_EXT_IE_IRQ(port)) {
7208c2ecf20Sopenharmony_ci				u32 csi2_status, csi2_clear;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci				csi2_status = readl(csi_rx_base +
7238c2ecf20Sopenharmony_ci						CIO2_REG_IRQCTRL_STATUS);
7248c2ecf20Sopenharmony_ci				csi2_clear = csi2_status;
7258c2ecf20Sopenharmony_ci				err_mask =
7268c2ecf20Sopenharmony_ci					BIT_MASK(ARRAY_SIZE(cio2_irq_errs)) - 1;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci				while (csi2_status & err_mask) {
7298c2ecf20Sopenharmony_ci					i = ffs(csi2_status) - 1;
7308c2ecf20Sopenharmony_ci					dev_err(dev,
7318c2ecf20Sopenharmony_ci						"CSI-2 receiver port %i: %s\n",
7328c2ecf20Sopenharmony_ci							port, cio2_irq_errs[i]);
7338c2ecf20Sopenharmony_ci					csi2_status &= ~BIT(i);
7348c2ecf20Sopenharmony_ci				}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci				writel(csi2_clear,
7378c2ecf20Sopenharmony_ci				       csi_rx_base + CIO2_REG_IRQCTRL_CLEAR);
7388c2ecf20Sopenharmony_ci				if (csi2_status)
7398c2ecf20Sopenharmony_ci					dev_warn(dev,
7408c2ecf20Sopenharmony_ci						 "unknown CSI2 error 0x%x on port %i\n",
7418c2ecf20Sopenharmony_ci						 csi2_status, port);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci				ie_status &= ~CIO2_INT_EXT_IE_IRQ(port);
7448c2ecf20Sopenharmony_ci			}
7458c2ecf20Sopenharmony_ci		}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci		writel(ie_clear, base + CIO2_REG_INT_STS_EXT_IE);
7488c2ecf20Sopenharmony_ci		if (ie_status)
7498c2ecf20Sopenharmony_ci			dev_warn(dev, "unknown interrupt 0x%x on IE\n",
7508c2ecf20Sopenharmony_ci				 ie_status);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci		int_status &= ~(CIO2_INT_IOIE | CIO2_INT_IOIRQ);
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (int_status)
7568c2ecf20Sopenharmony_ci		dev_warn(dev, "unknown interrupt 0x%x on INT\n", int_status);
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistatic irqreturn_t cio2_irq(int irq, void *cio2_ptr)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = cio2_ptr;
7628c2ecf20Sopenharmony_ci	void __iomem *const base = cio2->base;
7638c2ecf20Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
7648c2ecf20Sopenharmony_ci	u32 int_status;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	int_status = readl(base + CIO2_REG_INT_STS);
7678c2ecf20Sopenharmony_ci	dev_dbg(dev, "isr enter - interrupt status 0x%x\n", int_status);
7688c2ecf20Sopenharmony_ci	if (!int_status)
7698c2ecf20Sopenharmony_ci		return IRQ_NONE;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	do {
7728c2ecf20Sopenharmony_ci		writel(int_status, base + CIO2_REG_INT_STS);
7738c2ecf20Sopenharmony_ci		cio2_irq_handle_once(cio2, int_status);
7748c2ecf20Sopenharmony_ci		int_status = readl(base + CIO2_REG_INT_STS);
7758c2ecf20Sopenharmony_ci		if (int_status)
7768c2ecf20Sopenharmony_ci			dev_dbg(dev, "pending status 0x%x\n", int_status);
7778c2ecf20Sopenharmony_ci	} while (int_status);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci/**************** Videobuf2 interface ****************/
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic void cio2_vb2_return_all_buffers(struct cio2_queue *q,
7858c2ecf20Sopenharmony_ci					enum vb2_buffer_state state)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	unsigned int i;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++) {
7908c2ecf20Sopenharmony_ci		if (q->bufs[i]) {
7918c2ecf20Sopenharmony_ci			atomic_dec(&q->bufs_queued);
7928c2ecf20Sopenharmony_ci			vb2_buffer_done(&q->bufs[i]->vbb.vb2_buf,
7938c2ecf20Sopenharmony_ci					state);
7948c2ecf20Sopenharmony_ci			q->bufs[i] = NULL;
7958c2ecf20Sopenharmony_ci		}
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic int cio2_vb2_queue_setup(struct vb2_queue *vq,
8008c2ecf20Sopenharmony_ci				unsigned int *num_buffers,
8018c2ecf20Sopenharmony_ci				unsigned int *num_planes,
8028c2ecf20Sopenharmony_ci				unsigned int sizes[],
8038c2ecf20Sopenharmony_ci				struct device *alloc_devs[])
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vq);
8068c2ecf20Sopenharmony_ci	struct cio2_queue *q = vb2q_to_cio2_queue(vq);
8078c2ecf20Sopenharmony_ci	unsigned int i;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	*num_planes = q->format.num_planes;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	for (i = 0; i < *num_planes; ++i) {
8128c2ecf20Sopenharmony_ci		sizes[i] = q->format.plane_fmt[i].sizeimage;
8138c2ecf20Sopenharmony_ci		alloc_devs[i] = &cio2->pci_dev->dev;
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	*num_buffers = clamp_val(*num_buffers, 1, CIO2_MAX_BUFFERS);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* Initialize buffer queue */
8198c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++) {
8208c2ecf20Sopenharmony_ci		q->bufs[i] = NULL;
8218c2ecf20Sopenharmony_ci		cio2_fbpt_entry_init_dummy(cio2, &q->fbpt[i * CIO2_MAX_LOPS]);
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci	atomic_set(&q->bufs_queued, 0);
8248c2ecf20Sopenharmony_ci	q->bufs_first = 0;
8258c2ecf20Sopenharmony_ci	q->bufs_next = 0;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	return 0;
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci/* Called after each buffer is allocated */
8318c2ecf20Sopenharmony_cistatic int cio2_vb2_buf_init(struct vb2_buffer *vb)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue);
8348c2ecf20Sopenharmony_ci	struct device *dev = &cio2->pci_dev->dev;
8358c2ecf20Sopenharmony_ci	struct cio2_buffer *b =
8368c2ecf20Sopenharmony_ci		container_of(vb, struct cio2_buffer, vbb.vb2_buf);
8378c2ecf20Sopenharmony_ci	unsigned int pages = PFN_UP(vb->planes[0].length);
8388c2ecf20Sopenharmony_ci	unsigned int lops = DIV_ROUND_UP(pages + 1, CIO2_LOP_ENTRIES);
8398c2ecf20Sopenharmony_ci	struct sg_table *sg;
8408c2ecf20Sopenharmony_ci	struct sg_dma_page_iter sg_iter;
8418c2ecf20Sopenharmony_ci	unsigned int i, j;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	if (lops <= 0 || lops > CIO2_MAX_LOPS) {
8448c2ecf20Sopenharmony_ci		dev_err(dev, "%s: bad buffer size (%i)\n", __func__,
8458c2ecf20Sopenharmony_ci			vb->planes[0].length);
8468c2ecf20Sopenharmony_ci		return -ENOSPC;		/* Should never happen */
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	memset(b->lop, 0, sizeof(b->lop));
8508c2ecf20Sopenharmony_ci	/* Allocate LOP table */
8518c2ecf20Sopenharmony_ci	for (i = 0; i < lops; i++) {
8528c2ecf20Sopenharmony_ci		b->lop[i] = dma_alloc_coherent(dev, PAGE_SIZE,
8538c2ecf20Sopenharmony_ci					       &b->lop_bus_addr[i], GFP_KERNEL);
8548c2ecf20Sopenharmony_ci		if (!b->lop[i])
8558c2ecf20Sopenharmony_ci			goto fail;
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	/* Fill LOP */
8598c2ecf20Sopenharmony_ci	sg = vb2_dma_sg_plane_desc(vb, 0);
8608c2ecf20Sopenharmony_ci	if (!sg)
8618c2ecf20Sopenharmony_ci		return -ENOMEM;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	if (sg->nents && sg->sgl)
8648c2ecf20Sopenharmony_ci		b->offset = sg->sgl->offset;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	i = j = 0;
8678c2ecf20Sopenharmony_ci	for_each_sg_dma_page(sg->sgl, &sg_iter, sg->nents, 0) {
8688c2ecf20Sopenharmony_ci		if (!pages--)
8698c2ecf20Sopenharmony_ci			break;
8708c2ecf20Sopenharmony_ci		b->lop[i][j] = PFN_DOWN(sg_page_iter_dma_address(&sg_iter));
8718c2ecf20Sopenharmony_ci		j++;
8728c2ecf20Sopenharmony_ci		if (j == CIO2_LOP_ENTRIES) {
8738c2ecf20Sopenharmony_ci			i++;
8748c2ecf20Sopenharmony_ci			j = 0;
8758c2ecf20Sopenharmony_ci		}
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	b->lop[i][j] = PFN_DOWN(cio2->dummy_page_bus_addr);
8798c2ecf20Sopenharmony_ci	return 0;
8808c2ecf20Sopenharmony_cifail:
8818c2ecf20Sopenharmony_ci	while (i--)
8828c2ecf20Sopenharmony_ci		dma_free_coherent(dev, PAGE_SIZE, b->lop[i], b->lop_bus_addr[i]);
8838c2ecf20Sopenharmony_ci	return -ENOMEM;
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci/* Transfer buffer ownership to cio2 */
8878c2ecf20Sopenharmony_cistatic void cio2_vb2_buf_queue(struct vb2_buffer *vb)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue);
8908c2ecf20Sopenharmony_ci	struct cio2_queue *q =
8918c2ecf20Sopenharmony_ci		container_of(vb->vb2_queue, struct cio2_queue, vbq);
8928c2ecf20Sopenharmony_ci	struct cio2_buffer *b =
8938c2ecf20Sopenharmony_ci		container_of(vb, struct cio2_buffer, vbb.vb2_buf);
8948c2ecf20Sopenharmony_ci	struct cio2_fbpt_entry *entry;
8958c2ecf20Sopenharmony_ci	unsigned long flags;
8968c2ecf20Sopenharmony_ci	unsigned int i, j, next = q->bufs_next;
8978c2ecf20Sopenharmony_ci	int bufs_queued = atomic_inc_return(&q->bufs_queued);
8988c2ecf20Sopenharmony_ci	u32 fbpt_rp;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	dev_dbg(&cio2->pci_dev->dev, "queue buffer %d\n", vb->index);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	/*
9038c2ecf20Sopenharmony_ci	 * This code queues the buffer to the CIO2 DMA engine, which starts
9048c2ecf20Sopenharmony_ci	 * running once streaming has started. It is possible that this code
9058c2ecf20Sopenharmony_ci	 * gets pre-empted due to increased CPU load. Upon this, the driver
9068c2ecf20Sopenharmony_ci	 * does not get an opportunity to queue new buffers to the CIO2 DMA
9078c2ecf20Sopenharmony_ci	 * engine. When the DMA engine encounters an FBPT entry without the
9088c2ecf20Sopenharmony_ci	 * VALID bit set, the DMA engine halts, which requires a restart of
9098c2ecf20Sopenharmony_ci	 * the DMA engine and sensor, to continue streaming.
9108c2ecf20Sopenharmony_ci	 * This is not desired and is highly unlikely given that there are
9118c2ecf20Sopenharmony_ci	 * 32 FBPT entries that the DMA engine needs to process, to run into
9128c2ecf20Sopenharmony_ci	 * an FBPT entry, without the VALID bit set. We try to mitigate this
9138c2ecf20Sopenharmony_ci	 * by disabling interrupts for the duration of this queueing.
9148c2ecf20Sopenharmony_ci	 */
9158c2ecf20Sopenharmony_ci	local_irq_save(flags);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	fbpt_rp = (readl(cio2->base + CIO2_REG_CDMARI(CIO2_DMA_CHAN))
9188c2ecf20Sopenharmony_ci		   >> CIO2_CDMARI_FBPT_RP_SHIFT)
9198c2ecf20Sopenharmony_ci		   & CIO2_CDMARI_FBPT_RP_MASK;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	/*
9228c2ecf20Sopenharmony_ci	 * fbpt_rp is the fbpt entry that the dma is currently working
9238c2ecf20Sopenharmony_ci	 * on, but since it could jump to next entry at any time,
9248c2ecf20Sopenharmony_ci	 * assume that we might already be there.
9258c2ecf20Sopenharmony_ci	 */
9268c2ecf20Sopenharmony_ci	fbpt_rp = (fbpt_rp + 1) % CIO2_MAX_BUFFERS;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	if (bufs_queued <= 1 || fbpt_rp == next)
9298c2ecf20Sopenharmony_ci		/* Buffers were drained */
9308c2ecf20Sopenharmony_ci		next = (fbpt_rp + 1) % CIO2_MAX_BUFFERS;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++) {
9338c2ecf20Sopenharmony_ci		/*
9348c2ecf20Sopenharmony_ci		 * We have allocated CIO2_MAX_BUFFERS circularly for the
9358c2ecf20Sopenharmony_ci		 * hw, the user has requested N buffer queue. The driver
9368c2ecf20Sopenharmony_ci		 * ensures N <= CIO2_MAX_BUFFERS and guarantees that whenever
9378c2ecf20Sopenharmony_ci		 * user queues a buffer, there necessarily is a free buffer.
9388c2ecf20Sopenharmony_ci		 */
9398c2ecf20Sopenharmony_ci		if (!q->bufs[next]) {
9408c2ecf20Sopenharmony_ci			q->bufs[next] = b;
9418c2ecf20Sopenharmony_ci			entry = &q->fbpt[next * CIO2_MAX_LOPS];
9428c2ecf20Sopenharmony_ci			cio2_fbpt_entry_init_buf(cio2, b, entry);
9438c2ecf20Sopenharmony_ci			local_irq_restore(flags);
9448c2ecf20Sopenharmony_ci			q->bufs_next = (next + 1) % CIO2_MAX_BUFFERS;
9458c2ecf20Sopenharmony_ci			for (j = 0; j < vb->num_planes; j++)
9468c2ecf20Sopenharmony_ci				vb2_set_plane_payload(vb, j,
9478c2ecf20Sopenharmony_ci					q->format.plane_fmt[j].sizeimage);
9488c2ecf20Sopenharmony_ci			return;
9498c2ecf20Sopenharmony_ci		}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		dev_dbg(&cio2->pci_dev->dev, "entry %i was full!\n", next);
9528c2ecf20Sopenharmony_ci		next = (next + 1) % CIO2_MAX_BUFFERS;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	local_irq_restore(flags);
9568c2ecf20Sopenharmony_ci	dev_err(&cio2->pci_dev->dev, "error: all cio2 entries were full!\n");
9578c2ecf20Sopenharmony_ci	atomic_dec(&q->bufs_queued);
9588c2ecf20Sopenharmony_ci	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci/* Called when each buffer is freed */
9628c2ecf20Sopenharmony_cistatic void cio2_vb2_buf_cleanup(struct vb2_buffer *vb)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vb->vb2_queue);
9658c2ecf20Sopenharmony_ci	struct cio2_buffer *b =
9668c2ecf20Sopenharmony_ci		container_of(vb, struct cio2_buffer, vbb.vb2_buf);
9678c2ecf20Sopenharmony_ci	unsigned int i;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	/* Free LOP table */
9708c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_MAX_LOPS; i++) {
9718c2ecf20Sopenharmony_ci		if (b->lop[i])
9728c2ecf20Sopenharmony_ci			dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE,
9738c2ecf20Sopenharmony_ci					  b->lop[i], b->lop_bus_addr[i]);
9748c2ecf20Sopenharmony_ci	}
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic int cio2_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	struct cio2_queue *q = vb2q_to_cio2_queue(vq);
9808c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vq);
9818c2ecf20Sopenharmony_ci	int r;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	cio2->cur_queue = q;
9848c2ecf20Sopenharmony_ci	atomic_set(&q->frame_sequence, 0);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	r = pm_runtime_get_sync(&cio2->pci_dev->dev);
9878c2ecf20Sopenharmony_ci	if (r < 0) {
9888c2ecf20Sopenharmony_ci		dev_info(&cio2->pci_dev->dev, "failed to set power %d\n", r);
9898c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(&cio2->pci_dev->dev);
9908c2ecf20Sopenharmony_ci		return r;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	r = media_pipeline_start(&q->vdev.entity, &q->pipe);
9948c2ecf20Sopenharmony_ci	if (r)
9958c2ecf20Sopenharmony_ci		goto fail_pipeline;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	r = cio2_hw_init(cio2, q);
9988c2ecf20Sopenharmony_ci	if (r)
9998c2ecf20Sopenharmony_ci		goto fail_hw;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* Start streaming on sensor */
10028c2ecf20Sopenharmony_ci	r = v4l2_subdev_call(q->sensor, video, s_stream, 1);
10038c2ecf20Sopenharmony_ci	if (r)
10048c2ecf20Sopenharmony_ci		goto fail_csi2_subdev;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	cio2->streaming = true;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	return 0;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cifail_csi2_subdev:
10118c2ecf20Sopenharmony_ci	cio2_hw_exit(cio2, q);
10128c2ecf20Sopenharmony_cifail_hw:
10138c2ecf20Sopenharmony_ci	media_pipeline_stop(&q->vdev.entity);
10148c2ecf20Sopenharmony_cifail_pipeline:
10158c2ecf20Sopenharmony_ci	dev_dbg(&cio2->pci_dev->dev, "failed to start streaming (%d)\n", r);
10168c2ecf20Sopenharmony_ci	cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_QUEUED);
10178c2ecf20Sopenharmony_ci	pm_runtime_put(&cio2->pci_dev->dev);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	return r;
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic void cio2_vb2_stop_streaming(struct vb2_queue *vq)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct cio2_queue *q = vb2q_to_cio2_queue(vq);
10258c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = vb2_get_drv_priv(vq);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (v4l2_subdev_call(q->sensor, video, s_stream, 0))
10288c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
10298c2ecf20Sopenharmony_ci			"failed to stop sensor streaming\n");
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	cio2_hw_exit(cio2, q);
10328c2ecf20Sopenharmony_ci	synchronize_irq(cio2->pci_dev->irq);
10338c2ecf20Sopenharmony_ci	cio2_vb2_return_all_buffers(q, VB2_BUF_STATE_ERROR);
10348c2ecf20Sopenharmony_ci	media_pipeline_stop(&q->vdev.entity);
10358c2ecf20Sopenharmony_ci	pm_runtime_put(&cio2->pci_dev->dev);
10368c2ecf20Sopenharmony_ci	cio2->streaming = false;
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_cistatic const struct vb2_ops cio2_vb2_ops = {
10408c2ecf20Sopenharmony_ci	.buf_init = cio2_vb2_buf_init,
10418c2ecf20Sopenharmony_ci	.buf_queue = cio2_vb2_buf_queue,
10428c2ecf20Sopenharmony_ci	.buf_cleanup = cio2_vb2_buf_cleanup,
10438c2ecf20Sopenharmony_ci	.queue_setup = cio2_vb2_queue_setup,
10448c2ecf20Sopenharmony_ci	.start_streaming = cio2_vb2_start_streaming,
10458c2ecf20Sopenharmony_ci	.stop_streaming = cio2_vb2_stop_streaming,
10468c2ecf20Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
10478c2ecf20Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
10488c2ecf20Sopenharmony_ci};
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci/**************** V4L2 interface ****************/
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cistatic int cio2_v4l2_querycap(struct file *file, void *fh,
10538c2ecf20Sopenharmony_ci			      struct v4l2_capability *cap)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = video_drvdata(file);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	strscpy(cap->driver, CIO2_NAME, sizeof(cap->driver));
10588c2ecf20Sopenharmony_ci	strscpy(cap->card, CIO2_DEVICE_NAME, sizeof(cap->card));
10598c2ecf20Sopenharmony_ci	snprintf(cap->bus_info, sizeof(cap->bus_info),
10608c2ecf20Sopenharmony_ci		 "PCI:%s", pci_name(cio2->pci_dev));
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	return 0;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic int cio2_v4l2_enum_fmt(struct file *file, void *fh,
10668c2ecf20Sopenharmony_ci			      struct v4l2_fmtdesc *f)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	if (f->index >= ARRAY_SIZE(formats))
10698c2ecf20Sopenharmony_ci		return -EINVAL;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	f->pixelformat = formats[f->index].fourcc;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	return 0;
10748c2ecf20Sopenharmony_ci}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci/* The format is validated in cio2_video_link_validate() */
10778c2ecf20Sopenharmony_cistatic int cio2_v4l2_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
10788c2ecf20Sopenharmony_ci{
10798c2ecf20Sopenharmony_ci	struct cio2_queue *q = file_to_cio2_queue(file);
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	f->fmt.pix_mp = q->format;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	return 0;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	const struct ipu3_cio2_fmt *fmt;
10898c2ecf20Sopenharmony_ci	struct v4l2_pix_format_mplane *mpix = &f->fmt.pix_mp;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	fmt = cio2_find_format(&mpix->pixelformat, NULL);
10928c2ecf20Sopenharmony_ci	if (!fmt)
10938c2ecf20Sopenharmony_ci		fmt = &formats[0];
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/* Only supports up to 4224x3136 */
10968c2ecf20Sopenharmony_ci	if (mpix->width > CIO2_IMAGE_MAX_WIDTH)
10978c2ecf20Sopenharmony_ci		mpix->width = CIO2_IMAGE_MAX_WIDTH;
10988c2ecf20Sopenharmony_ci	if (mpix->height > CIO2_IMAGE_MAX_LENGTH)
10998c2ecf20Sopenharmony_ci		mpix->height = CIO2_IMAGE_MAX_LENGTH;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	mpix->num_planes = 1;
11028c2ecf20Sopenharmony_ci	mpix->pixelformat = fmt->fourcc;
11038c2ecf20Sopenharmony_ci	mpix->colorspace = V4L2_COLORSPACE_RAW;
11048c2ecf20Sopenharmony_ci	mpix->field = V4L2_FIELD_NONE;
11058c2ecf20Sopenharmony_ci	memset(mpix->reserved, 0, sizeof(mpix->reserved));
11068c2ecf20Sopenharmony_ci	mpix->plane_fmt[0].bytesperline = cio2_bytesperline(mpix->width);
11078c2ecf20Sopenharmony_ci	mpix->plane_fmt[0].sizeimage = mpix->plane_fmt[0].bytesperline *
11088c2ecf20Sopenharmony_ci							mpix->height;
11098c2ecf20Sopenharmony_ci	memset(mpix->plane_fmt[0].reserved, 0,
11108c2ecf20Sopenharmony_ci	       sizeof(mpix->plane_fmt[0].reserved));
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	/* use default */
11138c2ecf20Sopenharmony_ci	mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
11148c2ecf20Sopenharmony_ci	mpix->quantization = V4L2_QUANTIZATION_DEFAULT;
11158c2ecf20Sopenharmony_ci	mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	return 0;
11188c2ecf20Sopenharmony_ci}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic int cio2_v4l2_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
11218c2ecf20Sopenharmony_ci{
11228c2ecf20Sopenharmony_ci	struct cio2_queue *q = file_to_cio2_queue(file);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	cio2_v4l2_try_fmt(file, fh, f);
11258c2ecf20Sopenharmony_ci	q->format = f->fmt.pix_mp;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	return 0;
11288c2ecf20Sopenharmony_ci}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_cistatic int
11318c2ecf20Sopenharmony_cicio2_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	if (input->index > 0)
11348c2ecf20Sopenharmony_ci		return -EINVAL;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	strscpy(input->name, "camera", sizeof(input->name));
11378c2ecf20Sopenharmony_ci	input->type = V4L2_INPUT_TYPE_CAMERA;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	return 0;
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_cistatic int
11438c2ecf20Sopenharmony_cicio2_video_g_input(struct file *file, void *fh, unsigned int *input)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	*input = 0;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic int
11518c2ecf20Sopenharmony_cicio2_video_s_input(struct file *file, void *fh, unsigned int input)
11528c2ecf20Sopenharmony_ci{
11538c2ecf20Sopenharmony_ci	return input == 0 ? 0 : -EINVAL;
11548c2ecf20Sopenharmony_ci}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations cio2_v4l2_fops = {
11578c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
11588c2ecf20Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
11598c2ecf20Sopenharmony_ci	.open = v4l2_fh_open,
11608c2ecf20Sopenharmony_ci	.release = vb2_fop_release,
11618c2ecf20Sopenharmony_ci	.poll = vb2_fop_poll,
11628c2ecf20Sopenharmony_ci	.mmap = vb2_fop_mmap,
11638c2ecf20Sopenharmony_ci};
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops cio2_v4l2_ioctl_ops = {
11668c2ecf20Sopenharmony_ci	.vidioc_querycap = cio2_v4l2_querycap,
11678c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = cio2_v4l2_enum_fmt,
11688c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap_mplane = cio2_v4l2_g_fmt,
11698c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap_mplane = cio2_v4l2_s_fmt,
11708c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap_mplane = cio2_v4l2_try_fmt,
11718c2ecf20Sopenharmony_ci	.vidioc_reqbufs = vb2_ioctl_reqbufs,
11728c2ecf20Sopenharmony_ci	.vidioc_create_bufs = vb2_ioctl_create_bufs,
11738c2ecf20Sopenharmony_ci	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
11748c2ecf20Sopenharmony_ci	.vidioc_querybuf = vb2_ioctl_querybuf,
11758c2ecf20Sopenharmony_ci	.vidioc_qbuf = vb2_ioctl_qbuf,
11768c2ecf20Sopenharmony_ci	.vidioc_dqbuf = vb2_ioctl_dqbuf,
11778c2ecf20Sopenharmony_ci	.vidioc_streamon = vb2_ioctl_streamon,
11788c2ecf20Sopenharmony_ci	.vidioc_streamoff = vb2_ioctl_streamoff,
11798c2ecf20Sopenharmony_ci	.vidioc_expbuf = vb2_ioctl_expbuf,
11808c2ecf20Sopenharmony_ci	.vidioc_enum_input = cio2_video_enum_input,
11818c2ecf20Sopenharmony_ci	.vidioc_g_input	= cio2_video_g_input,
11828c2ecf20Sopenharmony_ci	.vidioc_s_input	= cio2_video_s_input,
11838c2ecf20Sopenharmony_ci};
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_cistatic int cio2_subdev_subscribe_event(struct v4l2_subdev *sd,
11868c2ecf20Sopenharmony_ci				       struct v4l2_fh *fh,
11878c2ecf20Sopenharmony_ci				       struct v4l2_event_subscription *sub)
11888c2ecf20Sopenharmony_ci{
11898c2ecf20Sopenharmony_ci	if (sub->type != V4L2_EVENT_FRAME_SYNC)
11908c2ecf20Sopenharmony_ci		return -EINVAL;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	/* Line number. For now only zero accepted. */
11938c2ecf20Sopenharmony_ci	if (sub->id != 0)
11948c2ecf20Sopenharmony_ci		return -EINVAL;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	return v4l2_event_subscribe(fh, sub, 0, NULL);
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
12028c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt fmt_default = {
12038c2ecf20Sopenharmony_ci		.width = 1936,
12048c2ecf20Sopenharmony_ci		.height = 1096,
12058c2ecf20Sopenharmony_ci		.code = formats[0].mbus_code,
12068c2ecf20Sopenharmony_ci		.field = V4L2_FIELD_NONE,
12078c2ecf20Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_RAW,
12088c2ecf20Sopenharmony_ci		.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
12098c2ecf20Sopenharmony_ci		.quantization = V4L2_QUANTIZATION_DEFAULT,
12108c2ecf20Sopenharmony_ci		.xfer_func = V4L2_XFER_FUNC_DEFAULT,
12118c2ecf20Sopenharmony_ci	};
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	/* Initialize try_fmt */
12148c2ecf20Sopenharmony_ci	format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SINK);
12158c2ecf20Sopenharmony_ci	*format = fmt_default;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	/* same as sink */
12188c2ecf20Sopenharmony_ci	format = v4l2_subdev_get_try_format(sd, fh->pad, CIO2_PAD_SOURCE);
12198c2ecf20Sopenharmony_ci	*format = fmt_default;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	return 0;
12228c2ecf20Sopenharmony_ci}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci/*
12258c2ecf20Sopenharmony_ci * cio2_subdev_get_fmt - Handle get format by pads subdev method
12268c2ecf20Sopenharmony_ci * @sd : pointer to v4l2 subdev structure
12278c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad config
12288c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
12298c2ecf20Sopenharmony_ci * return -EINVAL or zero on success
12308c2ecf20Sopenharmony_ci */
12318c2ecf20Sopenharmony_cistatic int cio2_subdev_get_fmt(struct v4l2_subdev *sd,
12328c2ecf20Sopenharmony_ci			       struct v4l2_subdev_pad_config *cfg,
12338c2ecf20Sopenharmony_ci			       struct v4l2_subdev_format *fmt)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	mutex_lock(&q->subdev_lock);
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
12408c2ecf20Sopenharmony_ci		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
12418c2ecf20Sopenharmony_ci	else
12428c2ecf20Sopenharmony_ci		fmt->format = q->subdev_fmt;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	mutex_unlock(&q->subdev_lock);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	return 0;
12478c2ecf20Sopenharmony_ci}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci/*
12508c2ecf20Sopenharmony_ci * cio2_subdev_set_fmt - Handle set format by pads subdev method
12518c2ecf20Sopenharmony_ci * @sd : pointer to v4l2 subdev structure
12528c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad config
12538c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure
12548c2ecf20Sopenharmony_ci * return -EINVAL or zero on success
12558c2ecf20Sopenharmony_ci */
12568c2ecf20Sopenharmony_cistatic int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
12578c2ecf20Sopenharmony_ci			       struct v4l2_subdev_pad_config *cfg,
12588c2ecf20Sopenharmony_ci			       struct v4l2_subdev_format *fmt)
12598c2ecf20Sopenharmony_ci{
12608c2ecf20Sopenharmony_ci	struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
12618c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *mbus;
12628c2ecf20Sopenharmony_ci	u32 mbus_code = fmt->format.code;
12638c2ecf20Sopenharmony_ci	unsigned int i;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	/*
12668c2ecf20Sopenharmony_ci	 * Only allow setting sink pad format;
12678c2ecf20Sopenharmony_ci	 * source always propagates from sink
12688c2ecf20Sopenharmony_ci	 */
12698c2ecf20Sopenharmony_ci	if (fmt->pad == CIO2_PAD_SOURCE)
12708c2ecf20Sopenharmony_ci		return cio2_subdev_get_fmt(sd, cfg, fmt);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
12738c2ecf20Sopenharmony_ci		mbus = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
12748c2ecf20Sopenharmony_ci	else
12758c2ecf20Sopenharmony_ci		mbus = &q->subdev_fmt;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	fmt->format.code = formats[0].mbus_code;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++) {
12808c2ecf20Sopenharmony_ci		if (formats[i].mbus_code == mbus_code) {
12818c2ecf20Sopenharmony_ci			fmt->format.code = mbus_code;
12828c2ecf20Sopenharmony_ci			break;
12838c2ecf20Sopenharmony_ci		}
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	fmt->format.width = min_t(u32, fmt->format.width, CIO2_IMAGE_MAX_WIDTH);
12878c2ecf20Sopenharmony_ci	fmt->format.height = min_t(u32, fmt->format.height,
12888c2ecf20Sopenharmony_ci				   CIO2_IMAGE_MAX_LENGTH);
12898c2ecf20Sopenharmony_ci	fmt->format.field = V4L2_FIELD_NONE;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	mutex_lock(&q->subdev_lock);
12928c2ecf20Sopenharmony_ci	*mbus = fmt->format;
12938c2ecf20Sopenharmony_ci	mutex_unlock(&q->subdev_lock);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	return 0;
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_cistatic int cio2_subdev_enum_mbus_code(struct v4l2_subdev *sd,
12998c2ecf20Sopenharmony_ci				      struct v4l2_subdev_pad_config *cfg,
13008c2ecf20Sopenharmony_ci				      struct v4l2_subdev_mbus_code_enum *code)
13018c2ecf20Sopenharmony_ci{
13028c2ecf20Sopenharmony_ci	if (code->index >= ARRAY_SIZE(formats))
13038c2ecf20Sopenharmony_ci		return -EINVAL;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	code->code = formats[code->index].mbus_code;
13068c2ecf20Sopenharmony_ci	return 0;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_cistatic int cio2_subdev_link_validate_get_format(struct media_pad *pad,
13108c2ecf20Sopenharmony_ci						struct v4l2_subdev_format *fmt)
13118c2ecf20Sopenharmony_ci{
13128c2ecf20Sopenharmony_ci	if (is_media_entity_v4l2_subdev(pad->entity)) {
13138c2ecf20Sopenharmony_ci		struct v4l2_subdev *sd =
13148c2ecf20Sopenharmony_ci			media_entity_to_v4l2_subdev(pad->entity);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci		fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
13178c2ecf20Sopenharmony_ci		fmt->pad = pad->index;
13188c2ecf20Sopenharmony_ci		return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
13198c2ecf20Sopenharmony_ci	}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	return -EINVAL;
13228c2ecf20Sopenharmony_ci}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_cistatic int cio2_video_link_validate(struct media_link *link)
13258c2ecf20Sopenharmony_ci{
13268c2ecf20Sopenharmony_ci	struct video_device *vd = container_of(link->sink->entity,
13278c2ecf20Sopenharmony_ci						struct video_device, entity);
13288c2ecf20Sopenharmony_ci	struct cio2_queue *q = container_of(vd, struct cio2_queue, vdev);
13298c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = video_get_drvdata(vd);
13308c2ecf20Sopenharmony_ci	struct v4l2_subdev_format source_fmt;
13318c2ecf20Sopenharmony_ci	int ret;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	if (!media_entity_remote_pad(link->sink->entity->pads)) {
13348c2ecf20Sopenharmony_ci		dev_info(&cio2->pci_dev->dev,
13358c2ecf20Sopenharmony_ci			 "video node %s pad not connected\n", vd->name);
13368c2ecf20Sopenharmony_ci		return -ENOTCONN;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	ret = cio2_subdev_link_validate_get_format(link->source, &source_fmt);
13408c2ecf20Sopenharmony_ci	if (ret < 0)
13418c2ecf20Sopenharmony_ci		return 0;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	if (source_fmt.format.width != q->format.width ||
13448c2ecf20Sopenharmony_ci	    source_fmt.format.height != q->format.height) {
13458c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
13468c2ecf20Sopenharmony_ci			"Wrong width or height %ux%u (%ux%u expected)\n",
13478c2ecf20Sopenharmony_ci			q->format.width, q->format.height,
13488c2ecf20Sopenharmony_ci			source_fmt.format.width, source_fmt.format.height);
13498c2ecf20Sopenharmony_ci		return -EINVAL;
13508c2ecf20Sopenharmony_ci	}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	if (!cio2_find_format(&q->format.pixelformat, &source_fmt.format.code))
13538c2ecf20Sopenharmony_ci		return -EINVAL;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	return 0;
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops cio2_subdev_core_ops = {
13598c2ecf20Sopenharmony_ci	.subscribe_event = cio2_subdev_subscribe_event,
13608c2ecf20Sopenharmony_ci	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
13618c2ecf20Sopenharmony_ci};
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops cio2_subdev_internal_ops = {
13648c2ecf20Sopenharmony_ci	.open = cio2_subdev_open,
13658c2ecf20Sopenharmony_ci};
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops cio2_subdev_pad_ops = {
13688c2ecf20Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate_default,
13698c2ecf20Sopenharmony_ci	.get_fmt = cio2_subdev_get_fmt,
13708c2ecf20Sopenharmony_ci	.set_fmt = cio2_subdev_set_fmt,
13718c2ecf20Sopenharmony_ci	.enum_mbus_code = cio2_subdev_enum_mbus_code,
13728c2ecf20Sopenharmony_ci};
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops cio2_subdev_ops = {
13758c2ecf20Sopenharmony_ci	.core = &cio2_subdev_core_ops,
13768c2ecf20Sopenharmony_ci	.pad = &cio2_subdev_pad_ops,
13778c2ecf20Sopenharmony_ci};
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci/******* V4L2 sub-device asynchronous registration callbacks***********/
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_cistruct sensor_async_subdev {
13828c2ecf20Sopenharmony_ci	struct v4l2_async_subdev asd;
13838c2ecf20Sopenharmony_ci	struct csi2_bus_info csi2;
13848c2ecf20Sopenharmony_ci};
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci/* The .bound() notifier callback when a match is found */
13878c2ecf20Sopenharmony_cistatic int cio2_notifier_bound(struct v4l2_async_notifier *notifier,
13888c2ecf20Sopenharmony_ci			       struct v4l2_subdev *sd,
13898c2ecf20Sopenharmony_ci			       struct v4l2_async_subdev *asd)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = container_of(notifier,
13928c2ecf20Sopenharmony_ci					struct cio2_device, notifier);
13938c2ecf20Sopenharmony_ci	struct sensor_async_subdev *s_asd = container_of(asd,
13948c2ecf20Sopenharmony_ci					struct sensor_async_subdev, asd);
13958c2ecf20Sopenharmony_ci	struct cio2_queue *q;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if (cio2->queue[s_asd->csi2.port].sensor)
13988c2ecf20Sopenharmony_ci		return -EBUSY;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	q = &cio2->queue[s_asd->csi2.port];
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	q->csi2 = s_asd->csi2;
14038c2ecf20Sopenharmony_ci	q->sensor = sd;
14048c2ecf20Sopenharmony_ci	q->csi_rx_base = cio2->base + CIO2_REG_PIPE_BASE(q->csi2.port);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	return 0;
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci/* The .unbind callback */
14108c2ecf20Sopenharmony_cistatic void cio2_notifier_unbind(struct v4l2_async_notifier *notifier,
14118c2ecf20Sopenharmony_ci				 struct v4l2_subdev *sd,
14128c2ecf20Sopenharmony_ci				 struct v4l2_async_subdev *asd)
14138c2ecf20Sopenharmony_ci{
14148c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = container_of(notifier,
14158c2ecf20Sopenharmony_ci						struct cio2_device, notifier);
14168c2ecf20Sopenharmony_ci	struct sensor_async_subdev *s_asd = container_of(asd,
14178c2ecf20Sopenharmony_ci					struct sensor_async_subdev, asd);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	cio2->queue[s_asd->csi2.port].sensor = NULL;
14208c2ecf20Sopenharmony_ci}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci/* .complete() is called after all subdevices have been located */
14238c2ecf20Sopenharmony_cistatic int cio2_notifier_complete(struct v4l2_async_notifier *notifier)
14248c2ecf20Sopenharmony_ci{
14258c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = container_of(notifier, struct cio2_device,
14268c2ecf20Sopenharmony_ci						notifier);
14278c2ecf20Sopenharmony_ci	struct sensor_async_subdev *s_asd;
14288c2ecf20Sopenharmony_ci	struct v4l2_async_subdev *asd;
14298c2ecf20Sopenharmony_ci	struct cio2_queue *q;
14308c2ecf20Sopenharmony_ci	unsigned int pad;
14318c2ecf20Sopenharmony_ci	int ret;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	list_for_each_entry(asd, &cio2->notifier.asd_list, asd_list) {
14348c2ecf20Sopenharmony_ci		s_asd = container_of(asd, struct sensor_async_subdev, asd);
14358c2ecf20Sopenharmony_ci		q = &cio2->queue[s_asd->csi2.port];
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci		for (pad = 0; pad < q->sensor->entity.num_pads; pad++)
14388c2ecf20Sopenharmony_ci			if (q->sensor->entity.pads[pad].flags &
14398c2ecf20Sopenharmony_ci						MEDIA_PAD_FL_SOURCE)
14408c2ecf20Sopenharmony_ci				break;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci		if (pad == q->sensor->entity.num_pads) {
14438c2ecf20Sopenharmony_ci			dev_err(&cio2->pci_dev->dev,
14448c2ecf20Sopenharmony_ci				"failed to find src pad for %s\n",
14458c2ecf20Sopenharmony_ci				q->sensor->name);
14468c2ecf20Sopenharmony_ci			return -ENXIO;
14478c2ecf20Sopenharmony_ci		}
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci		ret = media_create_pad_link(
14508c2ecf20Sopenharmony_ci				&q->sensor->entity, pad,
14518c2ecf20Sopenharmony_ci				&q->subdev.entity, CIO2_PAD_SINK,
14528c2ecf20Sopenharmony_ci				0);
14538c2ecf20Sopenharmony_ci		if (ret) {
14548c2ecf20Sopenharmony_ci			dev_err(&cio2->pci_dev->dev,
14558c2ecf20Sopenharmony_ci				"failed to create link for %s\n",
14568c2ecf20Sopenharmony_ci				q->sensor->name);
14578c2ecf20Sopenharmony_ci			return ret;
14588c2ecf20Sopenharmony_ci		}
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	return v4l2_device_register_subdev_nodes(&cio2->v4l2_dev);
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic const struct v4l2_async_notifier_operations cio2_async_ops = {
14658c2ecf20Sopenharmony_ci	.bound = cio2_notifier_bound,
14668c2ecf20Sopenharmony_ci	.unbind = cio2_notifier_unbind,
14678c2ecf20Sopenharmony_ci	.complete = cio2_notifier_complete,
14688c2ecf20Sopenharmony_ci};
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cistatic int cio2_parse_firmware(struct cio2_device *cio2)
14718c2ecf20Sopenharmony_ci{
14728c2ecf20Sopenharmony_ci	unsigned int i;
14738c2ecf20Sopenharmony_ci	int ret;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_NUM_PORTS; i++) {
14768c2ecf20Sopenharmony_ci		struct v4l2_fwnode_endpoint vep = {
14778c2ecf20Sopenharmony_ci			.bus_type = V4L2_MBUS_CSI2_DPHY
14788c2ecf20Sopenharmony_ci		};
14798c2ecf20Sopenharmony_ci		struct sensor_async_subdev *s_asd;
14808c2ecf20Sopenharmony_ci		struct v4l2_async_subdev *asd;
14818c2ecf20Sopenharmony_ci		struct fwnode_handle *ep;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci		ep = fwnode_graph_get_endpoint_by_id(
14848c2ecf20Sopenharmony_ci			dev_fwnode(&cio2->pci_dev->dev), i, 0,
14858c2ecf20Sopenharmony_ci			FWNODE_GRAPH_ENDPOINT_NEXT);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci		if (!ep)
14888c2ecf20Sopenharmony_ci			continue;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci		ret = v4l2_fwnode_endpoint_parse(ep, &vep);
14918c2ecf20Sopenharmony_ci		if (ret)
14928c2ecf20Sopenharmony_ci			goto err_parse;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci		asd = v4l2_async_notifier_add_fwnode_remote_subdev(
14958c2ecf20Sopenharmony_ci				&cio2->notifier, ep, sizeof(*s_asd));
14968c2ecf20Sopenharmony_ci		if (IS_ERR(asd)) {
14978c2ecf20Sopenharmony_ci			ret = PTR_ERR(asd);
14988c2ecf20Sopenharmony_ci			goto err_parse;
14998c2ecf20Sopenharmony_ci		}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci		s_asd = container_of(asd, struct sensor_async_subdev, asd);
15028c2ecf20Sopenharmony_ci		s_asd->csi2.port = vep.base.port;
15038c2ecf20Sopenharmony_ci		s_asd->csi2.lanes = vep.bus.mipi_csi2.num_data_lanes;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci		fwnode_handle_put(ep);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci		continue;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_cierr_parse:
15108c2ecf20Sopenharmony_ci		fwnode_handle_put(ep);
15118c2ecf20Sopenharmony_ci		return ret;
15128c2ecf20Sopenharmony_ci	}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	/*
15158c2ecf20Sopenharmony_ci	 * Proceed even without sensors connected to allow the device to
15168c2ecf20Sopenharmony_ci	 * suspend.
15178c2ecf20Sopenharmony_ci	 */
15188c2ecf20Sopenharmony_ci	cio2->notifier.ops = &cio2_async_ops;
15198c2ecf20Sopenharmony_ci	ret = v4l2_async_notifier_register(&cio2->v4l2_dev, &cio2->notifier);
15208c2ecf20Sopenharmony_ci	if (ret)
15218c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
15228c2ecf20Sopenharmony_ci			"failed to register async notifier : %d\n", ret);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	return ret;
15258c2ecf20Sopenharmony_ci}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci/**************** Queue initialization ****************/
15288c2ecf20Sopenharmony_cistatic const struct media_entity_operations cio2_media_ops = {
15298c2ecf20Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate,
15308c2ecf20Sopenharmony_ci};
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_cistatic const struct media_entity_operations cio2_video_entity_ops = {
15338c2ecf20Sopenharmony_ci	.link_validate = cio2_video_link_validate,
15348c2ecf20Sopenharmony_ci};
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_cistatic int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
15378c2ecf20Sopenharmony_ci{
15388c2ecf20Sopenharmony_ci	static const u32 default_width = 1936;
15398c2ecf20Sopenharmony_ci	static const u32 default_height = 1096;
15408c2ecf20Sopenharmony_ci	const struct ipu3_cio2_fmt dflt_fmt = formats[0];
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	struct video_device *vdev = &q->vdev;
15438c2ecf20Sopenharmony_ci	struct vb2_queue *vbq = &q->vbq;
15448c2ecf20Sopenharmony_ci	struct v4l2_subdev *subdev = &q->subdev;
15458c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt;
15468c2ecf20Sopenharmony_ci	int r;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	/* Initialize miscellaneous variables */
15498c2ecf20Sopenharmony_ci	mutex_init(&q->lock);
15508c2ecf20Sopenharmony_ci	mutex_init(&q->subdev_lock);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	/* Initialize formats to default values */
15538c2ecf20Sopenharmony_ci	fmt = &q->subdev_fmt;
15548c2ecf20Sopenharmony_ci	fmt->width = default_width;
15558c2ecf20Sopenharmony_ci	fmt->height = default_height;
15568c2ecf20Sopenharmony_ci	fmt->code = dflt_fmt.mbus_code;
15578c2ecf20Sopenharmony_ci	fmt->field = V4L2_FIELD_NONE;
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	q->format.width = default_width;
15608c2ecf20Sopenharmony_ci	q->format.height = default_height;
15618c2ecf20Sopenharmony_ci	q->format.pixelformat = dflt_fmt.fourcc;
15628c2ecf20Sopenharmony_ci	q->format.colorspace = V4L2_COLORSPACE_RAW;
15638c2ecf20Sopenharmony_ci	q->format.field = V4L2_FIELD_NONE;
15648c2ecf20Sopenharmony_ci	q->format.num_planes = 1;
15658c2ecf20Sopenharmony_ci	q->format.plane_fmt[0].bytesperline =
15668c2ecf20Sopenharmony_ci				cio2_bytesperline(q->format.width);
15678c2ecf20Sopenharmony_ci	q->format.plane_fmt[0].sizeimage = q->format.plane_fmt[0].bytesperline *
15688c2ecf20Sopenharmony_ci						q->format.height;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	/* Initialize fbpt */
15718c2ecf20Sopenharmony_ci	r = cio2_fbpt_init(cio2, q);
15728c2ecf20Sopenharmony_ci	if (r)
15738c2ecf20Sopenharmony_ci		goto fail_fbpt;
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	/* Initialize media entities */
15768c2ecf20Sopenharmony_ci	q->subdev_pads[CIO2_PAD_SINK].flags = MEDIA_PAD_FL_SINK |
15778c2ecf20Sopenharmony_ci		MEDIA_PAD_FL_MUST_CONNECT;
15788c2ecf20Sopenharmony_ci	q->subdev_pads[CIO2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
15798c2ecf20Sopenharmony_ci	subdev->entity.ops = &cio2_media_ops;
15808c2ecf20Sopenharmony_ci	subdev->internal_ops = &cio2_subdev_internal_ops;
15818c2ecf20Sopenharmony_ci	r = media_entity_pads_init(&subdev->entity, CIO2_PADS, q->subdev_pads);
15828c2ecf20Sopenharmony_ci	if (r) {
15838c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
15848c2ecf20Sopenharmony_ci			"failed initialize subdev media entity (%d)\n", r);
15858c2ecf20Sopenharmony_ci		goto fail_subdev_media_entity;
15868c2ecf20Sopenharmony_ci	}
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	q->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
15898c2ecf20Sopenharmony_ci	vdev->entity.ops = &cio2_video_entity_ops;
15908c2ecf20Sopenharmony_ci	r = media_entity_pads_init(&vdev->entity, 1, &q->vdev_pad);
15918c2ecf20Sopenharmony_ci	if (r) {
15928c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
15938c2ecf20Sopenharmony_ci			"failed initialize videodev media entity (%d)\n", r);
15948c2ecf20Sopenharmony_ci		goto fail_vdev_media_entity;
15958c2ecf20Sopenharmony_ci	}
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	/* Initialize subdev */
15988c2ecf20Sopenharmony_ci	v4l2_subdev_init(subdev, &cio2_subdev_ops);
15998c2ecf20Sopenharmony_ci	subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
16008c2ecf20Sopenharmony_ci	subdev->owner = THIS_MODULE;
16018c2ecf20Sopenharmony_ci	snprintf(subdev->name, sizeof(subdev->name),
16028c2ecf20Sopenharmony_ci		 CIO2_ENTITY_NAME " %td", q - cio2->queue);
16038c2ecf20Sopenharmony_ci	subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
16048c2ecf20Sopenharmony_ci	v4l2_set_subdevdata(subdev, cio2);
16058c2ecf20Sopenharmony_ci	r = v4l2_device_register_subdev(&cio2->v4l2_dev, subdev);
16068c2ecf20Sopenharmony_ci	if (r) {
16078c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
16088c2ecf20Sopenharmony_ci			"failed initialize subdev (%d)\n", r);
16098c2ecf20Sopenharmony_ci		goto fail_subdev;
16108c2ecf20Sopenharmony_ci	}
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	/* Initialize vbq */
16138c2ecf20Sopenharmony_ci	vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
16148c2ecf20Sopenharmony_ci	vbq->io_modes = VB2_USERPTR | VB2_MMAP | VB2_DMABUF;
16158c2ecf20Sopenharmony_ci	vbq->ops = &cio2_vb2_ops;
16168c2ecf20Sopenharmony_ci	vbq->mem_ops = &vb2_dma_sg_memops;
16178c2ecf20Sopenharmony_ci	vbq->buf_struct_size = sizeof(struct cio2_buffer);
16188c2ecf20Sopenharmony_ci	vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
16198c2ecf20Sopenharmony_ci	vbq->min_buffers_needed = 1;
16208c2ecf20Sopenharmony_ci	vbq->drv_priv = cio2;
16218c2ecf20Sopenharmony_ci	vbq->lock = &q->lock;
16228c2ecf20Sopenharmony_ci	r = vb2_queue_init(vbq);
16238c2ecf20Sopenharmony_ci	if (r) {
16248c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
16258c2ecf20Sopenharmony_ci			"failed to initialize videobuf2 queue (%d)\n", r);
16268c2ecf20Sopenharmony_ci		goto fail_subdev;
16278c2ecf20Sopenharmony_ci	}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	/* Initialize vdev */
16308c2ecf20Sopenharmony_ci	snprintf(vdev->name, sizeof(vdev->name),
16318c2ecf20Sopenharmony_ci		 "%s %td", CIO2_NAME, q - cio2->queue);
16328c2ecf20Sopenharmony_ci	vdev->release = video_device_release_empty;
16338c2ecf20Sopenharmony_ci	vdev->fops = &cio2_v4l2_fops;
16348c2ecf20Sopenharmony_ci	vdev->ioctl_ops = &cio2_v4l2_ioctl_ops;
16358c2ecf20Sopenharmony_ci	vdev->lock = &cio2->lock;
16368c2ecf20Sopenharmony_ci	vdev->v4l2_dev = &cio2->v4l2_dev;
16378c2ecf20Sopenharmony_ci	vdev->queue = &q->vbq;
16388c2ecf20Sopenharmony_ci	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING;
16398c2ecf20Sopenharmony_ci	video_set_drvdata(vdev, cio2);
16408c2ecf20Sopenharmony_ci	r = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
16418c2ecf20Sopenharmony_ci	if (r) {
16428c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
16438c2ecf20Sopenharmony_ci			"failed to register video device (%d)\n", r);
16448c2ecf20Sopenharmony_ci		goto fail_vdev;
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	/* Create link from CIO2 subdev to output node */
16488c2ecf20Sopenharmony_ci	r = media_create_pad_link(
16498c2ecf20Sopenharmony_ci		&subdev->entity, CIO2_PAD_SOURCE, &vdev->entity, 0,
16508c2ecf20Sopenharmony_ci		MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
16518c2ecf20Sopenharmony_ci	if (r)
16528c2ecf20Sopenharmony_ci		goto fail_link;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	return 0;
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_cifail_link:
16578c2ecf20Sopenharmony_ci	vb2_video_unregister_device(&q->vdev);
16588c2ecf20Sopenharmony_cifail_vdev:
16598c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(subdev);
16608c2ecf20Sopenharmony_cifail_subdev:
16618c2ecf20Sopenharmony_ci	media_entity_cleanup(&vdev->entity);
16628c2ecf20Sopenharmony_cifail_vdev_media_entity:
16638c2ecf20Sopenharmony_ci	media_entity_cleanup(&subdev->entity);
16648c2ecf20Sopenharmony_cifail_subdev_media_entity:
16658c2ecf20Sopenharmony_ci	cio2_fbpt_exit(q, &cio2->pci_dev->dev);
16668c2ecf20Sopenharmony_cifail_fbpt:
16678c2ecf20Sopenharmony_ci	mutex_destroy(&q->subdev_lock);
16688c2ecf20Sopenharmony_ci	mutex_destroy(&q->lock);
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	return r;
16718c2ecf20Sopenharmony_ci}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_cistatic void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
16748c2ecf20Sopenharmony_ci{
16758c2ecf20Sopenharmony_ci	vb2_video_unregister_device(&q->vdev);
16768c2ecf20Sopenharmony_ci	media_entity_cleanup(&q->vdev.entity);
16778c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(&q->subdev);
16788c2ecf20Sopenharmony_ci	media_entity_cleanup(&q->subdev.entity);
16798c2ecf20Sopenharmony_ci	cio2_fbpt_exit(q, &cio2->pci_dev->dev);
16808c2ecf20Sopenharmony_ci	mutex_destroy(&q->subdev_lock);
16818c2ecf20Sopenharmony_ci	mutex_destroy(&q->lock);
16828c2ecf20Sopenharmony_ci}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_cistatic int cio2_queues_init(struct cio2_device *cio2)
16858c2ecf20Sopenharmony_ci{
16868c2ecf20Sopenharmony_ci	int i, r;
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_QUEUES; i++) {
16898c2ecf20Sopenharmony_ci		r = cio2_queue_init(cio2, &cio2->queue[i]);
16908c2ecf20Sopenharmony_ci		if (r)
16918c2ecf20Sopenharmony_ci			break;
16928c2ecf20Sopenharmony_ci	}
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	if (i == CIO2_QUEUES)
16958c2ecf20Sopenharmony_ci		return 0;
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
16988c2ecf20Sopenharmony_ci		cio2_queue_exit(cio2, &cio2->queue[i]);
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	return r;
17018c2ecf20Sopenharmony_ci}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_cistatic void cio2_queues_exit(struct cio2_device *cio2)
17048c2ecf20Sopenharmony_ci{
17058c2ecf20Sopenharmony_ci	unsigned int i;
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_QUEUES; i++)
17088c2ecf20Sopenharmony_ci		cio2_queue_exit(cio2, &cio2->queue[i]);
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci/**************** PCI interface ****************/
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_cistatic int cio2_pci_probe(struct pci_dev *pci_dev,
17148c2ecf20Sopenharmony_ci			  const struct pci_device_id *id)
17158c2ecf20Sopenharmony_ci{
17168c2ecf20Sopenharmony_ci	struct cio2_device *cio2;
17178c2ecf20Sopenharmony_ci	int r;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL);
17208c2ecf20Sopenharmony_ci	if (!cio2)
17218c2ecf20Sopenharmony_ci		return -ENOMEM;
17228c2ecf20Sopenharmony_ci	cio2->pci_dev = pci_dev;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	r = pcim_enable_device(pci_dev);
17258c2ecf20Sopenharmony_ci	if (r) {
17268c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev, "failed to enable device (%d)\n", r);
17278c2ecf20Sopenharmony_ci		return r;
17288c2ecf20Sopenharmony_ci	}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	dev_info(&pci_dev->dev, "device 0x%x (rev: 0x%x)\n",
17318c2ecf20Sopenharmony_ci		 pci_dev->device, pci_dev->revision);
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	r = pcim_iomap_regions(pci_dev, 1 << CIO2_PCI_BAR, pci_name(pci_dev));
17348c2ecf20Sopenharmony_ci	if (r) {
17358c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev, "failed to remap I/O memory (%d)\n", r);
17368c2ecf20Sopenharmony_ci		return -ENODEV;
17378c2ecf20Sopenharmony_ci	}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	cio2->base = pcim_iomap_table(pci_dev)[CIO2_PCI_BAR];
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	pci_set_drvdata(pci_dev, cio2);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	pci_set_master(pci_dev);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	r = pci_set_dma_mask(pci_dev, CIO2_DMA_MASK);
17468c2ecf20Sopenharmony_ci	if (r) {
17478c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev, "failed to set DMA mask (%d)\n", r);
17488c2ecf20Sopenharmony_ci		return -ENODEV;
17498c2ecf20Sopenharmony_ci	}
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	r = pci_enable_msi(pci_dev);
17528c2ecf20Sopenharmony_ci	if (r) {
17538c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev, "failed to enable MSI (%d)\n", r);
17548c2ecf20Sopenharmony_ci		return r;
17558c2ecf20Sopenharmony_ci	}
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	r = cio2_fbpt_init_dummy(cio2);
17588c2ecf20Sopenharmony_ci	if (r)
17598c2ecf20Sopenharmony_ci		return r;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	mutex_init(&cio2->lock);
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	cio2->media_dev.dev = &cio2->pci_dev->dev;
17648c2ecf20Sopenharmony_ci	strscpy(cio2->media_dev.model, CIO2_DEVICE_NAME,
17658c2ecf20Sopenharmony_ci		sizeof(cio2->media_dev.model));
17668c2ecf20Sopenharmony_ci	snprintf(cio2->media_dev.bus_info, sizeof(cio2->media_dev.bus_info),
17678c2ecf20Sopenharmony_ci		 "PCI:%s", pci_name(cio2->pci_dev));
17688c2ecf20Sopenharmony_ci	cio2->media_dev.hw_revision = 0;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	media_device_init(&cio2->media_dev);
17718c2ecf20Sopenharmony_ci	r = media_device_register(&cio2->media_dev);
17728c2ecf20Sopenharmony_ci	if (r < 0)
17738c2ecf20Sopenharmony_ci		goto fail_mutex_destroy;
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	cio2->v4l2_dev.mdev = &cio2->media_dev;
17768c2ecf20Sopenharmony_ci	r = v4l2_device_register(&pci_dev->dev, &cio2->v4l2_dev);
17778c2ecf20Sopenharmony_ci	if (r) {
17788c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev,
17798c2ecf20Sopenharmony_ci			"failed to register V4L2 device (%d)\n", r);
17808c2ecf20Sopenharmony_ci		goto fail_media_device_unregister;
17818c2ecf20Sopenharmony_ci	}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	r = cio2_queues_init(cio2);
17848c2ecf20Sopenharmony_ci	if (r)
17858c2ecf20Sopenharmony_ci		goto fail_v4l2_device_unregister;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	v4l2_async_notifier_init(&cio2->notifier);
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	/* Register notifier for subdevices we care */
17908c2ecf20Sopenharmony_ci	r = cio2_parse_firmware(cio2);
17918c2ecf20Sopenharmony_ci	if (r)
17928c2ecf20Sopenharmony_ci		goto fail_clean_notifier;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	r = devm_request_irq(&pci_dev->dev, pci_dev->irq, cio2_irq,
17958c2ecf20Sopenharmony_ci			     IRQF_SHARED, CIO2_NAME, cio2);
17968c2ecf20Sopenharmony_ci	if (r) {
17978c2ecf20Sopenharmony_ci		dev_err(&pci_dev->dev, "failed to request IRQ (%d)\n", r);
17988c2ecf20Sopenharmony_ci		goto fail_clean_notifier;
17998c2ecf20Sopenharmony_ci	}
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(&pci_dev->dev);
18028c2ecf20Sopenharmony_ci	pm_runtime_allow(&pci_dev->dev);
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	return 0;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_cifail_clean_notifier:
18078c2ecf20Sopenharmony_ci	v4l2_async_notifier_unregister(&cio2->notifier);
18088c2ecf20Sopenharmony_ci	v4l2_async_notifier_cleanup(&cio2->notifier);
18098c2ecf20Sopenharmony_ci	cio2_queues_exit(cio2);
18108c2ecf20Sopenharmony_cifail_v4l2_device_unregister:
18118c2ecf20Sopenharmony_ci	v4l2_device_unregister(&cio2->v4l2_dev);
18128c2ecf20Sopenharmony_cifail_media_device_unregister:
18138c2ecf20Sopenharmony_ci	media_device_unregister(&cio2->media_dev);
18148c2ecf20Sopenharmony_ci	media_device_cleanup(&cio2->media_dev);
18158c2ecf20Sopenharmony_cifail_mutex_destroy:
18168c2ecf20Sopenharmony_ci	mutex_destroy(&cio2->lock);
18178c2ecf20Sopenharmony_ci	cio2_fbpt_exit_dummy(cio2);
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	return r;
18208c2ecf20Sopenharmony_ci}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_cistatic void cio2_pci_remove(struct pci_dev *pci_dev)
18238c2ecf20Sopenharmony_ci{
18248c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	media_device_unregister(&cio2->media_dev);
18278c2ecf20Sopenharmony_ci	v4l2_async_notifier_unregister(&cio2->notifier);
18288c2ecf20Sopenharmony_ci	v4l2_async_notifier_cleanup(&cio2->notifier);
18298c2ecf20Sopenharmony_ci	cio2_queues_exit(cio2);
18308c2ecf20Sopenharmony_ci	cio2_fbpt_exit_dummy(cio2);
18318c2ecf20Sopenharmony_ci	v4l2_device_unregister(&cio2->v4l2_dev);
18328c2ecf20Sopenharmony_ci	media_device_cleanup(&cio2->media_dev);
18338c2ecf20Sopenharmony_ci	mutex_destroy(&cio2->lock);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	pm_runtime_forbid(&pci_dev->dev);
18368c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(&pci_dev->dev);
18378c2ecf20Sopenharmony_ci}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_runtime_suspend(struct device *dev)
18408c2ecf20Sopenharmony_ci{
18418c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
18428c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
18438c2ecf20Sopenharmony_ci	void __iomem *const base = cio2->base;
18448c2ecf20Sopenharmony_ci	u16 pm;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C);
18478c2ecf20Sopenharmony_ci	dev_dbg(dev, "cio2 runtime suspend.\n");
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
18508c2ecf20Sopenharmony_ci	pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
18518c2ecf20Sopenharmony_ci	pm |= CIO2_PMCSR_D3;
18528c2ecf20Sopenharmony_ci	pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	return 0;
18558c2ecf20Sopenharmony_ci}
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_runtime_resume(struct device *dev)
18588c2ecf20Sopenharmony_ci{
18598c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
18608c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
18618c2ecf20Sopenharmony_ci	void __iomem *const base = cio2->base;
18628c2ecf20Sopenharmony_ci	u16 pm;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C);
18658c2ecf20Sopenharmony_ci	dev_dbg(dev, "cio2 runtime resume.\n");
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
18688c2ecf20Sopenharmony_ci	pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
18698c2ecf20Sopenharmony_ci	pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	return 0;
18728c2ecf20Sopenharmony_ci}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci/*
18758c2ecf20Sopenharmony_ci * Helper function to advance all the elements of a circular buffer by "start"
18768c2ecf20Sopenharmony_ci * positions
18778c2ecf20Sopenharmony_ci */
18788c2ecf20Sopenharmony_cistatic void arrange(void *ptr, size_t elem_size, size_t elems, size_t start)
18798c2ecf20Sopenharmony_ci{
18808c2ecf20Sopenharmony_ci	struct {
18818c2ecf20Sopenharmony_ci		size_t begin, end;
18828c2ecf20Sopenharmony_ci	} arr[2] = {
18838c2ecf20Sopenharmony_ci		{ 0, start - 1 },
18848c2ecf20Sopenharmony_ci		{ start, elems - 1 },
18858c2ecf20Sopenharmony_ci	};
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci#define CHUNK_SIZE(a) ((a)->end - (a)->begin + 1)
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	/* Loop as long as we have out-of-place entries */
18908c2ecf20Sopenharmony_ci	while (CHUNK_SIZE(&arr[0]) && CHUNK_SIZE(&arr[1])) {
18918c2ecf20Sopenharmony_ci		size_t size0, i;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci		/*
18948c2ecf20Sopenharmony_ci		 * Find the number of entries that can be arranged on this
18958c2ecf20Sopenharmony_ci		 * iteration.
18968c2ecf20Sopenharmony_ci		 */
18978c2ecf20Sopenharmony_ci		size0 = min(CHUNK_SIZE(&arr[0]), CHUNK_SIZE(&arr[1]));
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci		/* Swap the entries in two parts of the array. */
19008c2ecf20Sopenharmony_ci		for (i = 0; i < size0; i++) {
19018c2ecf20Sopenharmony_ci			u8 *d = ptr + elem_size * (arr[1].begin + i);
19028c2ecf20Sopenharmony_ci			u8 *s = ptr + elem_size * (arr[0].begin + i);
19038c2ecf20Sopenharmony_ci			size_t j;
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci			for (j = 0; j < elem_size; j++)
19068c2ecf20Sopenharmony_ci				swap(d[j], s[j]);
19078c2ecf20Sopenharmony_ci		}
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci		if (CHUNK_SIZE(&arr[0]) > CHUNK_SIZE(&arr[1])) {
19108c2ecf20Sopenharmony_ci			/* The end of the first array remains unarranged. */
19118c2ecf20Sopenharmony_ci			arr[0].begin += size0;
19128c2ecf20Sopenharmony_ci		} else {
19138c2ecf20Sopenharmony_ci			/*
19148c2ecf20Sopenharmony_ci			 * The first array is fully arranged so we proceed
19158c2ecf20Sopenharmony_ci			 * handling the next one.
19168c2ecf20Sopenharmony_ci			 */
19178c2ecf20Sopenharmony_ci			arr[0].begin = arr[1].begin;
19188c2ecf20Sopenharmony_ci			arr[0].end = arr[1].begin + size0 - 1;
19198c2ecf20Sopenharmony_ci			arr[1].begin += size0;
19208c2ecf20Sopenharmony_ci		}
19218c2ecf20Sopenharmony_ci	}
19228c2ecf20Sopenharmony_ci}
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_cistatic void cio2_fbpt_rearrange(struct cio2_device *cio2, struct cio2_queue *q)
19258c2ecf20Sopenharmony_ci{
19268c2ecf20Sopenharmony_ci	unsigned int i, j;
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	for (i = 0, j = q->bufs_first; i < CIO2_MAX_BUFFERS;
19298c2ecf20Sopenharmony_ci		i++, j = (j + 1) % CIO2_MAX_BUFFERS)
19308c2ecf20Sopenharmony_ci		if (q->bufs[j])
19318c2ecf20Sopenharmony_ci			break;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	if (i == CIO2_MAX_BUFFERS)
19348c2ecf20Sopenharmony_ci		return;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	if (j) {
19378c2ecf20Sopenharmony_ci		arrange(q->fbpt, sizeof(struct cio2_fbpt_entry) * CIO2_MAX_LOPS,
19388c2ecf20Sopenharmony_ci			CIO2_MAX_BUFFERS, j);
19398c2ecf20Sopenharmony_ci		arrange(q->bufs, sizeof(struct cio2_buffer *),
19408c2ecf20Sopenharmony_ci			CIO2_MAX_BUFFERS, j);
19418c2ecf20Sopenharmony_ci	}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	/*
19448c2ecf20Sopenharmony_ci	 * DMA clears the valid bit when accessing the buffer.
19458c2ecf20Sopenharmony_ci	 * When stopping stream in suspend callback, some of the buffers
19468c2ecf20Sopenharmony_ci	 * may be in invalid state. After resume, when DMA meets the invalid
19478c2ecf20Sopenharmony_ci	 * buffer, it will halt and stop receiving new data.
19488c2ecf20Sopenharmony_ci	 * To avoid DMA halting, set the valid bit for all buffers in FBPT.
19498c2ecf20Sopenharmony_ci	 */
19508c2ecf20Sopenharmony_ci	for (i = 0; i < CIO2_MAX_BUFFERS; i++)
19518c2ecf20Sopenharmony_ci		cio2_fbpt_entry_enable(cio2, q->fbpt + i * CIO2_MAX_LOPS);
19528c2ecf20Sopenharmony_ci}
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_suspend(struct device *dev)
19558c2ecf20Sopenharmony_ci{
19568c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = to_pci_dev(dev);
19578c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
19588c2ecf20Sopenharmony_ci	struct cio2_queue *q = cio2->cur_queue;
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	dev_dbg(dev, "cio2 suspend\n");
19618c2ecf20Sopenharmony_ci	if (!cio2->streaming)
19628c2ecf20Sopenharmony_ci		return 0;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	/* Stop stream */
19658c2ecf20Sopenharmony_ci	cio2_hw_exit(cio2, q);
19668c2ecf20Sopenharmony_ci	synchronize_irq(pci_dev->irq);
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	pm_runtime_force_suspend(dev);
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	/*
19718c2ecf20Sopenharmony_ci	 * Upon resume, hw starts to process the fbpt entries from beginning,
19728c2ecf20Sopenharmony_ci	 * so relocate the queued buffs to the fbpt head before suspend.
19738c2ecf20Sopenharmony_ci	 */
19748c2ecf20Sopenharmony_ci	cio2_fbpt_rearrange(cio2, q);
19758c2ecf20Sopenharmony_ci	q->bufs_first = 0;
19768c2ecf20Sopenharmony_ci	q->bufs_next = 0;
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci	return 0;
19798c2ecf20Sopenharmony_ci}
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_cistatic int __maybe_unused cio2_resume(struct device *dev)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	struct cio2_device *cio2 = dev_get_drvdata(dev);
19848c2ecf20Sopenharmony_ci	struct cio2_queue *q = cio2->cur_queue;
19858c2ecf20Sopenharmony_ci	int r;
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	dev_dbg(dev, "cio2 resume\n");
19888c2ecf20Sopenharmony_ci	if (!cio2->streaming)
19898c2ecf20Sopenharmony_ci		return 0;
19908c2ecf20Sopenharmony_ci	/* Start stream */
19918c2ecf20Sopenharmony_ci	r = pm_runtime_force_resume(&cio2->pci_dev->dev);
19928c2ecf20Sopenharmony_ci	if (r < 0) {
19938c2ecf20Sopenharmony_ci		dev_err(&cio2->pci_dev->dev,
19948c2ecf20Sopenharmony_ci			"failed to set power %d\n", r);
19958c2ecf20Sopenharmony_ci		return r;
19968c2ecf20Sopenharmony_ci	}
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	r = cio2_hw_init(cio2, q);
19998c2ecf20Sopenharmony_ci	if (r)
20008c2ecf20Sopenharmony_ci		dev_err(dev, "fail to init cio2 hw\n");
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	return r;
20038c2ecf20Sopenharmony_ci}
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_cistatic const struct dev_pm_ops cio2_pm_ops = {
20068c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(&cio2_runtime_suspend, &cio2_runtime_resume, NULL)
20078c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(&cio2_suspend, &cio2_resume)
20088c2ecf20Sopenharmony_ci};
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_cistatic const struct pci_device_id cio2_pci_id_table[] = {
20118c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) },
20128c2ecf20Sopenharmony_ci	{ }
20138c2ecf20Sopenharmony_ci};
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cio2_pci_id_table);
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_cistatic struct pci_driver cio2_pci_driver = {
20188c2ecf20Sopenharmony_ci	.name = CIO2_NAME,
20198c2ecf20Sopenharmony_ci	.id_table = cio2_pci_id_table,
20208c2ecf20Sopenharmony_ci	.probe = cio2_pci_probe,
20218c2ecf20Sopenharmony_ci	.remove = cio2_pci_remove,
20228c2ecf20Sopenharmony_ci	.driver = {
20238c2ecf20Sopenharmony_ci		.pm = &cio2_pm_ops,
20248c2ecf20Sopenharmony_ci	},
20258c2ecf20Sopenharmony_ci};
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_cimodule_pci_driver(cio2_pci_driver);
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
20308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
20318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jian Xu Zheng");
20328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
20338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
20348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
20358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPU3 CIO2 driver");
2036