Lines Matching +full:mipi +full:- +full:csi2
1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2013--2024 Intel Corporation
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>
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"
54 * Strings corresponding to CSI-2 receiver errors are here.
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 },
80 s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2) in ipu6_isys_csi2_get_link_freq() argument
86 if (!csi2) in ipu6_isys_csi2_get_link_freq()
87 return -EINVAL; in ipu6_isys_csi2_get_link_freq()
89 dev = &csi2->isys->adev->auxdev.dev; in ipu6_isys_csi2_get_link_freq()
90 src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); in ipu6_isys_csi2_get_link_freq()
93 csi2->asd.sd.name, PTR_ERR(src_pad)); in ipu6_isys_csi2_get_link_freq()
97 ext_sd = media_entity_to_v4l2_subdev(src_pad->entity); in ipu6_isys_csi2_get_link_freq()
98 if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name)) in ipu6_isys_csi2_get_link_freq()
99 return -ENODEV; in ipu6_isys_csi2_get_link_freq()
101 return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0); in ipu6_isys_csi2_get_link_freq()
108 struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); in csi2_subscribe_event() local
109 struct device *dev = &csi2->isys->adev->auxdev.dev; in csi2_subscribe_event()
111 dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", in csi2_subscribe_event()
112 sub->type, sub->id); in csi2_subscribe_event()
114 switch (sub->type) { in csi2_subscribe_event()
120 return -EINVAL; in csi2_subscribe_event()
130 * The input system CSI2+ receiver has several
132 * on the MIPI bus frequency F in Hz (sensor transmitter rate)
146 * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16
149 * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6
151 * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6
153 * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6
155 * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6
170 ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2, in ipu6_isys_csi2_calc_timing() argument
173 struct device *dev = &csi2->isys->adev->auxdev.dev; in ipu6_isys_csi2_calc_timing()
176 link_freq = ipu6_isys_csi2_get_link_freq(csi2); in ipu6_isys_csi2_calc_timing()
180 timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A, in ipu6_isys_csi2_calc_timing()
183 timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A, in ipu6_isys_csi2_calc_timing()
186 timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A, in ipu6_isys_csi2_calc_timing()
189 timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A, in ipu6_isys_csi2_calc_timing()
194 timing->ctermen, timing->csettle, in ipu6_isys_csi2_calc_timing()
195 timing->dtermen, timing->dsettle); in ipu6_isys_csi2_calc_timing()
200 void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2) in ipu6_isys_register_errors() argument
202 u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_register_errors()
204 struct ipu6_isys *isys = csi2->isys; in ipu6_isys_register_errors()
207 mask = isys->pdata->ipdata->csi2.irq_mask; in ipu6_isys_register_errors()
208 writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_register_errors()
210 csi2->receiver_errors |= irq & mask; in ipu6_isys_register_errors()
213 void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2) in ipu6_isys_csi2_error() argument
215 struct device *dev = &csi2->isys->adev->auxdev.dev; in ipu6_isys_csi2_error()
221 ipu6_isys_register_errors(csi2); in ipu6_isys_csi2_error()
222 status = csi2->receiver_errors; in ipu6_isys_csi2_error()
223 csi2->receiver_errors = 0; in ipu6_isys_csi2_error()
228 dev_err_ratelimited(dev, "csi2-%i error: %s\n", in ipu6_isys_csi2_error()
229 csi2->port, errors[i].error_string); in ipu6_isys_csi2_error()
238 struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); in ipu6_isys_csi2_set_stream() local
239 struct ipu6_isys *isys = csi2->isys; in ipu6_isys_csi2_set_stream()
240 struct device *dev = &isys->adev->auxdev.dev; in ipu6_isys_csi2_set_stream()
247 dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off", in ipu6_isys_csi2_set_stream()
248 csi2->port, nlanes); in ipu6_isys_csi2_set_stream()
250 cfg.port = csi2->port; in ipu6_isys_csi2_set_stream()
253 mask = isys->pdata->ipdata->csi2.irq_mask; in ipu6_isys_csi2_set_stream()
254 nports = isys->pdata->ipdata->csi2.nports; in ipu6_isys_csi2_set_stream()
257 writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE); in ipu6_isys_csi2_set_stream()
258 writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE); in ipu6_isys_csi2_set_stream()
261 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_csi2_set_stream()
264 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_csi2_set_stream()
267 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + in ipu6_isys_csi2_set_stream()
270 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + in ipu6_isys_csi2_set_stream()
273 isys->phy_set_power(isys, &cfg, timing, false); in ipu6_isys_csi2_set_stream()
275 writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT in ipu6_isys_csi2_set_stream()
276 (isys->pdata->ipdata->csi2.fw_access_port_ofs, in ipu6_isys_csi2_set_stream()
277 csi2->port)); in ipu6_isys_csi2_set_stream()
278 writel(0, isys->pdata->base + in ipu6_isys_csi2_set_stream()
279 CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port)); in ipu6_isys_csi2_set_stream()
285 writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST); in ipu6_isys_csi2_set_stream()
287 writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST); in ipu6_isys_csi2_set_stream()
291 writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i)); in ipu6_isys_csi2_set_stream()
292 writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT in ipu6_isys_csi2_set_stream()
293 (isys->pdata->ipdata->csi2.fw_access_port_ofs, i)); in ipu6_isys_csi2_set_stream()
298 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_csi2_set_stream()
301 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_csi2_set_stream()
304 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_csi2_set_stream()
307 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_csi2_set_stream()
310 csi2->base + CSI_PORT_REG_BASE_IRQ_CSI + in ipu6_isys_csi2_set_stream()
314 * Using event from firmware instead of irq to handle CSI2 sync event in ipu6_isys_csi2_set_stream()
315 * which can reduce system wakeups. If CSI2 sync irq enabled, we need in ipu6_isys_csi2_set_stream()
316 * disable the firmware CSI2 sync event to avoid duplicate handling. in ipu6_isys_csi2_set_stream()
318 writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + in ipu6_isys_csi2_set_stream()
320 writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + in ipu6_isys_csi2_set_stream()
322 writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + in ipu6_isys_csi2_set_stream()
324 writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + in ipu6_isys_csi2_set_stream()
326 writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC + in ipu6_isys_csi2_set_stream()
330 writel(0, csi2->base + CSI_REG_CSI_FE_MODE); in ipu6_isys_csi2_set_stream()
331 writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL); in ipu6_isys_csi2_set_stream()
333 csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL); in ipu6_isys_csi2_set_stream()
334 writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1), in ipu6_isys_csi2_set_stream()
335 csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF); in ipu6_isys_csi2_set_stream()
337 writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE); in ipu6_isys_csi2_set_stream()
338 writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE); in ipu6_isys_csi2_set_stream()
340 ret = isys->phy_set_power(isys, &cfg, timing, true); in ipu6_isys_csi2_set_stream()
342 dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port, in ipu6_isys_csi2_set_stream()
353 struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); in ipu6_isys_csi2_enable_streams() local
360 remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); in ipu6_isys_csi2_enable_streams()
361 remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); in ipu6_isys_csi2_enable_streams()
367 ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); in ipu6_isys_csi2_enable_streams()
371 ret = ipu6_isys_csi2_set_stream(sd, &timing, csi2->nlanes, true); in ipu6_isys_csi2_enable_streams()
375 ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, in ipu6_isys_csi2_enable_streams()
397 remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); in ipu6_isys_csi2_disable_streams()
398 remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); in ipu6_isys_csi2_disable_streams()
402 v4l2_subdev_disable_streams(remote_sd, remote_pad->index, sink_streams); in ipu6_isys_csi2_disable_streams()
412 struct device *dev = &asd->isys->adev->auxdev.dev; in ipu6_isys_csi2_set_sel()
417 if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) in ipu6_isys_csi2_set_sel()
418 return -EINVAL; in ipu6_isys_csi2_set_sel()
421 sel->pad, in ipu6_isys_csi2_set_sel()
422 sel->stream); in ipu6_isys_csi2_set_sel()
424 return -EINVAL; in ipu6_isys_csi2_set_sel()
426 src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); in ipu6_isys_csi2_set_sel()
428 return -EINVAL; in ipu6_isys_csi2_set_sel()
430 crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); in ipu6_isys_csi2_set_sel()
432 return -EINVAL; in ipu6_isys_csi2_set_sel()
435 sel->r.left = 0; in ipu6_isys_csi2_set_sel()
436 sel->r.width = sink_ffmt->width; in ipu6_isys_csi2_set_sel()
437 /* Non-bayer formats can't be single line cropped */ in ipu6_isys_csi2_set_sel()
438 if (!ipu6_isys_is_bayer_format(sink_ffmt->code)) in ipu6_isys_csi2_set_sel()
439 sel->r.top &= ~1; in ipu6_isys_csi2_set_sel()
440 sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT, in ipu6_isys_csi2_set_sel()
441 sink_ffmt->height - sel->r.top); in ipu6_isys_csi2_set_sel()
442 *crop = sel->r; in ipu6_isys_csi2_set_sel()
445 src_ffmt->width = sel->r.width; in ipu6_isys_csi2_set_sel()
446 src_ffmt->height = sel->r.height; in ipu6_isys_csi2_set_sel()
447 if (ipu6_isys_is_bayer_format(sink_ffmt->code)) in ipu6_isys_csi2_set_sel()
448 src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code, in ipu6_isys_csi2_set_sel()
449 sel->r.left, in ipu6_isys_csi2_set_sel()
450 sel->r.top); in ipu6_isys_csi2_set_sel()
452 sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, in ipu6_isys_csi2_set_sel()
453 src_ffmt->code); in ipu6_isys_csi2_set_sel()
466 if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) in ipu6_isys_csi2_get_sel()
467 return -EINVAL; in ipu6_isys_csi2_get_sel()
470 sel->pad, in ipu6_isys_csi2_get_sel()
471 sel->stream); in ipu6_isys_csi2_get_sel()
473 return -EINVAL; in ipu6_isys_csi2_get_sel()
475 crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); in ipu6_isys_csi2_get_sel()
477 return -EINVAL; in ipu6_isys_csi2_get_sel()
479 switch (sel->target) { in ipu6_isys_csi2_get_sel()
482 sel->r.left = 0; in ipu6_isys_csi2_get_sel()
483 sel->r.top = 0; in ipu6_isys_csi2_get_sel()
484 sel->r.width = sink_ffmt->width; in ipu6_isys_csi2_get_sel()
485 sel->r.height = sink_ffmt->height; in ipu6_isys_csi2_get_sel()
488 sel->r = *crop; in ipu6_isys_csi2_get_sel()
491 ret = -EINVAL; in ipu6_isys_csi2_get_sel()
518 void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2) in ipu6_isys_csi2_cleanup() argument
520 if (!csi2->isys) in ipu6_isys_csi2_cleanup()
523 v4l2_device_unregister_subdev(&csi2->asd.sd); in ipu6_isys_csi2_cleanup()
524 v4l2_subdev_cleanup(&csi2->asd.sd); in ipu6_isys_csi2_cleanup()
525 ipu6_isys_subdev_cleanup(&csi2->asd); in ipu6_isys_csi2_cleanup()
526 csi2->isys = NULL; in ipu6_isys_csi2_cleanup()
529 int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, in ipu6_isys_csi2_init() argument
533 struct device *dev = &isys->adev->auxdev.dev; in ipu6_isys_csi2_init()
536 csi2->isys = isys; in ipu6_isys_csi2_init()
537 csi2->base = base; in ipu6_isys_csi2_init()
538 csi2->port = index; in ipu6_isys_csi2_init()
540 csi2->asd.sd.entity.ops = &csi2_entity_ops; in ipu6_isys_csi2_init()
541 csi2->asd.isys = isys; in ipu6_isys_csi2_init()
542 ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, in ipu6_isys_csi2_init()
547 csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index; in ipu6_isys_csi2_init()
548 csi2->asd.supported_codes = csi2_supported_codes; in ipu6_isys_csi2_init()
549 snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), in ipu6_isys_csi2_init()
550 IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index); in ipu6_isys_csi2_init()
551 v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); in ipu6_isys_csi2_init()
552 ret = v4l2_subdev_init_finalize(&csi2->asd.sd); in ipu6_isys_csi2_init()
558 ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); in ipu6_isys_csi2_init()
567 ipu6_isys_csi2_cleanup(csi2); in ipu6_isys_csi2_init()
574 struct video_device *vdev = stream->asd->sd.devnode; in ipu6_isys_csi2_sof_event_by_stream()
575 struct device *dev = &stream->isys->adev->auxdev.dev; in ipu6_isys_csi2_sof_event_by_stream()
576 struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); in ipu6_isys_csi2_sof_event_by_stream() local
581 ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); in ipu6_isys_csi2_sof_event_by_stream()
584 dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", in ipu6_isys_csi2_sof_event_by_stream()
585 csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); in ipu6_isys_csi2_sof_event_by_stream()
590 struct device *dev = &stream->isys->adev->auxdev.dev; in ipu6_isys_csi2_eof_event_by_stream()
591 struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd); in ipu6_isys_csi2_eof_event_by_stream() local
592 u32 frame_sequence = atomic_read(&stream->sequence); in ipu6_isys_csi2_eof_event_by_stream()
594 dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", in ipu6_isys_csi2_eof_event_by_stream()
595 csi2->port, frame_sequence); in ipu6_isys_csi2_eof_event_by_stream()
599 struct ipu6_isys_csi2 *csi2, in ipu6_isys_csi2_get_remote_desc() argument
604 struct device *dev = &csi2->isys->adev->auxdev.dev; in ipu6_isys_csi2_get_remote_desc()
613 return -EPIPE; in ipu6_isys_csi2_get_remote_desc()
615 pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]); in ipu6_isys_csi2_get_remote_desc()
617 return -EPIPE; in ipu6_isys_csi2_get_remote_desc()
619 ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); in ipu6_isys_csi2_get_remote_desc()
625 return -EINVAL; in ipu6_isys_csi2_get_remote_desc()
638 return -EINVAL; in ipu6_isys_csi2_get_remote_desc()
641 if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) { in ipu6_isys_csi2_get_remote_desc()
642 dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); in ipu6_isys_csi2_get_remote_desc()
643 return -EINVAL; in ipu6_isys_csi2_get_remote_desc()