diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 3bd9ca658b54..4e58069e24ff 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -69,6 +69,8 @@ #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 */ /* @@ -111,6 +113,38 @@ static const struct vpe_us_coeffs us_coeffs[] = { 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, }, + { + /* Coefficients for Top Field Interlaced input */ + 0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3, + /* Coefficients for Bottom Field Interlaced input */ + 0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9, + }, +}; + +/* + * 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 { + unsigned long mdt_spacial_freq_thr_reg; /* VPE_DEI_REG2 */ + unsigned long edi_config_reg; /* VPE_DEI_REG3 */ + unsigned long edi_lut_reg0; /* VPE_DEI_REG4 */ + unsigned long edi_lut_reg1; /* VPE_DEI_REG5 */ + unsigned long edi_lut_reg2; /* VPE_DEI_REG6 */ + unsigned long edi_lut_reg3; /* VPE_DEI_REG7 */ +}; + +/* + * default expert DEI register values, unlikely to be modified. + */ +static const struct vpe_dei_regs dei_regs = { + 0x020C0804u, + 0x0118100Fu, + 0x08040200u, + 0x1010100Cu, + 0x10101010u, + 0x10101010u, }; /* @@ -118,6 +152,7 @@ static const struct vpe_us_coeffs us_coeffs[] = { */ struct vpe_port_data { enum vpdma_channel channel; /* VPDMA channel */ + u8 vb_index; /* input frame f, f-1, f-2 index */ u8 vb_part; /* plane index for co-panar formats */ }; @@ -126,6 +161,12 @@ struct vpe_port_data { */ #define VPE_PORT_LUMA1_IN 0 #define VPE_PORT_CHROMA1_IN 1 +#define VPE_PORT_LUMA2_IN 2 +#define VPE_PORT_CHROMA2_IN 3 +#define VPE_PORT_LUMA3_IN 4 +#define VPE_PORT_CHROMA3_IN 5 +#define VPE_PORT_MV_IN 6 +#define VPE_PORT_MV_OUT 7 #define VPE_PORT_LUMA_OUT 8 #define VPE_PORT_CHROMA_OUT 9 #define VPE_PORT_RGB_OUT 10 @@ -133,12 +174,40 @@ struct vpe_port_data { static const struct vpe_port_data port_data[11] = { [VPE_PORT_LUMA1_IN] = { .channel = VPE_CHAN_LUMA1_IN, + .vb_index = 0, .vb_part = VPE_LUMA, }, [VPE_PORT_CHROMA1_IN] = { .channel = VPE_CHAN_CHROMA1_IN, + .vb_index = 0, .vb_part = VPE_CHROMA, }, + [VPE_PORT_LUMA2_IN] = { + .channel = VPE_CHAN_LUMA2_IN, + .vb_index = 1, + .vb_part = VPE_LUMA, + }, + [VPE_PORT_CHROMA2_IN] = { + .channel = VPE_CHAN_CHROMA2_IN, + .vb_index = 1, + .vb_part = VPE_CHROMA, + }, + [VPE_PORT_LUMA3_IN] = { + .channel = VPE_CHAN_LUMA3_IN, + .vb_index = 2, + .vb_part = VPE_LUMA, + }, + [VPE_PORT_CHROMA3_IN] = { + .channel = VPE_CHAN_CHROMA3_IN, + .vb_index = 2, + .vb_part = VPE_CHROMA, + }, + [VPE_PORT_MV_IN] = { + .channel = VPE_CHAN_MV_IN, + }, + [VPE_PORT_MV_OUT] = { + .channel = VPE_CHAN_MV_OUT, + }, [VPE_PORT_LUMA_OUT] = { .channel = VPE_CHAN_LUMA_OUT, .vb_part = VPE_LUMA, @@ -210,6 +279,7 @@ struct vpe_q_data { unsigned int height; /* frame height */ unsigned int bytesperline[VPE_MAX_PLANES]; /* bytes per line in memory */ enum v4l2_colorspace colorspace; + enum v4l2_field field; /* supported field value */ unsigned int flags; unsigned int sizeimage[VPE_MAX_PLANES]; /* image size in memory */ struct v4l2_rect c_rect; /* crop/compose rectangle */ @@ -219,6 +289,7 @@ 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) enum { Q_DATA_SRC = 0, @@ -270,6 +341,7 @@ struct vpe_ctx { struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_ctrl_handler hdl; + unsigned int field; /* current field */ unsigned int sequence; /* current frame/field seq */ unsigned int aborting; /* abort after next irq */ @@ -277,13 +349,19 @@ struct vpe_ctx { unsigned int bufs_completed; /* bufs done in this batch */ struct vpe_q_data q_data[2]; /* src & dst queue data */ - struct vb2_buffer *src_vb; + struct vb2_buffer *src_vbs[VPE_MAX_SRC_BUFS]; struct vb2_buffer *dst_vb; + dma_addr_t mv_buf_dma[2]; /* dma addrs of motion vector in/out bufs */ + void *mv_buf[2]; /* virtual addrs of motion vector bufs */ + size_t mv_buf_size; /* current motion vector buffer size */ struct vpdma_buf mmr_adb; /* shadow reg addr/data block */ struct vpdma_desc_list desc_list; /* DMA descriptor list */ + bool deinterlacing; /* using de-interlacer */ bool load_mmrs; /* have new shadow reg values */ + + unsigned int src_mv_buf_selector; }; @@ -359,8 +437,7 @@ struct vpe_mmr_adb { struct vpdma_adb_hdr us3_hdr; u32 us3_regs[8]; struct vpdma_adb_hdr dei_hdr; - u32 dei_regs[1]; - u32 dei_pad[3]; + u32 dei_regs[8]; struct vpdma_adb_hdr sc_hdr; u32 sc_regs[1]; u32 sc_pad[3]; @@ -385,6 +462,80 @@ static void init_adb_hdrs(struct vpe_ctx *ctx) VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, VPE_CSC_CSC00); }; +/* + * 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. + */ +static int 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]); + + return -ENOMEM; + } + + ctx->mv_buf_size = size; + ctx->src_mv_buf_selector = 0; + + return 0; +} + +static void free_mv_buffers(struct vpe_ctx *ctx) +{ + realloc_mv_buffers(ctx, 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. + */ +static void free_vbs(struct vpe_ctx *ctx) +{ + struct vpe_dev *dev = ctx->dev; + unsigned long flags; + + if (ctx->src_vbs[2] == NULL) + return; + + 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); + } + spin_unlock_irqrestore(&dev->lock, flags); +} + /* * Enable or disable the VPE clocks */ @@ -426,6 +577,7 @@ static void vpe_top_vpdma_reset(struct vpe_dev *dev) static void set_us_coefficients(struct vpe_ctx *ctx) { struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; u32 *us1_reg = &mmr_adb->us1_regs[0]; u32 *us2_reg = &mmr_adb->us2_regs[0]; u32 *us3_reg = &mmr_adb->us3_regs[0]; @@ -433,6 +585,9 @@ 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 */ + cp += sizeof(us_coeffs[0]) / sizeof(*cp); + end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp); while (cp < end_cp) { @@ -473,14 +628,28 @@ static void set_cfg_and_line_modes(struct vpe_ctx *ctx) /* 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); + vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN); /* frame start for input luma */ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, VPE_CHAN_LUMA1_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_LUMA2_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_LUMA3_IN); /* frame start for input chroma */ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, VPE_CHAN_CHROMA1_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_CHROMA2_IN); + vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, + VPE_CHAN_CHROMA3_IN); + + /* 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; } @@ -524,13 +693,14 @@ static void set_dst_registers(struct vpe_ctx *ctx) /* * Set the de-interlacer shadow register values */ -static void set_dei_regs_bypass(struct vpe_ctx *ctx) +static void set_dei_regs(struct vpe_ctx *ctx) { struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; unsigned int src_h = s_q_data->c_rect.height; unsigned int src_w = s_q_data->c_rect.width; u32 *dei_mmr0 = &mmr_adb->dei_regs[0]; + bool deinterlace = true; u32 val = 0; /* @@ -539,7 +709,13 @@ static void set_dei_regs_bypass(struct vpe_ctx *ctx) * for both progressive and interlace content in interlace bypass mode. * It has been recommended not to use progressive bypass mode. */ - val = VPE_DEI_INTERLACE_BYPASS; + if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) || + !(s_q_data->flags & Q_DATA_INTERLACED)) { + deinterlace = false; + val = VPE_DEI_INTERLACE_BYPASS; + } + + src_h = deinterlace ? src_h * 2 : src_h; val |= (src_h << VPE_DEI_HEIGHT_SHIFT) | (src_w << VPE_DEI_WIDTH_SHIFT) | @@ -550,6 +726,22 @@ static void set_dei_regs_bypass(struct vpe_ctx *ctx) ctx->load_mmrs = true; } +static void set_dei_shadow_registers(struct vpe_ctx *ctx) +{ + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + u32 *dei_mmr = &mmr_adb->dei_regs[0]; + const struct vpe_dei_regs *cur = &dei_regs; + + dei_mmr[2] = cur->mdt_spacial_freq_thr_reg; + dei_mmr[3] = cur->edi_config_reg; + dei_mmr[4] = cur->edi_lut_reg0; + dei_mmr[5] = cur->edi_lut_reg1; + dei_mmr[6] = cur->edi_lut_reg2; + dei_mmr[7] = cur->edi_lut_reg3; + + ctx->load_mmrs = true; +} + static void set_csc_coeff_bypass(struct vpe_ctx *ctx) { struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; @@ -578,10 +770,35 @@ static void set_sc_regs_bypass(struct vpe_ctx *ctx) */ static int set_srcdst_params(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]; + size_t mv_buf_size; + int ret; + ctx->sequence = 0; + ctx->field = V4L2_FIELD_TOP; + + if ((s_q_data->flags & Q_DATA_INTERLACED) && + !(d_q_data->flags & Q_DATA_INTERLACED)) { + const struct vpdma_data_format *mv = + &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + + ctx->deinterlacing = 1; + mv_buf_size = + (s_q_data->width * s_q_data->height * mv->depth) >> 3; + } else { + ctx->deinterlacing = 0; + mv_buf_size = 0; + } + + free_vbs(ctx); + + ret = realloc_mv_buffers(ctx, mv_buf_size); + if (ret) + return ret; set_cfg_and_line_modes(ctx); - set_dei_regs_bypass(ctx); + set_dei_regs(ctx); set_csc_coeff_bypass(ctx); set_sc_regs_bypass(ctx); @@ -608,6 +825,9 @@ 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->m2m_ctx) < needed) return 0; @@ -735,17 +955,25 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) struct v4l2_rect *c_rect = &q_data->c_rect; struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; - int plane = fmt->coplanar ? p_data->vb_part : 0; + int mv_buf_selector = !ctx->src_mv_buf_selector; dma_addr_t dma_addr; u32 flags = 0; - vpdma_fmt = fmt->vpdma_fmt[plane]; - dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); - if (!dma_addr) { - vpe_err(ctx->dev, - "acquiring output buffer(%d) dma_addr failed\n", - port); - return; + if (port == VPE_PORT_MV_OUT) { + vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + dma_addr = ctx->mv_buf_dma[mv_buf_selector]; + } 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 (!dma_addr) { + vpe_err(ctx->dev, + "acquiring output buffer(%d) dma_addr failed\n", + port); + return; + } } if (q_data->flags & Q_DATA_FRAME_1D) @@ -761,23 +989,31 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) { struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC]; const struct vpe_port_data *p_data = &port_data[port]; - struct vb2_buffer *vb = ctx->src_vb; + struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index]; struct v4l2_rect *c_rect = &q_data->c_rect; struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; - int plane = fmt->coplanar ? p_data->vb_part : 0; - int field = 0; + int mv_buf_selector = ctx->src_mv_buf_selector; + int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM; dma_addr_t dma_addr; u32 flags = 0; - vpdma_fmt = fmt->vpdma_fmt[plane]; + if (port == VPE_PORT_MV_IN) { + vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + dma_addr = ctx->mv_buf_dma[mv_buf_selector]; + } else { + /* to incorporate interleaved formats */ + int plane = fmt->coplanar ? p_data->vb_part : 0; - dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); - if (!dma_addr) { - vpe_err(ctx->dev, - "acquiring input buffer(%d) dma_addr failed\n", - port); - return; + vpdma_fmt = fmt->vpdma_fmt[plane]; + + dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); + if (!dma_addr) { + vpe_err(ctx->dev, + "acquiring input buffer(%d) dma_addr failed\n", + port); + return; + } } if (q_data->flags & Q_DATA_FRAME_1D) @@ -795,7 +1031,8 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) static void enable_irqs(struct vpe_ctx *ctx) { write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE); - write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DS1_UV_ERROR_INT); + 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); } @@ -818,8 +1055,15 @@ static void device_run(void *priv) struct vpe_ctx *ctx = priv; struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; - ctx->src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - WARN_ON(ctx->src_vb == NULL); + if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) { + ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + WARN_ON(ctx->src_vbs[2] == NULL); + ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + WARN_ON(ctx->src_vbs[1] == NULL); + } + + ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + WARN_ON(ctx->src_vbs[0] == NULL); ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); WARN_ON(ctx->dst_vb == NULL); @@ -831,28 +1075,67 @@ static void device_run(void *priv) ctx->load_mmrs = false; } + /* output data descriptors */ + if (ctx->deinterlacing) + add_out_dtd(ctx, VPE_PORT_MV_OUT); + add_out_dtd(ctx, VPE_PORT_LUMA_OUT); if (d_q_data->fmt->coplanar) add_out_dtd(ctx, VPE_PORT_CHROMA_OUT); + /* input data descriptors */ + if (ctx->deinterlacing) { + add_in_dtd(ctx, VPE_PORT_LUMA3_IN); + add_in_dtd(ctx, VPE_PORT_CHROMA3_IN); + + add_in_dtd(ctx, VPE_PORT_LUMA2_IN); + add_in_dtd(ctx, VPE_PORT_CHROMA2_IN); + } + add_in_dtd(ctx, VPE_PORT_LUMA1_IN); add_in_dtd(ctx, VPE_PORT_CHROMA1_IN); + 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); + + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_LUMA3_IN); + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, + VPE_CHAN_CHROMA3_IN); + + vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN); + } + /* sync on channel control descriptors for output ports */ 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); + enable_irqs(ctx); vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf); vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list); } +static void dei_error(struct vpe_ctx *ctx) +{ + dev_warn(ctx->dev->v4l2_dev.dev, + "received DEI error interrupt\n"); +} + static void ds1_uv_error(struct vpe_ctx *ctx) { dev_warn(ctx->dev->v4l2_dev.dev, @@ -863,6 +1146,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) { struct vpe_dev *dev = (struct vpe_dev *)data; struct vpe_ctx *ctx; + struct vpe_q_data *d_q_data; struct vb2_buffer *s_vb, *d_vb; struct v4l2_buffer *s_buf, *d_buf; unsigned long flags; @@ -886,9 +1170,15 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) goto handled; } - if (irqst1 & VPE_DS1_UV_ERROR_INT) { - irqst1 &= ~VPE_DS1_UV_ERROR_INT; - ds1_uv_error(ctx); + if (irqst1) { + if (irqst1 & VPE_DEI_ERROR_INT) { + irqst1 &= ~VPE_DEI_ERROR_INT; + dei_error(ctx); + } + if (irqst1 & VPE_DS1_UV_ERROR_INT) { + irqst1 &= ~VPE_DS1_UV_ERROR_INT; + ds1_uv_error(ctx); + } } if (irqst0) { @@ -911,10 +1201,13 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) vpdma_reset_desc_list(&ctx->desc_list); + /* the previous dst mv buffer becomes the next src mv buffer */ + ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector; + if (ctx->aborting) goto finished; - s_vb = ctx->src_vb; + s_vb = ctx->src_vbs[0]; d_vb = ctx->dst_vb; s_buf = &s_vb->v4l2_buf; d_buf = &d_vb->v4l2_buf; @@ -924,16 +1217,35 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) d_buf->flags |= V4L2_BUF_FLAG_TIMECODE; d_buf->timecode = s_buf->timecode; } - d_buf->sequence = ctx->sequence; + d_buf->field = ctx->field; - ctx->sequence++; + d_q_data = &ctx->q_data[Q_DATA_DST]; + if (d_q_data->flags & Q_DATA_INTERLACED) { + if (ctx->field == V4L2_FIELD_BOTTOM) { + ctx->sequence++; + ctx->field = V4L2_FIELD_TOP; + } else { + WARN_ON(ctx->field != V4L2_FIELD_TOP); + ctx->field = V4L2_FIELD_BOTTOM; + } + } else { + ctx->sequence++; + } + + if (ctx->deinterlacing) + s_vb = ctx->src_vbs[2]; spin_lock_irqsave(&dev->lock, flags); 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]; + } + ctx->bufs_completed++; if (ctx->bufs_completed < ctx->bufs_per_job) { device_run(ctx); @@ -1012,6 +1324,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix->width = q_data->width; pix->height = q_data->height; pix->pixelformat = q_data->fmt->fourcc; + pix->field = q_data->field; if (V4L2_TYPE_IS_OUTPUT(f->type)) { pix->colorspace = q_data->colorspace; @@ -1047,7 +1360,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, return -EINVAL; } - pix->field = V4L2_FIELD_NONE; + if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE) + pix->field = V4L2_FIELD_NONE; v4l_bound_align_image(&pix->width, MIN_W, MAX_W, W_ALIGN, &pix->height, MIN_H, MAX_H, H_ALIGN, @@ -1124,6 +1438,7 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) q_data->width = pix->width; q_data->height = pix->height; q_data->colorspace = pix->colorspace; + q_data->field = pix->field; for (i = 0; i < pix->num_planes; i++) { plane_fmt = &pix->plane_fmt[i]; @@ -1137,6 +1452,11 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) q_data->c_rect.width = q_data->width; q_data->c_rect.height = q_data->height; + if (q_data->field == V4L2_FIELD_ALTERNATE) + q_data->flags |= Q_DATA_INTERLACED; + else + q_data->flags &= ~Q_DATA_INTERLACED; + 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]); @@ -1451,6 +1771,7 @@ static int vpe_open(struct file *file) s_q_data->sizeimage[VPE_LUMA] = (s_q_data->width * s_q_data->height * s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; s_q_data->colorspace = V4L2_COLORSPACE_SMPTE240M; + s_q_data->field = V4L2_FIELD_NONE; s_q_data->c_rect.left = 0; s_q_data->c_rect.top = 0; s_q_data->c_rect.width = s_q_data->width; @@ -1459,6 +1780,7 @@ static int vpe_open(struct file *file) ctx->q_data[Q_DATA_DST] = *s_q_data; + set_dei_shadow_registers(ctx); set_src_registers(ctx); set_dst_registers(ctx); ret = set_srcdst_params(ctx); @@ -1513,6 +1835,8 @@ 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);