From 37f49e37ab4cb5d0bc4c60eb5c6d4dd57db767bb Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 07:44:59 +0000 Subject: [PATCH] gmac get mac form eeprom --- kernel/drivers/staging/media/imx/imx-media-csi.c | 492 ++++++++++++++++++++++++++++++++++++------------------ 1 files changed, 325 insertions(+), 167 deletions(-) diff --git a/kernel/drivers/staging/media/imx/imx-media-csi.c b/kernel/drivers/staging/media/imx/imx-media-csi.c index c7df0ff..d9a8667 100644 --- a/kernel/drivers/staging/media/imx/imx-media-csi.c +++ b/kernel/drivers/staging/media/imx/imx-media-csi.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * V4L2 Capture CSI Subdev for Freescale i.MX5/6 SOC * * Copyright (c) 2014-2017 Mentor Graphics Inc. * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/delay.h> #include <linux/gcd.h> @@ -37,11 +33,11 @@ * has not requested planar formats, we should allow 8 pixel * alignment. */ -#define MIN_W 176 -#define MIN_H 144 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 4096 #define MAX_H 4096 -#define W_ALIGN 4 /* multiple of 16 pixels */ +#define W_ALIGN 1 /* multiple of 2 pixels */ #define H_ALIGN 1 /* multiple of 2 lines */ #define S_ALIGN 1 /* multiple of 2 */ @@ -60,9 +56,10 @@ struct csi_priv { struct device *dev; struct ipu_soc *ipu; - struct imx_media_dev *md; struct v4l2_subdev sd; struct media_pad pad[CSI_NUM_PADS]; + struct v4l2_async_notifier notifier; + /* the video device at IDMAC output pad */ struct imx_media_video_dev *vdev; struct imx_media_fim *fim; @@ -114,6 +111,7 @@ u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ + bool interweave_swap; /* swap top/bottom lines when interweaving */ struct completion last_eof_comp; }; @@ -122,9 +120,14 @@ return container_of(sdev, struct csi_priv, sd); } +static inline struct csi_priv *notifier_to_dev(struct v4l2_async_notifier *n) +{ + return container_of(n, struct csi_priv, notifier); +} + static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep) { - return ep->bus_type != V4L2_MBUS_CSI2; + return ep->bus_type != V4L2_MBUS_CSI2_DPHY; } static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) @@ -161,8 +164,7 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, struct v4l2_fwnode_endpoint *ep) { - struct device_node *endpoint, *port; - struct media_entity *src; + struct fwnode_handle *endpoint; struct v4l2_subdev *sd; struct media_pad *pad; @@ -173,50 +175,43 @@ return -EPIPE; sd = priv->src_sd; - src = &sd->entity; - if (src->function == MEDIA_ENT_F_VID_MUX) { + switch (sd->grp_id) { + case IMX_MEDIA_GRP_ID_CSI_MUX: /* - * CSI is connected directly to video mux, skip up to + * CSI is connected directly to CSI mux, skip up to * CSI-2 receiver if it is in the path, otherwise stay - * with video mux. + * with the CSI mux. */ - sd = imx_media_find_upstream_subdev(priv->md, src, - IMX_MEDIA_GRP_ID_CSI2); - if (!IS_ERR(sd)) - src = &sd->entity; + sd = imx_media_pipeline_subdev(&sd->entity, + IMX_MEDIA_GRP_ID_CSI2, + true); + if (IS_ERR(sd)) + sd = priv->src_sd; + break; + case IMX_MEDIA_GRP_ID_CSI2: + break; + default: + /* + * the source is neither the CSI mux nor the CSI-2 receiver, + * get the source pad directly upstream from CSI itself. + */ + sd = &priv->sd; + break; } - /* - * If the source is neither the video mux nor the CSI-2 receiver, - * get the source pad directly upstream from CSI itself. - */ - if (src->function != MEDIA_ENT_F_VID_MUX && - sd->grp_id != IMX_MEDIA_GRP_ID_CSI2) - src = &priv->sd.entity; - - /* get source pad of entity directly upstream from src */ - pad = imx_media_find_upstream_pad(priv->md, src, 0); - if (IS_ERR(pad)) - return PTR_ERR(pad); - - sd = media_entity_to_v4l2_subdev(pad->entity); - - /* - * NOTE: this assumes an OF-graph port id is the same as a - * media pad index. - */ - port = of_graph_get_port_by_id(sd->dev->of_node, pad->index); - if (!port) + /* get source pad of entity directly upstream from sd */ + pad = imx_media_pipeline_pad(&sd->entity, 0, 0, true); + if (!pad) return -ENODEV; - endpoint = of_get_next_child(port, NULL); - of_node_put(port); - if (!endpoint) - return -ENODEV; + endpoint = imx_media_get_pad_fwnode(pad); + if (IS_ERR(endpoint)) + return PTR_ERR(endpoint); - v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep); - of_node_put(endpoint); + v4l2_fwnode_endpoint_parse(endpoint, ep); + + fwnode_handle_put(endpoint); return 0; } @@ -295,6 +290,9 @@ if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num)) ipu_idmac_clear_buffer(priv->idmac_ch, priv->ipu_buf_num); + + if (priv->interweave_swap) + phys += vdev->fmt.fmt.pix.bytesperline; ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys); } @@ -408,23 +406,24 @@ struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; + struct v4l2_mbus_framefmt *outfmt; + bool passthrough, interweave; struct ipu_image image; u32 passthrough_bits; u32 passthrough_cycles; dma_addr_t phys[2]; - bool passthrough; u32 burst_size; int ret; infmt = &priv->format_mbus[CSI_SINK_PAD]; incc = priv->cc[CSI_SINK_PAD]; + outfmt = &priv->format_mbus[CSI_SRC_PAD_IDMAC]; ipu_cpmem_zero(priv->idmac_ch); memset(&image, 0, sizeof(image)); image.pix = vdev->fmt.fmt.pix; - image.rect.width = image.pix.width; - image.rect.height = image.pix.height; + image.rect = vdev->compose; csi_idmac_setup_vb2_buf(priv, phys); @@ -433,6 +432,16 @@ passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc); passthrough_cycles = 1; + + /* + * If the field type at capture interface is interlaced, and + * the output IDMAC pad is sequential, enable interweave at + * the IDMAC output channel. + */ + interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && + V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); + priv->interweave_swap = interweave && + image.pix.field == V4L2_FIELD_INTERLACED_BT; switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: @@ -447,18 +456,24 @@ case V4L2_PIX_FMT_SGBRG16: case V4L2_PIX_FMT_SGRBG16: case V4L2_PIX_FMT_SRGGB16: - case V4L2_PIX_FMT_Y16: + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y12: burst_size = 8; passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_NV12: burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; passthrough_bits = 16; - /* Skip writing U and V components to odd rows */ - ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); + /* + * Skip writing U and V components to odd rows (but not + * when enabling IDMAC interweaving, they are incompatible). + */ + if (!interweave) + ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: @@ -473,7 +488,7 @@ passthrough_cycles = incc->cycles; break; } - /* fallthrough - non-passthrough RGB565 (CSI-2 bus) */ + fallthrough; /* non-passthrough RGB565 (CSI-2 bus) */ default: burst_size = (image.pix.width & 0xf) ? 8 : 16; passthrough_bits = 16; @@ -481,6 +496,12 @@ } if (passthrough) { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.phys0 += image.pix.bytesperline; + image.phys1 += image.pix.bytesperline; + } + ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width * passthrough_cycles, image.rect.height); @@ -490,6 +511,11 @@ ipu_cpmem_set_format_passthrough(priv->idmac_ch, passthrough_bits); } else { + if (priv->interweave_swap) { + /* start interweave scan at 1st top line (2nd line) */ + image.rect.top = 1; + } + ret = ipu_cpmem_set_image(priv->idmac_ch, &image); if (ret) goto unsetup_vb2; @@ -519,10 +545,12 @@ ipu_smfc_set_burstsize(priv->smfc, burst_size); - if (image.pix.field == V4L2_FIELD_NONE && - V4L2_FIELD_HAS_BOTH(infmt->field)) + if (interweave) ipu_cpmem_interlaced_scan(priv->idmac_ch, - image.pix.bytesperline); + priv->interweave_swap ? + -image.pix.bytesperline : + image.pix.bytesperline, + image.pix.pixelformat); ipu_idmac_set_double_buffer(priv->idmac_ch, true); @@ -579,7 +607,7 @@ outfmt = &vdev->fmt.fmt.pix; - ret = imx_media_alloc_dma_buf(priv->md, &priv->underrun_buf, + ret = imx_media_alloc_dma_buf(priv->dev, &priv->underrun_buf, outfmt->sizeimage); if (ret) goto out_put_ipu; @@ -599,8 +627,8 @@ } priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu, - priv->idmac_ch, - IPU_IRQ_NFB4EOF); + priv->idmac_ch, + IPU_IRQ_NFB4EOF); ret = devm_request_irq(priv->dev, priv->nfb4eof_irq, csi_idmac_nfb4eof_interrupt, 0, "imx-smfc-nfb4eof", priv); @@ -633,7 +661,7 @@ out_unsetup: csi_idmac_unsetup(priv, VB2_BUF_STATE_QUEUED); out_free_dma_buf: - imx_media_free_dma_buf(priv->md, &priv->underrun_buf); + imx_media_free_dma_buf(priv->dev, &priv->underrun_buf); out_put_ipu: csi_idmac_put_ipu_resources(priv); return ret; @@ -665,7 +693,7 @@ csi_idmac_unsetup(priv, VB2_BUF_STATE_ERROR); - imx_media_free_dma_buf(priv->md, &priv->underrun_buf); + imx_media_free_dma_buf(priv->dev, &priv->underrun_buf); /* cancel the EOF timeout timer */ del_timer_sync(&priv->eof_timeout_timer); @@ -692,12 +720,7 @@ priv->upstream_ep.bus.parallel.flags : priv->upstream_ep.bus.mipi_csi2.flags; - /* - * we need to pass input frame to CSI interface, but - * with translated field type from output format - */ if_fmt = *infmt; - if_fmt.field = outfmt->field; crop = priv->crop; /* @@ -715,7 +738,7 @@ priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt); + ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt, outfmt); ipu_csi_set_dest(priv->csi, priv->dest); @@ -920,7 +943,10 @@ switch (fi->pad) { case CSI_SINK_PAD: - /* No limits on input frame interval */ + /* No limits on valid input frame intervals */ + if (fi->interval.numerator == 0 || + fi->interval.denominator == 0) + fi->interval = *input_fi; /* Reset output intervals and frame skipping ratio to 1:1 */ priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval; priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval; @@ -1035,6 +1061,8 @@ v4l2_ctrl_handler_free(&priv->ctrl_hdlr); v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0); priv->sink = NULL; + /* do not apply IC burst alignment in csi_try_crop */ + priv->active_output_pad = CSI_SRC_PAD_IDMAC; goto out; } @@ -1063,10 +1091,10 @@ remote_sd = media_entity_to_v4l2_subdev(remote->entity); switch (remote_sd->grp_id) { - case IMX_MEDIA_GRP_ID_VDIC: + case IMX_MEDIA_GRP_ID_IPU_VDIC: priv->dest = IPU_CSI_DEST_VDIC; break; - case IMX_MEDIA_GRP_ID_IC_PRP: + case IMX_MEDIA_GRP_ID_IPU_IC_PRP: priv->dest = IPU_CSI_DEST_IC; break; default: @@ -1087,7 +1115,7 @@ struct v4l2_subdev_format *sink_fmt) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; bool is_csi2; int ret; @@ -1117,8 +1145,7 @@ */ #if 0 mutex_unlock(&priv->lock); - vc_num = imx_media_find_mipi_csi2_channel(priv->md, - &priv->sd.entity); + vc_num = imx_media_find_mipi_csi2_channel(&priv->sd.entity); if (vc_num < 0) return vc_num; mutex_lock(&priv->lock); @@ -1171,12 +1198,21 @@ struct v4l2_mbus_framefmt *infmt, struct v4l2_fwnode_endpoint *upstream_ep) { + u32 in_height; + crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; /* adjust crop left/width to h/w alignment restrictions */ crop->left &= ~0x3; - crop->width &= ~0x7; + if (priv->active_output_pad == CSI_SRC_PAD_DIRECT) + crop->width &= ~0x7; /* multiple of 8 pixels (IC burst) */ + else + crop->width &= ~0x1; /* multiple of 2 pixels */ + + in_height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + in_height *= 2; /* * FIXME: not sure why yet, but on interlaced bt.656, @@ -1187,12 +1223,12 @@ if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { - crop->height = infmt->height; - crop->top = (infmt->height == 480) ? 2 : 0; + crop->height = in_height; + crop->top = (in_height == 480) ? 2 : 0; } else { - crop->height = min_t(__u32, infmt->height, crop->height); - if (crop->top + crop->height > infmt->height) - crop->top = infmt->height - crop->height; + crop->height = min_t(__u32, in_height, crop->height); + if (crop->top + crop->height > in_height) + crop->top = in_height - crop->height; } } @@ -1201,7 +1237,7 @@ struct v4l2_subdev_mbus_code_enum *code) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; int ret = 0; @@ -1209,12 +1245,12 @@ mutex_lock(&priv->lock); infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, code->which); - incc = imx_media_find_mbus_format(infmt->code, CS_SEL_ANY, true); + incc = imx_media_find_mbus_format(infmt->code, PIXFMT_SEL_ANY); switch (code->pad) { case CSI_SINK_PAD: - ret = imx_media_enum_mbus_format(&code->code, code->index, - CS_SEL_ANY, true); + ret = imx_media_enum_mbus_formats(&code->code, code->index, + PIXFMT_SEL_ANY); break; case CSI_SRC_PAD_DIRECT: case CSI_SRC_PAD_IDMAC: @@ -1231,11 +1267,13 @@ } code->code = infmt->code; } else { - u32 cs_sel = (incc->cs == IPUV3_COLORSPACE_YUV) ? - CS_SEL_YUV : CS_SEL_RGB; - ret = imx_media_enum_ipu_format(&code->code, - code->index, - cs_sel); + enum imx_pixfmt_sel fmt_sel = + (incc->cs == IPUV3_COLORSPACE_YUV) ? + PIXFMT_SEL_YUV : PIXFMT_SEL_RGB; + + ret = imx_media_enum_ipu_formats(&code->code, + code->index, + fmt_sel); } break; default: @@ -1342,6 +1380,55 @@ return ret; } +static void csi_try_field(struct csi_priv *priv, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *sdformat) +{ + struct v4l2_mbus_framefmt *infmt = + __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which); + + /* + * no restrictions on sink pad field type except must + * be initialized. + */ + if (sdformat->pad == CSI_SINK_PAD) { + if (sdformat->format.field == V4L2_FIELD_ANY) + sdformat->format.field = V4L2_FIELD_NONE; + return; + } + + switch (infmt->field) { + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + /* + * If the user requests sequential at the source pad, + * allow it (along with possibly inverting field order). + * Otherwise passthrough the field type. + */ + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = infmt->field; + break; + case V4L2_FIELD_ALTERNATE: + /* + * This driver does not support alternate field mode, and + * the CSI captures a whole frame, so the CSI never presents + * alternate mode at its source pads. If user has not + * already requested sequential, translate ALTERNATE at + * sink pad to SEQ_TB or SEQ_BT at the source pad depending + * on input height (assume NTSC BT order if 480 total active + * frame lines, otherwise PAL TB order). + */ + if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field)) + sdformat->format.field = (infmt->height == 480 / 2) ? + V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB; + break; + default: + /* Passthrough for all other input field types */ + sdformat->format.field = infmt->field; + break; + } +} + static void csi_try_fmt(struct csi_priv *priv, struct v4l2_fwnode_endpoint *upstream_ep, struct v4l2_subdev_pad_config *cfg, @@ -1359,8 +1446,7 @@ switch (sdformat->pad) { case CSI_SRC_PAD_DIRECT: case CSI_SRC_PAD_IDMAC: - incc = imx_media_find_mbus_format(infmt->code, - CS_SEL_ANY, true); + incc = imx_media_find_mbus_format(infmt->code, PIXFMT_SEL_ANY); sdformat->format.width = compose->width; sdformat->format.height = compose->height; @@ -1369,69 +1455,63 @@ sdformat->format.code = infmt->code; *cc = incc; } else { - u32 cs_sel = (incc->cs == IPUV3_COLORSPACE_YUV) ? - CS_SEL_YUV : CS_SEL_RGB; + enum imx_pixfmt_sel fmt_sel = + (incc->cs == IPUV3_COLORSPACE_YUV) ? + PIXFMT_SEL_YUV : PIXFMT_SEL_RGB; *cc = imx_media_find_ipu_format(sdformat->format.code, - cs_sel); + fmt_sel); if (!*cc) { - imx_media_enum_ipu_format(&code, 0, cs_sel); - *cc = imx_media_find_ipu_format(code, cs_sel); + imx_media_enum_ipu_formats(&code, 0, fmt_sel); + *cc = imx_media_find_ipu_format(code, fmt_sel); sdformat->format.code = (*cc)->codes[0]; } } - if (sdformat->pad == CSI_SRC_PAD_DIRECT || - sdformat->format.field != V4L2_FIELD_NONE) - sdformat->format.field = infmt->field; - - /* - * translate V4L2_FIELD_ALTERNATE to SEQ_TB or SEQ_BT - * depending on input height (assume NTSC top-bottom - * order if 480 lines, otherwise PAL bottom-top order). - */ - if (sdformat->format.field == V4L2_FIELD_ALTERNATE) { - sdformat->format.field = (infmt->height == 480) ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; - } + csi_try_field(priv, cfg, sdformat); /* propagate colorimetry from sink */ sdformat->format.colorspace = infmt->colorspace; sdformat->format.xfer_func = infmt->xfer_func; sdformat->format.quantization = infmt->quantization; sdformat->format.ycbcr_enc = infmt->ycbcr_enc; + break; case CSI_SINK_PAD: v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W, W_ALIGN, &sdformat->format.height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); + *cc = imx_media_find_mbus_format(sdformat->format.code, + PIXFMT_SEL_ANY); + if (!*cc) { + imx_media_enum_mbus_formats(&code, 0, + PIXFMT_SEL_YUV_RGB); + *cc = imx_media_find_mbus_format(code, + PIXFMT_SEL_YUV_RGB); + sdformat->format.code = (*cc)->codes[0]; + } + + csi_try_field(priv, cfg, sdformat); + /* Reset crop and compose rectangles */ crop->left = 0; crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; + if (sdformat->format.field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; compose->width = crop->width; compose->height = crop->height; - *cc = imx_media_find_mbus_format(sdformat->format.code, - CS_SEL_ANY, true); - if (!*cc) { - imx_media_enum_mbus_format(&code, 0, - CS_SEL_ANY, false); - *cc = imx_media_find_mbus_format(code, - CS_SEL_ANY, false); - sdformat->format.code = (*cc)->codes[0]; - } - - imx_media_fill_default_mbus_fields( - &sdformat->format, infmt, - priv->active_output_pad == CSI_SRC_PAD_DIRECT); break; } + + imx_media_try_colorimetry(&sdformat->format, + priv->active_output_pad == CSI_SRC_PAD_DIRECT); } static int csi_set_fmt(struct v4l2_subdev *sd, @@ -1439,10 +1519,8 @@ struct v4l2_subdev_format *sdformat) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct imx_media_video_dev *vdev = priv->vdev; - struct v4l2_fwnode_endpoint upstream_ep; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; const struct imx_media_pixfmt *cc; - struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; int ret; @@ -1494,19 +1572,9 @@ } } - if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) - goto out; + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->cc[sdformat->pad] = cc; - priv->cc[sdformat->pad] = cc; - - /* propagate IDMAC output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, - &priv->format_mbus[CSI_SRC_PAD_IDMAC], - priv->cc[CSI_SRC_PAD_IDMAC]); - mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt); - - return 0; out: mutex_unlock(&priv->lock); return ret; @@ -1536,6 +1604,8 @@ sel->r.top = 0; sel->r.width = infmt->width; sel->r.height = infmt->height; + if (infmt->field == V4L2_FIELD_ALTERNATE) + sel->r.height *= 2; break; case V4L2_SEL_TGT_CROP: sel->r = *crop; @@ -1579,7 +1649,7 @@ struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint upstream_ep; + struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; struct v4l2_mbus_framefmt *infmt; struct v4l2_rect *crop, *compose; int pad, ret; @@ -1686,18 +1756,12 @@ return v4l2_event_unsubscribe(fh, sub); } -/* - * retrieve our pads parsed from the OF graph by the media device - */ static int csi_registered(struct v4l2_subdev *sd) { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct ipu_csi *csi; int i, ret; u32 code; - - /* get media device */ - priv->md = dev_get_drvdata(sd->v4l2_dev->dev); /* get handle to IPU CSI */ csi = ipu_csi_get(priv->ipu, priv->csi_id); @@ -1708,12 +1772,9 @@ priv->csi = csi; for (i = 0; i < CSI_NUM_PADS; i++) { - priv->pad[i].flags = (i == CSI_SINK_PAD) ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - code = 0; if (i != CSI_SINK_PAD) - imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV); + imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV); /* set a default mbus format */ ret = imx_media_init_mbus_fmt(&priv->format_mbus[i], @@ -1742,21 +1803,22 @@ goto put_csi; } - ret = media_entity_pads_init(&sd->entity, CSI_NUM_PADS, priv->pad); - if (ret) + priv->vdev = imx_media_capture_device_init(priv->sd.dev, + &priv->sd, + CSI_SRC_PAD_IDMAC); + if (IS_ERR(priv->vdev)) { + ret = PTR_ERR(priv->vdev); goto free_fim; + } ret = imx_media_capture_device_register(priv->vdev); if (ret) - goto free_fim; - - ret = imx_media_add_video_device(priv->md, priv->vdev); - if (ret) - goto unreg; + goto remove_vdev; return 0; -unreg: - imx_media_capture_device_unregister(priv->vdev); + +remove_vdev: + imx_media_capture_device_remove(priv->vdev); free_fim: if (priv->fim) imx_media_fim_free(priv->fim); @@ -1770,6 +1832,7 @@ struct csi_priv *priv = v4l2_get_subdevdata(sd); imx_media_capture_device_unregister(priv->vdev); + imx_media_capture_device_remove(priv->vdev); if (priv->fim) imx_media_fim_free(priv->fim); @@ -1778,9 +1841,32 @@ ipu_csi_put(priv->csi); } +/* + * The CSI has only one fwnode endpoint, at the sink pad. Verify the + * endpoint belongs to us, and return CSI_SINK_PAD. + */ +static int csi_get_fwnode_pad(struct media_entity *entity, + struct fwnode_endpoint *endpoint) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct csi_priv *priv = v4l2_get_subdevdata(sd); + struct fwnode_handle *csi_port = dev_fwnode(priv->dev); + struct fwnode_handle *csi_ep; + int ret; + + csi_ep = fwnode_get_next_child_node(csi_port, NULL); + + ret = endpoint->local_fwnode == csi_ep ? CSI_SINK_PAD : -ENXIO; + + fwnode_handle_put(csi_ep); + + return ret; +} + static const struct media_entity_operations csi_entity_ops = { .link_setup = csi_link_setup, .link_validate = v4l2_subdev_link_validate, + .get_fwnode_pad = csi_get_fwnode_pad, }; static const struct v4l2_subdev_core_ops csi_core_ops = { @@ -1817,12 +1903,74 @@ .unregistered = csi_unregistered, }; +static int imx_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct csi_priv *priv = notifier_to_dev(notifier); + struct media_pad *sink = &priv->sd.entity.pads[CSI_SINK_PAD]; + + /* + * If the subdev is a video mux, it must be one of the CSI + * muxes. Mark it as such via its group id. + */ + if (sd->entity.function == MEDIA_ENT_F_VID_MUX) + sd->grp_id = IMX_MEDIA_GRP_ID_CSI_MUX; + + return v4l2_create_fwnode_links_to_pad(sd, sink); +} + +static const struct v4l2_async_notifier_operations csi_notify_ops = { + .bound = imx_csi_notify_bound, +}; + +static int imx_csi_async_register(struct csi_priv *priv) +{ + struct v4l2_async_subdev *asd = NULL; + struct fwnode_handle *ep; + unsigned int port; + int ret; + + v4l2_async_notifier_init(&priv->notifier); + + /* get this CSI's port id */ + ret = fwnode_property_read_u32(dev_fwnode(priv->dev), "reg", &port); + if (ret < 0) + return ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(priv->dev->parent), + port, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (ep) { + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &priv->notifier, ep, sizeof(*asd)); + + fwnode_handle_put(ep); + + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + /* OK if asd already exists */ + if (ret != -EEXIST) + return ret; + } + } + + priv->notifier.ops = &csi_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&priv->sd, + &priv->notifier); + if (ret) + return ret; + + return v4l2_async_register_subdev(&priv->sd); +} + static int imx_csi_probe(struct platform_device *pdev) { struct ipu_client_platformdata *pdata; struct pinctrl *pinctrl; struct csi_priv *priv; - int ret; + int i, ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -1843,6 +1991,8 @@ priv->csi_id = pdata->csi; priv->smfc_id = (priv->csi_id == 0) ? 0 : 2; + priv->active_output_pad = CSI_SRC_PAD_IDMAC; + timer_setup(&priv->eof_timeout_timer, csi_idmac_eof_timeout, 0); spin_lock_init(&priv->irqlock); @@ -1856,14 +2006,18 @@ priv->sd.owner = THIS_MODULE; priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; priv->sd.grp_id = priv->csi_id ? - IMX_MEDIA_GRP_ID_CSI1 : IMX_MEDIA_GRP_ID_CSI0; + IMX_MEDIA_GRP_ID_IPU_CSI1 : IMX_MEDIA_GRP_ID_IPU_CSI0; imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name), priv->sd.grp_id, ipu_get_num(priv->ipu)); - priv->vdev = imx_media_capture_device_init(&priv->sd, - CSI_SRC_PAD_IDMAC); - if (IS_ERR(priv->vdev)) - return PTR_ERR(priv->vdev); + for (i = 0; i < CSI_NUM_PADS; i++) + priv->pad[i].flags = (i == CSI_SINK_PAD) ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, CSI_NUM_PADS, + priv->pad); + if (ret) + return ret; mutex_init(&priv->lock); @@ -1886,15 +2040,18 @@ goto free; } - ret = v4l2_async_register_subdev(&priv->sd); + ret = imx_csi_async_register(priv); if (ret) - goto free; + goto cleanup; return 0; + +cleanup: + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); free: v4l2_ctrl_handler_free(&priv->ctrl_hdlr); mutex_destroy(&priv->lock); - imx_media_capture_device_remove(priv->vdev); return ret; } @@ -1905,7 +2062,8 @@ v4l2_ctrl_handler_free(&priv->ctrl_hdlr); mutex_destroy(&priv->lock); - imx_media_capture_device_remove(priv->vdev); + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); -- Gitblit v1.6.2