On Thu 29 August 2013 14:32:49 Archit Taneja wrote:
> VPE is a block which consists of a single memory to memory path which can
> perform chrominance up/down sampling, de-interlacing, scaling, and color space
> conversion of raster or tiled YUV420 coplanar, YUV422 coplanar or YUV422
> interleaved video formats.
> 
> We create a mem2mem driver based primarily on the mem2mem-testdev example.
> The de-interlacer, scaler and color space converter are all bypassed for now
> to keep the driver simple. Chroma up/down sampler blocks are implemented, so
> conversion beteen different YUV formats is possible.
> 
> Each mem2mem context allocates a buffer for VPE MMR values which it will use
> when it gets access to the VPE HW via the mem2mem queue, it also allocates
> a VPDMA descriptor list to which configuration and data descriptors are added.
> 
> Based on the information received via v4l2 ioctls for the source and
> destination queues, the driver configures the values for the MMRs, and stores
> them in the buffer. There are also some VPDMA parameters like frame start and
> line mode which needs to be configured, these are configured by direct 
> register
> writes via the VPDMA helper functions.
> 
> The driver's device_run() mem2mem op will add each descriptor based on how the
> source and destination queues are set up for the given ctx, once the list is
> prepared, it's submitted to VPDMA, these descriptors when parsed by VPDMA will
> upload MMR registers, start DMA of video buffers on the various input and 
> output
> clients/ports.
> 
> When the list is parsed completely(and the DMAs on all the output ports done),
> an interrupt is generated which we use to notify that the source and 
> destination
> buffers are done.
> 
> The rest of the driver is quite similar to other mem2mem drivers, we use the
> multiplane v4l2 ioctls as the HW support coplanar formats.
> 
> Signed-off-by: Archit Taneja <arc...@ti.com>

Thanks for the patch. Just a few small comments below...

> ---
>  drivers/media/platform/Kconfig           |   16 +
>  drivers/media/platform/Makefile          |    2 +
>  drivers/media/platform/ti-vpe/Makefile   |    5 +
>  drivers/media/platform/ti-vpe/vpe.c      | 1740 
> ++++++++++++++++++++++++++++++
>  drivers/media/platform/ti-vpe/vpe_regs.h |  496 +++++++++
>  5 files changed, 2259 insertions(+)
>  create mode 100644 drivers/media/platform/ti-vpe/Makefile
>  create mode 100644 drivers/media/platform/ti-vpe/vpe.c
>  create mode 100644 drivers/media/platform/ti-vpe/vpe_regs.h
> 
> diff --git a/drivers/media/platform/ti-vpe/vpe.c 
> b/drivers/media/platform/ti-vpe/vpe.c
> new file mode 100644
> index 0000000..85b0880
> --- /dev/null
> +++ b/drivers/media/platform/ti-vpe/vpe.c

<snip>

> +static int vpe_enum_fmt(struct file *file, void *priv,
> +                             struct v4l2_fmtdesc *f)
> +{
> +     if (V4L2_TYPE_IS_OUTPUT(f->type))
> +             return __enum_fmt(f, VPE_FMT_TYPE_OUTPUT);
> +     else

The line above isn't necessary.

> +             return __enum_fmt(f, VPE_FMT_TYPE_CAPTURE);
> +}
> +
> +static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +     struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
> +     struct vpe_ctx *ctx = file2ctx(file);
> +     struct vb2_queue *vq;
> +     struct vpe_q_data *q_data;
> +     int i;
> +
> +     vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
> +     if (!vq)
> +             return -EINVAL;
> +
> +     q_data = get_q_data(ctx, f->type);
> +
> +     pix->width = q_data->width;
> +     pix->height = q_data->height;
> +     pix->pixelformat = q_data->fmt->fourcc;
> +     pix->colorspace = q_data->colorspace;
> +     pix->num_planes = q_data->fmt->coplanar ? 2 : 1;
> +
> +     for (i = 0; i < pix->num_planes; i++) {
> +             pix->plane_fmt[i].bytesperline = q_data->bytesperline[i];
> +             pix->plane_fmt[i].sizeimage = q_data->sizeimage[i];
> +     }
> +
> +     return 0;
> +}
> +
> +static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
> +                    struct vpe_fmt *fmt, int type)
> +{
> +     struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
> +     struct v4l2_plane_pix_format *plane_fmt;
> +     int i;
> +
> +     if (!fmt || !(fmt->types & type)) {
> +             vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
> +                     pix->pixelformat);
> +             return -EINVAL;
> +     }
> +
> +     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,
> +                           S_ALIGN);
> +
> +     pix->num_planes = fmt->coplanar ? 2 : 1;
> +     pix->pixelformat = fmt->fourcc;
> +     pix->colorspace = fmt->fourcc == V4L2_PIX_FMT_RGB24 ?
> +                     V4L2_COLORSPACE_SRGB : V4L2_COLORSPACE_SMPTE170M;

pix->priv should be set to NULL as well.

> +
> +
> +     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);
> +             else
> +                     plane_fmt->bytesperline = pix->width;
> +
> +             plane_fmt->sizeimage =
> +                             (pix->height * pix->width * depth) >> 3;
> +     }
> +
> +     return 0;
> +}
> +
> +static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +     struct vpe_fmt *fmt = find_format(f);
> +
> +     if (V4L2_TYPE_IS_OUTPUT(f->type))
> +             return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_OUTPUT);
> +     else
> +             return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_CAPTURE);
> +}
> +
> +static int __vpe_s_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;
> +     struct vpe_q_data *q_data;
> +     struct vb2_queue *vq;
> +     int i;
> +
> +     vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
> +     if (!vq)
> +             return -EINVAL;
> +
> +     if (vb2_is_busy(vq)) {
> +             vpe_err(ctx->dev, "queue busy\n");
> +             return -EBUSY;
> +     }
> +
> +     q_data = get_q_data(ctx, f->type);
> +     if (!q_data)
> +             return -EINVAL;
> +
> +     q_data->fmt             = find_format(f);
> +     q_data->width           = pix->width;
> +     q_data->height          = pix->height;
> +     q_data->colorspace      = pix->colorspace;
> +
> +     for (i = 0; i < pix->num_planes; i++) {
> +             plane_fmt = &pix->plane_fmt[i];
> +
> +             q_data->bytesperline[i] = plane_fmt->bytesperline;
> +             q_data->sizeimage[i]    = plane_fmt->sizeimage;
> +     }
> +
> +     q_data->c_rect.left     = 0;
> +     q_data->c_rect.top      = 0;
> +     q_data->c_rect.width    = q_data->width;
> +     q_data->c_rect.height   = q_data->height;
> +
> +     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)
> +             vpe_dbg(ctx->dev, " bpl_uv %d\n",
> +                     q_data->bytesperline[VPE_CHROMA]);
> +
> +     return 0;
> +}
> +
> +static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +     int ret;
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     ret = vpe_try_fmt(file, priv, f);
> +     if (ret)
> +             return ret;
> +
> +     ret = __vpe_s_fmt(ctx, f);
> +     if (ret)
> +             return ret;
> +
> +     if (V4L2_TYPE_IS_OUTPUT(f->type))
> +             set_src_registers(ctx);
> +     else
> +             set_dst_registers(ctx);
> +
> +     return set_srcdst_params(ctx);
> +}
> +
> +static int vpe_reqbufs(struct file *file, void *priv,
> +                    struct v4l2_requestbuffers *reqbufs)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
> +}
> +
> +static int vpe_querybuf(struct file *file, void *priv, struct v4l2_buffer 
> *buf)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int vpe_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int vpe_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
> +}
> +
> +static int vpe_streamon(struct file *file, void *priv, enum v4l2_buf_type 
> type)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
> +}
> +
> +static int vpe_streamoff(struct file *file, void *priv, enum v4l2_buf_type 
> type)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     vpe_dump_regs(ctx->dev);
> +     vpdma_dump_regs(ctx->dev->vpdma);
> +
> +     return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
> +}
> +
> +#define V4L2_CID_TRANS_NUM_BUFS              (V4L2_CID_USER_BASE + 0x1000)

Reserve a control range for this driver in include/uapi/linux/v4l2-controls.h.
Similar to the ones already defined there.

That will ensure that controls for this driver have unique IDs.

> +
> +static int vpe_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +     struct vpe_ctx *ctx =
> +             container_of(ctrl->handler, struct vpe_ctx, hdl);
> +
> +     switch (ctrl->id) {
> +     case V4L2_CID_TRANS_NUM_BUFS:
> +             ctx->bufs_per_job = ctrl->val;
> +             break;
> +
> +     default:
> +             vpe_err(ctx->dev, "Invalid control\n");
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops vpe_ctrl_ops = {
> +     .s_ctrl = vpe_s_ctrl,
> +};
> +
> +static const struct v4l2_ioctl_ops vpe_ioctl_ops = {
> +     .vidioc_querycap        = vpe_querycap,
> +
> +     .vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt,
> +     .vidioc_g_fmt_vid_cap_mplane    = vpe_g_fmt,
> +     .vidioc_try_fmt_vid_cap_mplane  = vpe_try_fmt,
> +     .vidioc_s_fmt_vid_cap_mplane    = vpe_s_fmt,
> +
> +     .vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt,
> +     .vidioc_g_fmt_vid_out_mplane    = vpe_g_fmt,
> +     .vidioc_try_fmt_vid_out_mplane  = vpe_try_fmt,
> +     .vidioc_s_fmt_vid_out_mplane    = vpe_s_fmt,
> +
> +     .vidioc_reqbufs         = vpe_reqbufs,
> +     .vidioc_querybuf        = vpe_querybuf,
> +
> +     .vidioc_qbuf            = vpe_qbuf,
> +     .vidioc_dqbuf           = vpe_dqbuf,
> +
> +     .vidioc_streamon        = vpe_streamon,
> +     .vidioc_streamoff       = vpe_streamoff,
> +     .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> +     .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +/*
> + * Queue operations
> + */
> +static int vpe_queue_setup(struct vb2_queue *vq,
> +                        const struct v4l2_format *fmt,
> +                        unsigned int *nbuffers, unsigned int *nplanes,
> +                        unsigned int sizes[], void *alloc_ctxs[])
> +{
> +     int i;
> +     struct vpe_ctx *ctx = vb2_get_drv_priv(vq);
> +     struct vpe_q_data *q_data;
> +
> +     q_data = get_q_data(ctx, vq->type);
> +
> +     *nplanes = q_data->fmt->coplanar ? 2 : 1;
> +
> +     for (i = 0; i < *nplanes; i++) {
> +             sizes[i] = q_data->sizeimage[i];
> +             alloc_ctxs[i] = ctx->dev->alloc_ctx;
> +     }
> +
> +     vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers,
> +             sizes[VPE_LUMA]);
> +     if (q_data->fmt->coplanar)
> +             vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]);
> +
> +     return 0;
> +}
> +
> +static int vpe_buf_prepare(struct vb2_buffer *vb)
> +{
> +     struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +     struct vpe_q_data *q_data;
> +     int i, num_planes;
> +
> +     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;
> +
> +     for (i = 0; i < num_planes; i++) {
> +             if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
> +                     vpe_err(ctx->dev,
> +                             "data will not fit into plane (%lu < %lu)\n",
> +                             vb2_plane_size(vb, i),
> +                             (long) q_data->sizeimage[i]);
> +                     return -EINVAL;
> +             }
> +     }
> +
> +     for (i = 0; i < num_planes; i++)
> +             vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
> +
> +     return 0;
> +}
> +
> +static void vpe_buf_queue(struct vb2_buffer *vb)
> +{
> +     struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +     v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
> +}
> +
> +static void vpe_wait_prepare(struct vb2_queue *q)
> +{
> +     struct vpe_ctx *ctx = vb2_get_drv_priv(q);
> +     vpe_unlock(ctx);
> +}
> +
> +static void vpe_wait_finish(struct vb2_queue *q)
> +{
> +     struct vpe_ctx *ctx = vb2_get_drv_priv(q);
> +     vpe_lock(ctx);
> +}
> +
> +static struct vb2_ops vpe_qops = {
> +     .queue_setup     = vpe_queue_setup,
> +     .buf_prepare     = vpe_buf_prepare,
> +     .buf_queue       = vpe_buf_queue,
> +     .wait_prepare    = vpe_wait_prepare,
> +     .wait_finish     = vpe_wait_finish,
> +};
> +
> +static int queue_init(void *priv, struct vb2_queue *src_vq,
> +                   struct vb2_queue *dst_vq)
> +{
> +     struct vpe_ctx *ctx = priv;
> +     int ret;
> +
> +     memset(src_vq, 0, sizeof(*src_vq));
> +     src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +     src_vq->io_modes = VB2_MMAP;
> +     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_MONOTONIC;

Shouldn't this be TIMESTAMP_COPY?

> +
> +     ret = vb2_queue_init(src_vq);
> +     if (ret)
> +             return ret;
> +
> +     memset(dst_vq, 0, sizeof(*dst_vq));
> +     dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +     dst_vq->io_modes = VB2_MMAP;
> +     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_MONOTONIC;

Ditto.

> +
> +     return vb2_queue_init(dst_vq);
> +}
> +
> +static const struct v4l2_ctrl_config vpe_bufs_per_job = {
> +     .ops = &vpe_ctrl_ops,
> +     .id = V4L2_CID_TRANS_NUM_BUFS,
> +     .name = "Buffers Per Transaction",
> +     .type = V4L2_CTRL_TYPE_INTEGER,
> +     .def = VPE_DEF_BUFS_PER_JOB,
> +     .min = 1,
> +     .max = VIDEO_MAX_FRAME,
> +     .step = 1,
> +};
> +
> +/*
> + * File operations
> + */
> +static int vpe_open(struct file *file)
> +{
> +     struct vpe_dev *dev = video_drvdata(file);
> +     struct vpe_ctx *ctx = NULL;
> +     struct vpe_q_data *s_q_data;
> +     struct v4l2_ctrl_handler *hdl;
> +     int ret;
> +
> +     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;
> +
> +     ctx->dev = dev;
> +
> +     if (mutex_lock_interruptible(&dev->dev_mutex)) {
> +             ret = -ERESTARTSYS;
> +             goto free_ctx;
> +     }
> +
> +     ret = vpdma_create_desc_list(&ctx->desc_list, VPE_DESC_LIST_SIZE,
> +                     VPDMA_LIST_TYPE_NORMAL);
> +     if (ret != 0)
> +             goto unlock;
> +
> +     ret = vpdma_alloc_desc_buf(&ctx->mmr_adb, sizeof(struct vpe_mmr_adb));
> +     if (ret != 0)
> +             goto free_desc_list;
> +
> +     init_adb_hdrs(ctx);
> +
> +     v4l2_fh_init(&ctx->fh, video_devdata(file));
> +     file->private_data = &ctx->fh;
> +
> +     hdl = &ctx->hdl;
> +     v4l2_ctrl_handler_init(hdl, 1);
> +     v4l2_ctrl_new_custom(hdl, &vpe_bufs_per_job, NULL);
> +     if (hdl->error) {
> +             ret = hdl->error;
> +             goto exit_fh;
> +     }
> +     ctx->fh.ctrl_handler = hdl;
> +     v4l2_ctrl_handler_setup(hdl);
> +
> +     s_q_data = &ctx->q_data[Q_DATA_SRC];
> +     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->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3;
> +     s_q_data->colorspace = V4L2_COLORSPACE_SMPTE240M;
> +     s_q_data->c_rect.left = 0;
> +     s_q_data->c_rect.top = 0;
> +     s_q_data->c_rect.width = s_q_data->width;
> +     s_q_data->c_rect.height = s_q_data->height;
> +     s_q_data->flags = 0;
> +
> +     ctx->q_data[Q_DATA_DST] = *s_q_data;
> +
> +     set_src_registers(ctx);
> +     set_dst_registers(ctx);
> +     ret = set_srcdst_params(ctx);
> +     if (ret)
> +             goto exit_fh;
> +
> +     ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
> +
> +     if (IS_ERR(ctx->m2m_ctx)) {
> +             ret = PTR_ERR(ctx->m2m_ctx);
> +             goto exit_fh;
> +     }
> +
> +     v4l2_fh_add(&ctx->fh);
> +
> +     /*
> +      * for now, just report the creation of the first instance, we can later
> +      * optimize the driver to enable or disable clocks when the first
> +      * instance is created or the last instance released
> +      */
> +     if (atomic_inc_return(&dev->num_instances) == 1)
> +             vpe_dbg(dev, "first instance created\n");
> +
> +     ctx->bufs_per_job = VPE_DEF_BUFS_PER_JOB;
> +
> +     ctx->load_mmrs = true;
> +
> +     vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n",
> +             ctx, ctx->m2m_ctx);
> +
> +     mutex_unlock(&dev->dev_mutex);
> +
> +     return 0;
> +exit_fh:
> +     v4l2_ctrl_handler_free(hdl);
> +     v4l2_fh_exit(&ctx->fh);
> +     vpdma_free_desc_buf(&ctx->mmr_adb);
> +free_desc_list:
> +     vpdma_free_desc_list(&ctx->desc_list);
> +unlock:
> +     mutex_unlock(&dev->dev_mutex);
> +free_ctx:
> +     kfree(ctx);
> +     return ret;
> +}
> +
> +static int vpe_release(struct file *file)
> +{
> +     struct vpe_dev *dev = video_drvdata(file);
> +     struct vpe_ctx *ctx = file2ctx(file);
> +
> +     vpe_dbg(dev, "releasing instance %p\n", ctx);
> +
> +     mutex_lock(&dev->dev_mutex);
> +     vpdma_free_desc_list(&ctx->desc_list);
> +     vpdma_free_desc_buf(&ctx->mmr_adb);
> +
> +     v4l2_fh_del(&ctx->fh);
> +     v4l2_fh_exit(&ctx->fh);
> +     v4l2_ctrl_handler_free(&ctx->hdl);
> +     v4l2_m2m_ctx_release(ctx->m2m_ctx);
> +
> +     kfree(ctx);
> +
> +     /*
> +      * for now, just report the release of the last instance, we can later
> +      * optimize the driver to enable or disable clocks when the first
> +      * instance is created or the last instance released
> +      */
> +     if (atomic_dec_return(&dev->num_instances) == 0)
> +             vpe_dbg(dev, "last instance released\n");
> +
> +     mutex_unlock(&dev->dev_mutex);
> +
> +     return 0;
> +}
> +
> +static unsigned int vpe_poll(struct file *file,
> +                          struct poll_table_struct *wait)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +     struct vpe_dev *dev = ctx->dev;
> +     int ret;
> +
> +     mutex_lock(&dev->dev_mutex);
> +     ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
> +     mutex_unlock(&dev->dev_mutex);
> +     return ret;
> +}
> +
> +static int vpe_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +     struct vpe_ctx *ctx = file2ctx(file);
> +     struct vpe_dev *dev = ctx->dev;
> +     int ret;
> +
> +     if (mutex_lock_interruptible(&dev->dev_mutex))
> +             return -ERESTARTSYS;
> +     ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
> +     mutex_unlock(&dev->dev_mutex);
> +     return ret;
> +}
> +
> +static const struct v4l2_file_operations vpe_fops = {
> +     .owner          = THIS_MODULE,
> +     .open           = vpe_open,
> +     .release        = vpe_release,
> +     .poll           = vpe_poll,
> +     .unlocked_ioctl = video_ioctl2,
> +     .mmap           = vpe_mmap,
> +};
> +
> +static struct video_device vpe_videodev = {
> +     .name           = VPE_MODULE_NAME,
> +     .fops           = &vpe_fops,
> +     .ioctl_ops      = &vpe_ioctl_ops,
> +     .minor          = -1,
> +     .release        = video_device_release,
> +     .vfl_dir        = VFL_DIR_M2M,
> +};
> +
> +static struct v4l2_m2m_ops m2m_ops = {
> +     .device_run     = device_run,
> +     .job_ready      = job_ready,
> +     .job_abort      = job_abort,
> +     .lock           = vpe_lock,
> +     .unlock         = vpe_unlock,
> +};
> +
> +static int vpe_runtime_get(struct platform_device *pdev)
> +{
> +     int r;
> +
> +     dev_dbg(&pdev->dev, "vpe_runtime_get\n");
> +
> +     r = pm_runtime_get_sync(&pdev->dev);
> +     WARN_ON(r < 0);
> +     return r < 0 ? r : 0;
> +}
> +
> +static void vpe_runtime_put(struct platform_device *pdev)
> +{
> +
> +     int r;
> +
> +     dev_dbg(&pdev->dev, "vpe_runtime_put\n");
> +
> +     r = pm_runtime_put_sync(&pdev->dev);
> +     WARN_ON(r < 0 && r != -ENOSYS);
> +}
> +
> +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);
> +
> +     spin_lock_init(&dev->lock);
> +
> +     ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> +     if (ret)
> +             return ret;
> +
> +     atomic_set(&dev->num_instances, 0);
> +     mutex_init(&dev->dev_mutex);
> +
> +     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpe");
> +     dev->base = devm_ioremap_resource(&pdev->dev, res);
> +     if (IS_ERR(dev->base)) {
> +             ret = PTR_ERR(dev->base);
> +             goto v4l2_dev_unreg;
> +     }
> +
> +     irq = platform_get_irq(pdev, 0);
> +     ret = devm_request_irq(&pdev->dev, irq, vpe_irq, 0, VPE_MODULE_NAME,
> +                     dev);
> +     if (ret)
> +             goto v4l2_dev_unreg;
> +
> +     platform_set_drvdata(pdev, dev);
> +
> +     dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> +     if (IS_ERR(dev->alloc_ctx)) {
> +             vpe_err(dev, "Failed to alloc vb2 context\n");
> +             ret = PTR_ERR(dev->alloc_ctx);
> +             goto v4l2_dev_unreg;
> +     }
> +
> +     dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
> +     if (IS_ERR(dev->m2m_dev)) {
> +             vpe_err(dev, "Failed to init mem2mem device\n");
> +             ret = PTR_ERR(dev->m2m_dev);
> +             goto rel_ctx;
> +     }
> +
> +     pm_runtime_enable(&pdev->dev);
> +
> +     ret = vpe_runtime_get(pdev);
> +     if (ret)
> +             goto rel_m2m;
> +
> +     /* Perform clk enable followed by reset */
> +     vpe_set_clock_enable(dev, 1);
> +
> +     vpe_top_reset(dev);
> +
> +     func = read_field_reg(dev, VPE_PID, VPE_PID_FUNC_MASK,
> +             VPE_PID_FUNC_SHIFT);
> +     vpe_dbg(dev, "VPE PID function %x\n", func);
> +
> +     vpe_top_vpdma_reset(dev);
> +
> +     dev->vpdma = vpdma_create(pdev);
> +     if (IS_ERR(dev->vpdma))
> +             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");
> +             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);
> +
> +     return 0;
> +
> +runtime_put:
> +     vpe_runtime_put(pdev);
> +rel_m2m:
> +     pm_runtime_disable(&pdev->dev);
> +     v4l2_m2m_release(dev->m2m_dev);
> +rel_ctx:
> +     vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
> +v4l2_dev_unreg:
> +     v4l2_device_unregister(&dev->v4l2_dev);
> +
> +     return ret;
> +}
> +
> +static int vpe_remove(struct platform_device *pdev)
> +{
> +     struct vpe_dev *dev =
> +             (struct vpe_dev *) platform_get_drvdata(pdev);
> +
> +     v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME);
> +
> +     v4l2_m2m_release(dev->m2m_dev);
> +     video_unregister_device(&dev->vfd);
> +     v4l2_device_unregister(&dev->v4l2_dev);
> +     vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
> +
> +     vpe_set_clock_enable(dev, 0);
> +     vpe_runtime_put(pdev);
> +     pm_runtime_disable(&pdev->dev);
> +
> +     return 0;
> +}
> +
> +#if defined(CONFIG_OF)
> +static const struct of_device_id vpe_of_match[] = {
> +     {
> +             .compatible = "ti,vpe",
> +     },
> +     {},
> +};
> +#else
> +#define vpe_of_match NULL
> +#endif
> +
> +static struct platform_driver vpe_pdrv = {
> +     .probe          = vpe_probe,
> +     .remove         = vpe_remove,
> +     .driver         = {
> +             .name   = VPE_MODULE_NAME,
> +             .owner  = THIS_MODULE,
> +             .of_match_table = vpe_of_match,
> +     },
> +};
> +
> +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_DESCRIPTION("TI VPE driver");
> +MODULE_AUTHOR("Dale Farnsworth, <d...@farnsworth.org>");
> +MODULE_LICENSE("GPL");

Regards,

        Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to