diff options
Diffstat (limited to 'drivers/media/platform/ti-vpe/vpe.c')
-rw-r--r-- | drivers/media/platform/ti-vpe/vpe.c | 471 |
1 files changed, 388 insertions, 83 deletions
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 0189f7f7cb03..f0156b7759e9 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -44,6 +44,7 @@ #include <media/videobuf2-dma-contig.h> #include "vpdma.h" +#include "vpdma_priv.h" #include "vpe_regs.h" #include "sc.h" #include "csc.h" @@ -53,8 +54,8 @@ /* minimum and maximum frame sizes */ #define MIN_W 32 #define MIN_H 32 -#define MAX_W 1920 -#define MAX_H 1080 +#define MAX_W 2048 +#define MAX_H 1184 /* required alignments */ #define S_ALIGN 0 /* multiple of 1 */ @@ -141,7 +142,7 @@ struct vpe_dei_regs { */ static const struct vpe_dei_regs dei_regs = { .mdt_spacial_freq_thr_reg = 0x020C0804u, - .edi_config_reg = 0x0118100Fu, + .edi_config_reg = 0x0118100Cu, .edi_lut_reg0 = 0x08040200u, .edi_lut_reg1 = 0x1010100Cu, .edi_lut_reg2 = 0x10101010u, @@ -236,7 +237,7 @@ struct vpe_fmt { static struct vpe_fmt vpe_formats[] = { { - .name = "YUV 422 co-planar", + .name = "NV16 YUV 422 co-planar", .fourcc = V4L2_PIX_FMT_NV16, .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, .coplanar = 1, @@ -245,7 +246,7 @@ static struct vpe_fmt vpe_formats[] = { }, }, { - .name = "YUV 420 co-planar", + .name = "NV12 YUV 420 co-planar", .fourcc = V4L2_PIX_FMT_NV12, .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, .coplanar = 1, @@ -258,7 +259,7 @@ static struct vpe_fmt vpe_formats[] = { .fourcc = V4L2_PIX_FMT_YUYV, .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, .coplanar = 0, - .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422], + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422], }, }, { @@ -266,7 +267,7 @@ static struct vpe_fmt vpe_formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, .coplanar = 0, - .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CY422], + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422], }, }, { @@ -301,6 +302,22 @@ static struct vpe_fmt vpe_formats[] = { .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], }, }, + { + .name = "RGB565", + .fourcc = V4L2_PIX_FMT_RGB565, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB565], + }, + }, + { + .name = "RGB5551", + .fourcc = V4L2_PIX_FMT_RGB555, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGBA16_5551], + }, + }, }; /* @@ -310,6 +327,7 @@ static struct vpe_fmt vpe_formats[] = { struct vpe_q_data { unsigned int width; /* frame width */ unsigned int height; /* frame height */ + unsigned int nplanes; /* Current number of planes */ unsigned int bytesperline[VPE_MAX_PLANES]; /* bytes per line in memory */ enum v4l2_colorspace colorspace; enum v4l2_field field; /* supported field value */ @@ -320,9 +338,13 @@ struct vpe_q_data { }; /* vpe_q_data flag bits */ -#define Q_DATA_FRAME_1D (1 << 0) -#define Q_DATA_MODE_TILED (1 << 1) -#define Q_DATA_INTERLACED (1 << 2) +#define Q_DATA_FRAME_1D BIT(0) +#define Q_DATA_MODE_TILED BIT(1) +#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 { Q_DATA_SRC = 0, @@ -362,6 +384,7 @@ struct vpe_dev { void __iomem *base; struct resource *res; + struct vpdma_data vpdma_data; struct vpdma_data *vpdma; /* vpdma data handle */ struct sc_data *sc; /* scaler data handle */ struct csc_data *csc; /* csc data handle */ @@ -416,7 +439,7 @@ static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx, case V4L2_BUF_TYPE_VIDEO_CAPTURE: return &ctx->q_data[Q_DATA_DST]; default: - BUG(); + return NULL; } return NULL; } @@ -584,7 +607,10 @@ static void free_vbs(struct vpe_ctx *ctx) spin_lock_irqsave(&dev->lock, flags); if (ctx->src_vbs[2]) { v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE); - v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE); + if (ctx->src_vbs[1] && (ctx->src_vbs[1] != ctx->src_vbs[2])) + v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE); + ctx->src_vbs[2] = NULL; + ctx->src_vbs[1] = NULL; } spin_unlock_irqrestore(&dev->lock, flags); } @@ -638,7 +664,7 @@ static void set_us_coefficients(struct vpe_ctx *ctx) 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); end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp); @@ -655,14 +681,13 @@ static void set_us_coefficients(struct vpe_ctx *ctx) /* * Set the upsampler config mode and the VPDMA line mode in the shadow MMRs. */ -static void set_cfg_and_line_modes(struct vpe_ctx *ctx) +static void set_cfg_modes(struct vpe_ctx *ctx) { struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; u32 *us1_reg0 = &mmr_adb->us1_regs[0]; u32 *us2_reg0 = &mmr_adb->us2_regs[0]; u32 *us3_reg0 = &mmr_adb->us3_regs[0]; - int line_mode = 1; int cfg_mode = 1; /* @@ -670,15 +695,24 @@ static void set_cfg_and_line_modes(struct vpe_ctx *ctx) * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing. */ - if (fmt->fourcc == V4L2_PIX_FMT_NV12) { + if (fmt->fourcc == V4L2_PIX_FMT_NV12) cfg_mode = 0; - line_mode = 0; /* double lines to line buffer */ - } write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); + ctx->load_mmrs = true; +} + +static void set_line_modes(struct vpe_ctx *ctx) +{ + struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; + int line_mode = 1; + + if (fmt->fourcc == V4L2_PIX_FMT_NV12) + line_mode = 0; /* double lines to line buffer */ + /* regs for now */ vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN); vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN); @@ -703,8 +737,6 @@ static void set_cfg_and_line_modes(struct vpe_ctx *ctx) /* frame start for MV in client */ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, VPE_CHAN_MV_IN); - - ctx->load_mmrs = true; } /* @@ -727,9 +759,11 @@ static void set_dst_registers(struct vpe_ctx *ctx) struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; u32 val = 0; - if (clrspc == V4L2_COLORSPACE_SRGB) + if (clrspc == V4L2_COLORSPACE_SRGB) { val |= VPE_RGB_OUT_SELECT; - else if (fmt->fourcc == V4L2_PIX_FMT_NV16) + vpdma_set_bg_color(ctx->dev->vpdma, + (struct vpdma_data_format *)fmt->vpdma_fmt[0], 0xff); + } else if (fmt->fourcc == V4L2_PIX_FMT_NV16) val |= VPE_COLOR_SEPARATE_422; /* @@ -765,8 +799,7 @@ static void set_dei_regs(struct vpe_ctx *ctx) * for both progressive and interlace content in interlace bypass mode. * It has been recommended not to use progressive bypass mode. */ - if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) || - !(s_q_data->flags & Q_DATA_INTERLACED)) { + if (!(s_q_data->flags & Q_IS_INTERLACED) || !ctx->deinterlacing) { deinterlace = false; val = VPE_DEI_INTERLACE_BYPASS; } @@ -798,6 +831,23 @@ static void set_dei_shadow_registers(struct vpe_ctx *ctx) ctx->load_mmrs = true; } +static void config_edi_input_mode(struct vpe_ctx *ctx, int mode) +{ + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + u32 *edi_config_reg = &mmr_adb->dei_regs[3]; + + if (mode & 0x2) + write_field(edi_config_reg, 1, 1, 2); /* EDI_ENABLE_3D */ + + if (mode & 0x3) + write_field(edi_config_reg, 1, 1, 3); /* EDI_CHROMA_3D */ + + write_field(edi_config_reg, mode, VPE_EDI_INP_MODE_MASK, + VPE_EDI_INP_MODE_SHIFT); + + ctx->load_mmrs = true; +} + /* * Set the shadow registers whose values are modified when either the * source or destination format is changed. @@ -817,8 +867,8 @@ static int set_srcdst_params(struct vpe_ctx *ctx) ctx->sequence = 0; ctx->field = V4L2_FIELD_TOP; - if ((s_q_data->flags & Q_DATA_INTERLACED) && - !(d_q_data->flags & Q_DATA_INTERLACED)) { + if ((s_q_data->flags & Q_IS_INTERLACED) && + !(d_q_data->flags & Q_IS_INTERLACED)) { int bytes_per_line; const struct vpdma_data_format *mv = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; @@ -842,12 +892,13 @@ static int set_srcdst_params(struct vpe_ctx *ctx) } free_vbs(ctx); + ctx->src_vbs[2] = ctx->src_vbs[1] = ctx->src_vbs[0] = NULL; ret = realloc_mv_buffers(ctx, mv_buf_size); if (ret) return ret; - set_cfg_and_line_modes(ctx); + set_cfg_modes(ctx); set_dei_regs(ctx); csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], @@ -881,15 +932,14 @@ static struct vpe_ctx *file2ctx(struct file *file) static int job_ready(void *priv) { struct vpe_ctx *ctx = priv; - int needed = ctx->bufs_per_job; - - if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) - needed += 2; /* need additional two most recent fields */ - - if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < needed) - return 0; - if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < needed) + /* + * 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; return 1; @@ -993,22 +1043,38 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) int mv_buf_selector = !ctx->src_mv_buf_selector; dma_addr_t dma_addr; u32 flags = 0; + u32 offset = 0; if (port == VPE_PORT_MV_OUT) { vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; dma_addr = ctx->mv_buf_dma[mv_buf_selector]; + q_data = &ctx->q_data[Q_DATA_SRC]; } else { /* to incorporate interleaved formats */ int plane = fmt->coplanar ? p_data->vb_part : 0; vpdma_fmt = fmt->vpdma_fmt[plane]; - dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); + /* + * If we are using a single plane buffer and + * we need to set a separate vpdma chroma channel. + */ + if (q_data->nplanes == 1 && plane) { + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Compute required offset */ + offset = q_data->bytesperline[0] * q_data->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; } if (q_data->flags & Q_DATA_FRAME_1D) @@ -1016,8 +1082,12 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) if (q_data->flags & Q_DATA_MODE_TILED) flags |= VPDMA_DATA_MODE_TILED; + vpdma_set_max_size(ctx->dev->vpdma, VPDMA_MAX_SIZE1, + MAX_W, MAX_H); + vpdma_add_out_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect, - vpdma_fmt, dma_addr, p_data->channel, flags); + vpdma_fmt, dma_addr, MAX_OUT_WIDTH_REG1, + MAX_OUT_HEIGHT_REG1, p_data->channel, flags); } static void add_in_dtd(struct vpe_ctx *ctx, int port) @@ -1033,6 +1103,7 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) int frame_width, frame_height; dma_addr_t dma_addr; u32 flags = 0; + u32 offset = 0; if (port == VPE_PORT_MV_IN) { vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; @@ -1042,14 +1113,49 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) int plane = fmt->coplanar ? p_data->vb_part : 0; vpdma_fmt = fmt->vpdma_fmt[plane]; - - dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); + /* + * If we are using a single plane buffer and + * we need to set a separate vpdma chroma channel. + */ + if (q_data->nplanes == 1 && plane) { + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Compute required offset */ + offset = q_data->bytesperline[0] * q_data->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 input buffer(%d) dma_addr failed\n", + "acquiring output buffer(%d) dma_addr failed\n", port); return; } + /* Apply the offset */ + dma_addr += offset; + + 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) @@ -1077,7 +1183,7 @@ static void enable_irqs(struct vpe_ctx *ctx) write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT | VPE_DS1_UV_ERROR_INT); - vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, true); + vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, true); } static void disable_irqs(struct vpe_ctx *ctx) @@ -1085,7 +1191,7 @@ static void disable_irqs(struct vpe_ctx *ctx) write_reg(ctx->dev, VPE_INT0_ENABLE0_CLR, 0xffffffff); write_reg(ctx->dev, VPE_INT0_ENABLE1_CLR, 0xffffffff); - vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, false); + vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, 0, false); } /* device_run() - prepares and starts the device @@ -1098,23 +1204,49 @@ static void 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]; - if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) { - ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - WARN_ON(ctx->src_vbs[2] == NULL); - ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - WARN_ON(ctx->src_vbs[1] == NULL); + 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); + WARN_ON(ctx->src_vbs[0] == NULL); } - ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - WARN_ON(ctx->src_vbs[0] == NULL); ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); WARN_ON(ctx->dst_vb == NULL); + if (ctx->deinterlacing) { + + if (ctx->src_vbs[2] == NULL) { + ctx->src_vbs[2] = ctx->src_vbs[0]; + WARN_ON(ctx->src_vbs[2] == NULL); + ctx->src_vbs[1] = ctx->src_vbs[0]; + WARN_ON(ctx->src_vbs[1] == 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) */ + } + /* config descriptors */ if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) { vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb); vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb); + + set_line_modes(ctx); + ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr; ctx->load_mmrs = false; } @@ -1202,7 +1334,7 @@ static void device_run(void *priv) enable_irqs(ctx); vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf); - vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list); + vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list, 0); } static void dei_error(struct vpe_ctx *ctx) @@ -1225,6 +1357,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) struct vb2_v4l2_buffer *s_vb, *d_vb; unsigned long flags; u32 irqst0, irqst1; + bool list_complete = false; irqst0 = read_reg(dev, VPE_INT0_STATUS0); if (irqst0) { @@ -1257,17 +1390,24 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) if (irqst0) { if (irqst0 & VPE_INT0_LIST0_COMPLETE) - vpdma_clear_list_stat(ctx->dev->vpdma); + vpdma_clear_list_stat(ctx->dev->vpdma, 0, 0); irqst0 &= ~(VPE_INT0_LIST0_COMPLETE); + list_complete = true; } if (irqst0 | irqst1) { - dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: " - "INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n", + dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n", irqst0, irqst1); } + /* + * Setup next operation only when list complete IRQ occurs + * otherwise, skip the following code + */ + if (!list_complete) + goto handled; + disable_irqs(ctx); vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); @@ -1295,7 +1435,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) d_vb->sequence = ctx->sequence; 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; if (ctx->field == V4L2_FIELD_BOTTOM) { ctx->sequence++; @@ -1309,12 +1449,28 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) ctx->sequence++; } - if (ctx->deinterlacing) - s_vb = ctx->src_vbs[2]; + 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); - v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE); + + 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) { @@ -1322,8 +1478,16 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) 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; + ctx->bufs_completed++; - if (ctx->bufs_completed < ctx->bufs_per_job) { + if (ctx->bufs_completed < ctx->bufs_per_job && job_ready(ctx)) { device_run(ctx); goto handled; } @@ -1414,7 +1578,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix->colorspace = s_q_data->colorspace; } - pix->num_planes = q_data->fmt->coplanar ? 2 : 1; + pix->num_planes = q_data->nplanes; for (i = 0; i < pix->num_planes; i++) { pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; @@ -1430,7 +1594,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_plane_pix_format *plane_fmt; unsigned int w_align; - int i, depth, depth_bytes; + int i, depth, depth_bytes, height; if (!fmt || !(fmt->types & type)) { vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n", @@ -1438,7 +1602,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, 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; depth = fmt->vpdma_fmt[VPE_LUMA]->depth; @@ -1450,28 +1615,53 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, */ depth_bytes = depth >> 3; - if (depth_bytes == 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 + } 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 */ - w_align = order_base_2(VPDMA_DESC_ALIGN / depth_bytes); + + /* + * 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); + } v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, &pix->height, MIN_H, MAX_H, H_ALIGN, S_ALIGN); - pix->num_planes = fmt->coplanar ? 2 : 1; + if (!pix->num_planes) + pix->num_planes = fmt->coplanar ? 2 : 1; + else if (pix->num_planes > 1 && !fmt->coplanar) + pix->num_planes = 1; + 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 (fmt->fourcc == V4L2_PIX_FMT_RGB24 || fmt->fourcc == V4L2_PIX_FMT_BGR24 || @@ -1479,7 +1669,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, fmt->fourcc == V4L2_PIX_FMT_BGR32) { pix->colorspace = V4L2_COLORSPACE_SRGB; } else { - if (pix->height > 1280) /* HD */ + if (height > 1280) /* HD */ pix->colorspace = V4L2_COLORSPACE_REC709; else /* SD */ pix->colorspace = V4L2_COLORSPACE_SMPTE170M; @@ -1496,6 +1686,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, else plane_fmt->bytesperline = pix->width; + if (pix->num_planes == 1 && fmt->coplanar) + depth += fmt->vpdma_fmt[VPE_CHROMA]->depth; plane_fmt->sizeimage = (pix->height * pix->width * depth) >> 3; @@ -1542,6 +1734,7 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) q_data->height = pix->height; q_data->colorspace = pix->colorspace; q_data->field = pix->field; + q_data->nplanes = pix->num_planes; for (i = 0; i < pix->num_planes; i++) { plane_fmt = &pix->plane_fmt[i]; @@ -1556,14 +1749,20 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) q_data->c_rect.height = q_data->height; 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 - 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", f->type, q_data->width, q_data->height, q_data->fmt->fourcc, q_data->bytesperline[VPE_LUMA]); - if (q_data->fmt->coplanar) + if (q_data->nplanes == 2) vpe_dbg(ctx->dev, " bpl_uv %d\n", q_data->bytesperline[VPE_CHROMA]); @@ -1594,6 +1793,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) { struct vpe_q_data *q_data; + int height; if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) @@ -1628,13 +1828,22 @@ static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) 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) { vpe_err(ctx->dev, "negative values for top and left\n"); s->r.top = s->r.left = 0; } 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 */ if (s->r.left + s->r.width > q_data->width) @@ -1784,6 +1993,7 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = { .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, .vidioc_streamon = v4l2_m2m_ioctl_streamon, .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, @@ -1804,14 +2014,14 @@ static int vpe_queue_setup(struct vb2_queue *vq, q_data = get_q_data(ctx, vq->type); - *nplanes = q_data->fmt->coplanar ? 2 : 1; + *nplanes = q_data->nplanes; for (i = 0; i < *nplanes; i++) sizes[i] = q_data->sizeimage[i]; vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers, sizes[VPE_LUMA]); - if (q_data->fmt->coplanar) + if (q_data->nplanes == 2) vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]); return 0; @@ -1827,14 +2037,15 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); q_data = get_q_data(ctx, vb->vb2_queue->type); - num_planes = q_data->fmt->coplanar ? 2 : 1; + num_planes = q_data->nplanes; 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; } else { if (vbuf->field != V4L2_FIELD_TOP && - vbuf->field != V4L2_FIELD_BOTTOM) + vbuf->field != V4L2_FIELD_BOTTOM && + vbuf->field != V4L2_FIELD_SEQ_TB) return -EINVAL; } } @@ -1863,9 +2074,98 @@ static void vpe_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static int check_srcdst_sizes(struct vpe_ctx *ctx) +{ + struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; + struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; + unsigned int src_w = s_q_data->c_rect.width; + unsigned int src_h = s_q_data->c_rect.height; + unsigned int dst_w = d_q_data->c_rect.width; + unsigned int dst_h = d_q_data->c_rect.height; + + if (src_w == dst_w && src_h == dst_h) + return 0; + + if (src_h <= SC_MAX_PIXEL_HEIGHT && + src_w <= SC_MAX_PIXEL_WIDTH && + dst_h <= SC_MAX_PIXEL_HEIGHT && + dst_w <= SC_MAX_PIXEL_WIDTH) + return 0; + + return -1; +} + +static void vpe_return_all_buffers(struct vpe_ctx *ctx, struct vb2_queue *q, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vb; + unsigned long flags; + + 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); + + if (ctx->src_vbs[0] && + (ctx->src_vbs[0] != ctx->src_vbs[1]) && + (ctx->src_vbs[0] != ctx->src_vbs[2])) + v4l2_m2m_buf_done(ctx->src_vbs[0], state); + + ctx->src_vbs[2] = NULL; + ctx->src_vbs[1] = NULL; + ctx->src_vbs[0] = NULL; + + spin_unlock_irqrestore(&ctx->dev->lock, flags); + } else { + if (ctx->dst_vb) { + spin_lock_irqsave(&ctx->dev->lock, flags); + + v4l2_m2m_buf_done(ctx->dst_vb, state); + ctx->dst_vb = NULL; + spin_unlock_irqrestore(&ctx->dev->lock, flags); + } + } +} + static int vpe_start_streaming(struct vb2_queue *q, unsigned int count) { - /* currently we do nothing here */ + struct vpe_ctx *ctx = vb2_get_drv_priv(q); + + /* 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 (ctx->sequence != 0) + set_srcdst_params(ctx); return 0; } @@ -1876,6 +2176,8 @@ static void vpe_stop_streaming(struct vb2_queue *q) vpe_dump_regs(ctx->dev); vpdma_dump_regs(ctx->dev->vpdma); + + vpe_return_all_buffers(ctx, q, VB2_BUF_STATE_ERROR); } static const struct vb2_ops vpe_qops = { @@ -1995,6 +2297,7 @@ static int vpe_open(struct file *file) s_q_data->fmt = &vpe_formats[2]; s_q_data->width = 1920; s_q_data->height = 1080; + s_q_data->nplanes = 1; s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width * s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] * @@ -2068,11 +2371,13 @@ static int vpe_release(struct file *file) vpe_dbg(dev, "releasing instance %p\n", ctx); mutex_lock(&dev->dev_mutex); - free_vbs(ctx); free_mv_buffers(ctx); vpdma_free_desc_list(&ctx->desc_list); vpdma_free_desc_buf(&ctx->mmr_adb); + vpdma_free_desc_buf(&ctx->sc_coeff_v); + vpdma_free_desc_buf(&ctx->sc_coeff_h); + v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->hdl); @@ -2235,23 +2540,22 @@ static int vpe_probe(struct platform_device *pdev) vpe_top_vpdma_reset(dev); - dev->sc = sc_create(pdev); + dev->sc = sc_create(pdev, "sc"); if (IS_ERR(dev->sc)) { ret = PTR_ERR(dev->sc); goto runtime_put; } - dev->csc = csc_create(pdev); + dev->csc = csc_create(pdev, "csc"); if (IS_ERR(dev->csc)) { ret = PTR_ERR(dev->csc); goto runtime_put; } - dev->vpdma = vpdma_create(pdev, vpe_fw_cb); - if (IS_ERR(dev->vpdma)) { - ret = PTR_ERR(dev->vpdma); + dev->vpdma = &dev->vpdma_data; + ret = vpdma_create(pdev, dev->vpdma, vpe_fw_cb); + if (ret) goto runtime_put; - } return 0; @@ -2290,6 +2594,7 @@ static const struct of_device_id vpe_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, vpe_of_match); #endif static struct platform_driver vpe_pdrv = { |