File Index
Files and directories
Sign in
FILE

ipu6-isys-csi2.c

File preview with syntax highlighting.

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