/* minimum and maximum frame sizes */ #define MIN_W 32 #define MIN_H 32 #define MAX_W 2048 #define MAX_H 2048
/* required alignments */ #define S_ALIGN 0 /* multiple of 1 */ #define H_ALIGN 1 /* multiple of 2 */
/* flags that indicate a format can be used for capture/output */ #define VPE_FMT_TYPE_CAPTURE (1 << 0) #define VPE_FMT_TYPE_OUTPUT (1 << 1)
/* used as plane indices */ #define VPE_MAX_PLANES 2 #define VPE_LUMA 0 #define VPE_CHROMA 1
/* per m2m context info */ #define VPE_MAX_SRC_BUFS 3 /* need 3 src fields to de-interlace */
#define VPE_DEF_BUFS_PER_JOB 1 /* default one buffer per batch job */
/* * each VPE context can need up to 3 config descriptors, 7 input descriptors, * 3 output descriptors, and 10 control descriptors
*/ #define VPE_DESC_LIST_SIZE (10 * VPDMA_DTD_DESC_SIZE + \
13 * VPDMA_CFD_CTD_DESC_SIZE)
/* * the following registers are for configuring some of the parameters of the * motion and edge detection blocks inside DEI, these generally remain the same, * these could be passed later via userspace if some one needs to tweak these.
*/ struct vpe_dei_regs { unsignedlong mdt_spacial_freq_thr_reg; /* VPE_DEI_REG2 */ unsignedlong edi_config_reg; /* VPE_DEI_REG3 */ unsignedlong edi_lut_reg0; /* VPE_DEI_REG4 */ unsignedlong edi_lut_reg1; /* VPE_DEI_REG5 */ unsignedlong edi_lut_reg2; /* VPE_DEI_REG6 */ unsignedlong edi_lut_reg3; /* VPE_DEI_REG7 */
};
/* driver info for each of the supported video formats */ struct vpe_fmt {
u32 fourcc; /* standard format identifier */
u8 types; /* CAPTURE and/or OUTPUT */
u8 coplanar; /* set for unpacked Luma and Chroma */ /* vpdma format info for each plane */ struct vpdma_data_format const *vpdma_fmt[VPE_MAX_PLANES];
};
/* * per-queue, driver-specific private data. * there is one source queue and one destination queue for each m2m context.
*/ struct vpe_q_data { /* current v4l2 format info */ struct v4l2_format format; unsignedint flags; struct v4l2_rect c_rect; /* crop/compose rectangle */ struct vpe_fmt *fmt; /* format info */
};
/* * there is one vpe_dev structure in the driver, it is shared by * all instances.
*/ struct vpe_dev { struct v4l2_device v4l2_dev; struct video_device vfd; struct v4l2_m2m_dev *m2m_dev;
/* * Allocate or re-allocate the motion vector DMA buffers * There are two buffers, one for input and one for output. * However, the roles are reversed after each field is processed. * In other words, after each field is processed, the previous * output (dst) MV buffer becomes the new input (src) MV buffer.
*/ staticint realloc_mv_buffers(struct vpe_ctx *ctx, size_t size)
{ struct device *dev = ctx->dev->v4l2_dev.dev;
if (ctx->mv_buf_size == size) return 0;
if (ctx->mv_buf[0])
dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0],
ctx->mv_buf_dma[0]);
if (ctx->mv_buf[1])
dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1],
ctx->mv_buf_dma[1]);
if (size == 0) return 0;
ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0],
GFP_KERNEL); if (!ctx->mv_buf[0]) {
vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); return -ENOMEM;
}
ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1],
GFP_KERNEL); if (!ctx->mv_buf[1]) {
vpe_err(ctx->dev, "failed to allocate motion vector buffer\n");
dma_free_coherent(dev, size, ctx->mv_buf[0],
ctx->mv_buf_dma[0]);
/* * While de-interlacing, we keep the two most recent input buffers * around. This function frees those two buffers when we have * finished processing the current stream.
*/ staticvoid free_vbs(struct vpe_ctx *ctx)
{ struct vpe_dev *dev = ctx->dev; unsignedlong flags;
/* frame start for MV in client */
vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
VPE_CHAN_MV_IN);
}
/* * Set the shadow registers that are modified when the source * format changes.
*/ staticvoid set_src_registers(struct vpe_ctx *ctx)
{
set_us_coefficients(ctx);
}
/* * Set the shadow registers that are modified when the destination * format changes.
*/ staticvoid set_dst_registers(struct vpe_ctx *ctx)
{ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; conststruct v4l2_format_info *finfo;
u32 val = 0;
finfo = v4l2_format_info(fmt->fourcc); if (v4l2_is_format_rgb(finfo)) {
val |= VPE_RGB_OUT_SELECT;
vpdma_set_bg_color(ctx->dev->vpdma,
(struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff);
} elseif (fmt->fourcc == V4L2_PIX_FMT_NV16)
val |= VPE_COLOR_SEPARATE_422;
/* * the source of CHR_DS and CSC is always the scaler, irrespective of * whether it's used or not
*/
val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER;
if (fmt->fourcc != V4L2_PIX_FMT_NV12 &&
fmt->fourcc != V4L2_PIX_FMT_NV21)
val |= VPE_DS_BYPASS;
/* * according to TRM, we should set DEI in progressive bypass mode when * the input content is progressive, however, DEI is bypassed correctly * for both progressive and interlace content in interlace bypass mode. * It has been recommended not to use progressive bypass mode.
*/ if (!(s_q_data->flags & Q_IS_INTERLACED) || !ctx->deinterlacing) {
deinterlace = false;
val = VPE_DEI_INTERLACE_BYPASS;
}
if ((s_q_data->flags & Q_IS_INTERLACED) &&
!(d_q_data->flags & Q_IS_INTERLACED)) { int bytes_per_line; conststruct vpdma_data_format *mv =
&vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
/* * we make sure that the source image has a 16 byte aligned * stride, we need to do the same for the motion vector buffer * by aligning it's stride to the next 16 byte boundary. this * extra space will not be used by the de-interlacer, but will * ensure that vpdma operates correctly
*/
bytes_per_line = ALIGN((spix->width * mv->depth) >> 3,
VPDMA_STRIDE_ALIGN);
mv_buf_size = bytes_per_line * spix->height;
/* * job_ready() - check whether an instance is ready to be scheduled to run
*/ staticint job_ready(void *priv)
{ struct vpe_ctx *ctx = priv;
/* * This check is needed as this might be called directly from driver * When called by m2m framework, this will always satisfy, but when * called from vpe_irq, this might fail. (src stream with zero buffers)
*/ if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) <= 0 ||
v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) <= 0) return 0;
pix = &q_data->format.fmt.pix_mp;
vpdma_fmt = fmt->vpdma_fmt[plane]; /* * If we are using a single plane buffer and * we need to set a separate vpdma chroma channel.
*/ if (pix->num_planes == 1 && plane) {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); /* Compute required offset */
offset = pix->plane_fmt[0].bytesperline * pix->height;
} else {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); /* Use address as is, no offset */
offset = 0;
} if (!dma_addr) {
vpe_err(ctx->dev, "acquiring output buffer(%d) dma_addr failed\n",
port); return;
} /* Apply the offset */
dma_addr += offset;
stride = pix->plane_fmt[VPE_LUMA].bytesperline;
}
if (q_data->flags & Q_DATA_FRAME_1D)
flags |= VPDMA_DATA_FRAME_1D; if (q_data->flags & Q_DATA_MODE_TILED)
flags |= VPDMA_DATA_MODE_TILED;
vpdma_fmt = fmt->vpdma_fmt[plane]; /* * If we are using a single plane buffer and * we need to set a separate vpdma chroma channel.
*/ if (pix->num_planes == 1 && plane) {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); /* Compute required offset */
offset = pix->plane_fmt[0].bytesperline * pix->height;
} else {
dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); /* Use address as is, no offset */
offset = 0;
} if (!dma_addr) {
vpe_err(ctx->dev, "acquiring output buffer(%d) dma_addr failed\n",
port); return;
} /* Apply the offset */
dma_addr += offset;
stride = pix->plane_fmt[VPE_LUMA].bytesperline;
/* * field used in VPDMA desc = 0 (top) / 1 (bottom) * Use top or bottom field from same vb alternately * For each de-interlacing operation, f,f-1,f-2 should be one * of TBT or BTB
*/ if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB ||
q_data->flags & Q_DATA_INTERLACED_SEQ_BT) { /* Select initial value based on format */ if (q_data->flags & Q_DATA_INTERLACED_SEQ_BT)
field = 1; else
field = 0;
/* Toggle for each vb_index and each operation */
field = (field + p_data->vb_index + ctx->sequence) % 2;
if (field) { int height = pix->height / 2; int bpp;
/* device_run() - prepares and starts the device * * This function is only called when both the source and destination * buffers are in place.
*/ staticvoid device_run(void *priv)
{ struct vpe_ctx *ctx = priv; struct sc_data *sc = ctx->dev->sc; struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; conststruct v4l2_format_info *d_finfo;
if (ctx->deinterlacing && s_q_data->flags & Q_IS_SEQ_XX &&
ctx->sequence % 2 == 0) { /* When using SEQ_XX type buffers, each buffer has two fields * each buffer has two fields (top & bottom) * Removing one buffer is actually getting two fields * Alternate between two operations:- * Even : consume one field but DO NOT REMOVE from queue * Odd : consume other field and REMOVE from queue
*/
ctx->src_vbs[0] = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
WARN_ON(ctx->src_vbs[0] == NULL);
} else {
ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
WARN_ON(ctx->src_vbs[0] == NULL);
}
/* * we have output the first 2 frames through line average, we * now switch to EDI de-interlacer
*/ if (ctx->sequence == 2)
config_edi_input_mode(ctx, 0x3); /* EDI (Y + UV) */
}
if (ctx->deinterlacing)
add_in_dtd(ctx, VPE_PORT_MV_IN);
/* sync on channel control descriptors for input ports */
vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN);
vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN);
if (ctx->deinterlacing) {
vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
VPE_CHAN_LUMA2_IN);
vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
VPE_CHAN_CHROMA2_IN);
/* sync on channel control descriptors for output ports */ if (v4l2_is_format_rgb(d_finfo)) {
vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
VPE_CHAN_RGB_OUT);
} else {
vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
VPE_CHAN_LUMA_OUT); if (d_q_data->fmt->coplanar)
vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
VPE_CHAN_CHROMA_OUT);
}
if (ctx->deinterlacing)
vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT);
if (ctx->deinterlacing) { /* * Allow source buffer to be dequeued only if it won't be used * in the next iteration. All vbs are initialized to first * buffer and we are shifting buffers every iteration, for the * first two iterations, no buffer will be dequeued. * This ensures that driver will keep (n-2)th (n-1)th and (n)th * field when deinterlacing is enabled
*/ if (ctx->src_vbs[2] != ctx->src_vbs[1])
s_vb = ctx->src_vbs[2]; else
s_vb = NULL;
}
spin_lock_irqsave(&dev->lock, flags);
if (s_vb)
v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
spin_unlock_irqrestore(&dev->lock, flags);
if (ctx->deinterlacing) {
ctx->src_vbs[2] = ctx->src_vbs[1];
ctx->src_vbs[1] = ctx->src_vbs[0];
}
/* * Since the vb2_buf_done has already been called fir therse * buffer we can now NULL them out so that we won't try * to clean out stray pointer later on.
*/
ctx->src_vbs[0] = NULL;
ctx->dst_vb = NULL;
staticint __enum_fmt(struct v4l2_fmtdesc *f, u32 type)
{ int i, index; struct vpe_fmt *fmt = NULL;
index = 0; for (i = 0; i < ARRAY_SIZE(vpe_formats); ++i) { if (vpe_formats[i].types & type) { if (index == f->index) {
fmt = &vpe_formats[i]; break;
}
index++;
}
}
/* * the line stride should 16 byte aligned for VPDMA to work, based on * the bytes per pixel, figure out how much the width should be aligned * to make sure line stride is 16 byte aligned
*/
depth_bytes = depth >> 3;
if (depth_bytes == 3) { /* * if bpp is 3(as in some RGB formats), the pixel width doesn't * really help in ensuring line stride is 16 byte aligned
*/
w_align = 4;
} else { /* * for the remainder bpp(4, 2 and 1), the pixel width alignment * can ensure a line stride alignment of 16 bytes. For example, * if bpp is 2, then the line stride can be 16 byte aligned if * the width is 8 byte aligned
*/
/* * HACK: using order_base_2() here causes lots of asm output * errors with smatch, on i386: * ./arch/x86/include/asm/bitops.h:457:22: * warning: asm output is not an lvalue * Perhaps some gcc optimization is doing the wrong thing * there. * Let's get rid of them by doing the calculus on two steps
*/
w_align = roundup_pow_of_two(VPDMA_DESC_ALIGN / depth_bytes);
w_align = ilog2(w_align);
}
/* * For the actual image parameters, we need to consider the field * height of the image for SEQ_XX buffers.
*/ if (pix->field == V4L2_FIELD_SEQ_TB || pix->field == V4L2_FIELD_SEQ_BT)
height = pix->height / 2; else
height = pix->height;
if (!pix->colorspace) { if (v4l2_is_format_rgb(finfo)) {
pix->colorspace = V4L2_COLORSPACE_SRGB;
} else { if (height > 1280) /* HD */
pix->colorspace = V4L2_COLORSPACE_REC709; else/* SD */
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
}
}
for (i = 0; i < pix->num_planes; i++) {
plane_fmt = &pix->plane_fmt[i];
depth = fmt->vpdma_fmt[i]->depth;
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
(s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) return -EINVAL;
q_data = get_q_data(ctx, s->type); if (!q_data) return -EINVAL;
pix = &q_data->format.fmt.pix_mp;
switch (s->target) { case V4L2_SEL_TGT_COMPOSE: /* * COMPOSE target is only valid for capture buffer type, return * error for output buffer type
*/ if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; break; case V4L2_SEL_TGT_CROP: /* * CROP target is only valid for output buffer type, return * error for capture buffer type
*/ if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; break; /* * bound and default crop/compose targets are invalid targets to * try/set
*/ default: return -EINVAL;
}
/* * For SEQ_XX buffers, crop height should be less than the height of * the field height, not the buffer height
*/ if (q_data->flags & Q_IS_SEQ_XX)
height = pix->height / 2; else
height = pix->height;
if (s->r.top < 0 || s->r.left < 0) {
vpe_err(ctx->dev, "negative values for top and left\n");
s->r.top = s->r.left = 0;
}
/* * defines number of buffers/frames a context can process with VPE before * switching to a different context. default value is 1 buffer per context
*/ #define V4L2_CID_VPE_BUFS_PER_JOB (V4L2_CID_USER_TI_VPE_BASE + 0)
for (i = 0; i < pix->num_planes; i++) { if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) {
vpe_err(ctx->dev, "data will not fit into plane (%lu < %lu)\n",
vb2_plane_size(vb, i),
(long)pix->plane_fmt[i].sizeimage); return -EINVAL;
}
}
for (i = 0; i < pix->num_planes; i++)
vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage);
for (;;) { if (V4L2_TYPE_IS_OUTPUT(q->type))
vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); else
vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); if (!vb) break;
spin_lock_irqsave(&ctx->dev->lock, flags);
v4l2_m2m_buf_done(vb, state);
spin_unlock_irqrestore(&ctx->dev->lock, flags);
}
/* * Cleanup the in-transit vb2 buffers that have been * removed from their respective queue already but for * which procecessing has not been completed yet.
*/ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
spin_lock_irqsave(&ctx->dev->lock, flags);
if (ctx->src_vbs[2])
v4l2_m2m_buf_done(ctx->src_vbs[2], state);
if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2]))
v4l2_m2m_buf_done(ctx->src_vbs[1], state);
/* Check any of the size exceed maximum scaling sizes */ if (check_srcdst_sizes(ctx)) {
vpe_err(ctx->dev, "Conversion setup failed, check source and destination parameters\n"
);
vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_QUEUED); return -EINVAL;
}
if (ctx->deinterlacing)
config_edi_input_mode(ctx, 0x0);
if (IS_ERR(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx); goto exit_fh;
}
v4l2_fh_add(&ctx->fh);
/* * for now, just report the creation of the first instance, we can later * optimize the driver to enable or disable clocks when the first * instance is created or the last instance released
*/ if (atomic_inc_return(&dev->num_instances) == 1)
vpe_dbg(dev, "first instance created\n");
/* * for now, just report the release of the last instance, we can later * optimize the driver to enable or disable clocks when the first * instance is created or the last instance released
*/ if (atomic_dec_return(&dev->num_instances) == 0)
vpe_dbg(dev, "last instance released\n");
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.