/* * ceu_bus_fmt - describe a 8-bits yuyv format the sensor can produce * * @mbus_code: bus format code * @fmt_order: CEU_CAMCR.DTARY ordering of input components (Y, Cb, Cr) * @fmt_order_swap: swapped CEU_CAMCR.DTARY ordering of input components * (Y, Cr, Cb) * @swapped: does Cr appear before Cb? * @bps: number of bits sent over bus for each sample * @bpp: number of bits per pixels unit
*/ struct ceu_mbus_fmt {
u32 mbus_code;
u32 fmt_order;
u32 fmt_order_swap; bool swapped;
u8 bps;
u8 bpp;
};
/* * ceu_buffer - Link vb2 buffer to the list of available buffers.
*/ struct ceu_buffer { struct vb2_v4l2_buffer vb; struct list_head queue;
};
/* * ceu_fmt - describe a memory output format supported by CEU interface. * * @fourcc: memory layout fourcc format code * @bpp: number of bits for each pixel stored in memory
*/ struct ceu_fmt {
u32 fourcc;
u32 bpp;
};
/* * ceu_format_list - List of supported memory output formats * * If sensor provides any YUYV bus format, all the following planar memory * formats are available thanks to CEU re-ordering and sub-sampling * capabilities.
*/ staticconststruct ceu_fmt ceu_fmt_list[] = {
{
.fourcc = V4L2_PIX_FMT_NV16,
.bpp = 16,
},
{
.fourcc = V4L2_PIX_FMT_NV61,
.bpp = 16,
},
{
.fourcc = V4L2_PIX_FMT_NV12,
.bpp = 12,
},
{
.fourcc = V4L2_PIX_FMT_NV21,
.bpp = 12,
},
{
.fourcc = V4L2_PIX_FMT_YUYV,
.bpp = 16,
},
{
.fourcc = V4L2_PIX_FMT_UYVY,
.bpp = 16,
},
{
.fourcc = V4L2_PIX_FMT_YVYU,
.bpp = 16,
},
{
.fourcc = V4L2_PIX_FMT_VYUY,
.bpp = 16,
},
};
for (i = 0; i < ARRAY_SIZE(ceu_fmt_list); i++, fmt++) if (fmt->fourcc == fourcc) return fmt;
return NULL;
}
staticbool ceu_fmt_mplane(struct v4l2_pix_format_mplane *pix)
{ switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_VYUY: returnfalse; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: returntrue; default: returnfalse;
}
}
/* Set the frame capture period for both image capture and data sync. */
capwr = (pix->height << 16) | pix->width * mbus_fmt->bpp / 8;
/* * Swap input data endianness by default. * In data fetch mode bytes are received in chunks of 8 bytes. * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first) * The data is however by default written to memory in reverse order: * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte) * * Use CEU_CDOCR[2:0] to swap data ordering.
*/
cdocr = CEU_CDOCR_SWAP_ENDIANNESS;
/* * Configure CAMCR and CDOCR: * match input components ordering with memory output format and * handle downsampling to YUV420. * * If the memory output planar format is 'swapped' (Cr before Cb) and * input format is not, use the swapped version of CAMCR.DTARY. * * If the memory output planar format is not 'swapped' (Cb before Cr) * and input format is, use the swapped version of CAMCR.DTARY. * * CEU by default downsample to planar YUV420 (CDCOR[4] = 0). * If output is planar YUV422 set CDOCR[4] = 1 * * No downsample for data fetch sync mode.
*/ switch (pix->pixelformat) { /* Data fetch sync mode */ case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY:
camcr = CEU_CAMCR_JPEG;
cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
cfzsr = (pix->height << 16) | pix->width;
cdwdr = pix->plane_fmt[0].bytesperline; break;
/* Non-swapped planar image capture mode. */ case V4L2_PIX_FMT_NV16:
cdocr |= CEU_CDOCR_NO_DOWSAMPLE;
fallthrough; case V4L2_PIX_FMT_NV12: if (mbus_fmt->swapped)
camcr = mbus_fmt->fmt_order_swap; else
camcr = mbus_fmt->fmt_order;
/* TODO: handle 16 bit bus width with DTIF bit in CAMCR */
ceu_write(ceudev, CEU_CAMCR, camcr);
ceu_write(ceudev, CEU_CDOCR, cdocr);
ceu_write(ceudev, CEU_CAPCR, CEU_CAPCR_BUS_WIDTH256);
/* * TODO: make CAMOR offsets configurable. * CAMOR wants to know the number of blanks between a VS/HS signal * and valid data. This value should actually come from the sensor...
*/
ceu_write(ceudev, CEU_CAMOR, 0);
/* TODO: 16 bit bus width require re-calculation of cdwdr and cfzsr */
ceu_write(ceudev, CEU_CAPWR, capwr);
ceu_write(ceudev, CEU_CFSZR, cfzsr);
ceu_write(ceudev, CEU_CDWDR, cdwdr);
return 0;
}
/* * ceu_capture() - Trigger start of a capture sequence. * * Program the CEU DMA registers with addresses where to transfer image data.
*/ staticint ceu_capture(struct ceu_device *ceudev)
{ struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix;
dma_addr_t phys_addr_top;
/* Unexpected interrupt. */ if (!(status & CEU_CEIER_MASK)) return IRQ_NONE;
spin_lock(&ceudev->lock);
/* Stale interrupt from a released buffer, ignore it. */
vbuf = ceudev->active; if (!vbuf) {
spin_unlock(&ceudev->lock); return IRQ_HANDLED;
}
/* * When a VBP interrupt occurs, no capture end interrupt will occur * and the image of that frame is not captured correctly.
*/ if (status & CEU_CEIER_VBP) {
dev_err(ceudev->dev, "VBP interrupt: abort capture\n"); goto error_irq_out;
}
/* Prepare to return the 'previous' buffer. */
vbuf->vb2_buf.timestamp = ktime_get_ns();
vbuf->sequence = ceudev->sequence++;
vbuf->field = ceudev->field;
/* Prepare a new 'active' buffer and trigger a new capture. */ if (!list_empty(&ceudev->capture)) {
buf = list_first_entry(&ceudev->capture, struct ceu_buffer,
queue);
list_del(&buf->queue);
ceudev->active = &buf->vb;
ceu_capture(ceudev);
}
/* Return the 'previous' buffer. */
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
spin_unlock(&ceudev->lock);
return IRQ_HANDLED;
error_irq_out: /* Return the 'previous' buffer and all queued ones. */
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_ERROR);
/* * ceu_calc_plane_sizes() - Fill per-plane 'struct v4l2_plane_pix_format' * information according to the currently configured * pixel format. * @ceu_device: CEU device. * @ceu_fmt: Active image format. * @pix: Pixel format information (store line width and image sizes)
*/ staticvoid ceu_calc_plane_sizes(struct ceu_device *ceudev, conststruct ceu_fmt *ceu_fmt, struct v4l2_pix_format_mplane *pix)
{ unsignedint bpl, szimage;
switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_VYUY:
pix->num_planes = 1;
bpl = pix->width * ceu_fmt->bpp / 8;
szimage = pix->height * bpl;
ceu_update_plane_sizes(&pix->plane_fmt[0], bpl, szimage); break;
/* * ceu_vb2_setup() - is called to check whether the driver can accept the * requested number of buffers and to fill in plane sizes * for the current frame format, if required.
*/ staticint ceu_vb2_setup(struct vb2_queue *vq, unsignedint *count, unsignedint *num_planes, unsignedint sizes[], struct device *alloc_devs[])
{ struct ceu_device *ceudev = vb2_get_drv_priv(vq); struct v4l2_pix_format_mplane *pix = &ceudev->v4l2_pix; unsignedint i;
/* num_planes is set: just check plane sizes. */ if (*num_planes) { for (i = 0; i < pix->num_planes; i++) if (sizes[i] < pix->plane_fmt[i].sizeimage) return -EINVAL;
return 0;
}
/* num_planes not set: called from REQBUFS, just set plane sizes. */
*num_planes = pix->num_planes; for (i = 0; i < pix->num_planes; i++)
sizes[i] = pix->plane_fmt[i].sizeimage;
for (i = 0; i < pix->num_planes; i++) { if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) {
dev_err(ceudev->dev, "Plane size too small (%lu < %u)\n",
vb2_plane_size(vb, i),
pix->plane_fmt[i].sizeimage); return -EINVAL;
}
vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage);
}
/* * __ceu_try_fmt() - test format on CEU and sensor * @ceudev: The CEU device. * @v4l2_fmt: format to test. * @sd_mbus_code: the media bus code accepted by the subdevice; output param. * * Returns 0 for success, < 0 for errors.
*/ staticint __ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt,
u32 *sd_mbus_code)
{ struct ceu_subdev *ceu_sd = ceudev->sd; struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; struct v4l2_subdev_pad_config pad_cfg; struct v4l2_subdev_state pad_state = {
.pads = &pad_cfg,
}; conststruct ceu_fmt *ceu_fmt;
u32 mbus_code_old;
u32 mbus_code; int ret;
/* * Set format on sensor sub device: bus format used to produce memory * format is selected depending on YUV component ordering or * at initialization time.
*/ struct v4l2_subdev_format sd_format = {
.which = V4L2_SUBDEV_FORMAT_TRY,
};
mbus_code_old = ceu_sd->mbus_fmt.mbus_code;
switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV:
mbus_code = MEDIA_BUS_FMT_YUYV8_2X8; break; case V4L2_PIX_FMT_UYVY:
mbus_code = MEDIA_BUS_FMT_UYVY8_2X8; break; case V4L2_PIX_FMT_YVYU:
mbus_code = MEDIA_BUS_FMT_YVYU8_2X8; break; case V4L2_PIX_FMT_VYUY:
mbus_code = MEDIA_BUS_FMT_VYUY8_2X8; break; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21:
mbus_code = ceu_sd->mbus_fmt.mbus_code; break;
/* * ceu_set_fmt() - Apply the supplied format to both sensor and CEU
*/ staticint ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
{ struct ceu_subdev *ceu_sd = ceudev->sd; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd;
u32 mbus_code; int ret;
/* * Set format on sensor sub device: bus format used to produce memory * format is selected at initialization time.
*/ struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
ret = __ceu_try_fmt(ceudev, v4l2_fmt, &mbus_code); if (ret) return ret;
format.format.code = mbus_code;
v4l2_fill_mbus_format_mplane(&format.format, &v4l2_fmt->fmt.pix_mp);
ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format); if (ret) return ret;
/* * ceu_init_mbus_fmt() - Query sensor for supported formats and initialize * CEU media bus format used to produce memory formats. * * Find out if sensor can produce a permutation of 8-bits YUYV bus format. * From a single 8-bits YUYV bus format the CEU can produce several memory * output formats: * - NV[12|21|16|61] through image fetch mode; * - YUYV422 if sensor provides YUYV422 * * TODO: Other YUYV422 permutations through data fetch sync mode and DTARY * TODO: Binary data (eg. JPEG) and raw formats through data fetch sync mode
*/ staticint ceu_init_mbus_fmt(struct ceu_device *ceudev)
{ struct ceu_subdev *ceu_sd = ceudev->sd; struct ceu_mbus_fmt *mbus_fmt = &ceu_sd->mbus_fmt; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; bool yuyv_bus_fmt = false;
/* Find out if sensor can produce any permutation of 8-bits YUYV422. */ while (!yuyv_bus_fmt &&
!v4l2_subdev_call(v4l2_sd, pad, enum_mbus_code,
NULL, &sd_mbus_fmt)) { switch (sd_mbus_fmt.code) { case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8:
yuyv_bus_fmt = true; break; default: /* * Only support 8-bits YUYV bus formats at the moment; * * TODO: add support for binary formats (data sync * fetch mode).
*/ break;
}
sd_mbus_fmt.index++;
}
if (!yuyv_bus_fmt) return -ENXIO;
/* * Save the first encountered YUYV format as "mbus_fmt" and use it * to output all planar YUV422 and YUV420 (NV*) formats to memory as * well as for data synch fetch mode (YUYV - YVYU etc. ).
*/
mbus_fmt->mbus_code = sd_mbus_fmt.code;
mbus_fmt->bps = 8;
/* Annotate the selected bus format components ordering. */ switch (sd_mbus_fmt.code) { case MEDIA_BUS_FMT_YUYV8_2X8:
mbus_fmt->fmt_order = CEU_CAMCR_DTARY_8_YUYV;
mbus_fmt->fmt_order_swap = CEU_CAMCR_DTARY_8_YVYU;
mbus_fmt->swapped = false;
mbus_fmt->bpp = 16; break;
mutex_lock(&ceudev->mlock); /* Causes soft-reset and sensor power on on first open */
ret = pm_runtime_resume_and_get(ceudev->dev);
mutex_unlock(&ceudev->mlock);
/* * Make sure we can generate output image formats and apply * default one.
*/
ret = ceu_init_mbus_fmt(ceudev); if (ret) {
ceudev->sd = ceu_sd_old; return -EINVAL;
}
ret = ceu_set_default_fmt(ceudev); if (ret) {
ceudev->sd = ceu_sd_old; return -EINVAL;
}
/* Now that we're sure we can use the sensor, power off the old one. */
v4l2_subdev_call(ceu_sd_old->v4l2_sd, core, s_power, 0);
v4l2_subdev_call(ceudev->sd->v4l2_sd, core, s_power, 1);
/* * ceu_vdev_release() - release CEU video device memory when last reference * to this driver is closed
*/ staticvoid ceu_vdev_release(struct video_device *vdev)
{ struct ceu_device *ceudev = video_get_drvdata(vdev);
/* * Make sure at least one sensor is primary and use it to initialize * ceu formats.
*/ if (!ceudev->sd) {
ceudev->sd = ceudev->subdevs[0];
ceudev->sd_index = 0;
}
v4l2_sd = ceudev->sd->v4l2_sd;
ret = ceu_init_mbus_fmt(ceudev); if (ret) return ret;
ret = ceu_set_default_fmt(ceudev); if (ret) return ret;
ep = of_graph_get_endpoint_by_regs(of, 0, i); if (!ep) {
dev_err(ceudev->dev, "No subdevice connected on endpoint %u.\n", i);
ret = -ENODEV; goto error_cleanup;
}
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &fw_ep); if (ret) {
dev_err(ceudev->dev, "Unable to parse endpoint #%u: %d.\n", i, ret); goto error_cleanup;
}
/* Setup the ceu subdevice and the async subdevice. */
ceu_sd = v4l2_async_nf_add_fwnode_remote(&ceudev->notifier,
of_fwnode_handle(ep), struct ceu_subdev); if (IS_ERR(ceu_sd)) {
ret = PTR_ERR(ceu_sd); goto error_cleanup;
}
ceu_sd->mbus_flags = fw_ep.bus.parallel.flags;
ceudev->subdevs[i] = ceu_sd;
/* * struct ceu_data - Platform specific CEU data * @irq_mask: CETCR mask with all interrupt sources enabled. The mask differs * between SH4 and RZ platforms.
*/ struct ceu_data {
u32 irq_mask;
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.