diff options
Diffstat (limited to 'drivers/media/platform/ti-vpe/vpe.c')
| -rw-r--r-- | drivers/media/platform/ti-vpe/vpe.c | 591 |
1 files changed, 440 insertions, 151 deletions
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 4e58069e24f..972f43f6920 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -30,6 +30,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <linux/log2.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> @@ -42,22 +43,20 @@ #include "vpdma.h" #include "vpe_regs.h" +#include "sc.h" +#include "csc.h" #define VPE_MODULE_NAME "vpe" /* minimum and maximum frame sizes */ -#define MIN_W 128 -#define MIN_H 128 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 1920 #define MAX_H 1080 /* required alignments */ #define S_ALIGN 0 /* multiple of 1 */ #define H_ALIGN 1 /* multiple of 2 */ -#define W_ALIGN 1 /* multiple of 2 */ - -/* multiple of 128 bits, line stride, 16 bytes */ -#define L_ALIGN 4 /* flags that indicate a format can be used for capture/output */ #define VPE_FMT_TYPE_CAPTURE (1 << 0) @@ -268,6 +267,38 @@ static struct vpe_fmt vpe_formats[] = { .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CY422], }, }, + { + .name = "RGB888 packed", + .fourcc = V4L2_PIX_FMT_RGB24, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], + }, + }, + { + .name = "ARGB32", + .fourcc = V4L2_PIX_FMT_RGB32, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], + }, + }, + { + .name = "BGR888 packed", + .fourcc = V4L2_PIX_FMT_BGR24, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], + }, + }, + { + .name = "ABGR32", + .fourcc = V4L2_PIX_FMT_BGR32, + .types = VPE_FMT_TYPE_CAPTURE, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], + }, + }, }; /* @@ -327,9 +358,12 @@ struct vpe_dev { int irq; void __iomem *base; + struct resource *res; struct vb2_alloc_ctx *alloc_ctx; struct vpdma_data *vpdma; /* vpdma data handle */ + struct sc_data *sc; /* scaler data handle */ + struct csc_data *csc; /* csc data handle */ }; /* @@ -356,6 +390,8 @@ struct vpe_ctx { 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_buf sc_coeff_h; /* h coeff buffer */ + struct vpdma_buf sc_coeff_v; /* v coeff buffer */ struct vpdma_desc_list desc_list; /* DMA descriptor list */ bool deinterlacing; /* using de-interlacer */ @@ -374,8 +410,10 @@ static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx, { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return &ctx->q_data[Q_DATA_SRC]; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: return &ctx->q_data[Q_DATA_DST]; default: BUG(); @@ -438,14 +476,23 @@ struct vpe_mmr_adb { u32 us3_regs[8]; struct vpdma_adb_hdr dei_hdr; u32 dei_regs[8]; - struct vpdma_adb_hdr sc_hdr; - u32 sc_regs[1]; - u32 sc_pad[3]; + struct vpdma_adb_hdr sc_hdr0; + u32 sc_regs0[7]; + u32 sc_pad0[1]; + struct vpdma_adb_hdr sc_hdr8; + u32 sc_regs8[6]; + u32 sc_pad8[2]; + struct vpdma_adb_hdr sc_hdr17; + u32 sc_regs17[9]; + u32 sc_pad17[3]; struct vpdma_adb_hdr csc_hdr; u32 csc_regs[6]; u32 csc_pad[2]; }; +#define GET_OFFSET_TOP(ctx, obj, reg) \ + ((obj)->res->start - ctx->dev->res->start + reg) + #define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a) \ VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a) /* @@ -458,8 +505,14 @@ static void init_adb_hdrs(struct vpe_ctx *ctx) VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0); VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0); VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE); - VPE_SET_MMR_ADB_HDR(ctx, sc_hdr, sc_regs, VPE_SC_MP_SC0); - VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, VPE_CSC_CSC00); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr0, sc_regs0, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0)); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr8, sc_regs8, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC8)); + VPE_SET_MMR_ADB_HDR(ctx, sc_hdr17, sc_regs17, + GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC17)); + VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, + GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00)); }; /* @@ -670,17 +723,20 @@ static void set_src_registers(struct vpe_ctx *ctx) static void set_dst_registers(struct vpe_ctx *ctx) { struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + enum v4l2_colorspace clrspc = ctx->q_data[Q_DATA_DST].colorspace; struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; u32 val = 0; - /* select RGB path when color space conversion is supported in future */ - if (fmt->fourcc == V4L2_PIX_FMT_RGB24) - val |= VPE_RGB_OUT_SELECT | VPE_CSC_SRC_DEI_SCALER; + if (clrspc == V4L2_COLORSPACE_SRGB) + val |= VPE_RGB_OUT_SELECT; else if (fmt->fourcc == V4L2_PIX_FMT_NV16) val |= VPE_COLOR_SEPARATE_422; - /* The source of CHR_DS is always the scaler, whether it's used or not */ - val |= VPE_DS_SRC_DEI_SCALER; + /* + * 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) val |= VPE_DS_BYPASS; @@ -742,28 +798,6 @@ static void set_dei_shadow_registers(struct vpe_ctx *ctx) ctx->load_mmrs = true; } -static void set_csc_coeff_bypass(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - u32 *shadow_csc_reg5 = &mmr_adb->csc_regs[5]; - - *shadow_csc_reg5 |= VPE_CSC_BYPASS; - - ctx->load_mmrs = true; -} - -static void set_sc_regs_bypass(struct vpe_ctx *ctx) -{ - struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; - u32 *sc_reg0 = &mmr_adb->sc_regs[0]; - u32 val = 0; - - val |= VPE_SC_BYPASS; - *sc_reg0 = val; - - ctx->load_mmrs = true; -} - /* * Set the shadow registers whose values are modified when either the * source or destination format is changed. @@ -772,6 +806,11 @@ 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]; + struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; + 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; size_t mv_buf_size; int ret; @@ -780,12 +819,23 @@ static int set_srcdst_params(struct vpe_ctx *ctx) if ((s_q_data->flags & Q_DATA_INTERLACED) && !(d_q_data->flags & Q_DATA_INTERLACED)) { + int bytes_per_line; const struct 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 boundry. this + * extra space will not be used by the de-interlacer, but will + * ensure that vpdma operates correctly + */ + bytes_per_line = ALIGN((s_q_data->width * mv->depth) >> 3, + VPDMA_STRIDE_ALIGN); + mv_buf_size = bytes_per_line * s_q_data->height; + ctx->deinterlacing = 1; - mv_buf_size = - (s_q_data->width * s_q_data->height * mv->depth) >> 3; + src_h <<= 1; } else { ctx->deinterlacing = 0; mv_buf_size = 0; @@ -799,8 +849,16 @@ static int set_srcdst_params(struct vpe_ctx *ctx) set_cfg_and_line_modes(ctx); set_dei_regs(ctx); - set_csc_coeff_bypass(ctx); - set_sc_regs_bypass(ctx); + + csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], + s_q_data->colorspace, d_q_data->colorspace); + + sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); + sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); + + sc_config_scaler(ctx->dev->sc, &mmr_adb->sc_regs0[0], + &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], + src_w, src_h, dst_w, dst_h); return 0; } @@ -831,6 +889,9 @@ static int job_ready(void *priv) if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed) return 0; + if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed) + return 0; + return 1; } @@ -916,35 +977,10 @@ static void vpe_dump_regs(struct vpe_dev *dev) DUMPREG(DEI_FMD_STATUS_R0); DUMPREG(DEI_FMD_STATUS_R1); DUMPREG(DEI_FMD_STATUS_R2); - DUMPREG(SC_MP_SC0); - DUMPREG(SC_MP_SC1); - DUMPREG(SC_MP_SC2); - DUMPREG(SC_MP_SC3); - DUMPREG(SC_MP_SC4); - DUMPREG(SC_MP_SC5); - DUMPREG(SC_MP_SC6); - DUMPREG(SC_MP_SC8); - DUMPREG(SC_MP_SC9); - DUMPREG(SC_MP_SC10); - DUMPREG(SC_MP_SC11); - DUMPREG(SC_MP_SC12); - DUMPREG(SC_MP_SC13); - DUMPREG(SC_MP_SC17); - DUMPREG(SC_MP_SC18); - DUMPREG(SC_MP_SC19); - DUMPREG(SC_MP_SC20); - DUMPREG(SC_MP_SC21); - DUMPREG(SC_MP_SC22); - DUMPREG(SC_MP_SC23); - DUMPREG(SC_MP_SC24); - DUMPREG(SC_MP_SC25); - DUMPREG(CSC_CSC00); - DUMPREG(CSC_CSC01); - DUMPREG(CSC_CSC02); - DUMPREG(CSC_CSC03); - DUMPREG(CSC_CSC04); - DUMPREG(CSC_CSC05); #undef DUMPREG + + sc_dump_regs(dev->sc); + csc_dump_regs(dev->csc); } static void add_out_dtd(struct vpe_ctx *ctx, int port) @@ -952,7 +988,6 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST]; const struct vpe_port_data *p_data = &port_data[port]; struct vb2_buffer *vb = ctx->dst_vb; - struct v4l2_rect *c_rect = &q_data->c_rect; struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; int mv_buf_selector = !ctx->src_mv_buf_selector; @@ -981,8 +1016,8 @@ 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_add_out_dtd(&ctx->desc_list, c_rect, vpdma_fmt, dma_addr, - p_data->channel, flags); + vpdma_add_out_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect, + vpdma_fmt, dma_addr, p_data->channel, flags); } static void add_in_dtd(struct vpe_ctx *ctx, int port) @@ -990,11 +1025,11 @@ 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_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 mv_buf_selector = ctx->src_mv_buf_selector; int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM; + int frame_width, frame_height; dma_addr_t dma_addr; u32 flags = 0; @@ -1021,8 +1056,15 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) if (q_data->flags & Q_DATA_MODE_TILED) flags |= VPDMA_DATA_MODE_TILED; - vpdma_add_in_dtd(&ctx->desc_list, q_data->width, q_data->height, - c_rect, vpdma_fmt, dma_addr, p_data->channel, field, flags); + frame_width = q_data->c_rect.width; + frame_height = q_data->c_rect.height; + + if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12) + frame_height /= 2; + + vpdma_add_in_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect, + vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width, + frame_height, 0, 0); } /* @@ -1053,6 +1095,7 @@ static void disable_irqs(struct vpe_ctx *ctx) 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]; if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) { @@ -1075,13 +1118,37 @@ static void device_run(void *priv) ctx->load_mmrs = false; } + if (sc->loaded_coeff_h != ctx->sc_coeff_h.dma_addr || + sc->load_coeff_h) { + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_h); + vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, + &ctx->sc_coeff_h, 0); + + sc->loaded_coeff_h = ctx->sc_coeff_h.dma_addr; + sc->load_coeff_h = false; + } + + if (sc->loaded_coeff_v != ctx->sc_coeff_v.dma_addr || + sc->load_coeff_v) { + vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_v); + vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, + &ctx->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); + + sc->loaded_coeff_v = ctx->sc_coeff_v.dma_addr; + sc->load_coeff_v = 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); + if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { + add_out_dtd(ctx, VPE_PORT_RGB_OUT); + } else { + 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) { @@ -1117,9 +1184,16 @@ static void device_run(void *priv) } /* 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 (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { + 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); @@ -1198,6 +1272,8 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); + vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); vpdma_reset_desc_list(&ctx->desc_list); @@ -1212,16 +1288,17 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) s_buf = &s_vb->v4l2_buf; d_buf = &d_vb->v4l2_buf; + d_buf->flags = s_buf->flags; + d_buf->timestamp = s_buf->timestamp; - if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) { - d_buf->flags |= V4L2_BUF_FLAG_TIMECODE; + if (s_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) { + d_buf->field = ctx->field; if (ctx->field == V4L2_FIELD_BOTTOM) { ctx->sequence++; ctx->field = V4L2_FIELD_TOP; @@ -1230,6 +1307,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) ctx->field = V4L2_FIELD_BOTTOM; } } else { + d_buf->field = V4L2_FIELD_NONE; ctx->sequence++; } @@ -1268,8 +1346,9 @@ static int vpe_querycap(struct file *file, void *priv, { strncpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card) - 1); - strlcpy(cap->bus_info, VPE_MODULE_NAME, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + VPE_MODULE_NAME); + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1352,7 +1431,8 @@ 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; - int i; + unsigned int w_align; + int i, depth, depth_bytes; if (!fmt || !(fmt->types & type)) { vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n", @@ -1363,40 +1443,65 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, 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, + depth = fmt->vpdma_fmt[VPE_LUMA]->depth; + + /* + * 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 + */ + w_align = order_base_2(VPDMA_DESC_ALIGN / depth_bytes); + + 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; pix->pixelformat = fmt->fourcc; - if (type == VPE_FMT_TYPE_CAPTURE) { - struct vpe_q_data *s_q_data; - - /* get colorspace from the source queue */ - s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - - pix->colorspace = s_q_data->colorspace; - } else { - if (!pix->colorspace) - pix->colorspace = V4L2_COLORSPACE_SMPTE240M; + if (!pix->colorspace) { + if (fmt->fourcc == V4L2_PIX_FMT_RGB24 || + fmt->fourcc == V4L2_PIX_FMT_BGR24 || + fmt->fourcc == V4L2_PIX_FMT_RGB32 || + fmt->fourcc == V4L2_PIX_FMT_BGR32) { + pix->colorspace = V4L2_COLORSPACE_SRGB; + } else { + if (pix->height > 1280) /* HD */ + pix->colorspace = V4L2_COLORSPACE_REC709; + else /* SD */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + } } + memset(pix->reserved, 0, sizeof(pix->reserved)); for (i = 0; i < pix->num_planes; i++) { - int depth; - plane_fmt = &pix->plane_fmt[i]; depth = fmt->vpdma_fmt[i]->depth; if (i == VPE_LUMA) - plane_fmt->bytesperline = - round_up((pix->width * depth) >> 3, - 1 << L_ALIGN); + plane_fmt->bytesperline = (pix->width * depth) >> 3; else plane_fmt->bytesperline = pix->width; plane_fmt->sizeimage = (pix->height * pix->width * depth) >> 3; + + memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); } return 0; @@ -1488,6 +1593,151 @@ static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) return set_srcdst_params(ctx); } +static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) +{ + struct vpe_q_data *q_data; + + 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; + + 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; + } + + 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); + + /* adjust left/top if cropping rectangle is out of bounds */ + if (s->r.left + s->r.width > q_data->width) + s->r.left = q_data->width - s->r.width; + if (s->r.top + s->r.height > q_data->height) + s->r.top = q_data->height - s->r.height; + + return 0; +} + +static int vpe_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct vpe_ctx *ctx = file2ctx(file); + struct vpe_q_data *q_data; + bool use_c_rect = false; + + 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; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + use_c_rect = true; + break; + case V4L2_SEL_TGT_CROP: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + use_c_rect = true; + break; + default: + return -EINVAL; + } + + if (use_c_rect) { + /* + * for CROP/COMPOSE target type, return c_rect params from the + * respective buffer type + */ + s->r = q_data->c_rect; + } else { + /* + * for DEFAULT/BOUNDS target type, return width and height from + * S_FMT of the respective buffer type + */ + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->width; + s->r.height = q_data->height; + } + + return 0; +} + + +static int vpe_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct vpe_ctx *ctx = file2ctx(file); + struct vpe_q_data *q_data; + struct v4l2_selection sel = *s; + int ret; + + ret = __vpe_try_selection(ctx, &sel); + if (ret) + return ret; + + q_data = get_q_data(ctx, sel.type); + if (!q_data) + return -EINVAL; + + if ((q_data->c_rect.left == sel.r.left) && + (q_data->c_rect.top == sel.r.top) && + (q_data->c_rect.width == sel.r.width) && + (q_data->c_rect.height == sel.r.height)) { + vpe_dbg(ctx->dev, + "requested crop/compose values are already set\n"); + return 0; + } + + q_data->c_rect = sel.r; + + return set_srcdst_params(ctx); +} + static int vpe_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { @@ -1575,6 +1825,9 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = { .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, + .vidioc_g_selection = vpe_g_selection, + .vidioc_s_selection = vpe_s_selection, + .vidioc_reqbufs = vpe_reqbufs, .vidioc_querybuf = vpe_querybuf, @@ -1627,6 +1880,16 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) q_data = get_q_data(ctx, vb->vb2_queue->type); num_planes = q_data->fmt->coplanar ? 2 : 1; + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!(q_data->flags & Q_DATA_INTERLACED)) { + vb->v4l2_buf.field = V4L2_FIELD_NONE; + } else { + if (vb->v4l2_buf.field != V4L2_FIELD_TOP && + vb->v4l2_buf.field != V4L2_FIELD_BOTTOM) + return -EINVAL; + } + } + for (i = 0; i < num_planes; i++) { if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { vpe_err(ctx->dev, @@ -1677,12 +1940,12 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &vpe_qops; src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -1690,12 +1953,12 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &vpe_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1724,11 +1987,6 @@ static int vpe_open(struct file *file) vpe_dbg(dev, "vpe_open\n"); - if (!dev->vpdma->ready) { - vpe_err(dev, "vpdma firmware not loaded\n"); - return -ENODEV; - } - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -1749,6 +2007,14 @@ static int vpe_open(struct file *file) if (ret != 0) goto free_desc_list; + ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_h, SC_COEF_SRAM_SIZE); + if (ret != 0) + goto free_mmr_adb; + + ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE); + if (ret != 0) + goto free_sc_h; + init_adb_hdrs(ctx); v4l2_fh_init(&ctx->fh, video_devdata(file)); @@ -1768,9 +2034,11 @@ 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->sizeimage[VPE_LUMA] = (s_q_data->width * s_q_data->height * + s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width * s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; - s_q_data->colorspace = V4L2_COLORSPACE_SMPTE240M; + s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] * + s_q_data->height); + s_q_data->colorspace = V4L2_COLORSPACE_REC709; s_q_data->field = V4L2_FIELD_NONE; s_q_data->c_rect.left = 0; s_q_data->c_rect.top = 0; @@ -1817,6 +2085,10 @@ static int vpe_open(struct file *file) exit_fh: v4l2_ctrl_handler_free(hdl); v4l2_fh_exit(&ctx->fh); + vpdma_free_desc_buf(&ctx->sc_coeff_v); +free_sc_h: + vpdma_free_desc_buf(&ctx->sc_coeff_h); +free_mmr_adb: vpdma_free_desc_buf(&ctx->mmr_adb); free_desc_list: vpdma_free_desc_list(&ctx->desc_list); @@ -1900,7 +2172,7 @@ static struct video_device vpe_videodev = { .fops = &vpe_fops, .ioctl_ops = &vpe_ioctl_ops, .minor = -1, - .release = video_device_release, + .release = video_device_release_empty, .vfl_dir = VFL_DIR_M2M, }; @@ -1934,16 +2206,45 @@ static void vpe_runtime_put(struct platform_device *pdev) WARN_ON(r < 0 && r != -ENOSYS); } +static void vpe_fw_cb(struct platform_device *pdev) +{ + struct vpe_dev *dev = platform_get_drvdata(pdev); + struct video_device *vfd; + int ret; + + vfd = &dev->vfd; + *vfd = vpe_videodev; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + vpe_err(dev, "Failed to register video device\n"); + + vpe_set_clock_enable(dev, 0); + vpe_runtime_put(pdev); + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(dev->m2m_dev); + vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + v4l2_device_unregister(&dev->v4l2_dev); + + return; + } + + video_set_drvdata(vfd, dev); + snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name); + dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", + vfd->num); +} + static int vpe_probe(struct platform_device *pdev) { struct vpe_dev *dev; - struct video_device *vfd; - struct resource *res; int ret, irq, func; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (IS_ERR(dev)) - return PTR_ERR(dev); + if (!dev) + return -ENOMEM; spin_lock_init(&dev->lock); @@ -1954,16 +2255,17 @@ static int vpe_probe(struct platform_device *pdev) atomic_set(&dev->num_instances, 0); mutex_init(&dev->dev_mutex); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpe_top"); + dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "vpe_top"); /* * HACK: we get resource info from device tree in the form of a list of * VPE sub blocks, the driver currently uses only the base of vpe_top * for register access, the driver should be changed later to access * registers based on the sub block base addresses */ - dev->base = devm_ioremap(&pdev->dev, res->start, SZ_32K); - if (IS_ERR(dev->base)) { - ret = PTR_ERR(dev->base); + dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K); + if (!dev->base) { + ret = -ENOMEM; goto v4l2_dev_unreg; } @@ -2006,25 +2308,23 @@ static int vpe_probe(struct platform_device *pdev) vpe_top_vpdma_reset(dev); - dev->vpdma = vpdma_create(pdev); - if (IS_ERR(dev->vpdma)) + dev->sc = sc_create(pdev); + if (IS_ERR(dev->sc)) { + ret = PTR_ERR(dev->sc); goto runtime_put; + } - vfd = &dev->vfd; - *vfd = vpe_videodev; - vfd->lock = &dev->dev_mutex; - vfd->v4l2_dev = &dev->v4l2_dev; - - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - vpe_err(dev, "Failed to register video device\n"); + dev->csc = csc_create(pdev); + if (IS_ERR(dev->csc)) { + ret = PTR_ERR(dev->csc); goto runtime_put; } - video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name); - dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", - vfd->num); + dev->vpdma = vpdma_create(pdev, vpe_fw_cb); + if (IS_ERR(dev->vpdma)) { + ret = PTR_ERR(dev->vpdma); + goto runtime_put; + } return 0; @@ -2081,18 +2381,7 @@ static struct platform_driver vpe_pdrv = { }, }; -static void __exit vpe_exit(void) -{ - platform_driver_unregister(&vpe_pdrv); -} - -static int __init vpe_init(void) -{ - return platform_driver_register(&vpe_pdrv); -} - -module_init(vpe_init); -module_exit(vpe_exit); +module_platform_driver(vpe_pdrv); MODULE_DESCRIPTION("TI VPE driver"); MODULE_AUTHOR("Dale Farnsworth, <dale@farnsworth.org>"); |
