mirror of
https://github.com/torvalds/linux.git
synced 2024-11-20 02:51:44 +00:00
[media] media: ti-vpe: Add support for SEQ_TB buffers
The video source can generate the data in the SEQ_TB buffer format. In the case of TI SoC, the IVA_HD can generate the interlaced content in the SEQ_TB buffer format. This is the format where the top and bottom field data can be contained in a single buffer. For example, for NV12, interlaced format, the data in Y buffer will be arranged as Y-top followed by Y-bottom. And likewise for UV plane. Also, queuing one buffer of SEQ_TB is equivalent to queuing two different buffers for top and bottom fields. Driver needs to take care of this when handling source buffer lists. Signed-off-by: Nikhil Devshatwar <nikhil.nd@ti.com> Signed-off-by: Benoit Parrot <bparrot@ti.com> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
parent
5dc07f20b6
commit
823f4208b2
@ -320,9 +320,13 @@ struct vpe_q_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* vpe_q_data flag bits */
|
/* vpe_q_data flag bits */
|
||||||
#define Q_DATA_FRAME_1D (1 << 0)
|
#define Q_DATA_FRAME_1D BIT(0)
|
||||||
#define Q_DATA_MODE_TILED (1 << 1)
|
#define Q_DATA_MODE_TILED BIT(1)
|
||||||
#define Q_DATA_INTERLACED (1 << 2)
|
#define Q_DATA_INTERLACED_ALTERNATE BIT(2)
|
||||||
|
#define Q_DATA_INTERLACED_SEQ_TB BIT(3)
|
||||||
|
|
||||||
|
#define Q_IS_INTERLACED (Q_DATA_INTERLACED_ALTERNATE | \
|
||||||
|
Q_DATA_INTERLACED_SEQ_TB)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
Q_DATA_SRC = 0,
|
Q_DATA_SRC = 0,
|
||||||
@ -638,7 +642,7 @@ static void set_us_coefficients(struct vpe_ctx *ctx)
|
|||||||
|
|
||||||
cp = &us_coeffs[0].anchor_fid0_c0;
|
cp = &us_coeffs[0].anchor_fid0_c0;
|
||||||
|
|
||||||
if (s_q_data->flags & Q_DATA_INTERLACED) /* interlaced */
|
if (s_q_data->flags & Q_IS_INTERLACED) /* interlaced */
|
||||||
cp += sizeof(us_coeffs[0]) / sizeof(*cp);
|
cp += sizeof(us_coeffs[0]) / sizeof(*cp);
|
||||||
|
|
||||||
end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp);
|
end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp);
|
||||||
@ -765,8 +769,7 @@ static void set_dei_regs(struct vpe_ctx *ctx)
|
|||||||
* for both progressive and interlace content in interlace bypass mode.
|
* for both progressive and interlace content in interlace bypass mode.
|
||||||
* It has been recommended not to use progressive bypass mode.
|
* It has been recommended not to use progressive bypass mode.
|
||||||
*/
|
*/
|
||||||
if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) ||
|
if (!(s_q_data->flags & Q_IS_INTERLACED) || !ctx->deinterlacing) {
|
||||||
!(s_q_data->flags & Q_DATA_INTERLACED)) {
|
|
||||||
deinterlace = false;
|
deinterlace = false;
|
||||||
val = VPE_DEI_INTERLACE_BYPASS;
|
val = VPE_DEI_INTERLACE_BYPASS;
|
||||||
}
|
}
|
||||||
@ -834,8 +837,8 @@ static int set_srcdst_params(struct vpe_ctx *ctx)
|
|||||||
ctx->sequence = 0;
|
ctx->sequence = 0;
|
||||||
ctx->field = V4L2_FIELD_TOP;
|
ctx->field = V4L2_FIELD_TOP;
|
||||||
|
|
||||||
if ((s_q_data->flags & Q_DATA_INTERLACED) &&
|
if ((s_q_data->flags & Q_IS_INTERLACED) &&
|
||||||
!(d_q_data->flags & Q_DATA_INTERLACED)) {
|
!(d_q_data->flags & Q_IS_INTERLACED)) {
|
||||||
int bytes_per_line;
|
int bytes_per_line;
|
||||||
const struct vpdma_data_format *mv =
|
const struct vpdma_data_format *mv =
|
||||||
&vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
|
&vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
|
||||||
@ -1066,6 +1069,28 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port)
|
|||||||
port);
|
port);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB) {
|
||||||
|
/*
|
||||||
|
* Use top or bottom field from same vb alternately
|
||||||
|
* f,f-1,f-2 = TBT when seq is even
|
||||||
|
* f,f-1,f-2 = BTB when seq is odd
|
||||||
|
*/
|
||||||
|
field = (p_data->vb_index + (ctx->sequence % 2)) % 2;
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
/*
|
||||||
|
* bottom field of a SEQ_TB buffer
|
||||||
|
* Skip the top field data by
|
||||||
|
*/
|
||||||
|
int height = q_data->height / 2;
|
||||||
|
int bpp = fmt->fourcc == V4L2_PIX_FMT_NV12 ?
|
||||||
|
1 : (vpdma_fmt->depth >> 3);
|
||||||
|
if (plane)
|
||||||
|
height /= 2;
|
||||||
|
dma_addr += q_data->width * height * bpp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (q_data->flags & Q_DATA_FRAME_1D)
|
if (q_data->flags & Q_DATA_FRAME_1D)
|
||||||
@ -1114,9 +1139,22 @@ static void device_run(void *priv)
|
|||||||
struct vpe_ctx *ctx = priv;
|
struct vpe_ctx *ctx = priv;
|
||||||
struct sc_data *sc = ctx->dev->sc;
|
struct sc_data *sc = ctx->dev->sc;
|
||||||
struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
|
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];
|
||||||
|
|
||||||
|
if (ctx->deinterlacing && s_q_data->flags & Q_DATA_INTERLACED_SEQ_TB &&
|
||||||
|
ctx->sequence % 2 == 0) {
|
||||||
|
/* When using SEQ_TB buffers, When using it first time,
|
||||||
|
* No need to remove the buffer as the next field is present
|
||||||
|
* in the same buffer. (so that job_ready won't fail)
|
||||||
|
* It will be removed when using bottom field
|
||||||
|
*/
|
||||||
|
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);
|
ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
|
||||||
WARN_ON(ctx->src_vbs[0] == NULL);
|
WARN_ON(ctx->src_vbs[0] == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
||||||
WARN_ON(ctx->dst_vb == NULL);
|
WARN_ON(ctx->dst_vb == NULL);
|
||||||
|
|
||||||
@ -1320,7 +1358,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
|
|||||||
d_vb->sequence = ctx->sequence;
|
d_vb->sequence = ctx->sequence;
|
||||||
|
|
||||||
d_q_data = &ctx->q_data[Q_DATA_DST];
|
d_q_data = &ctx->q_data[Q_DATA_DST];
|
||||||
if (d_q_data->flags & Q_DATA_INTERLACED) {
|
if (d_q_data->flags & Q_IS_INTERLACED) {
|
||||||
d_vb->field = ctx->field;
|
d_vb->field = ctx->field;
|
||||||
if (ctx->field == V4L2_FIELD_BOTTOM) {
|
if (ctx->field == V4L2_FIELD_BOTTOM) {
|
||||||
ctx->sequence++;
|
ctx->sequence++;
|
||||||
@ -1334,12 +1372,28 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
|
|||||||
ctx->sequence++;
|
ctx->sequence++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->deinterlacing)
|
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];
|
s_vb = ctx->src_vbs[2];
|
||||||
|
else
|
||||||
|
s_vb = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->lock, flags);
|
spin_lock_irqsave(&dev->lock, flags);
|
||||||
|
|
||||||
|
if (s_vb)
|
||||||
v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
|
v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
|
||||||
|
|
||||||
v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
|
v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&dev->lock, flags);
|
spin_unlock_irqrestore(&dev->lock, flags);
|
||||||
|
|
||||||
if (ctx->deinterlacing) {
|
if (ctx->deinterlacing) {
|
||||||
@ -1455,7 +1509,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
|
|||||||
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
|
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
|
||||||
struct v4l2_plane_pix_format *plane_fmt;
|
struct v4l2_plane_pix_format *plane_fmt;
|
||||||
unsigned int w_align;
|
unsigned int w_align;
|
||||||
int i, depth, depth_bytes;
|
int i, depth, depth_bytes, height;
|
||||||
|
|
||||||
if (!fmt || !(fmt->types & type)) {
|
if (!fmt || !(fmt->types & type)) {
|
||||||
vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
|
vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
|
||||||
@ -1463,7 +1517,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE)
|
if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE
|
||||||
|
&& pix->field != V4L2_FIELD_SEQ_TB)
|
||||||
pix->field = V4L2_FIELD_NONE;
|
pix->field = V4L2_FIELD_NONE;
|
||||||
|
|
||||||
depth = fmt->vpdma_fmt[VPE_LUMA]->depth;
|
depth = fmt->vpdma_fmt[VPE_LUMA]->depth;
|
||||||
@ -1497,6 +1552,15 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
|
|||||||
pix->num_planes = fmt->coplanar ? 2 : 1;
|
pix->num_planes = fmt->coplanar ? 2 : 1;
|
||||||
pix->pixelformat = fmt->fourcc;
|
pix->pixelformat = fmt->fourcc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the actual image parameters, we need to consider the field
|
||||||
|
* height of the image for SEQ_TB buffers.
|
||||||
|
*/
|
||||||
|
if (pix->field == V4L2_FIELD_SEQ_TB)
|
||||||
|
height = pix->height / 2;
|
||||||
|
else
|
||||||
|
height = pix->height;
|
||||||
|
|
||||||
if (!pix->colorspace) {
|
if (!pix->colorspace) {
|
||||||
if (fmt->fourcc == V4L2_PIX_FMT_RGB24 ||
|
if (fmt->fourcc == V4L2_PIX_FMT_RGB24 ||
|
||||||
fmt->fourcc == V4L2_PIX_FMT_BGR24 ||
|
fmt->fourcc == V4L2_PIX_FMT_BGR24 ||
|
||||||
@ -1504,7 +1568,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
|
|||||||
fmt->fourcc == V4L2_PIX_FMT_BGR32) {
|
fmt->fourcc == V4L2_PIX_FMT_BGR32) {
|
||||||
pix->colorspace = V4L2_COLORSPACE_SRGB;
|
pix->colorspace = V4L2_COLORSPACE_SRGB;
|
||||||
} else {
|
} else {
|
||||||
if (pix->height > 1280) /* HD */
|
if (height > 1280) /* HD */
|
||||||
pix->colorspace = V4L2_COLORSPACE_REC709;
|
pix->colorspace = V4L2_COLORSPACE_REC709;
|
||||||
else /* SD */
|
else /* SD */
|
||||||
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
||||||
@ -1581,9 +1645,15 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
|
|||||||
q_data->c_rect.height = q_data->height;
|
q_data->c_rect.height = q_data->height;
|
||||||
|
|
||||||
if (q_data->field == V4L2_FIELD_ALTERNATE)
|
if (q_data->field == V4L2_FIELD_ALTERNATE)
|
||||||
q_data->flags |= Q_DATA_INTERLACED;
|
q_data->flags |= Q_DATA_INTERLACED_ALTERNATE;
|
||||||
|
else if (q_data->field == V4L2_FIELD_SEQ_TB)
|
||||||
|
q_data->flags |= Q_DATA_INTERLACED_SEQ_TB;
|
||||||
else
|
else
|
||||||
q_data->flags &= ~Q_DATA_INTERLACED;
|
q_data->flags &= ~Q_IS_INTERLACED;
|
||||||
|
|
||||||
|
/* the crop height is halved for the case of SEQ_TB buffers */
|
||||||
|
if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
|
||||||
|
q_data->c_rect.height /= 2;
|
||||||
|
|
||||||
vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
|
vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
|
||||||
f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
|
f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
|
||||||
@ -1619,6 +1689,7 @@ static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
|
|||||||
static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
|
static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
|
||||||
{
|
{
|
||||||
struct vpe_q_data *q_data;
|
struct vpe_q_data *q_data;
|
||||||
|
int height;
|
||||||
|
|
||||||
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
|
if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
|
||||||
(s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
|
(s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
|
||||||
@ -1653,13 +1724,22 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For SEQ_TB buffers, crop height should be less than the height of
|
||||||
|
* the field height, not the buffer height
|
||||||
|
*/
|
||||||
|
if (q_data->flags & Q_DATA_INTERLACED_SEQ_TB)
|
||||||
|
height = q_data->height / 2;
|
||||||
|
else
|
||||||
|
height = q_data->height;
|
||||||
|
|
||||||
if (s->r.top < 0 || s->r.left < 0) {
|
if (s->r.top < 0 || s->r.left < 0) {
|
||||||
vpe_err(ctx->dev, "negative values for top and left\n");
|
vpe_err(ctx->dev, "negative values for top and left\n");
|
||||||
s->r.top = s->r.left = 0;
|
s->r.top = s->r.left = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1,
|
v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1,
|
||||||
&s->r.height, MIN_H, q_data->height, H_ALIGN, S_ALIGN);
|
&s->r.height, MIN_H, height, H_ALIGN, S_ALIGN);
|
||||||
|
|
||||||
/* adjust left/top if cropping rectangle is out of bounds */
|
/* adjust left/top if cropping rectangle is out of bounds */
|
||||||
if (s->r.left + s->r.width > q_data->width)
|
if (s->r.left + s->r.width > q_data->width)
|
||||||
@ -1855,11 +1935,12 @@ static int vpe_buf_prepare(struct vb2_buffer *vb)
|
|||||||
num_planes = q_data->fmt->coplanar ? 2 : 1;
|
num_planes = q_data->fmt->coplanar ? 2 : 1;
|
||||||
|
|
||||||
if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
|
||||||
if (!(q_data->flags & Q_DATA_INTERLACED)) {
|
if (!(q_data->flags & Q_IS_INTERLACED)) {
|
||||||
vbuf->field = V4L2_FIELD_NONE;
|
vbuf->field = V4L2_FIELD_NONE;
|
||||||
} else {
|
} else {
|
||||||
if (vbuf->field != V4L2_FIELD_TOP &&
|
if (vbuf->field != V4L2_FIELD_TOP &&
|
||||||
vbuf->field != V4L2_FIELD_BOTTOM)
|
vbuf->field != V4L2_FIELD_BOTTOM &&
|
||||||
|
vbuf->field != V4L2_FIELD_SEQ_TB)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user