brintos

brintos / linux-shallow public Read only

0
0
Text · 18.7 KiB · 051898c Raw
650 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */5 6#include <linux/atomic.h>7#include <linux/bitfield.h>8#include <linux/bits.h>9#include <linux/delay.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/io.h>13#include <linux/minmax.h>14#include <linux/sprintf.h>15 16#include <media/media-entity.h>17#include <media/v4l2-ctrls.h>18#include <media/v4l2-device.h>19#include <media/v4l2-event.h>20#include <media/v4l2-subdev.h>21 22#include "ipu6-bus.h"23#include "ipu6-isys.h"24#include "ipu6-isys-csi2.h"25#include "ipu6-isys-subdev.h"26#include "ipu6-platform-isys-csi2-reg.h"27 28static const u32 csi2_supported_codes[] = {29	MEDIA_BUS_FMT_RGB565_1X16,30	MEDIA_BUS_FMT_RGB888_1X24,31	MEDIA_BUS_FMT_UYVY8_1X16,32	MEDIA_BUS_FMT_YUYV8_1X16,33	MEDIA_BUS_FMT_SBGGR10_1X10,34	MEDIA_BUS_FMT_SGBRG10_1X10,35	MEDIA_BUS_FMT_SGRBG10_1X10,36	MEDIA_BUS_FMT_SRGGB10_1X10,37	MEDIA_BUS_FMT_SBGGR12_1X12,38	MEDIA_BUS_FMT_SGBRG12_1X12,39	MEDIA_BUS_FMT_SGRBG12_1X12,40	MEDIA_BUS_FMT_SRGGB12_1X12,41	MEDIA_BUS_FMT_SBGGR8_1X8,42	MEDIA_BUS_FMT_SGBRG8_1X8,43	MEDIA_BUS_FMT_SGRBG8_1X8,44	MEDIA_BUS_FMT_SRGGB8_1X8,45	MEDIA_BUS_FMT_META_8,46	MEDIA_BUS_FMT_META_10,47	MEDIA_BUS_FMT_META_12,48	MEDIA_BUS_FMT_META_16,49	MEDIA_BUS_FMT_META_24,50	051};52 53/*54 * Strings corresponding to CSI-2 receiver errors are here.55 * Corresponding macros are defined in the header file.56 */57static const struct ipu6_csi2_error dphy_rx_errors[] = {58	{ "Single packet header error corrected", true },59	{ "Multiple packet header errors detected", true },60	{ "Payload checksum (CRC) error", true },61	{ "Transfer FIFO overflow", false },62	{ "Reserved short packet data type detected", true },63	{ "Reserved long packet data type detected", true },64	{ "Incomplete long packet detected", false },65	{ "Frame sync error", false },66	{ "Line sync error", false },67	{ "DPHY recoverable synchronization error", true },68	{ "DPHY fatal error", false },69	{ "DPHY elastic FIFO overflow", false },70	{ "Inter-frame short packet discarded", true },71	{ "Inter-frame long packet discarded", true },72	{ "MIPI pktgen overflow", false },73	{ "MIPI pktgen data loss", false },74	{ "FIFO overflow", false },75	{ "Lane deskew", false },76	{ "SOT sync error", false },77	{ "HSIDLE detected", false }78};79 80s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)81{82	struct media_pad *src_pad;83	struct v4l2_subdev *ext_sd;84	struct device *dev;85 86	if (!csi2)87		return -EINVAL;88 89	dev = &csi2->isys->adev->auxdev.dev;90	src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);91	if (IS_ERR(src_pad)) {92		dev_err(dev, "can't get source pad of %s (%ld)\n",93			csi2->asd.sd.name, PTR_ERR(src_pad));94		return PTR_ERR(src_pad);95	}96 97	ext_sd = media_entity_to_v4l2_subdev(src_pad->entity);98	if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name))99		return -ENODEV;100 101	return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0);102}103 104static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,105				struct v4l2_event_subscription *sub)106{107	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);108	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);109	struct device *dev = &csi2->isys->adev->auxdev.dev;110 111	dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",112		sub->type, sub->id);113 114	switch (sub->type) {115	case V4L2_EVENT_FRAME_SYNC:116		return v4l2_event_subscribe(fh, sub, 10, NULL);117	case V4L2_EVENT_CTRL:118		return v4l2_ctrl_subscribe_event(fh, sub);119	default:120		return -EINVAL;121	}122}123 124static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {125	.subscribe_event = csi2_subscribe_event,126	.unsubscribe_event = v4l2_event_subdev_unsubscribe,127};128 129/*130 * The input system CSI2+ receiver has several131 * parameters affecting the receiver timings. These depend132 * on the MIPI bus frequency F in Hz (sensor transmitter rate)133 * as follows:134 *	register value = (A/1e9 + B * UI) / COUNT_ACC135 * where136 *	UI = 1 / (2 * F) in seconds137 *	COUNT_ACC = counter accuracy in seconds138 *	COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8.139 *140 * A and B are coefficients from the table below,141 * depending whether the register minimum or maximum value is142 * calculated.143 *				       Minimum     Maximum144 * Clock lane			       A     B     A     B145 * reg_rx_csi_dly_cnt_termen_clane     0     0    38     0146 * reg_rx_csi_dly_cnt_settle_clane    95    -8   300   -16147 * Data lanes148 * reg_rx_csi_dly_cnt_termen_dlane0    0     0    35     4149 * reg_rx_csi_dly_cnt_settle_dlane0   85    -2   145    -6150 * reg_rx_csi_dly_cnt_termen_dlane1    0     0    35     4151 * reg_rx_csi_dly_cnt_settle_dlane1   85    -2   145    -6152 * reg_rx_csi_dly_cnt_termen_dlane2    0     0    35     4153 * reg_rx_csi_dly_cnt_settle_dlane2   85    -2   145    -6154 * reg_rx_csi_dly_cnt_termen_dlane3    0     0    35     4155 * reg_rx_csi_dly_cnt_settle_dlane3   85    -2   145    -6156 *157 * We use the minimum values of both A and B.158 */159 160#define DIV_SHIFT	8161#define CSI2_ACCINV	8162 163static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv)164{165	return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT)166			     / (s32)(link_freq >> DIV_SHIFT));167}168 169static int170ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2,171			   struct ipu6_isys_csi2_timing *timing, s32 accinv)172{173	struct device *dev = &csi2->isys->adev->auxdev.dev;174	s64 link_freq;175 176	link_freq = ipu6_isys_csi2_get_link_freq(csi2);177	if (link_freq < 0)178		return link_freq;179 180	timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A,181				      CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B,182				      link_freq, accinv);183	timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A,184				      CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B,185				      link_freq, accinv);186	timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A,187				      CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B,188				      link_freq, accinv);189	timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A,190				      CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B,191				      link_freq, accinv);192 193	dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n",194		timing->ctermen, timing->csettle,195		timing->dtermen, timing->dsettle);196 197	return 0;198}199 200void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2)201{202	u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +203			CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);204	struct ipu6_isys *isys = csi2->isys;205	u32 mask;206 207	mask = isys->pdata->ipdata->csi2.irq_mask;208	writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +209	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);210	csi2->receiver_errors |= irq & mask;211}212 213void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2)214{215	struct device *dev = &csi2->isys->adev->auxdev.dev;216	const struct ipu6_csi2_error *errors;217	u32 status;218	u32 i;219 220	/* register errors once more in case of interrupts are disabled */221	ipu6_isys_register_errors(csi2);222	status = csi2->receiver_errors;223	csi2->receiver_errors = 0;224	errors = dphy_rx_errors;225 226	for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {227		if (status & BIT(i))228			dev_err_ratelimited(dev, "csi2-%i error: %s\n",229					    csi2->port, errors[i].error_string);230	}231}232 233static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,234				     const struct ipu6_isys_csi2_timing *timing,235				     unsigned int nlanes, int enable)236{237	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);238	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);239	struct ipu6_isys *isys = csi2->isys;240	struct device *dev = &isys->adev->auxdev.dev;241	struct ipu6_isys_csi2_config cfg;242	unsigned int nports;243	int ret = 0;244	u32 mask = 0;245	u32 i;246 247	dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off",248		csi2->port, nlanes);249 250	cfg.port = csi2->port;251	cfg.nlanes = nlanes;252 253	mask = isys->pdata->ipdata->csi2.irq_mask;254	nports = isys->pdata->ipdata->csi2.nports;255 256	if (!enable) {257		writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE);258		writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE);259 260		writel(0,261		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +262		       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);263		writel(mask,264		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +265		       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);266		writel(0,267		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +268		       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);269		writel(0xffffffff,270		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +271		       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);272 273		isys->phy_set_power(isys, &cfg, timing, false);274 275		writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT276		       (isys->pdata->ipdata->csi2.fw_access_port_ofs,277			csi2->port));278		writel(0, isys->pdata->base +279		       CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port));280 281		return ret;282	}283 284	/* reset port reset */285	writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST);286	usleep_range(100, 200);287	writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST);288 289	/* enable port clock */290	for (i = 0; i < nports; i++) {291		writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i));292		writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT293		       (isys->pdata->ipdata->csi2.fw_access_port_ofs, i));294	}295 296	/* enable all error related irq */297	writel(mask,298	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +299	       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);300	writel(mask,301	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +302	       CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);303	writel(mask,304	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +305	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);306	writel(mask,307	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +308	       CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);309	writel(mask,310	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +311	       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);312 313	/*314	 * Using event from firmware instead of irq to handle CSI2 sync event315	 * which can reduce system wakeups. If CSI2 sync irq enabled, we need316	 * disable the firmware CSI2 sync event to avoid duplicate handling.317	 */318	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +319	       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);320	writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +321	       CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);322	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +323	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);324	writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +325	       CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);326	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +327	       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);328 329	/* configure to enable FE and PPI2CSI */330	writel(0, csi2->base + CSI_REG_CSI_FE_MODE);331	writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL);332	writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID,333	       csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL);334	writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1),335	       csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF);336 337	writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE);338	writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE);339 340	ret = isys->phy_set_power(isys, &cfg, timing, true);341	if (ret)342		dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port,343			ret);344 345	return ret;346}347 348static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd,349					 struct v4l2_subdev_state *state,350					 u32 pad, u64 streams_mask)351{352	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);353	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);354	struct ipu6_isys_csi2_timing timing = { };355	struct v4l2_subdev *remote_sd;356	struct media_pad *remote_pad;357	u64 sink_streams;358	int ret;359 360	remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);361	remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);362 363	sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC,364						       CSI2_PAD_SINK,365						       &streams_mask);366 367	ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);368	if (ret)369		return ret;370 371	ret = ipu6_isys_csi2_set_stream(sd, &timing, csi2->nlanes, true);372	if (ret)373		return ret;374 375	ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index,376					 sink_streams);377	if (ret) {378		ipu6_isys_csi2_set_stream(sd, NULL, 0, false);379		return ret;380	}381 382	return 0;383}384 385static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd,386					  struct v4l2_subdev_state *state,387					  u32 pad, u64 streams_mask)388{389	struct v4l2_subdev *remote_sd;390	struct media_pad *remote_pad;391	u64 sink_streams;392 393	sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC,394						       CSI2_PAD_SINK,395						       &streams_mask);396 397	remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);398	remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);399 400	ipu6_isys_csi2_set_stream(sd, NULL, 0, false);401 402	v4l2_subdev_disable_streams(remote_sd, remote_pad->index, sink_streams);403 404	return 0;405}406 407static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,408				  struct v4l2_subdev_state *state,409				  struct v4l2_subdev_selection *sel)410{411	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);412	struct device *dev = &asd->isys->adev->auxdev.dev;413	struct v4l2_mbus_framefmt *sink_ffmt;414	struct v4l2_mbus_framefmt *src_ffmt;415	struct v4l2_rect *crop;416 417	if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)418		return -EINVAL;419 420	sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,421								 sel->pad,422								 sel->stream);423	if (!sink_ffmt)424		return -EINVAL;425 426	src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);427	if (!src_ffmt)428		return -EINVAL;429 430	crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);431	if (!crop)432		return -EINVAL;433 434	/* Only vertical cropping is supported */435	sel->r.left = 0;436	sel->r.width = sink_ffmt->width;437	/* Non-bayer formats can't be single line cropped */438	if (!ipu6_isys_is_bayer_format(sink_ffmt->code))439		sel->r.top &= ~1;440	sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT,441			      sink_ffmt->height - sel->r.top);442	*crop = sel->r;443 444	/* update source pad format */445	src_ffmt->width = sel->r.width;446	src_ffmt->height = sel->r.height;447	if (ipu6_isys_is_bayer_format(sink_ffmt->code))448		src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code,449							       sel->r.left,450							       sel->r.top);451	dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",452		sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,453		src_ffmt->code);454 455	return 0;456}457 458static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,459				  struct v4l2_subdev_state *state,460				  struct v4l2_subdev_selection *sel)461{462	struct v4l2_mbus_framefmt *sink_ffmt;463	struct v4l2_rect *crop;464	int ret = 0;465 466	if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)467		return -EINVAL;468 469	sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,470								 sel->pad,471								 sel->stream);472	if (!sink_ffmt)473		return -EINVAL;474 475	crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);476	if (!crop)477		return -EINVAL;478 479	switch (sel->target) {480	case V4L2_SEL_TGT_CROP_DEFAULT:481	case V4L2_SEL_TGT_CROP_BOUNDS:482		sel->r.left = 0;483		sel->r.top = 0;484		sel->r.width = sink_ffmt->width;485		sel->r.height = sink_ffmt->height;486		break;487	case V4L2_SEL_TGT_CROP:488		sel->r = *crop;489		break;490	default:491		ret = -EINVAL;492	}493 494	return ret;495}496 497static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {498	.get_fmt = v4l2_subdev_get_fmt,499	.set_fmt = ipu6_isys_subdev_set_fmt,500	.get_selection = ipu6_isys_csi2_get_sel,501	.set_selection = ipu6_isys_csi2_set_sel,502	.enum_mbus_code = ipu6_isys_subdev_enum_mbus_code,503	.set_routing = ipu6_isys_subdev_set_routing,504	.enable_streams = ipu6_isys_csi2_enable_streams,505	.disable_streams = ipu6_isys_csi2_disable_streams,506};507 508static const struct v4l2_subdev_ops csi2_sd_ops = {509	.core = &csi2_sd_core_ops,510	.pad = &csi2_sd_pad_ops,511};512 513static const struct media_entity_operations csi2_entity_ops = {514	.link_validate = v4l2_subdev_link_validate,515	.has_pad_interdep = v4l2_subdev_has_pad_interdep,516};517 518void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2)519{520	if (!csi2->isys)521		return;522 523	v4l2_device_unregister_subdev(&csi2->asd.sd);524	v4l2_subdev_cleanup(&csi2->asd.sd);525	ipu6_isys_subdev_cleanup(&csi2->asd);526	csi2->isys = NULL;527}528 529int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2,530			struct ipu6_isys *isys,531			void __iomem *base, unsigned int index)532{533	struct device *dev = &isys->adev->auxdev.dev;534	int ret;535 536	csi2->isys = isys;537	csi2->base = base;538	csi2->port = index;539 540	csi2->asd.sd.entity.ops = &csi2_entity_ops;541	csi2->asd.isys = isys;542	ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,543				    NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS);544	if (ret)545		goto fail;546 547	csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index;548	csi2->asd.supported_codes = csi2_supported_codes;549	snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),550		 IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index);551	v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);552	ret = v4l2_subdev_init_finalize(&csi2->asd.sd);553	if (ret) {554		dev_err(dev, "failed to init v4l2 subdev\n");555		goto fail;556	}557 558	ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);559	if (ret) {560		dev_err(dev, "failed to register v4l2 subdev\n");561		goto fail;562	}563 564	return 0;565 566fail:567	ipu6_isys_csi2_cleanup(csi2);568 569	return ret;570}571 572void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream)573{574	struct video_device *vdev = stream->asd->sd.devnode;575	struct device *dev = &stream->isys->adev->auxdev.dev;576	struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);577	struct v4l2_event ev = {578		.type = V4L2_EVENT_FRAME_SYNC,579	};580 581	ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);582	v4l2_event_queue(vdev, &ev);583 584	dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",585		csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);586}587 588void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream)589{590	struct device *dev = &stream->isys->adev->auxdev.dev;591	struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);592	u32 frame_sequence = atomic_read(&stream->sequence);593 594	dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",595		csi2->port, frame_sequence);596}597 598int ipu6_isys_csi2_get_remote_desc(u32 source_stream,599				   struct ipu6_isys_csi2 *csi2,600				   struct media_entity *source_entity,601				   struct v4l2_mbus_frame_desc_entry *entry)602{603	struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;604	struct device *dev = &csi2->isys->adev->auxdev.dev;605	struct v4l2_mbus_frame_desc desc;606	struct v4l2_subdev *source;607	struct media_pad *pad;608	unsigned int i;609	int ret;610 611	source = media_entity_to_v4l2_subdev(source_entity);612	if (!source)613		return -EPIPE;614 615	pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]);616	if (!pad)617		return -EPIPE;618 619	ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);620	if (ret)621		return ret;622 623	if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {624		dev_err(dev, "Unsupported frame descriptor type\n");625		return -EINVAL;626	}627 628	for (i = 0; i < desc.num_entries; i++) {629		if (source_stream == desc.entry[i].stream) {630			desc_entry = &desc.entry[i];631			break;632		}633	}634 635	if (!desc_entry) {636		dev_err(dev, "Failed to find stream %u from remote subdev\n",637			source_stream);638		return -EINVAL;639	}640 641	if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) {642		dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);643		return -EINVAL;644	}645 646	*entry = *desc_entry;647 648	return 0;649}650