Commit 3c6640a6 authored by Steve Longerbeam's avatar Steve Longerbeam Committed by Mauro Carvalho Chehab

media: imx: Allow interweave with top/bottom lines swapped

Allow sequential->interlaced interweaving but with top/bottom
lines swapped to the output buffer.

This can be accomplished by adding one line length to IDMAC output
channel address, with a negative line length for the interlace offset.

This is to allow the seq-bt -> interlaced-bt transformation, where
bottom lines are still dominant (older in time) but with top lines
first in the interweaved output buffer.

With this support, the CSI can now allow seq-bt at its source pads,
e.g. the following transformations are allowed in CSI from sink to
source:

seq-tb -> seq-bt
seq-bt -> seq-bt
alternate -> seq-bt
Suggested-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: default avatarSteve Longerbeam <slongerbeam@gmail.com>
Reviewed-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent ab2f05cd
...@@ -106,6 +106,7 @@ struct prp_priv { ...@@ -106,6 +106,7 @@ struct prp_priv {
u32 frame_sequence; /* frame sequence counter */ u32 frame_sequence; /* frame sequence counter */
bool last_eof; /* waiting for last EOF at stream off */ bool last_eof; /* waiting for last EOF at stream off */
bool nfb4eof; /* NFB4EOF encountered during streaming */ bool nfb4eof; /* NFB4EOF encountered during streaming */
bool interweave_swap; /* swap top/bottom lines when interweaving */
struct completion last_eof_comp; struct completion last_eof_comp;
}; };
...@@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) ...@@ -235,6 +236,9 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch)
if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num)) if (ipu_idmac_buffer_is_ready(ch, priv->ipu_buf_num))
ipu_idmac_clear_buffer(ch, priv->ipu_buf_num); ipu_idmac_clear_buffer(ch, priv->ipu_buf_num);
if (priv->interweave_swap && ch == priv->out_ch)
phys += vdev->fmt.fmt.pix.bytesperline;
ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys); ipu_cpmem_set_buffer(ch, priv->ipu_buf_num, phys);
} }
...@@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv, ...@@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv,
* the IDMAC output channel. * the IDMAC output channel.
*/ */
interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) &&
V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) && V4L2_FIELD_IS_SEQUENTIAL(outfmt->field);
channel == priv->out_ch; priv->interweave_swap = interweave &&
image.pix.field == V4L2_FIELD_INTERLACED_BT;
if (rot_swap_width_height) { if (rot_swap_width_height) {
swap(image.pix.width, image.pix.height); swap(image.pix.width, image.pix.height);
...@@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv, ...@@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv,
(image.pix.width * outcc->bpp) >> 3; (image.pix.width * outcc->bpp) >> 3;
} }
if (priv->interweave_swap && channel == priv->out_ch) {
/* start interweave scan at 1st top line (2nd line) */
image.rect.top = 1;
}
image.phys0 = addr0; image.phys0 = addr0;
image.phys1 = addr1; image.phys1 = addr1;
...@@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv, ...@@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv,
* channels for planar 4:2:0 (but not when enabling IDMAC * channels for planar 4:2:0 (but not when enabling IDMAC
* interweaving, they are incompatible). * interweaving, they are incompatible).
*/ */
if (!interweave && (channel == priv->out_ch || if ((channel == priv->out_ch && !interweave) ||
channel == priv->rot_out_ch)) { channel == priv->rot_out_ch) {
switch (image.pix.pixelformat) { switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_YVU420:
...@@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv, ...@@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv,
if (rot_mode) if (rot_mode)
ipu_cpmem_set_rotation(channel, rot_mode); ipu_cpmem_set_rotation(channel, rot_mode);
if (interweave) if (interweave && channel == priv->out_ch)
ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline, ipu_cpmem_interlaced_scan(channel,
priv->interweave_swap ?
-image.pix.bytesperline :
image.pix.bytesperline,
image.pix.pixelformat); image.pix.pixelformat);
ret = ipu_ic_task_idma_init(priv->ic, channel, ret = ipu_ic_task_idma_init(priv->ic, channel,
......
...@@ -114,6 +114,7 @@ struct csi_priv { ...@@ -114,6 +114,7 @@ struct csi_priv {
u32 frame_sequence; /* frame sequence counter */ u32 frame_sequence; /* frame sequence counter */
bool last_eof; /* waiting for last EOF at stream off */ bool last_eof; /* waiting for last EOF at stream off */
bool nfb4eof; /* NFB4EOF encountered during streaming */ bool nfb4eof; /* NFB4EOF encountered during streaming */
bool interweave_swap; /* swap top/bottom lines when interweaving */
struct completion last_eof_comp; struct completion last_eof_comp;
}; };
...@@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv) ...@@ -286,6 +287,9 @@ static void csi_vb2_buf_done(struct csi_priv *priv)
if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num)) if (ipu_idmac_buffer_is_ready(priv->idmac_ch, priv->ipu_buf_num))
ipu_idmac_clear_buffer(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); ipu_cpmem_set_buffer(priv->idmac_ch, priv->ipu_buf_num, phys);
} }
...@@ -433,6 +437,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -433,6 +437,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
*/ */
interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) && interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) &&
V4L2_FIELD_IS_SEQUENTIAL(outfmt->field); V4L2_FIELD_IS_SEQUENTIAL(outfmt->field);
priv->interweave_swap = interweave &&
image.pix.field == V4L2_FIELD_INTERLACED_BT;
switch (image.pix.pixelformat) { switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SBGGR8:
...@@ -486,6 +492,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -486,6 +492,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
} }
if (passthrough) { 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, ipu_cpmem_set_resolution(priv->idmac_ch,
image.rect.width * passthrough_cycles, image.rect.width * passthrough_cycles,
image.rect.height); image.rect.height);
...@@ -495,6 +507,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -495,6 +507,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
ipu_cpmem_set_format_passthrough(priv->idmac_ch, ipu_cpmem_set_format_passthrough(priv->idmac_ch,
passthrough_bits); passthrough_bits);
} else { } 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); ret = ipu_cpmem_set_image(priv->idmac_ch, &image);
if (ret) if (ret)
goto unsetup_vb2; goto unsetup_vb2;
...@@ -526,6 +543,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -526,6 +543,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
if (interweave) if (interweave)
ipu_cpmem_interlaced_scan(priv->idmac_ch, ipu_cpmem_interlaced_scan(priv->idmac_ch,
priv->interweave_swap ?
-image.pix.bytesperline :
image.pix.bytesperline, image.pix.bytesperline,
image.pix.pixelformat); image.pix.pixelformat);
...@@ -1338,16 +1357,27 @@ static void csi_try_field(struct csi_priv *priv, ...@@ -1338,16 +1357,27 @@ static void csi_try_field(struct csi_priv *priv,
switch (infmt->field) { switch (infmt->field) {
case V4L2_FIELD_SEQ_TB: case V4L2_FIELD_SEQ_TB:
case V4L2_FIELD_SEQ_BT: 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: case V4L2_FIELD_ALTERNATE:
/* /*
* If the sink is sequential or alternating fields,
* allow only SEQ_TB at the source.
*
* This driver does not support alternate field mode, and * This driver does not support alternate field mode, and
* the CSI captures a whole frame, so the CSI never presents * the CSI captures a whole frame, so the CSI never presents
* alternate mode at its source pads. * 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).
*/ */
sdformat->format.field = V4L2_FIELD_SEQ_TB; if (!V4L2_FIELD_IS_SEQUENTIAL(sdformat->format.field))
sdformat->format.field = (infmt->height == 480 / 2) ?
V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB;
break; break;
default: default:
/* Passthrough for all other input field types */ /* Passthrough for all other input field types */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment