Commit 779680e2 authored by Jan Luebbe's avatar Jan Luebbe Committed by Mauro Carvalho Chehab

media: imx: add support for RGB565_2X8 on parallel bus

The IPU can only capture RGB565 with two 8-bit cycles in bayer/generic
mode on the parallel bus, compared to a specific mode on MIPI CSI-2.
To handle this, we extend imx_media_pixfmt with a cycles per pixel
field, which is used for generic formats on the parallel bus.

Based on the selected format and bus, we then update the width to
account for the multiple cycles per pixel.
Signed-off-by: default avatarJan Luebbe <jlu@pengutronix.de>
Signed-off-by: default avatarSteve Longerbeam <steve_longerbeam@mentor.com>
Reviewed-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent f6aaac7f
...@@ -122,10 +122,32 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev) ...@@ -122,10 +122,32 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev)
return container_of(sdev, struct csi_priv, sd); return container_of(sdev, struct csi_priv, sd);
} }
static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep)
{
return ep->bus_type != V4L2_MBUS_CSI2;
}
static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep)
{ {
return ep->bus_type != V4L2_MBUS_CSI2 && return is_parallel_bus(ep) && ep->bus.parallel.bus_width >= 16;
ep->bus.parallel.bus_width >= 16; }
/*
* Check for conditions that require the IPU to handle the
* data internally as generic data, aka passthrough mode:
* - raw bayer media bus formats, or
* - the CSI is receiving from a 16-bit parallel bus, or
* - the CSI is receiving from an 8-bit parallel bus and the incoming
* media bus format is other than UYVY8_2X8/YUYV8_2X8.
*/
static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep,
struct v4l2_mbus_framefmt *infmt,
const struct imx_media_pixfmt *incc)
{
return incc->bayer || is_parallel_16bit_bus(ep) ||
(is_parallel_bus(ep) &&
infmt->code != MEDIA_BUS_FMT_UYVY8_2X8 &&
infmt->code != MEDIA_BUS_FMT_YUYV8_2X8);
} }
/* /*
...@@ -371,15 +393,18 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv, ...@@ -371,15 +393,18 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv,
static int csi_idmac_setup_channel(struct csi_priv *priv) static int csi_idmac_setup_channel(struct csi_priv *priv)
{ {
struct imx_media_video_dev *vdev = priv->vdev; struct imx_media_video_dev *vdev = priv->vdev;
const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt; struct v4l2_mbus_framefmt *infmt;
struct ipu_image image; struct ipu_image image;
u32 passthrough_bits; u32 passthrough_bits;
u32 passthrough_cycles;
dma_addr_t phys[2]; dma_addr_t phys[2];
bool passthrough; bool passthrough;
u32 burst_size; u32 burst_size;
int ret; int ret;
infmt = &priv->format_mbus[CSI_SINK_PAD]; infmt = &priv->format_mbus[CSI_SINK_PAD];
incc = priv->cc[CSI_SINK_PAD];
ipu_cpmem_zero(priv->idmac_ch); ipu_cpmem_zero(priv->idmac_ch);
...@@ -393,12 +418,9 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -393,12 +418,9 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
image.phys0 = phys[0]; image.phys0 = phys[0];
image.phys1 = phys[1]; image.phys1 = phys[1];
/* passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc);
* Check for conditions that require the IPU to handle the passthrough_cycles = 1;
* data internally as generic data, aka passthrough mode:
* - raw bayer formats
* - the CSI is receiving from a 16-bit parallel bus
*/
switch (image.pix.pixelformat) { switch (image.pix.pixelformat) {
case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGBRG8:
...@@ -406,7 +428,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -406,7 +428,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
case V4L2_PIX_FMT_SRGGB8: case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_GREY:
burst_size = 16; burst_size = 16;
passthrough = true;
passthrough_bits = 8; passthrough_bits = 8;
break; break;
case V4L2_PIX_FMT_SBGGR16: case V4L2_PIX_FMT_SBGGR16:
...@@ -415,7 +436,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -415,7 +436,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
case V4L2_PIX_FMT_SRGGB16: case V4L2_PIX_FMT_SRGGB16:
case V4L2_PIX_FMT_Y16: case V4L2_PIX_FMT_Y16:
burst_size = 8; burst_size = 8;
passthrough = true;
passthrough_bits = 16; passthrough_bits = 16;
break; break;
case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV420:
...@@ -423,7 +443,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -423,7 +443,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
burst_size = (image.pix.width & 0x3f) ? burst_size = (image.pix.width & 0x3f) ?
((image.pix.width & 0x1f) ? ((image.pix.width & 0x1f) ?
((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64;
passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
passthrough_bits = 16; passthrough_bits = 16;
/* Skip writing U and V components to odd rows */ /* Skip writing U and V components to odd rows */
ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch);
...@@ -432,18 +451,25 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) ...@@ -432,18 +451,25 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_UYVY:
burst_size = (image.pix.width & 0x1f) ? burst_size = (image.pix.width & 0x1f) ?
((image.pix.width & 0xf) ? 8 : 16) : 32; ((image.pix.width & 0xf) ? 8 : 16) : 32;
passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
passthrough_bits = 16; passthrough_bits = 16;
break; break;
case V4L2_PIX_FMT_RGB565:
if (passthrough) {
burst_size = 16;
passthrough_bits = 8;
passthrough_cycles = incc->cycles;
break;
}
/* fallthrough for non-passthrough RGB565 (CSI-2 bus) */
default: default:
burst_size = (image.pix.width & 0xf) ? 8 : 16; burst_size = (image.pix.width & 0xf) ? 8 : 16;
passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
passthrough_bits = 16; passthrough_bits = 16;
break; break;
} }
if (passthrough) { if (passthrough) {
ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width, ipu_cpmem_set_resolution(priv->idmac_ch,
image.rect.width * passthrough_cycles,
image.rect.height); image.rect.height);
ipu_cpmem_set_stride(priv->idmac_ch, image.pix.bytesperline); ipu_cpmem_set_stride(priv->idmac_ch, image.pix.bytesperline);
ipu_cpmem_set_buffer(priv->idmac_ch, 0, image.phys0); ipu_cpmem_set_buffer(priv->idmac_ch, 0, image.phys0);
...@@ -635,17 +661,20 @@ static void csi_idmac_stop(struct csi_priv *priv) ...@@ -635,17 +661,20 @@ static void csi_idmac_stop(struct csi_priv *priv)
static int csi_setup(struct csi_priv *priv) static int csi_setup(struct csi_priv *priv)
{ {
struct v4l2_mbus_framefmt *infmt, *outfmt; struct v4l2_mbus_framefmt *infmt, *outfmt;
const struct imx_media_pixfmt *incc;
struct v4l2_mbus_config mbus_cfg; struct v4l2_mbus_config mbus_cfg;
struct v4l2_mbus_framefmt if_fmt; struct v4l2_mbus_framefmt if_fmt;
struct v4l2_rect crop;
infmt = &priv->format_mbus[CSI_SINK_PAD]; infmt = &priv->format_mbus[CSI_SINK_PAD];
incc = priv->cc[CSI_SINK_PAD];
outfmt = &priv->format_mbus[priv->active_output_pad]; outfmt = &priv->format_mbus[priv->active_output_pad];
/* compose mbus_config from the upstream endpoint */ /* compose mbus_config from the upstream endpoint */
mbus_cfg.type = priv->upstream_ep.bus_type; mbus_cfg.type = priv->upstream_ep.bus_type;
mbus_cfg.flags = (priv->upstream_ep.bus_type == V4L2_MBUS_CSI2) ? mbus_cfg.flags = is_parallel_bus(&priv->upstream_ep) ?
priv->upstream_ep.bus.mipi_csi2.flags : priv->upstream_ep.bus.parallel.flags :
priv->upstream_ep.bus.parallel.flags; priv->upstream_ep.bus.mipi_csi2.flags;
/* /*
* we need to pass input frame to CSI interface, but * we need to pass input frame to CSI interface, but
...@@ -653,8 +682,18 @@ static int csi_setup(struct csi_priv *priv) ...@@ -653,8 +682,18 @@ static int csi_setup(struct csi_priv *priv)
*/ */
if_fmt = *infmt; if_fmt = *infmt;
if_fmt.field = outfmt->field; if_fmt.field = outfmt->field;
crop = priv->crop;
/*
* if cycles is set, we need to handle this over multiple cycles as
* generic/bayer data
*/
if (is_parallel_bus(&priv->upstream_ep) && incc->cycles) {
if_fmt.width *= incc->cycles;
crop.width *= incc->cycles;
}
ipu_csi_set_window(priv->csi, &priv->crop); ipu_csi_set_window(priv->csi, &crop);
ipu_csi_set_downsize(priv->csi, ipu_csi_set_downsize(priv->csi,
priv->crop.width == 2 * priv->compose.width, priv->crop.width == 2 * priv->compose.width,
...@@ -1012,7 +1051,6 @@ static int csi_link_validate(struct v4l2_subdev *sd, ...@@ -1012,7 +1051,6 @@ static int csi_link_validate(struct v4l2_subdev *sd,
{ {
struct csi_priv *priv = v4l2_get_subdevdata(sd); struct csi_priv *priv = v4l2_get_subdevdata(sd);
struct v4l2_fwnode_endpoint upstream_ep = {}; struct v4l2_fwnode_endpoint upstream_ep = {};
const struct imx_media_pixfmt *incc;
bool is_csi2; bool is_csi2;
int ret; int ret;
...@@ -1030,17 +1068,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, ...@@ -1030,17 +1068,7 @@ static int csi_link_validate(struct v4l2_subdev *sd,
mutex_lock(&priv->lock); mutex_lock(&priv->lock);
priv->upstream_ep = upstream_ep; priv->upstream_ep = upstream_ep;
is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2); is_csi2 = !is_parallel_bus(&upstream_ep);
incc = priv->cc[CSI_SINK_PAD];
if (priv->dest != IPU_CSI_DEST_IDMAC &&
(incc->bayer || is_parallel_16bit_bus(&upstream_ep))) {
v4l2_err(&priv->sd,
"bayer/16-bit parallel buses must go to IDMAC pad\n");
ret = -EINVAL;
goto out;
}
if (is_csi2) { if (is_csi2) {
int vc_num = 0; int vc_num = 0;
/* /*
...@@ -1064,7 +1092,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, ...@@ -1064,7 +1092,7 @@ static int csi_link_validate(struct v4l2_subdev *sd,
/* select either parallel or MIPI-CSI2 as input to CSI */ /* select either parallel or MIPI-CSI2 as input to CSI */
ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2); ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2);
out:
mutex_unlock(&priv->lock); mutex_unlock(&priv->lock);
return ret; return ret;
} }
...@@ -1136,6 +1164,7 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd, ...@@ -1136,6 +1164,7 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_mbus_code_enum *code) struct v4l2_subdev_mbus_code_enum *code)
{ {
struct csi_priv *priv = v4l2_get_subdevdata(sd); struct csi_priv *priv = v4l2_get_subdevdata(sd);
struct v4l2_fwnode_endpoint upstream_ep;
const struct imx_media_pixfmt *incc; const struct imx_media_pixfmt *incc;
struct v4l2_mbus_framefmt *infmt; struct v4l2_mbus_framefmt *infmt;
int ret = 0; int ret = 0;
...@@ -1152,7 +1181,13 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd, ...@@ -1152,7 +1181,13 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd,
break; break;
case CSI_SRC_PAD_DIRECT: case CSI_SRC_PAD_DIRECT:
case CSI_SRC_PAD_IDMAC: case CSI_SRC_PAD_IDMAC:
if (incc->bayer) { ret = csi_get_upstream_endpoint(priv, &upstream_ep);
if (ret) {
v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
goto out;
}
if (requires_passthrough(&upstream_ep, infmt, incc)) {
if (code->index != 0) { if (code->index != 0) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
...@@ -1293,7 +1328,7 @@ static void csi_try_fmt(struct csi_priv *priv, ...@@ -1293,7 +1328,7 @@ static void csi_try_fmt(struct csi_priv *priv,
sdformat->format.width = compose->width; sdformat->format.width = compose->width;
sdformat->format.height = compose->height; sdformat->format.height = compose->height;
if (incc->bayer) { if (requires_passthrough(upstream_ep, infmt, incc)) {
sdformat->format.code = infmt->code; sdformat->format.code = infmt->code;
*cc = incc; *cc = incc;
} else { } else {
......
...@@ -78,6 +78,7 @@ static const struct imx_media_pixfmt rgb_formats[] = { ...@@ -78,6 +78,7 @@ static const struct imx_media_pixfmt rgb_formats[] = {
.codes = {MEDIA_BUS_FMT_RGB565_2X8_LE}, .codes = {MEDIA_BUS_FMT_RGB565_2X8_LE},
.cs = IPUV3_COLORSPACE_RGB, .cs = IPUV3_COLORSPACE_RGB,
.bpp = 16, .bpp = 16,
.cycles = 2,
}, { }, {
.fourcc = V4L2_PIX_FMT_RGB24, .fourcc = V4L2_PIX_FMT_RGB24,
.codes = { .codes = {
......
...@@ -62,6 +62,8 @@ struct imx_media_pixfmt { ...@@ -62,6 +62,8 @@ struct imx_media_pixfmt {
u32 fourcc; u32 fourcc;
u32 codes[4]; u32 codes[4];
int bpp; /* total bpp */ int bpp; /* total bpp */
/* cycles per pixel for generic (bayer) formats for the parallel bus */
int cycles;
enum ipu_color_space cs; enum ipu_color_space cs;
bool planar; /* is a planar format */ bool planar; /* is a planar format */
bool bayer; /* is a raw bayer format */ bool bayer; /* is a raw bayer format */
......
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