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: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steve Longerbeam <slongerbeam@gmail.com>
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
Steve Longerbeam 2019-01-09 13:30:13 -05:00 committed by Mauro Carvalho Chehab
parent ab2f05cdb1
commit 3c6640a606
2 changed files with 54 additions and 11 deletions

View file

@ -106,6 +106,7 @@ struct prp_priv {
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;
};
@ -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))
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);
}
@ -376,8 +380,9 @@ static int prp_setup_channel(struct prp_priv *priv,
* the IDMAC output channel.
*/
interweave = V4L2_FIELD_IS_INTERLACED(image.pix.field) &&
V4L2_FIELD_IS_SEQUENTIAL(outfmt->field) &&
channel == priv->out_ch;
V4L2_FIELD_IS_SEQUENTIAL(outfmt->field);
priv->interweave_swap = interweave &&
image.pix.field == V4L2_FIELD_INTERLACED_BT;
if (rot_swap_width_height) {
swap(image.pix.width, image.pix.height);
@ -388,6 +393,11 @@ static int prp_setup_channel(struct prp_priv *priv,
(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.phys1 = addr1;
@ -396,8 +406,8 @@ static int prp_setup_channel(struct prp_priv *priv,
* channels for planar 4:2:0 (but not when enabling IDMAC
* interweaving, they are incompatible).
*/
if (!interweave && (channel == priv->out_ch ||
channel == priv->rot_out_ch)) {
if ((channel == priv->out_ch && !interweave) ||
channel == priv->rot_out_ch) {
switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
@ -424,8 +434,11 @@ static int prp_setup_channel(struct prp_priv *priv,
if (rot_mode)
ipu_cpmem_set_rotation(channel, rot_mode);
if (interweave)
ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline,
if (interweave && channel == priv->out_ch)
ipu_cpmem_interlaced_scan(channel,
priv->interweave_swap ?
-image.pix.bytesperline :
image.pix.bytesperline,
image.pix.pixelformat);
ret = ipu_ic_task_idma_init(priv->ic, channel,

View file

@ -114,6 +114,7 @@ struct csi_priv {
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;
};
@ -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))
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);
}
@ -433,6 +437,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
*/
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:
@ -486,6 +492,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
}
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);
@ -495,6 +507,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
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;
@ -526,6 +543,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
if (interweave)
ipu_cpmem_interlaced_scan(priv->idmac_ch,
priv->interweave_swap ?
-image.pix.bytesperline :
image.pix.bytesperline,
image.pix.pixelformat);
@ -1338,16 +1357,27 @@ static void csi_try_field(struct csi_priv *priv,
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:
/*
* If the sink is sequential or alternating fields,
* allow only SEQ_TB at the source.
*
* 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.
* 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;
default:
/* Passthrough for all other input field types */