[PATCH v5 3/6] media: platform: visconti: Add Toshiba Visconti Video Input Interface driver user interace

Hans Verkuil hverkuil at xs4all.nl
Tue Jan 17 03:47:10 PST 2023


More comments below:

On 11/01/2023 03:24, Yuji Ishikawa wrote:
> Add support to Video Input Interface on Toshiba Visconti ARM SoCs.
> The interface device includes CSI2 Receiver,
> frame grabber, video DMAC and image signal processor.
> This patch provides the user interface layer.
> 
> A driver instance provides three /dev/videoX device files;
> one for RGB image capture, another one for optional RGB capture
> with different parameters and the last one for RAW capture.
> 
> Through the device files, the driver provides streaming (DMA-BUF) interface.
> A userland application should feed DMA-BUF instances for capture buffers.
> 
> The driver is based on media controller framework.
> Its operations are roughly mapped to two subdrivers;
> one for ISP and CSI2 receiver (yields 1 instance),
> the other for capture (yields 3 instances for each capture mode).
> 
> Signed-off-by: Yuji Ishikawa <yuji2.ishikawa at toshiba.co.jp>
> ---
> Changelog v2:
> - Resend v1 because a patch exceeds size limit.
> 
> Changelog v3:
> - Adapted to media control framework
> - Introduced ISP subdevice, capture device
> - Remove private IOCTLs and add vendor specific V4L2 controls
> - Change function name avoiding camelcase and uppercase letters
> 
> Changelog v4:
> - Split patches because the v3 patch exceeds size limit 
> - Stop using ID number to identify driver instance:
>   - Use dynamically allocated structure to hold HW specific context,
>     instead of static one.
>   - Call HW layer functions with the context structure instead of ID number
> - Use pm_runtime to trigger initialization of HW
>   along with open/close of device files.
> 
> Changelog v5:
> - Fix coding style problems in viif.c
> ---
>  drivers/media/platform/visconti/Makefile      |    1 +
>  drivers/media/platform/visconti/viif.c        |  545 ++++++++
>  drivers/media/platform/visconti/viif.h        |  203 +++
>  .../media/platform/visconti/viif_capture.c    | 1201 +++++++++++++++++
>  drivers/media/platform/visconti/viif_isp.c    |  846 ++++++++++++
>  5 files changed, 2796 insertions(+)
>  create mode 100644 drivers/media/platform/visconti/viif.c
>  create mode 100644 drivers/media/platform/visconti/viif.h
>  create mode 100644 drivers/media/platform/visconti/viif_capture.c
>  create mode 100644 drivers/media/platform/visconti/viif_isp.c
> 
> diff --git a/drivers/media/platform/visconti/Makefile b/drivers/media/platform/visconti/Makefile
> index e14b904df75..d7a23c1f4e8 100644
> --- a/drivers/media/platform/visconti/Makefile
> +++ b/drivers/media/platform/visconti/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the Visconti video input device driver
>  #
>  
> +visconti-viif-objs = viif.o viif_capture.o viif_isp.o
>  visconti-viif-objs += hwd_viif_csi2rx.o hwd_viif.o
>  
>  obj-$(CONFIG_VIDEO_VISCONTI_VIIF) += visconti-viif.o
> diff --git a/drivers/media/platform/visconti/viif.c b/drivers/media/platform/visconti/viif.c
> new file mode 100644
> index 00000000000..e29480dbb76
> --- /dev/null
> +++ b/drivers/media/platform/visconti/viif.c
> @@ -0,0 +1,545 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
> +/* Toshiba Visconti Video Capture Support
> + *
> + * (C) Copyright 2022 TOSHIBA CORPORATION
> + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <media/v4l2-fwnode.h>
> +
> +#include "viif.h"
> +
> +static inline struct viif_device *v4l2_to_viif(struct v4l2_device *v4l2_dev)
> +{
> +	return container_of(v4l2_dev, struct viif_device, v4l2_dev);
> +}
> +
> +static struct viif_subdev *to_viif_subdev(struct v4l2_async_subdev *asd)
> +{
> +	return container_of(asd, struct viif_subdev, asd);
> +}
> +
> +/* VSYNC mask setting of MAIN unit */
> +#define INT_M_SYNC_MASK_VSYNC_INT	 BIT(0)
> +#define INT_M_SYNC_MASK_LINES_DELAY_INT1 BIT(1)
> +#define INT_M_SYNC_MASK_LINES_DELAY_INT2 BIT(2)
> +#define INT_M_SYNC_MASK_SW_DELAY_INT0	 BIT(16)
> +#define INT_M_SYNC_MASK_SW_DELAY_INT1	 BIT(17)
> +#define INT_M_SYNC_MASK_SW_DELAY_INT2	 BIT(18)
> +
> +/* STATUS error mask setting of MAIN unit */
> +#define INT_M_MASK_L2ISP_SIZE_ERROR	     BIT(0)
> +#define INT_M_MASK_CRGBF_INTCRGERR_WRSTART   BIT(1)
> +#define INT_M_MASK_CRGBF_INTCRGERR_RDSTART   BIT(2)
> +#define INT_M_MASK_EMBED_ERROR		     BIT(3)
> +#define INT_M_MASK_USERDATA_ERROR	     BIT(4)
> +#define INT_M_MASK_L2ISP_POST0_TABLE_TIMEOUT BIT(8)
> +#define INT_M_MASK_L2ISP_POST1_TABLE_TIMEOUT BIT(9)
> +#define INT_M_MASK_L2ISP_GRID_TABLE_TIMEOUT  BIT(11)
> +#define INT_M_MASK_L1ISP_SIZE_ERROR0	     BIT(16)
> +#define INT_M_MASK_L1ISP_SIZE_ERROR1	     BIT(17)
> +#define INT_M_MASK_L1ISP_SIZE_ERROR2	     BIT(18)
> +#define INT_M_MASK_L1ISP_SIZE_ERROR3	     BIT(19)
> +#define INT_M_MASK_L1ISP_SIZE_ERROR4	     BIT(20)
> +#define INT_M_MASK_L1ISP_INT_ERR_CRGWRSTART  BIT(21)
> +#define INT_M_MASK_L1ISP_INT_ERR_CRGRDSTART  BIT(22)
> +#define INT_M_MASK_DELAY_INT_ERROR	     BIT(24)
> +
> +/* VSYNC mask settings of SUB unit */
> +#define INT_S_SYNC_MASK_VSYNC_INT	 BIT(0)
> +#define INT_S_SYNC_MASK_LINES_DELAY_INT1 BIT(1)
> +#define INT_S_SYNC_MASK_SW_DELAY_INT0	 BIT(16)
> +#define INT_S_SYNC_MASK_SW_DELAY_INT1	 BIT(17)
> +
> +/* STATUS error mask setting of SUB unit */
> +#define INT_S_MASK_SIZE_ERROR	   BIT(0)
> +#define INT_S_MASK_EMBED_ERROR	   BIT(1)
> +#define INT_S_MASK_USERDATA_ERROR  BIT(2)
> +#define INT_S_MASK_DELAY_INT_ERROR BIT(24)
> +#define INT_S_MASK_RESERVED_SET	   (BIT(16) | BIT(28))
> +
> +static void viif_vsync_irq_handler_w_isp(struct viif_device *viif_dev)
> +{
> +	u32 event_main, event_sub, status_err, l2_transfer_status;
> +	u64 ts;
> +
> +	ts = ktime_get_ns();
> +	hwd_viif_vsync_irq_handler(viif_dev->hwd_res, &event_main, &event_sub);
> +
> +	/* Delayed Vsync of MAIN unit */
> +	if (event_main & INT_M_SYNC_MASK_LINES_DELAY_INT2) {
> +		/* unmask timeout error of gamma table */
> +		hwd_viif_main_status_err_set_irq_mask(viif_dev->hwd_res,
> +						      INT_M_MASK_DELAY_INT_ERROR);
> +		viif_dev->masked_gamma_path = 0;
> +
> +		/* Get abort status of L2ISP */
> +		hwd_viif_isp_guard_start(viif_dev->hwd_res);
> +		hwd_viif_isp_get_info(viif_dev->hwd_res, NULL, &l2_transfer_status);
> +		hwd_viif_isp_guard_end(viif_dev->hwd_res);
> +
> +		status_err = viif_dev->status_err;
> +		viif_dev->status_err = 0;
> +
> +		visconti_viif_capture_switch_buffer(&viif_dev->cap_dev0, status_err,
> +						    l2_transfer_status, ts);
> +		visconti_viif_capture_switch_buffer(&viif_dev->cap_dev1, status_err,
> +						    l2_transfer_status, ts);
> +	}
> +
> +	/* Delayed Vsync of SUB unit */
> +	if (event_sub & INT_S_SYNC_MASK_LINES_DELAY_INT1)
> +		visconti_viif_capture_switch_buffer(&viif_dev->cap_dev2, 0, 0, ts);
> +}
> +
> +#define MASK_M_GAMMATBL_TIMEOUT 0x0700U
> +
> +static void viif_status_err_irq_handler(struct viif_device *viif_dev)
> +{
> +	u32 event_main, event_sub, val, mask;
> +
> +	hwd_viif_status_err_irq_handler(viif_dev->hwd_res, &event_main, &event_sub);
> +
> +	if (event_main) {
> +		/* mask for gamma table time out error which will be unmasked in the next Vsync */
> +		val = FIELD_GET(MASK_M_GAMMATBL_TIMEOUT, event_main);
> +		if (val) {
> +			viif_dev->masked_gamma_path |= val;
> +			mask = INT_M_MASK_DELAY_INT_ERROR |
> +			       FIELD_PREP(MASK_M_GAMMATBL_TIMEOUT, viif_dev->masked_gamma_path);
> +			hwd_viif_main_status_err_set_irq_mask(viif_dev->hwd_res, mask);
> +		}
> +
> +		viif_dev->status_err = event_main;
> +	}
> +	viif_dev->reported_err_main |= event_main;
> +	viif_dev->reported_err_sub |= event_sub;
> +	dev_err(viif_dev->dev, "MAIN/SUB error 0x%x 0x%x.\n", event_main, event_sub);
> +}
> +
> +static void viif_csi2rx_err_irq_handler(struct viif_device *viif_dev)
> +{
> +	u32 event;
> +
> +	event = hwd_viif_csi2rx_err_irq_handler(viif_dev->hwd_res);
> +	viif_dev->reported_err_csi2rx |= event;
> +	dev_err(viif_dev->dev, "CSI2RX error 0x%x.\n", event);
> +}
> +
> +static irqreturn_t visconti_viif_irq(int irq, void *dev_id)
> +{
> +	struct viif_device *viif_dev = dev_id;
> +	int irq_type = irq - viif_dev->irq[0];
> +
> +	spin_lock(&viif_dev->lock);
> +
> +	switch (irq_type) {
> +	case 0:
> +		viif_vsync_irq_handler_w_isp(viif_dev);
> +		break;
> +	case 1:
> +		viif_status_err_irq_handler(viif_dev);
> +		break;
> +	case 2:
> +		viif_csi2rx_err_irq_handler(viif_dev);
> +		break;
> +	}
> +
> +	spin_unlock(&viif_dev->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* ----- Async Notifier Operations----- */
> +static int visconti_viif_notify_bound(struct v4l2_async_notifier *notifier,
> +				      struct v4l2_subdev *v4l2_sd, struct v4l2_async_subdev *asd)
> +{
> +	struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
> +	struct viif_device *viif_dev = v4l2_to_viif(v4l2_dev);
> +	struct viif_subdev *viif_sd = to_viif_subdev(asd);
> +
> +	viif_sd->v4l2_sd = v4l2_sd;
> +	viif_dev->num_sd++;
> +
> +	return 0;
> +}
> +
> +static void visconti_viif_create_links(struct viif_device *viif_dev)
> +{
> +	unsigned int source_pad;
> +	int ret;
> +
> +	/* camera subdev pad0 -> isp suddev pad0 */
> +	ret = media_entity_get_fwnode_pad(&viif_dev->sd->v4l2_sd->entity,
> +					  viif_dev->sd->v4l2_sd->fwnode, MEDIA_PAD_FL_SOURCE);
> +	if (ret < 0) {
> +		dev_err(viif_dev->dev, "failed to find source pad\n");
> +		return;
> +	}
> +	source_pad = ret;
> +
> +	ret = media_create_pad_link(&viif_dev->sd->v4l2_sd->entity, source_pad,
> +				    &viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SINK,
> +				    MEDIA_LNK_FL_ENABLED);
> +	if (ret)
> +		dev_err(viif_dev->dev, "failed create_pad_link (camera:src -> isp:sink)\n");
> +
> +	ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH0,
> +				    &viif_dev->cap_dev0.vdev.entity, VIIF_CAPTURE_PAD_SINK,
> +				    MEDIA_LNK_FL_ENABLED);
> +	if (ret)
> +		dev_err(viif_dev->dev, "failed create_pad_link (isp:src -> capture0:sink)\n");
> +
> +	ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH1,
> +				    &viif_dev->cap_dev1.vdev.entity, VIIF_CAPTURE_PAD_SINK,
> +				    MEDIA_LNK_FL_ENABLED);
> +	if (ret)
> +		dev_err(viif_dev->dev, "failed create_pad_link (isp:src -> capture1:sink)\n");
> +
> +	ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH2,
> +				    &viif_dev->cap_dev2.vdev.entity, VIIF_CAPTURE_PAD_SINK,
> +				    MEDIA_LNK_FL_ENABLED);
> +	if (ret)
> +		dev_err(viif_dev->dev, "failed create_pad_link (isp:src -> capture2:sink)\n");
> +}
> +
> +static void visconti_viif_notify_unbind(struct v4l2_async_notifier *notifier,
> +					struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd)
> +{
> +	struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
> +	struct viif_subdev *viif_sd = to_viif_subdev(asd);
> +
> +	v4l2_dev->ctrl_handler = NULL;
> +	viif_sd->v4l2_sd = NULL;
> +}
> +
> +static int visconti_viif_notify_complete(struct v4l2_async_notifier *notifier)
> +{
> +	struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
> +	struct viif_device *viif_dev = v4l2_to_viif(v4l2_dev);
> +	int ret;
> +
> +	ret = v4l2_device_register_subdev_nodes(v4l2_dev);
> +	if (ret < 0) {
> +		dev_err(v4l2_dev->dev, "Failed to register subdev nodes\n");
> +		return ret;
> +	}
> +
> +	/* Make sure at least one sensor is primary and use it to initialize */
> +	if (!viif_dev->sd) {
> +		viif_dev->sd = &viif_dev->subdevs[0];
> +		viif_dev->sd_index = 0;
> +	}
> +
> +	ret = visconti_viif_capture_register_ctrl_handlers(viif_dev);
> +	if (ret)
> +		return ret;
> +
> +	visconti_viif_create_links(viif_dev);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_async_notifier_operations viif_notify_ops = {
> +	.bound = visconti_viif_notify_bound,
> +	.unbind = visconti_viif_notify_unbind,
> +	.complete = visconti_viif_notify_complete,
> +};
> +
> +/* ----- Probe and Remove ----- */
> +static int visconti_viif_init_async_subdevs(struct viif_device *viif_dev, unsigned int n_sd)
> +{
> +	/* Reserve memory for 'n_sd' viif_subdev descriptors. */
> +	viif_dev->subdevs =
> +		devm_kcalloc(viif_dev->dev, n_sd, sizeof(*viif_dev->subdevs), GFP_KERNEL);
> +	if (!viif_dev->subdevs)
> +		return -ENOMEM;
> +
> +	/* Reserve memory for 'n_sd' pointers to async_subdevices.
> +	 * viif_dev->asds members will point to &viif_dev.asd
> +	 */
> +	viif_dev->asds = devm_kcalloc(viif_dev->dev, n_sd, sizeof(*viif_dev->asds), GFP_KERNEL);
> +	if (!viif_dev->asds)
> +		return -ENOMEM;
> +
> +	viif_dev->sd = NULL;
> +	viif_dev->sd_index = 0;
> +	viif_dev->num_sd = 0;
> +
> +	return 0;
> +}
> +
> +static int visconti_viif_parse_dt(struct viif_device *viif_dev)
> +{
> +	struct device_node *of = viif_dev->dev->of_node;
> +	struct v4l2_fwnode_endpoint fw_ep;
> +	struct viif_subdev *viif_sd;
> +	struct device_node *ep;
> +	unsigned int i;
> +	int num_ep;
> +	int ret;
> +
> +	memset(&fw_ep, 0, sizeof(struct v4l2_fwnode_endpoint));
> +
> +	num_ep = of_graph_get_endpoint_count(of);
> +	if (!num_ep)
> +		return -ENODEV;
> +
> +	ret = visconti_viif_init_async_subdevs(viif_dev, num_ep);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < num_ep; i++) {
> +		ep = of_graph_get_endpoint_by_regs(of, 0, i);
> +		if (!ep) {
> +			dev_err(viif_dev->dev, "No subdevice connected on endpoint %u.\n", i);
> +			ret = -ENODEV;
> +			goto error_put_node;
> +		}
> +
> +		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &fw_ep);
> +		if (ret) {
> +			dev_err(viif_dev->dev, "Unable to parse endpoint #%u.\n", i);
> +			goto error_put_node;
> +		}
> +
> +		if (fw_ep.bus_type != V4L2_MBUS_CSI2_DPHY ||
> +		    fw_ep.bus.mipi_csi2.num_data_lanes == 0) {
> +			dev_err(viif_dev->dev, "missing CSI-2 properties in endpoint\n");
> +			ret = -EINVAL;
> +			goto error_put_node;
> +		}
> +
> +		/* Setup the ceu subdevice and the async subdevice. */
> +		viif_sd = &viif_dev->subdevs[i];
> +		INIT_LIST_HEAD(&viif_sd->asd.list);
> +
> +		viif_sd->mbus_flags = fw_ep.bus.mipi_csi2.flags;
> +		viif_sd->num_lane = fw_ep.bus.mipi_csi2.num_data_lanes;
> +		viif_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
> +		viif_sd->asd.match.fwnode =
> +			fwnode_graph_get_remote_port_parent(of_fwnode_handle(ep));
> +
> +		viif_dev->asds[i] = &viif_sd->asd;
> +		of_node_put(ep);
> +	}
> +
> +	return num_ep;
> +
> +error_put_node:
> +	of_node_put(ep);
> +	return ret;
> +}
> +
> +static const struct of_device_id visconti_viif_of_table[] = {
> +	{
> +		.compatible = "toshiba,visconti-viif",
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, visconti_viif_of_table);
> +
> +#define NUM_IRQS       3
> +#define IRQ_ID_STR     "viif"
> +#define MEDIA_MODEL    "visconti_viif"
> +#define MEDIA_BUS_INFO "platform:visconti_viif"
> +
> +static int visconti_viif_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *of_id;
> +	struct device *dev = &pdev->dev;
> +	struct viif_device *viif_dev;
> +	dma_addr_t table_paddr;
> +	int ret, i, num_sd;
> +
> +	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
> +	if (ret)
> +		return ret;
> +
> +	viif_dev = devm_kzalloc(dev, sizeof(*viif_dev), GFP_KERNEL);
> +	if (!viif_dev)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, viif_dev);
> +	viif_dev->dev = dev;
> +
> +	spin_lock_init(&viif_dev->lock);
> +	mutex_init(&viif_dev->pow_lock);
> +
> +	viif_dev->capture_reg = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(viif_dev->capture_reg))
> +		return PTR_ERR(viif_dev->capture_reg);
> +
> +	viif_dev->csi2host_reg = devm_platform_ioremap_resource(pdev, 1);
> +	if (IS_ERR(viif_dev->csi2host_reg))
> +		return PTR_ERR(viif_dev->csi2host_reg);
> +
> +	viif_dev->hwd_res = allocate_viif_res(dev, viif_dev->csi2host_reg, viif_dev->capture_reg);
> +
> +	for (i = 0; i < NUM_IRQS; i++) {
> +		ret = platform_get_irq(pdev, i);
> +		if (ret < 0) {
> +			dev_err(dev, "failed to acquire irq resource\n");
> +			return ret;
> +		}
> +		viif_dev->irq[i] = ret;
> +		ret = devm_request_irq(dev, viif_dev->irq[i], visconti_viif_irq, 0, IRQ_ID_STR,
> +				       viif_dev);
> +		if (ret) {
> +			dev_err(dev, "irq request failed\n");
> +			return ret;
> +		}
> +	}
> +
> +	viif_dev->table_vaddr =
> +		dma_alloc_wc(dev, sizeof(struct viif_table_area), &table_paddr, GFP_KERNEL);
> +	if (!viif_dev->table_vaddr) {
> +		dev_err(dev, "dma_alloc_wc failed\n");
> +		return -ENOMEM;
> +	}
> +	viif_dev->table_paddr = (struct viif_table_area *)table_paddr;
> +
> +	/* power control */
> +	pm_runtime_enable(dev);
> +
> +	/* build media_dev */
> +	viif_dev->media_dev.hw_revision = 0;
> +	strscpy(viif_dev->media_dev.model, MEDIA_MODEL, sizeof(viif_dev->media_dev.model));
> +	viif_dev->media_dev.dev = dev;
> +	strscpy(viif_dev->media_dev.bus_info, MEDIA_BUS_INFO, sizeof(viif_dev->media_dev.bus_info));
> +	media_device_init(&viif_dev->media_dev);
> +
> +	/* build v4l2_dev */
> +	viif_dev->v4l2_dev.mdev = &viif_dev->media_dev;
> +	ret = v4l2_device_register(dev, &viif_dev->v4l2_dev);
> +	if (ret)
> +		goto error_dma_free;
> +
> +	ret = media_device_register(&viif_dev->media_dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to register media device: %d\n", ret);
> +		goto error_v4l2_unregister;
> +	}
> +
> +	ret = visconti_viif_isp_register(viif_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register isp sub node: %d\n", ret);
> +		goto error_media_unregister;
> +	}
> +	ret = visconti_viif_capture_register(viif_dev);
> +	if (ret) {
> +		dev_err(dev, "failed to register capture node: %d\n", ret);
> +		goto error_media_unregister;
> +	}
> +
> +	/* check device type */
> +	of_id = of_match_device(visconti_viif_of_table, dev);
> +
> +	num_sd = visconti_viif_parse_dt(viif_dev);
> +	if (ret < 0) {
> +		ret = num_sd;
> +		goto error_media_unregister;
> +	}
> +
> +	viif_dev->notifier.v4l2_dev = &viif_dev->v4l2_dev;
> +	v4l2_async_nf_init(&viif_dev->notifier);
> +	for (i = 0; i < num_sd; i++)
> +		__v4l2_async_nf_add_subdev(&viif_dev->notifier, viif_dev->asds[i]);
> +	viif_dev->notifier.ops = &viif_notify_ops;
> +	ret = v4l2_async_nf_register(&viif_dev->v4l2_dev, &viif_dev->notifier);
> +	if (ret)
> +		goto error_media_unregister;
> +
> +	return 0;
> +
> +error_media_unregister:
> +	media_device_unregister(&viif_dev->media_dev);
> +error_v4l2_unregister:
> +	v4l2_device_unregister(&viif_dev->v4l2_dev);
> +error_dma_free:
> +	pm_runtime_disable(dev);
> +	dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->table_vaddr,
> +		    (dma_addr_t)viif_dev->table_paddr);
> +	return ret;
> +}
> +
> +static int visconti_viif_remove(struct platform_device *pdev)
> +{
> +	struct viif_device *viif_dev = platform_get_drvdata(pdev);
> +
> +	visconti_viif_isp_unregister(viif_dev);
> +	visconti_viif_capture_unregister(viif_dev);
> +	v4l2_async_nf_unregister(&viif_dev->notifier);
> +	media_device_unregister(&viif_dev->media_dev);
> +	v4l2_device_unregister(&viif_dev->v4l2_dev);
> +	pm_runtime_disable(&pdev->dev);
> +	dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->table_vaddr,
> +		    (dma_addr_t)viif_dev->table_paddr);
> +
> +	return 0;
> +}
> +
> +static int visconti_viif_runtime_suspend(struct device *dev)
> +{
> +	/* This callback is kicked when the last device-file is closed */
> +	return 0;
> +}
> +
> +static int visconti_viif_runtime_resume(struct device *dev)
> +{
> +	/* This callback is kicked when the first device-file is opened */
> +	struct viif_device *viif_dev = dev_get_drvdata(dev);
> +
> +	viif_dev->rawpack_mode = HWD_VIIF_RAWPACK_DISABLE;
> +
> +	mutex_lock(&viif_dev->pow_lock);
> +
> +	/* VSYNC mask setting of MAIN unit */
> +	hwd_viif_main_vsync_set_irq_mask(
> +		viif_dev->hwd_res, INT_M_SYNC_MASK_VSYNC_INT | INT_M_SYNC_MASK_LINES_DELAY_INT1 |
> +					   INT_M_SYNC_MASK_SW_DELAY_INT0 |
> +					   INT_M_SYNC_MASK_SW_DELAY_INT2);
> +
> +	/* STATUS error mask setting of MAIN unit */
> +	hwd_viif_main_status_err_set_irq_mask(viif_dev->hwd_res, INT_M_MASK_DELAY_INT_ERROR);
> +
> +	/* VSYNC mask settings of SUB unit */
> +	hwd_viif_sub_vsync_set_irq_mask(viif_dev->hwd_res, INT_S_SYNC_MASK_VSYNC_INT |
> +								   INT_S_SYNC_MASK_SW_DELAY_INT0 |
> +								   INT_S_SYNC_MASK_SW_DELAY_INT1);
> +
> +	/* STATUS error mask setting(unmask) of SUB unit */
> +	hwd_viif_sub_status_err_set_irq_mask(viif_dev->hwd_res,
> +					     INT_S_MASK_RESERVED_SET | INT_S_MASK_DELAY_INT_ERROR);
> +
> +	mutex_unlock(&viif_dev->pow_lock);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops visconti_viif_pm_ops = { SET_RUNTIME_PM_OPS(
> +	visconti_viif_runtime_suspend, visconti_viif_runtime_resume, NULL) };
> +
> +static struct platform_driver visconti_viif_driver = {
> +	.probe = visconti_viif_probe,
> +	.remove = visconti_viif_remove,
> +	.driver = {
> +			.name = "visconti_viif",
> +			.of_match_table = visconti_viif_of_table,
> +			.pm = &visconti_viif_pm_ops,
> +		},
> +};
> +
> +module_platform_driver(visconti_viif_driver);
> +
> +MODULE_AUTHOR("Yuji Ishikawa <yuji2.ishikawa at toshiba.co.jp>");
> +MODULE_DESCRIPTION("Toshiba Visconti Video Input driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/platform/visconti/viif.h b/drivers/media/platform/visconti/viif.h
> new file mode 100644
> index 00000000000..cd121ae3200
> --- /dev/null
> +++ b/drivers/media/platform/visconti/viif.h
> @@ -0,0 +1,203 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
> +/* Toshiba Visconti Video Capture Support
> + *
> + * (C) Copyright 2022 TOSHIBA CORPORATION
> + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
> + */
> +
> +#ifndef VIIF_H
> +#define VIIF_H
> +
> +#include <linux/visconti_viif.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-mediabus.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "hwd_viif.h"
> +
> +#define VIIF_ISP_REGBUF_0 0
> +#define VIIF_L2ISP_POST_0 0
> +#define VIIF_L2ISP_POST_1 1
> +
> +#define VIIF_CAPTURE_PAD_SINK  0
> +#define VIIF_ISP_PAD_SINK      0
> +#define VIIF_ISP_PAD_SRC_PATH0 1
> +#define VIIF_ISP_PAD_SRC_PATH1 2
> +#define VIIF_ISP_PAD_SRC_PATH2 3
> +#define VIIF_ISP_PAD_NUM       4
> +
> +#define CAPTURE_PATH_MAIN_POST0 0
> +#define CAPTURE_PATH_MAIN_POST1 1
> +#define CAPTURE_PATH_SUB	2
> +
> +#define VIIF_DPC_TABLE_BYTES	  8192
> +#define VIIF_LSC_TABLE_BYTES	  1536
> +#define VIIF_UNDIST_TABLE_BYTES	  8192
> +#define VIIF_L2_GAMMA_TABLE_BYTES 512
> +
> +#define VIIF_HW_AVAILABLE_IRQS 4
> +
> +struct viif_fmt {
> +	u32 fourcc;
> +	u8 bpp[3];
> +	u8 num_planes;
> +	u32 colorspace;
> +	u32 pitch_align;
> +};
> +
> +struct viif_subdev {
> +	struct v4l2_subdev *v4l2_sd;
> +	struct v4l2_async_subdev asd;
> +
> +	/* per-subdevice mbus configuration options */
> +	unsigned int mbus_flags;
> +	unsigned int mbus_code;
> +	unsigned int num_lane;
> +};
> +
> +struct viif_table_area {
> +	/* viif_l1_dpc_config */
> +	u32 dpc_table_h[VIIF_DPC_TABLE_BYTES / sizeof(u32)];
> +	u32 dpc_table_m[VIIF_DPC_TABLE_BYTES / sizeof(u32)];
> +	u32 dpc_table_l[VIIF_DPC_TABLE_BYTES / sizeof(u32)];
> +	/* viif_l1_lsc_config */
> +	u16 lsc_table_gr[VIIF_LSC_TABLE_BYTES / sizeof(u16)];
> +	u16 lsc_table_r[VIIF_LSC_TABLE_BYTES / sizeof(u16)];
> +	u16 lsc_table_b[VIIF_LSC_TABLE_BYTES / sizeof(u16)];
> +	u16 lsc_table_gb[VIIF_LSC_TABLE_BYTES / sizeof(u16)];
> +	/* viif_l2_undist_config */
> +	u32 undist_write_g[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)];
> +	u32 undist_read_b[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)];
> +	u32 undist_read_g[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)];
> +	u32 undist_read_r[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)];
> +	/* viif_l2_gamma_config */
> +	u16 l2_gamma_table[2][6][VIIF_L2_GAMMA_TABLE_BYTES / sizeof(u16)];
> +};
> +
> +/* capture device node information */
> +struct cap_dev {
> +	u32 pathid; /* 0 ... MAIN POST0, 1 ... MAIN POST1, 2 ... SUB */
> +	struct video_device vdev;
> +	struct media_pad capture_pad;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct mutex vlock; /* serialize ioctl to vb2_queue and video_device */
> +
> +	/* vb2 queue, capture buffer list and active buffer pointer */
> +	struct vb2_queue vb2_vq;
> +	struct list_head buf_queue;
> +	struct vb2_v4l2_buffer *active;
> +	struct vb2_v4l2_buffer *dma_active;
> +	int buf_cnt;
> +	unsigned int sequence;
> +
> +	/* currently configured field and pixel format */
> +	enum v4l2_field field;
> +	struct v4l2_pix_format_mplane v4l2_pix;
> +	unsigned int out_format;
> +	struct hwd_viif_img_area img_area;
> +	struct hwd_viif_out_process out_process;
> +
> +	struct viif_device *viif_dev;
> +};
> +
> +struct isp_subdev {
> +	struct v4l2_subdev sd;
> +	struct media_pad pads[VIIF_ISP_PAD_NUM];
> +	struct v4l2_subdev_pad_config pad_cfg[VIIF_ISP_PAD_NUM];
> +	struct mutex ops_lock; /* serialize V4L2 query */
> +	struct viif_device *viif_dev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +};
> +
> +struct hwd_viif_res;
> +
> +struct viif_device {
> +	struct device *dev;
> +	struct v4l2_device v4l2_dev;
> +	struct media_device media_dev;
> +	struct media_pipeline pipe;
> +	u32 masked_gamma_path;
> +	struct hwd_viif_func *func;
> +
> +	struct viif_subdev *subdevs;
> +	struct v4l2_async_subdev **asds;
> +	/* async subdev notification helpers */
> +	struct v4l2_async_notifier notifier;
> +
> +	/* the subdevice currently in use */
> +	struct viif_subdev *sd;
> +	unsigned int sd_index;
> +	unsigned int num_sd;
> +
> +	/* sub device node information */
> +	struct cap_dev cap_dev0;
> +	struct cap_dev cap_dev1;
> +	struct cap_dev cap_dev2;
> +	struct isp_subdev isp_subdev;
> +
> +	/* lock - serialize calls to low-level operations (hwd_xxxx) */
> +	/* also, this serialize access to capture buffer queue and active buffer */
> +	spinlock_t lock;
> +
> +	/* pow_lock - serialize power control*/
> +	struct mutex pow_lock;
> +
> +	struct {
> +		u32 clock_id;
> +		u32 csi2_clock_id;
> +		u32 csi2_reset_id;
> +	} clk_compat;
> +
> +	/* hwd_res - context of low level implementation */
> +	struct hwd_viif_res *hwd_res;
> +
> +	void __iomem *capture_reg;
> +	void __iomem *csi2host_reg;
> +	unsigned int irq[VIIF_HW_AVAILABLE_IRQS];
> +
> +	/* Un-cache table area */
> +	struct viif_table_area *table_vaddr;
> +	struct viif_table_area *table_paddr;
> +
> +	/* Rawpack mode */
> +	u32 rawpack_mode;
> +
> +	/* Error flag checked at delayed vsync handler  */
> +	u32 status_err;
> +
> +	/* Error flag checked at compound control GET_REPORTED_ERRORS  */
> +	u32 reported_err_main;
> +	u32 reported_err_sub;
> +	u32 reported_err_csi2rx;
> +};
> +
> +/* viif.c */
> +void visconti_viif_hw_on(struct viif_device *viif_dev);
> +void visconti_viif_hw_off(struct viif_device *viif_dev);
> +
> +/* viif_capture.c */
> +int visconti_viif_capture_register(struct viif_device *viif_dev);
> +void visconti_viif_capture_unregister(struct viif_device *viif_dev);
> +int visconti_viif_capture_register_ctrl_handlers(struct viif_device *viif_dev);
> +void visconti_viif_capture_switch_buffer(struct cap_dev *cap_dev, u32 status_err,
> +					 u32 l2_transfer_status, u64 timestamp);
> +
> +/* viif_isp.c */
> +int visconti_viif_isp_register(struct viif_device *viif_dev);
> +void visconti_viif_isp_unregister(struct viif_device *viif_dev);
> +int visconti_viif_isp_main_set_unit(struct viif_device *viif_dev);
> +int visconti_viif_isp_sub_set_unit(struct viif_device *viif_dev);
> +void visconti_viif_isp_set_compose_rect(struct viif_device *viif_dev,
> +					struct viif_l2_roi_config *roi);
> +
> +/* viif_controls.c */
> +int visconti_viif_isp_init_controls(struct viif_device *viif_dev);
> +
> +#endif /* VIIF_H */
> diff --git a/drivers/media/platform/visconti/viif_capture.c b/drivers/media/platform/visconti/viif_capture.c
> new file mode 100644
> index 00000000000..fa18aec4470
> --- /dev/null
> +++ b/drivers/media/platform/visconti/viif_capture.c
> @@ -0,0 +1,1201 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
> +/* Toshiba Visconti Video Capture Support
> + *
> + * (C) Copyright 2022 TOSHIBA CORPORATION
> + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/pm_runtime.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "viif.h"
> +
> +#define VIIF_CROP_MAX_X_ISP (8062U)
> +#define VIIF_CROP_MAX_Y_ISP (3966U)
> +#define VIIF_CROP_MIN_W	    (128U)
> +#define VIIF_CROP_MAX_W_ISP (8190U)
> +#define VIIF_CROP_MIN_H	    (128U)
> +#define VIIF_CROP_MAX_H_ISP (4094U)
> +
> +struct viif_buffer {
> +	struct vb2_v4l2_buffer vb;
> +	struct list_head queue;
> +};
> +
> +static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf)
> +{
> +	return container_of(vbuf, struct viif_buffer, vb);
> +}
> +
> +static inline struct cap_dev *video_drvdata_to_capdev(struct file *file)
> +{
> +	return (struct cap_dev *)video_drvdata(file);
> +}
> +
> +static inline struct cap_dev *vb2queue_to_capdev(struct vb2_queue *vq)
> +{
> +	return (struct cap_dev *)vb2_get_drv_priv(vq);
> +}
> +
> +/* ----- ISRs and VB2 Operations ----- */
> +static int viif_set_img(struct cap_dev *cap_dev, struct vb2_buffer *vb)
> +{
> +	struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix;
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct hwd_viif_img next_out_img;
> +	dma_addr_t phys_addr;
> +	int i, ret = 0;
> +
> +	next_out_img.width = pix->width;
> +	next_out_img.height = pix->height;
> +	next_out_img.format = cap_dev->out_format;
> +
> +	for (i = 0; i < pix->num_planes; i++) {
> +		next_out_img.pixelmap[i].pitch = pix->plane_fmt[i].bytesperline;
> +		phys_addr = vb2_dma_contig_plane_dma_addr(vb, i);
> +		next_out_img.pixelmap[i].pmap_paddr = phys_addr;
> +	}
> +
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) {
> +		hwd_viif_isp_guard_start(viif_dev->hwd_res);
> +		ret = hwd_viif_l2_set_img_transmission(viif_dev->hwd_res, VIIF_L2ISP_POST_0,
> +						       HWD_VIIF_ENABLE, &cap_dev->img_area,
> +						       &cap_dev->out_process, &next_out_img);
> +		hwd_viif_isp_guard_end(viif_dev->hwd_res);
> +		if (ret)
> +			dev_err(viif_dev->dev, "set img error. %d\n", ret);
> +	} else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) {
> +		hwd_viif_isp_guard_start(viif_dev->hwd_res);
> +		ret = hwd_viif_l2_set_img_transmission(viif_dev->hwd_res, VIIF_L2ISP_POST_1,
> +						       HWD_VIIF_ENABLE, &cap_dev->img_area,
> +						       &cap_dev->out_process, &next_out_img);
> +		hwd_viif_isp_guard_end(viif_dev->hwd_res);
> +		if (ret)
> +			dev_err(viif_dev->dev, "set img error. %d\n", ret);
> +	} else if (cap_dev->pathid == CAPTURE_PATH_SUB) {
> +		hwd_viif_isp_guard_start(viif_dev->hwd_res);
> +		ret = hwd_viif_sub_set_img_transmission(viif_dev->hwd_res, &next_out_img);
> +		hwd_viif_isp_guard_end(viif_dev->hwd_res);
> +		if (ret)
> +			dev_err(viif_dev->dev, "set img error. %d\n", ret);
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * viif_capture_switch_buffer() is called from interrupt service routine
> + * triggered by VSync with some fixed delay.
> + * The function may switch DMA target buffer by calling viif_set_img().
> + * The VIIF DMA HW captures the destination address at next VSync
> + * and completes transfer at one more after.
> + * Therefore, filled buffer is available at the one after next ISR.
> + *
> + * To avoid DMA HW getting stucked, we always need to set valid destination address.
> + * If a prepared buffer is not available, we reuse the buffer currently being transferred to.
> + *
> + * The cap_dev structure has two pointers and a queue to handle video buffers;
> + + Description of each item at the entry of this function:
> + * * buf_queue:  holds prepared buffers, set by vb2_queue()
> + * * active:     pointing at address captured (and to be filled) by DMA HW
> + * * dma_active: pointing at buffer filled by DMA HW
> + *
> + * Rules to update items:
> + * * when buf_queue is not empty, "active" buffer goes "dma_active"
> + * * when buf_queue is empty:
> + *   * "active" buffer stays the same (DMA HW fills the same buffer for coming two frames)
> + *   * "dma_active" gets NULL (filled buffer will be reused; should not go "DONE" at next ISR)
> + *
> + * Simulation:
> + * | buf_queue   | active  | dma_active | note |
> + * | X           | NULL    | NULL       |      |
> + * <QBUF BUF0>
> + * | X           | BUF0    | NULL       | BUF0 stays |
> + * | X           | BUF0    | NULL       | BUF0 stays |
> + * <QBUF BUF1>
> + * <QBUF BUF2>
> + * | BUF2 BUF1   | BUF0    | NULL       |      |
> + * | BUF2        | BUF1    | BUF0       | BUF0 goes DONE |
> + * | X           | BUF2    | BUF1       | BUF1 goes DONE, BUF2 stays |
> + * | X           | BUF2    | NULL       | BUF2 stays |
> + */
> +void visconti_viif_capture_switch_buffer(struct cap_dev *cap_dev, u32 status_err,
> +					 u32 l2_transfer_status, u64 timestamp)
> +{
> +	if (cap_dev->dma_active) {
> +		/* DMA has completed and another framebuffer instance is set */
> +		struct vb2_v4l2_buffer *vbuf = cap_dev->dma_active;
> +		enum vb2_buffer_state state;
> +
> +		cap_dev->buf_cnt--;
> +		vbuf->vb2_buf.timestamp = timestamp;
> +		vbuf->sequence = cap_dev->sequence++;
> +		vbuf->field = cap_dev->field;
> +		if (status_err || l2_transfer_status)
> +			state = VB2_BUF_STATE_ERROR;
> +		else
> +			state = VB2_BUF_STATE_DONE;
> +
> +		vb2_buffer_done(&vbuf->vb2_buf, state);
> +	}
> +
> +	/* QUEUE pop to register an instance as next DMA target; if empty, reuse current instance */
> +	if (!list_empty(&cap_dev->buf_queue)) {
> +		struct viif_buffer *buf =
> +			list_entry(cap_dev->buf_queue.next, struct viif_buffer, queue);
> +		list_del_init(&buf->queue);
> +		viif_set_img(cap_dev, &buf->vb.vb2_buf);
> +		cap_dev->active = &buf->vb;
> +		cap_dev->dma_active = cap_dev->active;
> +	} else {
> +		cap_dev->dma_active = NULL;
> +	}
> +}
> +
> +/* --- Capture buffer control --- */
> +static int viif_vb2_setup(struct vb2_queue *vq, unsigned int *count, unsigned int *num_planes,
> +			  unsigned int sizes[], struct device *alloc_devs[])
> +{
> +	struct cap_dev *cap_dev = vb2queue_to_capdev(vq);
> +	struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix;
> +	unsigned int i;
> +
> +	/* num_planes is set: just check plane sizes. */
> +	if (*num_planes) {
> +		for (i = 0; i < pix->num_planes; i++)
> +			if (sizes[i] < pix->plane_fmt[i].sizeimage)
> +				return -EINVAL;
> +
> +		return 0;
> +	}
> +
> +	/* num_planes not set: called from REQBUFS, just set plane sizes. */
> +	*num_planes = pix->num_planes;
> +	for (i = 0; i < pix->num_planes; i++)
> +		sizes[i] = pix->plane_fmt[i].sizeimage;
> +
> +	cap_dev->buf_cnt = 0;
> +
> +	return 0;
> +}
> +
> +static void viif_vb2_queue(struct vb2_buffer *vb)
> +{
> +	struct cap_dev *cap_dev = vb2queue_to_capdev(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct viif_buffer *buf = vb2_to_viif(vbuf);
> +	unsigned long irqflags;
> +
> +	spin_lock_irqsave(&viif_dev->lock, irqflags);
> +
> +	if (!cap_dev->active) {
> +		cap_dev->active = vbuf;
> +		viif_set_img(cap_dev, vb);
> +	} else {
> +		list_add_tail(&buf->queue, &cap_dev->buf_queue);
> +	}
> +	cap_dev->buf_cnt++;
> +
> +	spin_unlock_irqrestore(&viif_dev->lock, irqflags);
> +}
> +
> +static int viif_vb2_prepare(struct vb2_buffer *vb)
> +{
> +	struct cap_dev *cap_dev = vb2queue_to_capdev(vb->vb2_queue);
> +	struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix;
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	unsigned int i;
> +
> +	for (i = 0; i < pix->num_planes; i++) {
> +		if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) {
> +			dev_err(viif_dev->dev, "Plane size too small (%lu < %u)\n",
> +				vb2_plane_size(vb, i), pix->plane_fmt[i].sizeimage);
> +			return -EINVAL;
> +		}
> +
> +		vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage);
> +	}
> +	return 0;
> +}
> +
> +static int viif_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct cap_dev *cap_dev = vb2queue_to_capdev(vq);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +	unsigned long irqflags;
> +	int ret;
> +
> +	spin_lock_irqsave(&viif_dev->lock, irqflags);
> +
> +	/* note that pipe is shared among paths; see pipe.streaming_count member variable */
> +	ret = video_device_pipeline_start(&cap_dev->vdev, &viif_dev->pipe);
> +	if (ret)
> +		dev_err(viif_dev->dev, "start pipeline failed %d\n", ret);

Huh, why do you ignore the error and continue?

> +
> +	/* Currently, only path0 (MAIN POST0) initializes ISP and Camera */
> +	/* Possibly, initialization can be done when pipe.streaming_count==0 */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) {
> +		/* CSI2RX start */
> +		ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, video, s_stream, true);
> +		if (ret) {
> +			dev_err(viif_dev->dev, "Start isp subdevice stream failed. %d\n", ret);
> +			spin_unlock_irqrestore(&viif_dev->lock, irqflags);

On error all queued buffers have to be returned to vb2 with status VB2_BUF_STATE_QUEUED.
Otherwise the vb2 internal status will get confused. Similar to stop_streaming, just
with a different status (QUEUED instead of ERROR).

> +			return ret;
> +		}
> +	}
> +
> +	/* buffer control */
> +	cap_dev->sequence = 0;
> +
> +	/* finish critical section: some sensor driver (including imx219) calls schedule() */
> +	spin_unlock_irqrestore(&viif_dev->lock, irqflags);
> +
> +	/* Camera (CSI2 source) start streaming */
> +	/* Currently, only path0 (MAIN POST0) initializes ISP and Camera */
> +	/* Possibly, initialization can be done when pipe.streaming_count==0 */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) {
> +		ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, s_stream, true);
> +		if (ret) {
> +			dev_err(viif_dev->dev, "Start subdev stream failed. %d\n", ret);
> +			(void)v4l2_subdev_call(&viif_dev->isp_subdev.sd, video, s_stream, false);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void viif_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct cap_dev *cap_dev = vb2queue_to_capdev(vq);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +	struct viif_buffer *buf;
> +	unsigned long irqflags;
> +	int ret;
> +
> +	/* Currently, only path0 (MAIN POST0) stops ISP and Camera */
> +	/* Possibly, teardown can be done when pipe.streaming_count==0 */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) {
> +		ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, s_stream, false);
> +		if (ret)
> +			dev_err(viif_dev->dev, "Stop subdev stream failed. %d\n", ret);
> +	}
> +
> +	spin_lock_irqsave(&viif_dev->lock, irqflags);
> +
> +	/* Currently, only path0 (MAIN POST0) stops ISP and Camera */
> +	/* Possibly, teardown can be done when pipe.streaming_count==0 */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) {
> +		ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, video, s_stream, false);
> +		if (ret)
> +			dev_err(viif_dev->dev, "Stop isp subdevice stream failed %d\n", ret);
> +	}
> +
> +	/* buffer control */
> +	if (cap_dev->active) {
> +		vb2_buffer_done(&cap_dev->active->vb2_buf, VB2_BUF_STATE_ERROR);
> +		cap_dev->buf_cnt--;
> +		cap_dev->active = NULL;
> +	}
> +	if (cap_dev->dma_active) {
> +		vb2_buffer_done(&cap_dev->dma_active->vb2_buf, VB2_BUF_STATE_ERROR);
> +		cap_dev->buf_cnt--;
> +		cap_dev->dma_active = NULL;
> +	}
> +
> +	/* Release all queued buffers. */
> +	list_for_each_entry(buf, &cap_dev->buf_queue, queue) {
> +		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
> +		cap_dev->buf_cnt--;
> +	}
> +	INIT_LIST_HEAD(&cap_dev->buf_queue);
> +	if (cap_dev->buf_cnt)
> +		dev_err(viif_dev->dev, "Buffer count error %d\n", cap_dev->buf_cnt);
> +
> +	video_device_pipeline_stop(&cap_dev->vdev);
> +
> +	spin_unlock_irqrestore(&viif_dev->lock, irqflags);
> +}
> +
> +static const struct vb2_ops viif_vb2_ops = {
> +	.queue_setup = viif_vb2_setup,
> +	.buf_queue = viif_vb2_queue,
> +	.buf_prepare = viif_vb2_prepare,
> +	.wait_prepare = vb2_ops_wait_prepare,
> +	.wait_finish = vb2_ops_wait_finish,
> +	.start_streaming = viif_start_streaming,
> +	.stop_streaming = viif_stop_streaming,
> +};
> +
> +/* --- VIIF hardware settings --- */
> +/* L2ISP output csc setting for YUV to RGB(ITU-R BT.709) */
> +static const struct hwd_viif_csc_param viif_csc_yuv2rgb = {
> +	.r_cr_in_offset = 0x18000,
> +	.g_y_in_offset = 0x1f000,
> +	.b_cb_in_offset = 0x18000,
> +	.coef = {
> +			[0] = 0x1000,
> +			[1] = 0xfd12,
> +			[2] = 0xf8ad,
> +			[3] = 0x1000,
> +			[4] = 0x1d07,
> +			[5] = 0x0000,
> +			[6] = 0x1000,
> +			[7] = 0x0000,
> +			[8] = 0x18a2,
> +		},
> +	.r_cr_out_offset = 0x1000,
> +	.g_y_out_offset = 0x1000,
> +	.b_cb_out_offset = 0x1000,
> +};
> +
> +/* L2ISP output csc setting for RGB to YUV(ITU-R BT.709) */
> +static const struct hwd_viif_csc_param viif_csc_rgb2yuv = {
> +	.r_cr_in_offset = 0x1f000,
> +	.g_y_in_offset = 0x1f000,
> +	.b_cb_in_offset = 0x1f000,
> +	.coef = {
> +			[0] = 0x0b71,
> +			[1] = 0x0128,
> +			[2] = 0x0367,
> +			[3] = 0xf9b1,
> +			[4] = 0x082f,
> +			[5] = 0xfe20,
> +			[6] = 0xf891,
> +			[7] = 0xff40,
> +			[8] = 0x082f,
> +		},
> +	.r_cr_out_offset = 0x8000,
> +	.g_y_out_offset = 0x1000,
> +	.b_cb_out_offset = 0x8000,
> +};
> +
> +static int viif_l2_set_format(struct cap_dev *cap_dev)
> +{
> +	struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix;
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	const struct hwd_viif_csc_param *csc_param = NULL;
> +	struct v4l2_subdev_selection sel = {
> +		.target = V4L2_SEL_TGT_CROP,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	struct v4l2_subdev_format fmt = {
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	bool inp_is_rgb = false;
> +	bool out_is_rgb = false;
> +	u32 postid;
> +	int ret;
> +
> +	/* check path id */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) {
> +		sel.pad = VIIF_ISP_PAD_SRC_PATH0;
> +		fmt.pad = VIIF_ISP_PAD_SRC_PATH0;
> +		postid = VIIF_L2ISP_POST_0;
> +	} else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) {
> +		sel.pad = VIIF_ISP_PAD_SRC_PATH1;
> +		fmt.pad = VIIF_ISP_PAD_SRC_PATH1;
> +		postid = VIIF_L2ISP_POST_1;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	cap_dev->out_process.half_scale = HWD_VIIF_DISABLE;
> +	cap_dev->out_process.select_color = HWD_VIIF_COLOR_YUV_RGB;
> +	cap_dev->out_process.alpha = 0;
> +
> +	ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_selection, NULL, &sel);
> +	if (ret) {
> +		cap_dev->img_area.x = 0;
> +		cap_dev->img_area.y = 0;
> +		cap_dev->img_area.w = pix->width;
> +		cap_dev->img_area.h = pix->height;
> +	} else {
> +		cap_dev->img_area.x = sel.r.left;
> +		cap_dev->img_area.y = sel.r.top;
> +		cap_dev->img_area.w = sel.r.width;
> +		cap_dev->img_area.h = sel.r.height;
> +	}
> +
> +	ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_fmt, NULL, &fmt);
> +	if (!ret)
> +		inp_is_rgb = (fmt.format.code == MEDIA_BUS_FMT_RGB888_1X24);
> +
> +	switch (pix->pixelformat) {
> +	case V4L2_PIX_FMT_RGB24:
> +		cap_dev->out_format = HWD_VIIF_RGB888_PACKED;
> +		out_is_rgb = true;
> +		break;
> +	case V4L2_PIX_FMT_ABGR32:
> +		cap_dev->out_format = HWD_VIIF_ARGB8888_PACKED;
> +		cap_dev->out_process.alpha = 0xff;
> +		out_is_rgb = true;
> +		break;
> +	case V4L2_PIX_FMT_YUV422M:
> +		cap_dev->out_format = HWD_VIIF_YCBCR422_8_PLANAR;
> +		break;
> +	case V4L2_PIX_FMT_YUV444M:
> +		cap_dev->out_format = HWD_VIIF_RGB888_YCBCR444_8_PLANAR;
> +		break;
> +	case V4L2_PIX_FMT_Y16:
> +		cap_dev->out_format = HWD_VIIF_ONE_COLOR_16;
> +		cap_dev->out_process.select_color = HWD_VIIF_COLOR_Y_G;
> +		break;
> +	}
> +
> +	if (!inp_is_rgb && out_is_rgb)
> +		csc_param = &viif_csc_yuv2rgb; /* YUV -> RGB */
> +	else if (inp_is_rgb && !out_is_rgb)
> +		csc_param = &viif_csc_rgb2yuv; /* RGB -> YUV */
> +
> +	return hwd_viif_l2_set_output_csc(viif_dev->hwd_res, postid, csc_param);
> +}
> +
> +/* --- IOCTL Operations --- */
> +static const struct viif_fmt viif_fmt_list[] = {
> +	{
> +		.fourcc = V4L2_PIX_FMT_RGB24,
> +		.bpp = { 24, 0, 0 },
> +		.num_planes = 1,
> +		.colorspace = V4L2_COLORSPACE_SRGB,
> +		.pitch_align = 384,
> +	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_ABGR32,
> +		.bpp = { 32, 0, 0 },
> +		.num_planes = 1,
> +		.colorspace = V4L2_COLORSPACE_SRGB,
> +		.pitch_align = 512,
> +	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_YUV422M,
> +		.bpp = { 8, 4, 4 },
> +		.num_planes = 3,
> +		.colorspace = V4L2_COLORSPACE_REC709,
> +		.pitch_align = 128,
> +	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_YUV444M,
> +		.bpp = { 8, 8, 8 },
> +		.num_planes = 3,
> +		.colorspace = V4L2_COLORSPACE_REC709,
> +		.pitch_align = 128,
> +	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_Y16,
> +		.bpp = { 16, 0, 0 },
> +		.num_planes = 1,
> +		.colorspace = V4L2_COLORSPACE_REC709,
> +		.pitch_align = 128,
> +	},
> +};
> +
> +static const struct viif_fmt viif_rawfmt_list[] = {
> +	{
> +		.fourcc = V4L2_PIX_FMT_SRGGB10,
> +		.bpp = { 16, 0, 0 },
> +		.num_planes = 1,
> +		.colorspace = V4L2_COLORSPACE_SRGB,
> +		.pitch_align = 256,
> +	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_SRGGB12,
> +		.bpp = { 16, 0, 0 },
> +		.num_planes = 1,
> +		.colorspace = V4L2_COLORSPACE_SRGB,
> +		.pitch_align = 256,
> +	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_SRGGB14,
> +		.bpp = { 16, 0, 0 },
> +		.num_planes = 1,
> +		.colorspace = V4L2_COLORSPACE_SRGB,
> +		.pitch_align = 256,
> +	},
> +};
> +
> +static const struct viif_fmt *get_viif_fmt_from_fourcc(unsigned int fourcc)
> +{
> +	const struct viif_fmt *fmt = &viif_fmt_list[0];
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(viif_fmt_list); i++, fmt++)
> +		if (fmt->fourcc == fourcc)
> +			return fmt;
> +
> +	return NULL;
> +}
> +
> +static void viif_update_plane_sizes(struct v4l2_plane_pix_format *plane, unsigned int bpl,
> +				    unsigned int szimage)
> +{
> +	memset(plane, 0, sizeof(*plane));
> +
> +	plane->sizeimage = szimage;
> +	plane->bytesperline = bpl;
> +}
> +
> +static void viif_calc_plane_sizes(const struct viif_fmt *viif_fmt,
> +				  struct v4l2_pix_format_mplane *pix)
> +{
> +	unsigned int i, bpl, szimage;
> +
> +	for (i = 0; i < viif_fmt->num_planes; i++) {
> +		bpl = pix->width * viif_fmt->bpp[i] / 8;
> +		/* round up ptch */
> +		bpl = (bpl + (viif_fmt->pitch_align - 1)) / viif_fmt->pitch_align;
> +		bpl *= viif_fmt->pitch_align;
> +		szimage = pix->height * bpl;
> +		viif_update_plane_sizes(&pix->plane_fmt[i], bpl, szimage);
> +	}
> +	pix->num_planes = viif_fmt->num_planes;
> +}
> +
> +static int viif_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +
> +	strscpy(cap->card, "Toshiba VIIF", sizeof(cap->card));
> +	strscpy(cap->driver, "viif", sizeof(cap->driver));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:toshiba-viif-%s",
> +		 dev_name(viif_dev->dev));
> +	return 0;
> +}
> +
> +static int viif_enum_rawfmt(struct cap_dev *cap_dev, struct v4l2_fmtdesc *f)
> +{
> +	if (f->index >= ARRAY_SIZE(viif_rawfmt_list))
> +		return -EINVAL;
> +
> +	f->pixelformat = viif_rawfmt_list[f->index].fourcc;
> +
> +	return 0;
> +}
> +
> +static int viif_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +	const struct viif_fmt *fmt;
> +
> +	if (cap_dev->pathid == CAPTURE_PATH_SUB)
> +		return viif_enum_rawfmt(cap_dev, f);
> +
> +	if (f->index >= ARRAY_SIZE(viif_fmt_list))
> +		return -EINVAL;
> +
> +	fmt = &viif_fmt_list[f->index];
> +	f->pixelformat = fmt->fourcc;
> +
> +	return 0;
> +}
> +
> +/* size of minimum/maximum output image */
> +#define VIIF_MIN_OUTPUT_IMG_WIDTH     (128U)
> +#define VIIF_MAX_OUTPUT_IMG_WIDTH_ISP (5760U)
> +#define VIIF_MAX_OUTPUT_IMG_WIDTH_SUB (4096U)
> +
> +#define VIIF_MIN_OUTPUT_IMG_HEIGHT     (128U)
> +#define VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP (3240U)
> +#define VIIF_MAX_OUTPUT_IMG_HEIGHT_SUB (2160U)
> +
> +static int viif_try_fmt(struct cap_dev *cap_dev, struct v4l2_format *v4l2_fmt)
> +{
> +	struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp;
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct v4l2_subdev_format format = {
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	const struct viif_fmt *viif_fmt;
> +	int ret;
> +
> +	/* check path id */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0)
> +		format.pad = VIIF_ISP_PAD_SRC_PATH0;
> +	else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1)
> +		format.pad = VIIF_ISP_PAD_SRC_PATH1;
> +	else
> +		format.pad = VIIF_ISP_PAD_SRC_PATH2;
> +
> +	ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_fmt, NULL, &format);
> +	if (ret)
> +		return -EINVAL;
> +
> +	/* fourcc check */
> +	if (cap_dev->pathid == CAPTURE_PATH_SUB) {
> +		switch (format.format.code) {
> +		case MEDIA_BUS_FMT_SRGGB10_1X10:
> +		case MEDIA_BUS_FMT_SGRBG10_1X10:
> +		case MEDIA_BUS_FMT_SGBRG10_1X10:
> +		case MEDIA_BUS_FMT_SBGGR10_1X10:
> +			viif_fmt = &viif_rawfmt_list[0]; /*V4L2_PIX_FMT_SRGGB10*/
> +			pix->pixelformat = viif_fmt->fourcc;
> +			break;
> +		case MEDIA_BUS_FMT_SRGGB12_1X12:
> +		case MEDIA_BUS_FMT_SGRBG12_1X12:
> +		case MEDIA_BUS_FMT_SGBRG12_1X12:
> +		case MEDIA_BUS_FMT_SBGGR12_1X12:
> +			viif_fmt = &viif_rawfmt_list[1]; /*V4L2_PIX_FMT_SRGGB12*/
> +			pix->pixelformat = viif_fmt->fourcc;
> +			break;
> +		case MEDIA_BUS_FMT_SRGGB14_1X14:
> +		case MEDIA_BUS_FMT_SGRBG14_1X14:
> +		case MEDIA_BUS_FMT_SGBRG14_1X14:
> +		case MEDIA_BUS_FMT_SBGGR14_1X14:
> +			viif_fmt = &viif_rawfmt_list[2]; /*V4L2_PIX_FMT_SRGGB14*/
> +			pix->pixelformat = viif_fmt->fourcc;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	} else {
> +		viif_fmt = get_viif_fmt_from_fourcc(pix->pixelformat);
> +		if (!viif_fmt)
> +			return -EINVAL;
> +	}
> +
> +	/* min/max width, height check */
> +	if (pix->width < VIIF_MIN_OUTPUT_IMG_WIDTH)
> +		pix->width = VIIF_MIN_OUTPUT_IMG_WIDTH;
> +
> +	if (pix->width > VIIF_MAX_OUTPUT_IMG_WIDTH_ISP)
> +		pix->width = VIIF_MAX_OUTPUT_IMG_WIDTH_ISP;
> +
> +	if (pix->height < VIIF_MIN_OUTPUT_IMG_HEIGHT)
> +		pix->height = VIIF_MIN_OUTPUT_IMG_HEIGHT;
> +
> +	if (pix->height > VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP)
> +		pix->height = VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP;
> +
> +	/* consistency with isp::pad::src::fmt */
> +	if (pix->width != format.format.width)
> +		return -EINVAL;
> +	if (pix->height != format.format.height)
> +		return -EINVAL;
> +
> +	/* update derived parameters, such as bpp */
> +	viif_calc_plane_sizes(viif_fmt, pix);
> +
> +	return 0;
> +}
> +
> +static int viif_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +
> +	return viif_try_fmt(cap_dev, f);
> +}
> +
> +static int viif_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	int ret = 0;
> +
> +	if (vb2_is_streaming(&cap_dev->vb2_vq))

That should be vb2_is_busy(). Once buffers are allocated, you can no longer
change the format since that would change the buffer size as well.

> +		return -EBUSY;
> +
> +	if (f->type != cap_dev->vb2_vq.type)
> +		return -EINVAL;
> +
> +	ret = viif_try_fmt(cap_dev, f);
> +	if (ret)
> +		return ret;
> +
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) {
> +		/*
> +		 * A call to main_set_unit() is currently at ioctl(VIDIOC_S_FMT) context.
> +		 * This call can be moved to viif_isp_s_stream(),
> +		 * if you don't want to check the given format is compatible to HW.
> +		 */
> +		ret = visconti_viif_isp_main_set_unit(viif_dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	cap_dev->v4l2_pix = f->fmt.pix_mp;
> +	cap_dev->field = V4L2_FIELD_NONE;
> +
> +	if (cap_dev->pathid == CAPTURE_PATH_SUB) {
> +		cap_dev->out_format = HWD_VIIF_ONE_COLOR_16;
> +		ret = visconti_viif_isp_sub_set_unit(viif_dev);
> +	} else {
> +		ret = viif_l2_set_format(cap_dev);
> +	}
> +
> +	return ret;
> +}
> +
> +static int viif_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +
> +	f->fmt.pix_mp = cap_dev->v4l2_pix;
> +
> +	return 0;
> +}
> +
> +static int viif_enum_input(struct file *file, void *priv, struct v4l2_input *inp)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd;
> +	struct v4l2_subdev *v4l2_sd;
> +	int ret;
> +
> +	if (inp->index >= viif_dev->num_sd)
> +		return -EINVAL;
> +
> +	viif_sd = &viif_dev->subdevs[inp->index];
> +	v4l2_sd = viif_sd->v4l2_sd;
> +
> +	ret = v4l2_subdev_call(v4l2_sd, video, g_input_status, &inp->status);
> +	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
> +		return ret;
> +	inp->type = V4L2_INPUT_TYPE_CAMERA;
> +	inp->std = 0;
> +	if (v4l2_subdev_has_op(v4l2_sd, pad, dv_timings_cap))
> +		inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
> +	else
> +		inp->capabilities = V4L2_IN_CAP_STD;
> +	snprintf(inp->name, sizeof(inp->name), "Camera%u: %s", inp->index, viif_sd->v4l2_sd->name);
> +
> +	return 0;
> +}
> +
> +static int viif_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +
> +	*i = viif_dev->sd_index;
> +
> +	return 0;
> +}
> +
> +static int viif_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +
> +	if (i >= viif_dev->num_sd)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int viif_g_selection(struct file *file, void *priv, struct v4l2_selection *s)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct v4l2_subdev_selection sel = {
> +		.target = V4L2_SEL_TGT_CROP,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	int ret;
> +

This is missing validation checks for s->type and s->target.

I've pretty sure you didn't run the v4l2-compliance utility: that would have
failed on this.

> +	/* check path id */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0)
> +		sel.pad = VIIF_ISP_PAD_SRC_PATH0;
> +	else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1)
> +		sel.pad = VIIF_ISP_PAD_SRC_PATH1;
> +	else
> +		return -EINVAL;
> +
> +	ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, get_selection, NULL, &sel);
> +	s->r = sel.r;
> +
> +	return ret;
> +}
> +
> +static int viif_s_selection(struct file *file, void *priv, struct v4l2_selection *s)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct v4l2_subdev_selection sel = {
> +		.target = V4L2_SEL_TGT_CROP,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.r = s->r,
> +	};
> +	int ret;
> +

Same as above.

> +	/* check path id */
> +	if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0)
> +		sel.pad = VIIF_ISP_PAD_SRC_PATH0;
> +	else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1)
> +		sel.pad = VIIF_ISP_PAD_SRC_PATH1;
> +	else
> +		return -EINVAL;
> +
> +	if (s->r.left > VIIF_CROP_MAX_X_ISP || s->r.top > VIIF_CROP_MAX_Y_ISP ||
> +	    s->r.width < VIIF_CROP_MIN_W || s->r.width > VIIF_CROP_MAX_W_ISP ||
> +	    s->r.height < VIIF_CROP_MIN_H || s->r.height > VIIF_CROP_MAX_H_ISP) {
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l2_subdev_call(&viif_dev->isp_subdev.sd, pad, set_selection, NULL, &sel);
> +	s->r = sel.r;
> +
> +	return ret;
> +}
> +
> +static int viif_dv_timings_cap(struct file *file, void *priv_fh, struct v4l2_dv_timings_cap *cap)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +
> +	return v4l2_subdev_call(viif_sd->v4l2_sd, pad, dv_timings_cap, cap);
> +}
> +
> +static int viif_enum_dv_timings(struct file *file, void *priv_fh,
> +				struct v4l2_enum_dv_timings *timings)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +
> +	return v4l2_subdev_call(viif_sd->v4l2_sd, pad, enum_dv_timings, timings);
> +}
> +
> +static int viif_g_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +
> +	return v4l2_subdev_call(viif_sd->v4l2_sd, video, g_dv_timings, timings);
> +}
> +
> +static int viif_s_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +
> +	return v4l2_subdev_call(viif_sd->v4l2_sd, video, s_dv_timings, timings);
> +}
> +
> +static int viif_query_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +
> +	return v4l2_subdev_call(viif_sd->v4l2_sd, video, query_dv_timings, timings);
> +}
> +
> +static int viif_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +
> +	return v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_edid, edid);
> +}
> +
> +static int viif_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +
> +	return v4l2_subdev_call(viif_sd->v4l2_sd, pad, set_edid, edid);
> +}

Has this driver been tested with an HDMI receiver? If not, then I would recommend
dropping support for it until you actually can test with such hardware.

The DV_TIMINGS API is for HDMI/DVI/DisplayPort etc. interfaces, it's not meant
for CSI and similar interfaces.

> +
> +static int viif_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +
> +	return v4l2_g_parm_cap(video_devdata(file), viif_dev->sd->v4l2_sd, a);
> +}
> +
> +static int viif_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +
> +	return v4l2_s_parm_cap(video_devdata(file), viif_dev->sd->v4l2_sd, a);
> +}
> +
> +static int viif_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +	struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
> +	struct v4l2_subdev_frame_size_enum fse = {
> +		.code = viif_sd->mbus_code,
> +		.index = fsize->index,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	int ret;
> +
> +	ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_size, NULL, &fse);
> +	if (ret)
> +		return ret;
> +
> +	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> +	fsize->discrete.width = fse.max_width;
> +	fsize->discrete.height = fse.max_height;
> +
> +	return 0;
> +}
> +
> +static int viif_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival)
> +{
> +	struct viif_device *viif_dev = video_drvdata_to_capdev(file)->viif_dev;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +	struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
> +	struct v4l2_subdev_frame_interval_enum fie = {
> +		.code = viif_sd->mbus_code,
> +		.index = fival->index,
> +		.width = fival->width,
> +		.height = fival->height,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	int ret;
> +
> +	ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_interval, NULL, &fie);
> +	if (ret)
> +		return ret;
> +
> +	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
> +	fival->discrete = fie.interval;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops viif_ioctl_ops = {
> +	.vidioc_querycap = viif_querycap,
> +
> +	.vidioc_enum_fmt_vid_cap = viif_enum_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap_mplane = viif_try_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap_mplane = viif_s_fmt_vid_cap,
> +	.vidioc_g_fmt_vid_cap_mplane = viif_g_fmt_vid_cap,
> +
> +	.vidioc_enum_input = viif_enum_input,
> +	.vidioc_g_input = viif_g_input,
> +	.vidioc_s_input = viif_s_input,
> +
> +	.vidioc_g_selection = viif_g_selection,
> +	.vidioc_s_selection = viif_s_selection,
> +
> +	.vidioc_dv_timings_cap = viif_dv_timings_cap,
> +	.vidioc_enum_dv_timings = viif_enum_dv_timings,
> +	.vidioc_g_dv_timings = viif_g_dv_timings,
> +	.vidioc_s_dv_timings = viif_s_dv_timings,
> +	.vidioc_query_dv_timings = viif_query_dv_timings,
> +
> +	.vidioc_g_edid = viif_g_edid,
> +	.vidioc_s_edid = viif_s_edid,
> +
> +	.vidioc_g_parm = viif_g_parm,
> +	.vidioc_s_parm = viif_s_parm,
> +
> +	.vidioc_enum_framesizes = viif_enum_framesizes,
> +	.vidioc_enum_frameintervals = viif_enum_frameintervals,
> +
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +
> +	.vidioc_log_status = v4l2_ctrl_log_status,
> +	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +/* --- File Operations --- */
> +static int viif_capture_open(struct file *file)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	int ret;
> +
> +	ret = v4l2_fh_open(file);
> +	if (ret)
> +		return ret;
> +
> +	return pm_runtime_resume_and_get(viif_dev->dev);

If pm_runtime_resume_and_get fails, then v4l2_fh_release needs
to be called.

> +}
> +
> +static int viif_capture_release(struct file *file)
> +{
> +	struct cap_dev *cap_dev = video_drvdata_to_capdev(file);
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +
> +	vb2_fop_release(file);
> +	pm_runtime_put(viif_dev->dev);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations viif_fops = {
> +	.owner = THIS_MODULE,
> +	.open = viif_capture_open,
> +	.release = viif_capture_release,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = vb2_fop_mmap,
> +	.poll = vb2_fop_poll,
> +};
> +
> +/* ----- media control callbacks ----- */
> +static int viif_capture_link_validate(struct media_link *link)
> +{
> +	/* link validation at start-stream */
> +	return 0;
> +}
> +
> +static const struct media_entity_operations viif_media_ops = {
> +	.link_validate = viif_capture_link_validate,
> +};
> +
> +/* ----- attach ctrl callbacck handler ----- */
> +int visconti_viif_capture_register_ctrl_handlers(struct viif_device *viif_dev)
> +{
> +	int ret;
> +
> +	/* MAIN POST0: merge controls of ISP and CAPTURE0 */
> +	ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev0.ctrl_handler,
> +				    viif_dev->sd->v4l2_sd->ctrl_handler, NULL, true);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "Failed to add sensor ctrl_handler");
> +		return ret;
> +	}
> +	ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev0.ctrl_handler,
> +				    &viif_dev->isp_subdev.ctrl_handler, NULL, true);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "Failed to add isp subdev ctrl_handler");
> +		return ret;
> +	}
> +
> +	/* MAIN POST1: merge controls of ISP and CAPTURE0 */
> +	ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev1.ctrl_handler,
> +				    viif_dev->sd->v4l2_sd->ctrl_handler, NULL, true);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "Failed to add sensor ctrl_handler");
> +		return ret;
> +	}
> +	ret = v4l2_ctrl_add_handler(&viif_dev->cap_dev1.ctrl_handler,
> +				    &viif_dev->isp_subdev.ctrl_handler, NULL, true);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "Failed to add isp subdev ctrl_handler");
> +		return ret;
> +	}
> +
> +	/* SUB: no control is exported */
> +
> +	return 0;
> +}
> +
> +/* ----- register/remove capture device node ----- */
> +static int visconti_viif_capture_register_node(struct cap_dev *cap_dev)
> +{
> +	struct viif_device *viif_dev = cap_dev->viif_dev;
> +	struct v4l2_device *v4l2_dev = &viif_dev->v4l2_dev;
> +	struct video_device *vdev = &cap_dev->vdev;
> +	struct vb2_queue *q = &cap_dev->vb2_vq;
> +	static const char *const node_name[] = {
> +		"viif_capture_post0",
> +		"viif_capture_post1",
> +		"viif_capture_sub",
> +	};
> +	int ret;
> +
> +	INIT_LIST_HEAD(&cap_dev->buf_queue);
> +
> +	mutex_init(&cap_dev->vlock);
> +
> +	/* Initialize vb2 queue. */
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +	q->io_modes = VB2_DMABUF;

Why is there no VB2_MMAP?

> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	q->ops = &viif_vb2_ops;
> +	q->mem_ops = &vb2_dma_contig_memops;
> +	q->drv_priv = cap_dev;
> +	q->buf_struct_size = sizeof(struct viif_buffer);
> +	q->min_buffers_needed = 2;
> +	q->lock = &cap_dev->vlock;
> +	q->dev = viif_dev->v4l2_dev.dev;
> +
> +	ret = vb2_queue_init(q);
> +	if (ret)
> +		return ret;
> +
> +	/* Register the video device. */
> +	strscpy(vdev->name, node_name[cap_dev->pathid], sizeof(vdev->name));
> +	vdev->v4l2_dev = v4l2_dev;
> +	vdev->lock = &cap_dev->vlock;
> +	vdev->queue = &cap_dev->vb2_vq;
> +	vdev->ctrl_handler = NULL;
> +	vdev->fops = &viif_fops;
> +	vdev->ioctl_ops = &viif_ioctl_ops;
> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING;
> +	vdev->device_caps |= V4L2_CAP_IO_MC;
> +	vdev->entity.ops = &viif_media_ops;
> +	vdev->release = video_device_release_empty;
> +	video_set_drvdata(vdev, cap_dev);
> +	vdev->vfl_dir = VFL_DIR_RX;
> +	cap_dev->capture_pad.flags = MEDIA_PAD_FL_SINK;
> +
> +	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
> +	if (ret < 0) {
> +		dev_err(v4l2_dev->dev, "video_register_device failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = media_entity_pads_init(&vdev->entity, 1, &cap_dev->capture_pad);
> +	if (ret) {
> +		video_unregister_device(vdev);
> +		return ret;
> +	}
> +
> +	ret = v4l2_ctrl_handler_init(&cap_dev->ctrl_handler, 30);
> +	if (ret)
> +		return -ENOMEM;
> +
> +	cap_dev->vdev.ctrl_handler = &cap_dev->ctrl_handler;
> +
> +	return 0;
> +}
> +
> +int visconti_viif_capture_register(struct viif_device *viif_dev)
> +{
> +	int ret;
> +
> +	/* register MAIN POST0 (primary RGB output)*/
> +	viif_dev->cap_dev0.pathid = CAPTURE_PATH_MAIN_POST0;
> +	viif_dev->cap_dev0.viif_dev = viif_dev;
> +	ret = visconti_viif_capture_register_node(&viif_dev->cap_dev0);
> +	if (ret)
> +		return ret;
> +
> +	/* register MAIN POST1 (additional RGB output)*/
> +	viif_dev->cap_dev1.pathid = CAPTURE_PATH_MAIN_POST1;
> +	viif_dev->cap_dev1.viif_dev = viif_dev;
> +	ret = visconti_viif_capture_register_node(&viif_dev->cap_dev1);
> +	if (ret)
> +		return ret;
> +
> +	/* register SUB (RAW output) */
> +	viif_dev->cap_dev2.pathid = CAPTURE_PATH_SUB;
> +	viif_dev->cap_dev2.viif_dev = viif_dev;
> +	ret = visconti_viif_capture_register_node(&viif_dev->cap_dev2);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void visconti_viif_capture_unregister_node(struct cap_dev *cap_dev)
> +{
> +	media_entity_cleanup(&cap_dev->vdev.entity);
> +	v4l2_ctrl_handler_free(&cap_dev->ctrl_handler);
> +	vb2_video_unregister_device(&cap_dev->vdev);
> +	mutex_destroy(&cap_dev->vlock);
> +}
> +
> +void visconti_viif_capture_unregister(struct viif_device *viif_dev)
> +{
> +	visconti_viif_capture_unregister_node(&viif_dev->cap_dev0);
> +	visconti_viif_capture_unregister_node(&viif_dev->cap_dev1);
> +	visconti_viif_capture_unregister_node(&viif_dev->cap_dev2);
> +}
> diff --git a/drivers/media/platform/visconti/viif_isp.c b/drivers/media/platform/visconti/viif_isp.c
> new file mode 100644
> index 00000000000..9314e6e8661
> --- /dev/null
> +++ b/drivers/media/platform/visconti/viif_isp.c
> @@ -0,0 +1,846 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
> +/* Toshiba Visconti Video Capture Support
> + *
> + * (C) Copyright 2022 TOSHIBA CORPORATION
> + * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
> + */
> +
> +#include <linux/delay.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "viif.h"
> +
> +/* ----- supported MBUS formats ----- */
> +struct visconti_mbus_format {
> +	unsigned int code;
> +	unsigned int bpp;
> +	int rgb_out;
> +} static visconti_mbus_formats[] = {
> +	{ .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24, .rgb_out = 1 },
> +	{ .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_UYVY10_1X20, .bpp = 20, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16, .rgb_out = 1 },
> +	{ .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14, .rgb_out = 0 },
> +	{ .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14, .rgb_out = 0 },
> +};
> +
> +static int viif_get_mbus_rgb_out(unsigned int mbus_code)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++)
> +		if (visconti_mbus_formats[i].code == mbus_code)
> +			return visconti_mbus_formats[i].rgb_out;
> +
> +	/* YUV intermediate code by default */
> +	return 0;
> +}
> +
> +static unsigned int viif_get_mbus_bpp(unsigned int mbus_code)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++)
> +		if (visconti_mbus_formats[i].code == mbus_code)
> +			return visconti_mbus_formats[i].bpp;
> +
> +	/* default bpp value */
> +	return 24;
> +}
> +
> +static bool viif_is_valid_mbus_code(unsigned int mbus_code)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++)
> +		if (visconti_mbus_formats[i].code == mbus_code)
> +			return true;
> +	return false;
> +}
> +
> +/* ----- handling main processing path ----- */
> +static int viif_get_dv_timings(struct viif_device *viif_dev, struct v4l2_dv_timings *timings)
> +{
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +	struct v4l2_subdev_pad_config pad_cfg;
> +	struct v4l2_subdev_state pad_state = {
> +		.pads = &pad_cfg,
> +	};
> +	struct v4l2_subdev_format format = {
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +		.pad = 0,
> +	};
> +	struct v4l2_ctrl *ctrl;
> +	int ret;
> +
> +	/* some video I/F support dv_timings query */
> +	ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, g_dv_timings, timings);
> +	if (ret == 0)
> +		return 0;
> +
> +	/* others: call some discrete APIs */
> +	ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_fmt, &pad_state, &format);
> +	if (ret != 0)
> +		return ret;
> +
> +	timings->bt.width = format.format.width;
> +	timings->bt.height = format.format.height;
> +
> +	ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_HBLANK);
> +	if (!ctrl) {
> +		dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n");
> +		return -EINVAL;
> +	}
> +	timings->bt.hsync = v4l2_ctrl_g_ctrl(ctrl);
> +
> +	ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_VBLANK);
> +	if (!ctrl) {
> +		dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n");
> +		return -EINVAL;
> +	}
> +	timings->bt.vsync = v4l2_ctrl_g_ctrl(ctrl);
> +
> +	ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_PIXEL_RATE);
> +	if (!ctrl) {
> +		dev_err(viif_dev->dev, "subdev: V4L2_CID_PIXEL_RATE error.\n");
> +		return -EINVAL;
> +	}
> +	timings->bt.pixelclock = v4l2_ctrl_g_ctrl_int64(ctrl);
> +
> +	return 0;
> +}
> +
> +int visconti_viif_isp_main_set_unit(struct viif_device *viif_dev)
> +{
> +	unsigned int dt_image, color_type, rawpack, yuv_conv;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +	struct hwd_viif_input_img in_img_main;
> +	struct viif_l2_undist undist = { 0 };
> +	struct v4l2_dv_timings timings;
> +	struct v4l2_subdev_format fmt = {
> +		.pad = 0,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	int mag_hactive = 1;
> +	int ret = 0;
> +
> +	ret = viif_get_dv_timings(viif_dev, &timings);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "could not get timing information of subdev");
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_fmt, NULL, &fmt);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "could not get pad information of subdev");
> +		return -EINVAL;
> +	}
> +
> +	switch (fmt.format.code) {
> +	case MEDIA_BUS_FMT_RGB888_1X24:
> +		dt_image = VISCONTI_CSI2_DT_RGB888;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		dt_image = VISCONTI_CSI2_DT_YUV4228B;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		dt_image = VISCONTI_CSI2_DT_YUV42210B;
> +		break;
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		dt_image = VISCONTI_CSI2_DT_RGB565;
> +		break;
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +		dt_image = VISCONTI_CSI2_DT_RAW8;
> +		break;
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +		dt_image = VISCONTI_CSI2_DT_RAW10;
> +		break;
> +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> +		dt_image = VISCONTI_CSI2_DT_RAW12;
> +		break;
> +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> +		dt_image = VISCONTI_CSI2_DT_RAW14;
> +		break;
> +	default:
> +		dt_image = VISCONTI_CSI2_DT_RGB888;
> +		break;
> +	}
> +
> +	color_type = dt_image;
> +
> +	if (color_type == VISCONTI_CSI2_DT_RAW8 || color_type == VISCONTI_CSI2_DT_RAW10 ||
> +	    color_type == VISCONTI_CSI2_DT_RAW12) {
> +		rawpack = viif_dev->rawpack_mode;
> +		if (rawpack != HWD_VIIF_RAWPACK_DISABLE)
> +			mag_hactive = 2;
> +	} else {
> +		rawpack = HWD_VIIF_RAWPACK_DISABLE;
> +	}
> +
> +	if (color_type == VISCONTI_CSI2_DT_YUV4228B || color_type == VISCONTI_CSI2_DT_YUV42210B)
> +		yuv_conv = HWD_VIIF_YUV_CONV_INTERPOLATION;
> +	else
> +		yuv_conv = HWD_VIIF_YUV_CONV_REPEAT;
> +
> +	in_img_main.hactive_size = timings.bt.width;
> +	in_img_main.vactive_size = timings.bt.height;
> +	in_img_main.htotal_size = timings.bt.width * mag_hactive + timings.bt.hsync;
> +	in_img_main.vtotal_size = timings.bt.height + timings.bt.vsync;
> +	in_img_main.pixel_clock = timings.bt.pixelclock / 1000;
> +	in_img_main.vbp_size = timings.bt.vsync - 5;
> +
> +	in_img_main.interpolation_mode = HWD_VIIF_L1_INPUT_INTERPOLATION_LINE;
> +	in_img_main.input_num = 1;
> +	in_img_main.hobc_width = 0;
> +	in_img_main.hobc_margin = 0;
> +
> +	/* configuration of MAIN unit */
> +	ret = hwd_viif_main_set_unit(viif_dev->hwd_res, dt_image, &in_img_main, color_type, rawpack,
> +				     yuv_conv);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "main_set_unit error. %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Enable regbuf */
> +	hwd_viif_isp_set_regbuf_auto_transmission(viif_dev->hwd_res);
> +
> +	/* L2 UNDIST Enable through mode as default  */
> +	undist.through_mode = HWD_VIIF_ENABLE;
> +	undist.sensor_crop_ofs_h = 1 - in_img_main.hactive_size;
> +	undist.sensor_crop_ofs_v = 1 - in_img_main.vactive_size;
> +	undist.grid_node_num_h = 16;
> +	undist.grid_node_num_v = 16;
> +	ret = hwd_viif_l2_set_undist(viif_dev->hwd_res, &undist);
> +	if (ret)
> +		dev_err(viif_dev->dev, "l2_set_undist error. %d\n", ret);
> +	return ret;
> +}
> +
> +static unsigned int dt_image_from_mbus_code(unsigned int mbus_code)
> +{
> +	switch (mbus_code) {
> +	case MEDIA_BUS_FMT_RGB888_1X24:
> +		return VISCONTI_CSI2_DT_RGB888;
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		return VISCONTI_CSI2_DT_YUV4228B;
> +	case MEDIA_BUS_FMT_UYVY10_1X20:
> +		return VISCONTI_CSI2_DT_YUV42210B;
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		return VISCONTI_CSI2_DT_RGB565;
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +		return VISCONTI_CSI2_DT_RAW8;
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +		return VISCONTI_CSI2_DT_RAW10;
> +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> +		return VISCONTI_CSI2_DT_RAW12;
> +	case MEDIA_BUS_FMT_SRGGB14_1X14:
> +	case MEDIA_BUS_FMT_SGRBG14_1X14:
> +	case MEDIA_BUS_FMT_SGBRG14_1X14:
> +	case MEDIA_BUS_FMT_SBGGR14_1X14:
> +		return VISCONTI_CSI2_DT_RAW14;
> +	default:
> +		return VISCONTI_CSI2_DT_RGB888;
> +	}
> +}
> +
> +int visconti_viif_isp_sub_set_unit(struct viif_device *viif_dev)
> +{
> +	struct hwd_viif_input_img in_img_sub;
> +	struct v4l2_dv_timings timings;
> +	struct v4l2_subdev_format fmt = {
> +		.pad = 0,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	unsigned int dt_image;
> +	int ret;
> +
> +	ret = viif_get_dv_timings(viif_dev, &timings);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = v4l2_subdev_call(viif_dev->sd->v4l2_sd, pad, get_fmt, NULL, &fmt);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "could not get pad information of subdev");
> +		return -EINVAL;
> +	}
> +
> +	dt_image = dt_image_from_mbus_code(fmt.format.code);
> +
> +	in_img_sub.hactive_size = 0;
> +	in_img_sub.vactive_size = timings.bt.height;
> +	in_img_sub.htotal_size = timings.bt.width + timings.bt.hsync;
> +	in_img_sub.vtotal_size = timings.bt.height + timings.bt.vsync;
> +	in_img_sub.pixel_clock = timings.bt.pixelclock / 1000;
> +	in_img_sub.vbp_size = timings.bt.vsync - 5;
> +	in_img_sub.interpolation_mode = HWD_VIIF_L1_INPUT_INTERPOLATION_LINE;
> +	in_img_sub.input_num = 1;
> +	in_img_sub.hobc_width = 0;
> +	in_img_sub.hobc_margin = 0;
> +
> +	ret = hwd_viif_sub_set_unit(viif_dev->hwd_res, dt_image, &in_img_sub);
> +	if (ret)
> +		dev_err(viif_dev->dev, "sub_set_unit error. %d\n", ret);
> +
> +	return ret;
> +};
> +
> +/* ----- handling CSI2RX hardware ----- */
> +static int viif_csi2rx_initialize(struct viif_device *viif_dev)
> +{
> +	struct hwd_viif_csi2rx_line_err_target err_target = { 0 };
> +	struct hwd_viif_csi2rx_irq_mask csi2rx_mask;
> +	struct viif_subdev *viif_sd = viif_dev->sd;
> +	struct v4l2_mbus_config cfg = { 0 };
> +	struct v4l2_subdev_format fmt = {
> +		.pad = 0,
> +		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
> +	};
> +	struct v4l2_dv_timings timings;
> +	int num_lane, dphy_rate;
> +	int ret;
> +
> +	ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_mbus_config, 0, &cfg);
> +	if (ret) {
> +		dev_dbg(viif_dev->dev, "subdev: g_mbus_config error. %d\n", ret);
> +		num_lane = viif_sd->num_lane;
> +	} else {
> +		if (cfg.type != V4L2_MBUS_CSI2_DPHY)
> +			return -EINVAL;
> +		num_lane = cfg.bus.mipi_csi2.num_data_lanes;
> +	}
> +
> +	ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_fmt, 0, &fmt);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = viif_get_dv_timings(viif_dev, &timings);
> +	if (ret)
> +		return -EINVAL;
> +
> +	dphy_rate = (timings.bt.pixelclock / 1000) * viif_get_mbus_bpp(fmt.format.code) / num_lane;
> +	dphy_rate = dphy_rate / 1000;
> +
> +	/* check error for CH0: all supported DTs */
> +	err_target.dt[0] = VISCONTI_CSI2_DT_RGB565;
> +	err_target.dt[1] = VISCONTI_CSI2_DT_YUV4228B;
> +	err_target.dt[2] = VISCONTI_CSI2_DT_YUV42210B;
> +	err_target.dt[3] = VISCONTI_CSI2_DT_RGB888;
> +	err_target.dt[4] = VISCONTI_CSI2_DT_RAW8;
> +	err_target.dt[5] = VISCONTI_CSI2_DT_RAW10;
> +	err_target.dt[6] = VISCONTI_CSI2_DT_RAW12;
> +	err_target.dt[7] = VISCONTI_CSI2_DT_RAW14;
> +
> +	/* Define errors to be masked */
> +	csi2rx_mask.mask[0] = 0x0000000F; /*check all for PHY_FATAL*/
> +	csi2rx_mask.mask[1] = 0x0001000F; /*check all for PKT_FATAL*/
> +	csi2rx_mask.mask[2] = 0x000F0F0F; /*check all for FRAME_FATAL*/
> +	csi2rx_mask.mask[3] = 0x000F000F; /*check all for PHY*/
> +	csi2rx_mask.mask[4] = 0x000F000F; /*check all for PKT*/
> +	csi2rx_mask.mask[5] = 0x00FF00FF; /*check all for LINE*/
> +
> +	return hwd_viif_csi2rx_initialize(viif_dev->hwd_res, num_lane, HWD_VIIF_CSI2_DPHY_L0L1L2L3,
> +					 dphy_rate, HWD_VIIF_ENABLE, &err_target, &csi2rx_mask);
> +}
> +
> +static int viif_csi2rx_start(struct viif_device *viif_dev)
> +{
> +	struct hwd_viif_csi2rx_packet packet = { 0 };
> +	u32 vc_main = 0;
> +	u32 vc_sub = 0;
> +
> +	viif_dev->masked_gamma_path = 0U;
> +
> +	return hwd_viif_csi2rx_start(viif_dev->hwd_res, vc_main, vc_sub, &packet);
> +}
> +
> +static int viif_csi2rx_stop(struct viif_device *viif_dev)
> +{
> +	s32 ret;
> +
> +	ret = hwd_viif_csi2rx_stop(viif_dev->hwd_res);
> +	if (ret)
> +		dev_err(viif_dev->dev, "csi2rx_stop error. %d\n", ret);
> +
> +	hwd_viif_csi2rx_uninitialize(viif_dev->hwd_res);
> +
> +	return ret;
> +}
> +
> +/* ----- subdevice video operations ----- */
> +static int visconti_viif_isp_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +	int ret;
> +
> +	if (enable) {
> +		ret = viif_csi2rx_initialize(viif_dev);
> +		if (ret)
> +			return ret;
> +		return viif_csi2rx_start(viif_dev);
> +	} else {
> +		return viif_csi2rx_stop(viif_dev);
> +	}
> +}
> +
> +/* ----- subdevice pad operations ----- */
> +static int visconti_viif_isp_enum_mbus_code(struct v4l2_subdev *sd,
> +					    struct v4l2_subdev_state *sd_state,
> +					    struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->pad == 0) {
> +		/* sink */
> +		if (code->index > ARRAY_SIZE(visconti_mbus_formats) - 1)
> +			return -EINVAL;
> +		code->code = visconti_mbus_formats[code->index].code;
> +		return 0;
> +	}
> +
> +	/* source */
> +	if (code->index > 0)
> +		return -EINVAL;
> +	code->code = MEDIA_BUS_FMT_YUV8_1X24;
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *visconti_viif_isp_get_pad_fmt(struct v4l2_subdev *sd,
> +								struct v4l2_subdev_state *sd_state,
> +								unsigned int pad, u32 which)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +	struct v4l2_subdev_state state = {
> +		.pads = viif_dev->isp_subdev.pad_cfg,
> +	};
> +
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state, pad);
> +	else
> +		return v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, &state, pad);
> +}
> +
> +static struct v4l2_rect *visconti_viif_isp_get_pad_crop(struct v4l2_subdev *sd,
> +							struct v4l2_subdev_state *sd_state,
> +							unsigned int pad, u32 which)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +	struct v4l2_subdev_state state = {
> +		.pads = viif_dev->isp_subdev.pad_cfg,
> +	};
> +
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, sd_state, pad);
> +	else
> +		return v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, &state, pad);
> +}
> +
> +static struct v4l2_rect *visconti_viif_isp_get_pad_compose(struct v4l2_subdev *sd,
> +							   struct v4l2_subdev_state *sd_state,
> +							   unsigned int pad, u32 which)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +	struct v4l2_subdev_state state = {
> +		.pads = viif_dev->isp_subdev.pad_cfg,
> +	};
> +
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_compose(&viif_dev->isp_subdev.sd, sd_state, pad);
> +	else
> +		return v4l2_subdev_get_try_compose(&viif_dev->isp_subdev.sd, &state, pad);
> +}
> +
> +static int visconti_viif_isp_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state,
> +				     struct v4l2_subdev_format *fmt)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +
> +	mutex_lock(&viif_dev->isp_subdev.ops_lock);
> +	fmt->format = *visconti_viif_isp_get_pad_fmt(sd, sd_state, fmt->pad, fmt->which);
> +	mutex_unlock(&viif_dev->isp_subdev.ops_lock);
> +
> +	return 0;
> +}
> +
> +static void visconti_viif_isp_set_sink_fmt(struct v4l2_subdev *sd,
> +					   struct v4l2_subdev_state *sd_state,
> +					   struct v4l2_mbus_framefmt *format, u32 which)
> +{
> +	struct v4l2_mbus_framefmt *sink_fmt, *src0_fmt, *src1_fmt, *src2_fmt;
> +
> +	sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK, which);
> +	src0_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SRC_PATH0, which);
> +	src1_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SRC_PATH1, which);
> +	src2_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SRC_PATH2, which);
> +
> +	/* update mbus code only if it's available */
> +	if (viif_is_valid_mbus_code(format->code))
> +		sink_fmt->code = format->code;
> +
> +	/* sink::mbus_code is derived from src::mbus_code */
> +	if (viif_get_mbus_rgb_out(sink_fmt->code)) {
> +		src0_fmt->code = MEDIA_BUS_FMT_RGB888_1X24;
> +		src1_fmt->code = MEDIA_BUS_FMT_RGB888_1X24;
> +	} else {
> +		src0_fmt->code = MEDIA_BUS_FMT_YUV8_1X24;
> +		src1_fmt->code = MEDIA_BUS_FMT_YUV8_1X24;
> +	}
> +
> +	/* SRC2 (RAW output) follows SINK format */
> +	src2_fmt->code = format->code;
> +	src2_fmt->width = format->width;
> +	src2_fmt->height = format->height;
> +
> +	/* size check */
> +	sink_fmt->width = format->width;
> +	sink_fmt->height = format->height;
> +
> +	*format = *sink_fmt;
> +}
> +
> +static void visconti_viif_isp_set_src_fmt(struct v4l2_subdev *sd,
> +					  struct v4l2_subdev_state *sd_state,
> +					  struct v4l2_mbus_framefmt *format, unsigned int pad,
> +					  u32 which)
> +{
> +	struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
> +	struct v4l2_rect *src_crop;
> +
> +	sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK,
> +						 V4L2_SUBDEV_FORMAT_ACTIVE);
> +	src_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, pad, which);
> +	src_crop = visconti_viif_isp_get_pad_crop(sd, sd_state, pad, which);
> +
> +	/* sink::mbus_code is derived from src::mbus_code */
> +	if (viif_get_mbus_rgb_out(sink_fmt->code))
> +		src_fmt->code = MEDIA_BUS_FMT_RGB888_1X24;
> +	else
> +		src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24;
> +
> +	/*size check*/
> +	src_fmt->width = format->width;
> +	src_fmt->height = format->height;
> +
> +	/*update crop*/
> +	src_crop->width = format->width;
> +	src_crop->height = format->height;
> +
> +	*format = *src_fmt;
> +}
> +
> +static void visconti_viif_isp_set_src_fmt_rawpath(struct v4l2_subdev *sd,
> +						  struct v4l2_subdev_state *sd_state,
> +						  struct v4l2_mbus_framefmt *format,
> +						  unsigned int pad, u32 which)
> +{
> +	struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
> +
> +	sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK,
> +						 V4L2_SUBDEV_FORMAT_ACTIVE);
> +	src_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, pad, which);
> +
> +	/* RAWPATH SRC pad has just the same configuration as SINK pad */
> +	src_fmt->code = sink_fmt->code;
> +	src_fmt->width = sink_fmt->width;
> +	src_fmt->height = sink_fmt->height;
> +
> +	*format = *src_fmt;
> +}
> +
> +static int visconti_viif_isp_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state,
> +				     struct v4l2_subdev_format *fmt)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +
> +	mutex_lock(&viif_dev->isp_subdev.ops_lock);
> +
> +	if (fmt->pad == VIIF_ISP_PAD_SINK)
> +		visconti_viif_isp_set_sink_fmt(sd, sd_state, &fmt->format, fmt->which);
> +	else if (fmt->pad == VIIF_ISP_PAD_SRC_PATH2)
> +		visconti_viif_isp_set_src_fmt_rawpath(sd, sd_state, &fmt->format, fmt->pad,
> +						      fmt->which);
> +	else
> +		visconti_viif_isp_set_src_fmt(sd, sd_state, &fmt->format, fmt->pad, fmt->which);
> +
> +	mutex_unlock(&viif_dev->isp_subdev.ops_lock);
> +
> +	return 0;
> +}
> +
> +#define VISCONTI_VIIF_ISP_DEFAULT_WIDTH	  1920
> +#define VISCONTI_VIIF_ISP_DEFAULT_HEIGHT  1080
> +#define VISCONTI_VIIF_MAX_COMPOSED_WIDTH  8190
> +#define VISCONTI_VIIF_MAX_COMPOSED_HEIGHT 4094
> +
> +static int visconti_viif_isp_init_config(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +	struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
> +	struct v4l2_rect *src_crop, *sink_compose;
> +
> +	sink_fmt =
> +		v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state, VIIF_ISP_PAD_SINK);
> +	sink_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH;
> +	sink_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT;
> +	sink_fmt->field = V4L2_FIELD_NONE;
> +	sink_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
> +
> +	sink_compose =
> +		v4l2_subdev_get_try_compose(&viif_dev->isp_subdev.sd, sd_state, VIIF_ISP_PAD_SINK);
> +	sink_compose->top = 0;
> +	sink_compose->left = 0;
> +	sink_compose->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH;
> +	sink_compose->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT;
> +
> +	src_fmt = v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state,
> +					     VIIF_ISP_PAD_SRC_PATH0);
> +	src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH;
> +	src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT;
> +	src_fmt->field = V4L2_FIELD_NONE;
> +	src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24;
> +
> +	src_crop = v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, sd_state,
> +					    VIIF_ISP_PAD_SRC_PATH0);
> +	src_crop->top = 0;
> +	src_crop->left = 0;
> +	src_crop->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH;
> +	src_crop->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT;
> +
> +	src_fmt = v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state,
> +					     VIIF_ISP_PAD_SRC_PATH1);
> +	src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH;
> +	src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT;
> +	src_fmt->field = V4L2_FIELD_NONE;
> +	src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24;
> +
> +	src_crop = v4l2_subdev_get_try_crop(&viif_dev->isp_subdev.sd, sd_state,
> +					    VIIF_ISP_PAD_SRC_PATH1);
> +	src_crop->top = 0;
> +	src_crop->left = 0;
> +	src_crop->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH;
> +	src_crop->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT;
> +
> +	src_fmt = v4l2_subdev_get_try_format(&viif_dev->isp_subdev.sd, sd_state,
> +					     VIIF_ISP_PAD_SRC_PATH2);
> +	src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH;
> +	src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT;
> +	src_fmt->field = V4L2_FIELD_NONE;
> +	src_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
> +
> +	return 0;
> +}
> +
> +static int visconti_viif_isp_get_selection(struct v4l2_subdev *sd,
> +					   struct v4l2_subdev_state *sd_state,
> +					   struct v4l2_subdev_selection *sel)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +	struct v4l2_mbus_framefmt *sink_fmt;
> +	int ret = -EINVAL;
> +
> +	mutex_lock(&viif_dev->isp_subdev.ops_lock);
> +	if (sel->pad == VIIF_ISP_PAD_SINK) {
> +		/* SINK PAD */
> +		switch (sel->target) {
> +		case V4L2_SEL_TGT_CROP:
> +			sink_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, VIIF_ISP_PAD_SINK,
> +								 sel->which);
> +			sel->r.top = 0;
> +			sel->r.left = 0;
> +			sel->r.width = sink_fmt->width;
> +			sel->r.height = sink_fmt->height;
> +			ret = 0;
> +			break;
> +		case V4L2_SEL_TGT_COMPOSE:
> +			sel->r = *visconti_viif_isp_get_pad_compose(sd, sd_state, VIIF_ISP_PAD_SINK,
> +								    sel->which);
> +			ret = 0;
> +			break;
> +		case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> +			/* fixed value */
> +			sel->r.top = 0;
> +			sel->r.left = 0;
> +			sel->r.width = VISCONTI_VIIF_MAX_COMPOSED_WIDTH;
> +			sel->r.height = VISCONTI_VIIF_MAX_COMPOSED_HEIGHT;
> +			ret = 0;
> +			break;
> +		}
> +	} else if ((sel->pad == VIIF_ISP_PAD_SRC_PATH0) || (sel->pad == VIIF_ISP_PAD_SRC_PATH1)) {
> +		/* SRC PAD */
> +		switch (sel->target) {
> +		case V4L2_SEL_TGT_CROP:
> +			sel->r =
> +				*visconti_viif_isp_get_pad_crop(sd, sd_state, sel->pad, sel->which);
> +			ret = 0;
> +			break;
> +		}
> +	}
> +	mutex_unlock(&viif_dev->isp_subdev.ops_lock);
> +
> +	return ret;
> +}
> +
> +static int visconti_viif_isp_set_selection(struct v4l2_subdev *sd,
> +					   struct v4l2_subdev_state *sd_state,
> +					   struct v4l2_subdev_selection *sel)
> +{
> +	struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev;
> +	struct v4l2_mbus_framefmt *src_fmt;
> +	struct v4l2_rect *rect, *rect_compose;
> +	int ret = -EINVAL;
> +
> +	mutex_lock(&viif_dev->isp_subdev.ops_lock);
> +	/* only source::selection::crop is writable */
> +	if (sel->pad == VIIF_ISP_PAD_SRC_PATH0 || sel->pad == VIIF_ISP_PAD_SRC_PATH1) {
> +		switch (sel->target) {
> +		case V4L2_SEL_TGT_CROP: {
> +			/* check if new SRC::CROP is inside SINK::COMPOSE */
> +			rect_compose = visconti_viif_isp_get_pad_compose(
> +				sd, sd_state, VIIF_ISP_PAD_SINK, sel->which);
> +			if (sel->r.top < rect_compose->top || sel->r.left < rect_compose->left ||
> +			    (sel->r.top + sel->r.height) >
> +				    (rect_compose->top + rect_compose->height) ||
> +			    (sel->r.left + sel->r.width) >
> +				    (rect_compose->left + rect_compose->width)) {
> +				break;
> +			}
> +
> +			rect = visconti_viif_isp_get_pad_crop(sd, sd_state, sel->pad, sel->which);
> +			*rect = sel->r;
> +
> +			/* update SRC::FMT along with SRC::CROP */
> +			src_fmt = visconti_viif_isp_get_pad_fmt(sd, sd_state, sel->pad, sel->which);
> +			src_fmt->width = sel->r.width;
> +			src_fmt->height = sel->r.height;
> +			ret = 0;
> +			break;
> +		}
> +		}
> +	}
> +	mutex_unlock(&viif_dev->isp_subdev.ops_lock);
> +
> +	return ret;
> +}
> +
> +void visconti_viif_isp_set_compose_rect(struct viif_device *viif_dev,
> +					struct viif_l2_roi_config *roi)
> +{
> +	struct v4l2_rect *rect;
> +
> +	rect = visconti_viif_isp_get_pad_compose(&viif_dev->isp_subdev.sd, NULL, VIIF_ISP_PAD_SINK,
> +						 V4L2_SUBDEV_FORMAT_ACTIVE);
> +	rect->top = 0;
> +	rect->left = 0;
> +	rect->width = roi->corrected_hsize[0];
> +	rect->height = roi->corrected_vsize[0];
> +}
> +
> +static const struct media_entity_operations visconti_viif_isp_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static const struct v4l2_subdev_pad_ops visconti_viif_isp_pad_ops = {
> +	.enum_mbus_code = visconti_viif_isp_enum_mbus_code,
> +	.get_selection = visconti_viif_isp_get_selection,
> +	.set_selection = visconti_viif_isp_set_selection,
> +	.init_cfg = visconti_viif_isp_init_config,
> +	.get_fmt = visconti_viif_isp_get_fmt,
> +	.set_fmt = visconti_viif_isp_set_fmt,
> +	.link_validate = v4l2_subdev_link_validate_default,
> +};
> +
> +static const struct v4l2_subdev_video_ops visconti_viif_isp_video_ops = {
> +	.s_stream = visconti_viif_isp_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops visconti_viif_isp_ops = {
> +	.video = &visconti_viif_isp_video_ops,
> +	.pad = &visconti_viif_isp_pad_ops,
> +};
> +
> +/* ----- register/remove isp subdevice node ----- */
> +int visconti_viif_isp_register(struct viif_device *viif_dev)
> +{
> +	struct v4l2_subdev_state state = {
> +		.pads = viif_dev->isp_subdev.pad_cfg,
> +	};
> +	struct media_pad *pads = viif_dev->isp_subdev.pads;
> +	struct v4l2_subdev *sd = &viif_dev->isp_subdev.sd;
> +	int ret;
> +
> +	viif_dev->isp_subdev.viif_dev = viif_dev;
> +
> +	v4l2_subdev_init(sd, &visconti_viif_isp_ops);
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	sd->entity.ops = &visconti_viif_isp_media_ops;
> +	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
> +	sd->owner = THIS_MODULE;
> +	strscpy(sd->name, "visconti-viif:isp", sizeof(sd->name));
> +
> +	pads[0].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> +	pads[1].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> +	pads[2].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> +	pads[3].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> +
> +	mutex_init(&viif_dev->isp_subdev.ops_lock);
> +
> +	ret = media_entity_pads_init(&sd->entity, 4, pads);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "Failed on media_entity_pads_init\n");
> +		return ret;
> +	}
> +
> +	ret = v4l2_device_register_subdev(&viif_dev->v4l2_dev, sd);
> +	if (ret) {
> +		dev_err(viif_dev->dev, "Failed to resize ISP subdev\n");
> +		goto err_cleanup_media_entity;
> +	}
> +
> +	visconti_viif_isp_init_config(sd, &state);
> +
> +	return 0;
> +
> +err_cleanup_media_entity:
> +	media_entity_cleanup(&sd->entity);
> +	return ret;
> +}
> +
> +void visconti_viif_isp_unregister(struct viif_device *viif_dev)
> +{
> +	v4l2_device_unregister_subdev(&viif_dev->isp_subdev.sd);
> +	media_entity_cleanup(&viif_dev->isp_subdev.sd.entity);
> +}

Regards,

	Hans



More information about the linux-arm-kernel mailing list