|
@@ -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;
|
|
|
+
|
|
|
+ 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++;
|
|
|
+ }
|
|
|
|
|
|
- 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);
|
|
|
|