On 01/07/2017 03:11 AM, Steve Longerbeam wrote:
> This is a set of three media entity subdevice drivers for the i.MX
> Image Converter. The i.MX IC module contains three independent
> "tasks":
> 
> - Pre-processing Encode task: video frames are routed directly from
>   the CSI and can be scaled, color-space converted, and rotated.
>   Scaled output is limited to 1024x1024 resolution. Output frames
>   are routed to the camera interface entities (camif).
> 
> - Pre-processing Viewfinder task: this task can perform the same
>   conversions as the pre-process encode task, but in addition can
>   be used for hardware motion compensated deinterlacing. Frames can
>   come either directly from the CSI or from the SMFC entities (memory
>   buffers via IDMAC channels). Scaled output is limited to 1024x1024
>   resolution. Output frames can be routed to various sinks including
>   the post-processing task entities.
> 
> - Post-processing task: same conversions as pre-process encode. However
>   this entity sends frames to the i.MX IPU image converter which supports
>   image tiling, which allows scaled output up to 4096x4096 resolution.
>   Output frames can be routed to the camera interfaces.
> 
> Signed-off-by: Steve Longerbeam <steve_longerb...@mentor.com>
> ---
>  drivers/staging/media/imx/Makefile        |    2 +
>  drivers/staging/media/imx/imx-ic-common.c |  109 +++
>  drivers/staging/media/imx/imx-ic-pp.c     |  636 ++++++++++++++++
>  drivers/staging/media/imx/imx-ic-prpenc.c | 1033 +++++++++++++++++++++++++
>  drivers/staging/media/imx/imx-ic-prpvf.c  | 1179 
> +++++++++++++++++++++++++++++
>  drivers/staging/media/imx/imx-ic.h        |   38 +
>  6 files changed, 2997 insertions(+)
>  create mode 100644 drivers/staging/media/imx/imx-ic-common.c
>  create mode 100644 drivers/staging/media/imx/imx-ic-pp.c
>  create mode 100644 drivers/staging/media/imx/imx-ic-prpenc.c
>  create mode 100644 drivers/staging/media/imx/imx-ic-prpvf.c
>  create mode 100644 drivers/staging/media/imx/imx-ic.h
> 
> diff --git a/drivers/staging/media/imx/Makefile 
> b/drivers/staging/media/imx/Makefile
> index 3559d7b..d2a962c 100644
> --- a/drivers/staging/media/imx/Makefile
> +++ b/drivers/staging/media/imx/Makefile
> @@ -1,8 +1,10 @@
>  imx-media-objs := imx-media-dev.o imx-media-fim.o imx-media-internal-sd.o \
>       imx-media-of.o
> +imx-ic-objs := imx-ic-common.o imx-ic-prpenc.o imx-ic-prpvf.o imx-ic-pp.o
>  
>  obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o
>  obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o
> +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-ic.o
>  
>  obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
>  obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-smfc.o
> diff --git a/drivers/staging/media/imx/imx-ic-common.c 
> b/drivers/staging/media/imx/imx-ic-common.c
> new file mode 100644
> index 0000000..45706ca
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-ic-common.c
> @@ -0,0 +1,109 @@
> +/*
> + * V4L2 Image Converter Subdev for Freescale i.MX5/6 SOC
> + *
> + * Copyright (c) 2014-2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include "imx-media.h"
> +#include "imx-ic.h"
> +
> +static struct imx_ic_ops *ic_ops[IC_NUM_TASKS] = {
> +     [IC_TASK_ENCODER]        = &imx_ic_prpenc_ops,
> +     [IC_TASK_VIEWFINDER]     = &imx_ic_prpvf_ops,
> +     [IC_TASK_POST_PROCESSOR] = &imx_ic_pp_ops,
> +};
> +
> +static int imx_ic_probe(struct platform_device *pdev)
> +{
> +     struct imx_media_internal_sd_platformdata *pdata;
> +     struct imx_ic_priv *priv;
> +     int ret;
> +
> +     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +     if (!priv)
> +             return -ENOMEM;
> +
> +     platform_set_drvdata(pdev, &priv->sd);
> +     priv->dev = &pdev->dev;
> +
> +     /* get our ipu_id, grp_id and IC task id */
> +     pdata = priv->dev->platform_data;
> +     priv->ipu_id = pdata->ipu_id;
> +     switch (pdata->grp_id) {
> +     case IMX_MEDIA_GRP_ID_IC_PRPENC:
> +             priv->task_id = IC_TASK_ENCODER;
> +             break;
> +     case IMX_MEDIA_GRP_ID_IC_PRPVF:
> +             priv->task_id = IC_TASK_VIEWFINDER;
> +             break;
> +     case IMX_MEDIA_GRP_ID_IC_PP0...IMX_MEDIA_GRP_ID_IC_PP3:
> +             priv->task_id = IC_TASK_POST_PROCESSOR;
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     v4l2_subdev_init(&priv->sd, ic_ops[priv->task_id]->subdev_ops);
> +     v4l2_set_subdevdata(&priv->sd, priv);
> +     priv->sd.internal_ops = ic_ops[priv->task_id]->internal_ops;
> +     priv->sd.entity.ops = ic_ops[priv->task_id]->entity_ops;
> +     priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
> +     priv->sd.dev = &pdev->dev;
> +     priv->sd.owner = THIS_MODULE;
> +     priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
> +     priv->sd.grp_id = pdata->grp_id;
> +     strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name));
> +
> +     ret = ic_ops[priv->task_id]->init(priv);
> +     if (ret)
> +             return ret;
> +
> +     ret = v4l2_async_register_subdev(&priv->sd);
> +     if (ret)
> +             ic_ops[priv->task_id]->remove(priv);
> +
> +     return ret;
> +}
> +
> +static int imx_ic_remove(struct platform_device *pdev)
> +{
> +     struct v4l2_subdev *sd = platform_get_drvdata(pdev);
> +     struct imx_ic_priv *priv = container_of(sd, struct imx_ic_priv, sd);
> +
> +     ic_ops[priv->task_id]->remove(priv);
> +
> +     v4l2_async_unregister_subdev(&priv->sd);
> +     media_entity_cleanup(&priv->sd.entity);
> +     v4l2_device_unregister_subdev(sd);
> +
> +     return 0;
> +}
> +
> +static const struct platform_device_id imx_ic_ids[] = {
> +     { .name = "imx-ipuv3-ic" },
> +     { },
> +};
> +MODULE_DEVICE_TABLE(platform, imx_ic_ids);
> +
> +static struct platform_driver imx_ic_driver = {
> +     .probe = imx_ic_probe,
> +     .remove = imx_ic_remove,
> +     .id_table = imx_ic_ids,
> +     .driver = {
> +             .name = "imx-ipuv3-ic",
> +     },
> +};
> +module_platform_driver(imx_ic_driver);
> +
> +MODULE_DESCRIPTION("i.MX IC subdev driver");
> +MODULE_AUTHOR("Steve Longerbeam <steve_longerb...@mentor.com>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:imx-ipuv3-ic");
> diff --git a/drivers/staging/media/imx/imx-ic-pp.c 
> b/drivers/staging/media/imx/imx-ic-pp.c
> new file mode 100644
> index 0000000..1f75616
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-ic-pp.c
> @@ -0,0 +1,636 @@
> +/*
> + * V4L2 IC Post-Processor Subdev for Freescale i.MX5/6 SOC
> + *
> + * Copyright (c) 2014-2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <video/imx-ipu-image-convert.h>
> +#include <media/imx.h>
> +#include "imx-media.h"
> +#include "imx-ic.h"
> +
> +#define PP_NUM_PADS 2
> +
> +struct pp_priv {
> +     struct imx_media_dev *md;
> +     struct imx_ic_priv *ic_priv;
> +     int pp_id;
> +
> +     struct ipu_soc *ipu;
> +     struct ipu_image_convert_ctx *ic_ctx;
> +
> +     struct media_pad pad[PP_NUM_PADS];
> +     int input_pad;
> +     int output_pad;
> +
> +     /* our dma buffer sink ring */
> +     struct imx_media_dma_buf_ring *in_ring;
> +     /* the dma buffer ring we send to sink */
> +     struct imx_media_dma_buf_ring *out_ring;
> +     struct ipu_image_convert_run *out_run;
> +
> +     struct imx_media_dma_buf *inbuf; /* last input buffer */
> +
> +     bool stream_on;    /* streaming is on */
> +     bool stop;         /* streaming is stopping */
> +     spinlock_t irqlock;
> +
> +     struct v4l2_subdev *src_sd;
> +     struct v4l2_subdev *sink_sd;
> +
> +     struct v4l2_mbus_framefmt format_mbus[PP_NUM_PADS];
> +     const struct imx_media_pixfmt *cc[PP_NUM_PADS];
> +
> +     /* motion select control */
> +     struct v4l2_ctrl_handler ctrl_hdlr;
> +     int  rotation; /* degrees */
> +     bool hflip;
> +     bool vflip;
> +
> +     /* derived from rotation, hflip, vflip controls */
> +     enum ipu_rotate_mode rot_mode;
> +};
> +
> +static inline struct pp_priv *sd_to_priv(struct v4l2_subdev *sd)
> +{
> +     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> +
> +     return ic_priv->task_priv;
> +}
> +
> +static void pp_convert_complete(struct ipu_image_convert_run *run,
> +                             void *data)
> +{
> +     struct pp_priv *priv = data;
> +     struct imx_media_dma_buf *done;
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&priv->irqlock, flags);
> +
> +     done = imx_media_dma_buf_get_active(priv->out_ring);
> +     /* give the completed buffer to the sink */
> +     if (!WARN_ON(!done))
> +             imx_media_dma_buf_done(done, run->status ?
> +                                    IMX_MEDIA_BUF_STATUS_ERROR :
> +                                    IMX_MEDIA_BUF_STATUS_DONE);
> +
> +     /* we're done with the inbuf, queue it back */
> +     imx_media_dma_buf_queue(priv->in_ring, priv->inbuf->index);
> +
> +     spin_unlock_irqrestore(&priv->irqlock, flags);
> +}
> +
> +static void pp_queue_conversion(struct pp_priv *priv,
> +                             struct imx_media_dma_buf *inbuf)
> +{
> +     struct ipu_image_convert_run *run;
> +     struct imx_media_dma_buf *outbuf;
> +
> +     /* get next queued buffer and make it active */
> +     outbuf = imx_media_dma_buf_get_next_queued(priv->out_ring);
> +     imx_media_dma_buf_set_active(outbuf);
> +     priv->inbuf = inbuf;
> +
> +     run = &priv->out_run[outbuf->index];
> +     run->ctx = priv->ic_ctx;
> +     run->in_phys = inbuf->phys;
> +     run->out_phys = outbuf->phys;
> +     ipu_image_convert_queue(run);
> +}
> +
> +static long pp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
> +{
> +     struct pp_priv *priv = sd_to_priv(sd);
> +     struct imx_media_dma_buf_ring **ring;
> +     struct imx_media_dma_buf *buf;
> +     unsigned long flags;
> +
> +     switch (cmd) {
> +     case IMX_MEDIA_REQ_DMA_BUF_SINK_RING:
> +             /* src asks for a buffer ring */
> +             if (!priv->in_ring)
> +                     return -EINVAL;
> +             ring = (struct imx_media_dma_buf_ring **)arg;
> +             *ring = priv->in_ring;
> +             break;
> +     case IMX_MEDIA_NEW_DMA_BUF:
> +             /* src hands us a new buffer */
> +             spin_lock_irqsave(&priv->irqlock, flags);
> +             if (!priv->stop &&
> +                 !imx_media_dma_buf_get_active(priv->out_ring)) {
> +                     buf = imx_media_dma_buf_dequeue(priv->in_ring);
> +                     if (buf)
> +                             pp_queue_conversion(priv, buf);
> +             }
> +             spin_unlock_irqrestore(&priv->irqlock, flags);
> +             break;
> +     case IMX_MEDIA_REL_DMA_BUF_SINK_RING:
> +             /* src indicates sink buffer ring can be freed */
> +             if (!priv->in_ring)
> +                     return 0;
> +             v4l2_info(sd, "%s: freeing sink ring\n", __func__);
> +             imx_media_free_dma_buf_ring(priv->in_ring);
> +             priv->in_ring = NULL;
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static int pp_start(struct pp_priv *priv)
> +{
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     struct ipu_image image_in, image_out;
> +     const struct imx_media_pixfmt *incc;
> +     struct v4l2_mbus_framefmt *infmt;
> +     int i, in_size, ret;
> +
> +     /* ask the sink for the buffer ring */
> +     ret = v4l2_subdev_call(priv->sink_sd, core, ioctl,
> +                            IMX_MEDIA_REQ_DMA_BUF_SINK_RING,
> +                            &priv->out_ring);
> +     if (ret)
> +             return ret;
> +
> +     imx_media_mbus_fmt_to_ipu_image(&image_in,
> +                                     &priv->format_mbus[priv->input_pad]);
> +     imx_media_mbus_fmt_to_ipu_image(&image_out,
> +                                     &priv->format_mbus[priv->output_pad]);
> +
> +     priv->ipu = priv->md->ipu[ic_priv->ipu_id];
> +     priv->ic_ctx = ipu_image_convert_prepare(priv->ipu,
> +                                              IC_TASK_POST_PROCESSOR,
> +                                              &image_in, &image_out,
> +                                              priv->rot_mode,
> +                                              pp_convert_complete, priv);
> +     if (IS_ERR(priv->ic_ctx))
> +             return PTR_ERR(priv->ic_ctx);
> +
> +     infmt = &priv->format_mbus[priv->input_pad];
> +     incc = priv->cc[priv->input_pad];
> +     in_size = (infmt->width * incc->bpp * infmt->height) >> 3;
> +
> +     if (priv->in_ring) {
> +             v4l2_warn(&ic_priv->sd, "%s: dma-buf ring was not freed\n",
> +                       __func__);
> +             imx_media_free_dma_buf_ring(priv->in_ring);
> +     }
> +
> +     priv->in_ring = imx_media_alloc_dma_buf_ring(priv->md,
> +                                                  &priv->src_sd->entity,
> +                                                  &ic_priv->sd.entity,
> +                                                  in_size,
> +                                                  IMX_MEDIA_MIN_RING_BUFS,
> +                                                  true);
> +     if (IS_ERR(priv->in_ring)) {
> +             v4l2_err(&ic_priv->sd,
> +                      "failed to alloc dma-buf ring\n");
> +             ret = PTR_ERR(priv->in_ring);
> +             priv->in_ring = NULL;
> +             goto out_unprep;
> +     }
> +
> +     for (i = 0; i < IMX_MEDIA_MIN_RING_BUFS; i++)
> +             imx_media_dma_buf_queue(priv->in_ring, i);
> +
> +     priv->out_run = kzalloc(IMX_MEDIA_MAX_RING_BUFS *
> +                             sizeof(*priv->out_run), GFP_KERNEL);
> +     if (!priv->out_run) {
> +             v4l2_err(&ic_priv->sd, "failed to alloc src ring runs\n");
> +             ret = -ENOMEM;
> +             goto out_free_ring;
> +     }
> +
> +     priv->stop = false;
> +
> +     return 0;
> +
> +out_free_ring:
> +     imx_media_free_dma_buf_ring(priv->in_ring);
> +     priv->in_ring = NULL;
> +out_unprep:
> +     ipu_image_convert_unprepare(priv->ic_ctx);
> +     return ret;
> +}
> +
> +static void pp_stop(struct pp_priv *priv)
> +{
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&priv->irqlock, flags);
> +     priv->stop = true;
> +     spin_unlock_irqrestore(&priv->irqlock, flags);
> +
> +     ipu_image_convert_unprepare(priv->ic_ctx);
> +     kfree(priv->out_run);
> +
> +     priv->out_ring = NULL;
> +
> +     /* inform sink that its sink buffer ring can now be freed */
> +     v4l2_subdev_call(priv->sink_sd, core, ioctl,
> +                      IMX_MEDIA_REL_DMA_BUF_SINK_RING, 0);
> +}
> +
> +static int pp_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +     struct pp_priv *priv = sd_to_priv(sd);
> +     int ret = 0;
> +
> +     if (!priv->src_sd || !priv->sink_sd)
> +             return -EPIPE;
> +
> +     v4l2_info(sd, "stream %s\n", enable ? "ON" : "OFF");
> +
> +     if (enable && !priv->stream_on)
> +             ret = pp_start(priv);
> +     else if (!enable && priv->stream_on)
> +             pp_stop(priv);
> +
> +     if (!ret)
> +             priv->stream_on = enable;
> +     return ret;
> +}
> +
> +static int pp_enum_mbus_code(struct v4l2_subdev *sd,
> +                          struct v4l2_subdev_pad_config *cfg,
> +                          struct v4l2_subdev_mbus_code_enum *code)
> +{
> +     const struct imx_media_pixfmt *cc;
> +     u32 fourcc;
> +     int ret;
> +
> +     if (code->pad >= PP_NUM_PADS)
> +             return -EINVAL;
> +
> +     ret = ipu_image_convert_enum_format(code->index, &fourcc);
> +     if (ret)
> +             return ret;
> +
> +     /* convert returned fourcc to mbus code */
> +     cc = imx_media_find_format(fourcc, 0, true, true);
> +     if (WARN_ON(!cc))
> +             return -EINVAL;
> +
> +     code->code = cc->codes[0];
> +     return 0;
> +}
> +
> +static int pp_get_fmt(struct v4l2_subdev *sd,
> +                   struct v4l2_subdev_pad_config *cfg,
> +                   struct v4l2_subdev_format *sdformat)
> +{
> +     struct pp_priv *priv = sd_to_priv(sd);
> +
> +     if (sdformat->pad >= PP_NUM_PADS)
> +             return -EINVAL;
> +
> +     sdformat->format = priv->format_mbus[sdformat->pad];
> +
> +     return 0;
> +}
> +
> +static int pp_set_fmt(struct v4l2_subdev *sd,
> +                   struct v4l2_subdev_pad_config *cfg,
> +                   struct v4l2_subdev_format *sdformat)
> +{
> +     struct pp_priv *priv = sd_to_priv(sd);
> +     struct v4l2_mbus_framefmt *infmt, *outfmt;
> +     const struct imx_media_pixfmt *cc;
> +     struct ipu_image test_in, test_out;
> +     u32 code;
> +
> +     if (sdformat->pad >= PP_NUM_PADS)
> +             return -EINVAL;
> +
> +     if (priv->stream_on)
> +             return -EBUSY;
> +
> +     infmt = &priv->format_mbus[priv->input_pad];
> +     outfmt = &priv->format_mbus[priv->output_pad];
> +
> +     cc = imx_media_find_format(0, sdformat->format.code, true, true);
> +     if (!cc) {
> +             imx_media_enum_format(&code, 0, true, true);
> +             cc = imx_media_find_format(0, code, true, true);
> +             sdformat->format.code = cc->codes[0];
> +     }
> +
> +     if (sdformat->pad == priv->output_pad) {
> +             imx_media_mbus_fmt_to_ipu_image(&test_out, &sdformat->format);
> +             imx_media_mbus_fmt_to_ipu_image(&test_in, infmt);
> +             ipu_image_convert_adjust(&test_in, &test_out, priv->rot_mode);
> +             imx_media_ipu_image_to_mbus_fmt(&sdformat->format, &test_out);
> +     } else {
> +             imx_media_mbus_fmt_to_ipu_image(&test_in, &sdformat->format);
> +             imx_media_mbus_fmt_to_ipu_image(&test_out, outfmt);
> +             ipu_image_convert_adjust(&test_in, &test_out, priv->rot_mode);
> +             imx_media_ipu_image_to_mbus_fmt(&sdformat->format, &test_in);
> +     }
> +
> +     if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
> +             cfg->try_fmt = sdformat->format;
> +     } else {
> +             if (sdformat->pad == priv->output_pad) {
> +                     *outfmt = sdformat->format;
> +                     imx_media_ipu_image_to_mbus_fmt(infmt, &test_in);
> +             } else {
> +                     *infmt = sdformat->format;
> +                     imx_media_ipu_image_to_mbus_fmt(outfmt, &test_out);
> +             }
> +             priv->cc[sdformat->pad] = cc;
> +     }
> +
> +     return 0;
> +}
> +
> +static int pp_link_setup(struct media_entity *entity,
> +                      const struct media_pad *local,
> +                      const struct media_pad *remote, u32 flags)
> +{
> +     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> +     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> +     struct pp_priv *priv = ic_priv->task_priv;
> +     struct v4l2_subdev *remote_sd;
> +
> +     dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name,
> +             local->entity->name);
> +
> +     remote_sd = media_entity_to_v4l2_subdev(remote->entity);
> +
> +     if (local->flags & MEDIA_PAD_FL_SOURCE) {
> +             if (flags & MEDIA_LNK_FL_ENABLED) {
> +                     if (priv->sink_sd)
> +                             return -EBUSY;
> +                     priv->sink_sd = remote_sd;
> +             } else {
> +                     priv->sink_sd = NULL;
> +             }
> +     } else {
> +             if (flags & MEDIA_LNK_FL_ENABLED) {
> +                     if (priv->src_sd)
> +                             return -EBUSY;
> +                     priv->src_sd = remote_sd;
> +             } else {
> +                     priv->src_sd = NULL;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +static int pp_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +     struct pp_priv *priv = container_of(ctrl->handler,
> +                                            struct pp_priv, ctrl_hdlr);
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     enum ipu_rotate_mode rot_mode;
> +     bool hflip, vflip;
> +     int rotation, ret;
> +
> +     rotation = priv->rotation;
> +     hflip = priv->hflip;
> +     vflip = priv->vflip;
> +
> +     switch (ctrl->id) {
> +     case V4L2_CID_HFLIP:
> +             hflip = (ctrl->val == 1);
> +             break;
> +     case V4L2_CID_VFLIP:
> +             vflip = (ctrl->val == 1);
> +             break;
> +     case V4L2_CID_ROTATE:
> +             rotation = ctrl->val;
> +             break;
> +     default:
> +             v4l2_err(&ic_priv->sd, "Invalid control\n");
> +             return -EINVAL;
> +     }
> +
> +     ret = ipu_degrees_to_rot_mode(&rot_mode, rotation, hflip, vflip);
> +     if (ret)
> +             return ret;
> +
> +     if (rot_mode != priv->rot_mode) {
> +             struct v4l2_mbus_framefmt *infmt, *outfmt;
> +             struct ipu_image test_in, test_out;
> +
> +             /* can't change rotation mid-streaming */
> +             if (priv->stream_on)
> +                     return -EBUSY;
> +
> +             /*
> +              * make sure this rotation will work with current input/output
> +              * formats before setting
> +              */
> +             infmt = &priv->format_mbus[priv->input_pad];
> +             outfmt = &priv->format_mbus[priv->output_pad];
> +             imx_media_mbus_fmt_to_ipu_image(&test_in, infmt);
> +             imx_media_mbus_fmt_to_ipu_image(&test_out, outfmt);
> +
> +             ret = ipu_image_convert_verify(&test_in, &test_out, rot_mode);
> +             if (ret)
> +                     return ret;
> +
> +             priv->rot_mode = rot_mode;
> +             priv->rotation = rotation;
> +             priv->hflip = hflip;
> +             priv->vflip = vflip;
> +     }
> +
> +     return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops pp_ctrl_ops = {
> +     .s_ctrl = pp_s_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config pp_std_ctrl[] = {
> +     {
> +             .id = V4L2_CID_HFLIP,
> +             .name = "Horizontal Flip",
> +             .type = V4L2_CTRL_TYPE_BOOLEAN,
> +             .def =  0,
> +             .min =  0,
> +             .max =  1,
> +             .step = 1,
> +     }, {
> +             .id = V4L2_CID_VFLIP,
> +             .name = "Vertical Flip",
> +             .type = V4L2_CTRL_TYPE_BOOLEAN,
> +             .def =  0,
> +             .min =  0,
> +             .max =  1,
> +             .step = 1,
> +     }, {
> +             .id = V4L2_CID_ROTATE,
> +             .name = "Rotation",
> +             .type = V4L2_CTRL_TYPE_INTEGER,
> +             .def =   0,
> +             .min =   0,
> +             .max = 270,
> +             .step = 90,
> +     },
> +};
> +
> +#define PP_NUM_CONTROLS ARRAY_SIZE(pp_std_ctrl)
> +
> +static int pp_init_controls(struct pp_priv *priv)
> +{
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr;
> +     const struct v4l2_ctrl_config *c;
> +     int i, ret;
> +
> +     v4l2_ctrl_handler_init(hdlr, PP_NUM_CONTROLS);
> +
> +     for (i = 0; i < PP_NUM_CONTROLS; i++) {
> +             c = &pp_std_ctrl[i];
> +             v4l2_ctrl_new_std(hdlr, &pp_ctrl_ops,
> +                               c->id, c->min, c->max, c->step, c->def);
> +     }
> +
> +     ic_priv->sd.ctrl_handler = hdlr;
> +
> +     if (hdlr->error) {
> +             ret = hdlr->error;
> +             v4l2_ctrl_handler_free(hdlr);
> +             return ret;
> +     }
> +
> +     v4l2_ctrl_handler_setup(hdlr);
> +
> +     return 0;
> +}
> +
> +/*
> + * retrieve our pads parsed from the OF graph by the media device
> + */
> +static int pp_registered(struct v4l2_subdev *sd)
> +{
> +     struct pp_priv *priv = sd_to_priv(sd);
> +     struct imx_media_subdev *imxsd;
> +     struct imx_media_pad *pad;
> +     int i, ret;
> +
> +     /* get media device */
> +     priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
> +
> +     imxsd = imx_media_find_subdev_by_sd(priv->md, sd);
> +     if (IS_ERR(imxsd))
> +             return PTR_ERR(imxsd);
> +
> +     if (imxsd->num_sink_pads != 1 || imxsd->num_src_pads != 1)
> +             return -EINVAL;
> +
> +     for (i = 0; i < PP_NUM_PADS; i++) {
> +             pad = &imxsd->pad[i];
> +             priv->pad[i] = pad->pad;
> +             if (priv->pad[i].flags & MEDIA_PAD_FL_SINK)
> +                     priv->input_pad = i;
> +             else
> +                     priv->output_pad = i;
> +
> +             /* set a default mbus format  */
> +             ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
> +                                           640, 480, 0, V4L2_FIELD_NONE,
> +                                           &priv->cc[i]);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     ret = pp_init_controls(priv);
> +     if (ret)
> +             return ret;
> +
> +     ret = media_entity_pads_init(&sd->entity, PP_NUM_PADS, priv->pad);
> +     if (ret)
> +             goto free_ctrls;
> +
> +     return 0;
> +free_ctrls:
> +     v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
> +     return ret;
> +}
> +
> +static struct v4l2_subdev_pad_ops pp_pad_ops = {
> +     .enum_mbus_code = pp_enum_mbus_code,
> +     .get_fmt = pp_get_fmt,
> +     .set_fmt = pp_set_fmt,
> +};
> +
> +static struct v4l2_subdev_video_ops pp_video_ops = {
> +     .s_stream = pp_s_stream,
> +};
> +
> +static struct v4l2_subdev_core_ops pp_core_ops = {
> +     .ioctl = pp_ioctl,
> +};
> +
> +static struct media_entity_operations pp_entity_ops = {
> +     .link_setup = pp_link_setup,
> +     .link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static struct v4l2_subdev_ops pp_subdev_ops = {
> +     .video = &pp_video_ops,
> +     .pad = &pp_pad_ops,
> +     .core = &pp_core_ops,
> +};
> +
> +static struct v4l2_subdev_internal_ops pp_internal_ops = {
> +     .registered = pp_registered,
> +};
> +
> +static int pp_init(struct imx_ic_priv *ic_priv)
> +{
> +     struct pp_priv *priv;
> +
> +     priv = devm_kzalloc(ic_priv->dev, sizeof(*priv), GFP_KERNEL);
> +     if (!priv)
> +             return -ENOMEM;
> +
> +     ic_priv->task_priv = priv;
> +     priv->ic_priv = ic_priv;
> +     spin_lock_init(&priv->irqlock);
> +
> +     /* get our PP id */
> +     priv->pp_id = (ic_priv->sd.grp_id >> IMX_MEDIA_GRP_ID_IC_PP_BIT) - 1;
> +
> +     return 0;
> +}
> +
> +static void pp_remove(struct imx_ic_priv *ic_priv)
> +{
> +     struct pp_priv *priv = ic_priv->task_priv;
> +
> +     v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
> +}
> +
> +struct imx_ic_ops imx_ic_pp_ops = {
> +     .subdev_ops = &pp_subdev_ops,
> +     .internal_ops = &pp_internal_ops,
> +     .entity_ops = &pp_entity_ops,
> +     .init = pp_init,
> +     .remove = pp_remove,
> +};
> diff --git a/drivers/staging/media/imx/imx-ic-prpenc.c 
> b/drivers/staging/media/imx/imx-ic-prpenc.c
> new file mode 100644
> index 0000000..3d85a82
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-ic-prpenc.c
> @@ -0,0 +1,1033 @@
> +/*
> + * V4L2 Capture IC Encoder Subdev for Freescale i.MX5/6 SOC
> + *
> + * This subdevice handles capture of video frames from the CSI, which
> + * are routed directly to the Image Converter preprocess encode task,
> + * for resizing, colorspace conversion, and rotation.
> + *
> + * Copyright (c) 2012-2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/timer.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/imx.h>
> +#include "imx-media.h"
> +#include "imx-ic.h"
> +
> +#define PRPENC_NUM_PADS 2
> +
> +#define MAX_W_IC   1024
> +#define MAX_H_IC   1024
> +#define MAX_W_SINK 4096
> +#define MAX_H_SINK 4096
> +
> +struct prpenc_priv {
> +     struct imx_media_dev *md;
> +     struct imx_ic_priv *ic_priv;
> +
> +     /* IPU units we require */
> +     struct ipu_soc *ipu;
> +     struct ipu_ic *ic_enc;
> +
> +     struct media_pad pad[PRPENC_NUM_PADS];
> +     int input_pad;
> +     int output_pad;
> +
> +     struct ipuv3_channel *enc_ch;
> +     struct ipuv3_channel *enc_rot_in_ch;
> +     struct ipuv3_channel *enc_rot_out_ch;
> +
> +     /* the dma buffer ring to send to sink */
> +     struct imx_media_dma_buf_ring *out_ring;
> +     struct imx_media_dma_buf *next;
> +
> +     int ipu_buf_num;  /* ipu double buffer index: 0-1 */
> +
> +     struct v4l2_subdev *src_sd;
> +     struct v4l2_subdev *sink_sd;
> +
> +     /* the CSI id at link validate */
> +     int csi_id;
> +
> +     /* the attached sensor at stream on */
> +     struct imx_media_subdev *sensor;
> +
> +     struct v4l2_mbus_framefmt format_mbus[PRPENC_NUM_PADS];
> +     const struct imx_media_pixfmt *cc[PRPENC_NUM_PADS];
> +
> +     struct imx_media_dma_buf rot_buf[2];
> +
> +     /* controls */
> +     struct v4l2_ctrl_handler ctrl_hdlr;
> +     int  rotation; /* degrees */
> +     bool hflip;
> +     bool vflip;
> +
> +     /* derived from rotation, hflip, vflip controls */
> +     enum ipu_rotate_mode rot_mode;
> +
> +     spinlock_t irqlock;
> +
> +     struct timer_list eof_timeout_timer;
> +     int eof_irq;
> +     int nfb4eof_irq;
> +
> +     bool stream_on; /* streaming is on */
> +     bool last_eof;  /* waiting for last EOF at stream off */
> +     struct completion last_eof_comp;
> +};
> +
> +static inline struct prpenc_priv *sd_to_priv(struct v4l2_subdev *sd)
> +{
> +     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> +
> +     return ic_priv->task_priv;
> +}
> +
> +static void prpenc_put_ipu_resources(struct prpenc_priv *priv)
> +{
> +     if (!IS_ERR_OR_NULL(priv->ic_enc))
> +             ipu_ic_put(priv->ic_enc);
> +     priv->ic_enc = NULL;
> +
> +     if (!IS_ERR_OR_NULL(priv->enc_ch))
> +             ipu_idmac_put(priv->enc_ch);
> +     priv->enc_ch = NULL;
> +
> +     if (!IS_ERR_OR_NULL(priv->enc_rot_in_ch))
> +             ipu_idmac_put(priv->enc_rot_in_ch);
> +     priv->enc_rot_in_ch = NULL;
> +
> +     if (!IS_ERR_OR_NULL(priv->enc_rot_out_ch))
> +             ipu_idmac_put(priv->enc_rot_out_ch);
> +     priv->enc_rot_out_ch = NULL;
> +}
> +
> +static int prpenc_get_ipu_resources(struct prpenc_priv *priv)
> +{
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     int ret;
> +
> +     priv->ipu = priv->md->ipu[ic_priv->ipu_id];
> +
> +     priv->ic_enc = ipu_ic_get(priv->ipu, IC_TASK_ENCODER);
> +     if (IS_ERR(priv->ic_enc)) {
> +             v4l2_err(&ic_priv->sd, "failed to get IC ENC\n");
> +             ret = PTR_ERR(priv->ic_enc);
> +             goto out;
> +     }
> +
> +     priv->enc_ch = ipu_idmac_get(priv->ipu,
> +                                  IPUV3_CHANNEL_IC_PRP_ENC_MEM);
> +     if (IS_ERR(priv->enc_ch)) {
> +             v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
> +                      IPUV3_CHANNEL_IC_PRP_ENC_MEM);
> +             ret = PTR_ERR(priv->enc_ch);
> +             goto out;
> +     }
> +
> +     priv->enc_rot_in_ch = ipu_idmac_get(priv->ipu,
> +                                         IPUV3_CHANNEL_MEM_ROT_ENC);
> +     if (IS_ERR(priv->enc_rot_in_ch)) {
> +             v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
> +                      IPUV3_CHANNEL_MEM_ROT_ENC);
> +             ret = PTR_ERR(priv->enc_rot_in_ch);
> +             goto out;
> +     }
> +
> +     priv->enc_rot_out_ch = ipu_idmac_get(priv->ipu,
> +                                          IPUV3_CHANNEL_ROT_ENC_MEM);
> +     if (IS_ERR(priv->enc_rot_out_ch)) {
> +             v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n",
> +                      IPUV3_CHANNEL_ROT_ENC_MEM);
> +             ret = PTR_ERR(priv->enc_rot_out_ch);
> +             goto out;
> +     }
> +
> +     return 0;
> +out:
> +     prpenc_put_ipu_resources(priv);
> +     return ret;
> +}
> +
> +static irqreturn_t prpenc_eof_interrupt(int irq, void *dev_id)
> +{
> +     struct prpenc_priv *priv = dev_id;
> +     struct imx_media_dma_buf *done, *next;
> +     struct ipuv3_channel *channel;
> +
> +     spin_lock(&priv->irqlock);
> +
> +     if (priv->last_eof) {
> +             complete(&priv->last_eof_comp);
> +             priv->last_eof = false;
> +             goto unlock;
> +     }
> +
> +     /* inform CSI of this EOF so it can monitor frame intervals */
> +     v4l2_subdev_call(priv->src_sd, core, interrupt_service_routine,
> +                      0, NULL);
> +
> +     channel = (ipu_rot_mode_is_irt(priv->rot_mode)) ?
> +             priv->enc_rot_out_ch : priv->enc_ch;
> +
> +     done = imx_media_dma_buf_get_active(priv->out_ring);
> +     /* give the completed buffer to the sink  */
> +     if (!WARN_ON(!done))
> +             imx_media_dma_buf_done(done, IMX_MEDIA_BUF_STATUS_DONE);
> +
> +     /* priv->next buffer is now the active one */
> +     imx_media_dma_buf_set_active(priv->next);
> +
> +     /* bump the EOF timeout timer */
> +     mod_timer(&priv->eof_timeout_timer,
> +               jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
> +
> +     if (ipu_idmac_buffer_is_ready(channel, priv->ipu_buf_num))
> +             ipu_idmac_clear_buffer(channel, priv->ipu_buf_num);
> +
> +     /* get next queued buffer */
> +     next = imx_media_dma_buf_get_next_queued(priv->out_ring);
> +
> +     ipu_cpmem_set_buffer(channel, priv->ipu_buf_num, next->phys);
> +     ipu_idmac_select_buffer(channel, priv->ipu_buf_num);
> +
> +     /* toggle IPU double-buffer index */
> +     priv->ipu_buf_num ^= 1;
> +     priv->next = next;
> +
> +unlock:
> +     spin_unlock(&priv->irqlock);
> +     return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t prpenc_nfb4eof_interrupt(int irq, void *dev_id)
> +{
> +     struct prpenc_priv *priv = dev_id;
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     static const struct v4l2_event ev = {
> +             .type = V4L2_EVENT_IMX_NFB4EOF,
> +     };
> +
> +     v4l2_err(&ic_priv->sd, "NFB4EOF\n");
> +
> +     v4l2_subdev_notify_event(&ic_priv->sd, &ev);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +/*
> + * EOF timeout timer function.
> + */
> +static void prpenc_eof_timeout(unsigned long data)
> +{
> +     struct prpenc_priv *priv = (struct prpenc_priv *)data;
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     static const struct v4l2_event ev = {
> +             .type = V4L2_EVENT_IMX_EOF_TIMEOUT,
> +     };
> +
> +     v4l2_err(&ic_priv->sd, "EOF timeout\n");
> +
> +     v4l2_subdev_notify_event(&ic_priv->sd, &ev);
> +}
> +
> +static void prpenc_setup_channel(struct prpenc_priv *priv,
> +                              struct ipuv3_channel *channel,
> +                              enum ipu_rotate_mode rot_mode,
> +                              dma_addr_t addr0, dma_addr_t addr1,
> +                              bool rot_swap_width_height)
> +{
> +     struct v4l2_mbus_framefmt *infmt, *outfmt;
> +     unsigned int burst_size;
> +     struct ipu_image image;
> +
> +     infmt = &priv->format_mbus[priv->input_pad];
> +     outfmt = &priv->format_mbus[priv->output_pad];
> +
> +     if (rot_swap_width_height)
> +             swap(outfmt->width, outfmt->height);
> +
> +     ipu_cpmem_zero(channel);
> +
> +     imx_media_mbus_fmt_to_ipu_image(&image, outfmt);
> +
> +     image.phys0 = addr0;
> +     image.phys1 = addr1;
> +     ipu_cpmem_set_image(channel, &image);
> +
> +     if (channel == priv->enc_rot_in_ch ||
> +         channel == priv->enc_rot_out_ch) {
> +             burst_size = 8;
> +             ipu_cpmem_set_block_mode(channel);
> +     } else {
> +             burst_size = (outfmt->width & 0xf) ? 8 : 16;
> +     }
> +
> +     ipu_cpmem_set_burstsize(channel, burst_size);
> +
> +     if (rot_mode)
> +             ipu_cpmem_set_rotation(channel, rot_mode);
> +
> +     if (outfmt->field == V4L2_FIELD_NONE &&
> +         (V4L2_FIELD_HAS_BOTH(infmt->field) ||
> +          infmt->field == V4L2_FIELD_ALTERNATE) &&
> +         channel == priv->enc_ch)
> +             ipu_cpmem_interlaced_scan(channel, image.pix.bytesperline);
> +
> +     ipu_ic_task_idma_init(priv->ic_enc, channel,
> +                           outfmt->width, outfmt->height,
> +                           burst_size, rot_mode);
> +     ipu_cpmem_set_axi_id(channel, 1);
> +
> +     ipu_idmac_set_double_buffer(channel, true);
> +
> +     if (rot_swap_width_height)
> +             swap(outfmt->width, outfmt->height);
> +}
> +
> +static int prpenc_setup_rotation(struct prpenc_priv *priv)
> +{
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     struct v4l2_mbus_framefmt *infmt, *outfmt;
> +     const struct imx_media_pixfmt *outcc, *incc;
> +     struct imx_media_dma_buf *buf0, *buf1;
> +     int out_size, ret;
> +
> +     infmt = &priv->format_mbus[priv->input_pad];
> +     outfmt = &priv->format_mbus[priv->output_pad];
> +     incc = priv->cc[priv->input_pad];
> +     outcc = priv->cc[priv->output_pad];
> +
> +     out_size = (outfmt->width * outcc->bpp * outfmt->height) >> 3;
> +
> +     ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[0], out_size);
> +     if (ret) {
> +             v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[0], %d\n", ret);
> +             return ret;
> +     }
> +     ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[1], out_size);
> +     if (ret) {
> +             v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[1], %d\n", ret);
> +             goto free_rot0;
> +     }
> +
> +     ret = ipu_ic_task_init(priv->ic_enc,
> +                            infmt->width, infmt->height,
> +                            outfmt->height, outfmt->width,
> +                            incc->cs, outcc->cs);
> +     if (ret) {
> +             v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n", ret);
> +             goto free_rot1;
> +     }
> +
> +     /* init the IC ENC-->MEM IDMAC channel */
> +     prpenc_setup_channel(priv, priv->enc_ch,
> +                          IPU_ROTATE_NONE,
> +                          priv->rot_buf[0].phys,
> +                          priv->rot_buf[1].phys,
> +                          true);
> +
> +     /* init the MEM-->IC ENC ROT IDMAC channel */
> +     prpenc_setup_channel(priv, priv->enc_rot_in_ch,
> +                          priv->rot_mode,
> +                          priv->rot_buf[0].phys,
> +                          priv->rot_buf[1].phys,
> +                          true);
> +
> +     buf0 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> +     imx_media_dma_buf_set_active(buf0);
> +     buf1 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> +     priv->next = buf1;
> +
> +     /* init the destination IC ENC ROT-->MEM IDMAC channel */
> +     prpenc_setup_channel(priv, priv->enc_rot_out_ch,
> +                          IPU_ROTATE_NONE,
> +                          buf0->phys, buf1->phys,
> +                          false);
> +
> +     /* now link IC ENC-->MEM to MEM-->IC ENC ROT */
> +     ipu_idmac_link(priv->enc_ch, priv->enc_rot_in_ch);
> +
> +     /* enable the IC */
> +     ipu_ic_enable(priv->ic_enc);
> +
> +     /* set buffers ready */
> +     ipu_idmac_select_buffer(priv->enc_ch, 0);
> +     ipu_idmac_select_buffer(priv->enc_ch, 1);
> +     ipu_idmac_select_buffer(priv->enc_rot_out_ch, 0);
> +     ipu_idmac_select_buffer(priv->enc_rot_out_ch, 1);
> +
> +     /* enable the channels */
> +     ipu_idmac_enable_channel(priv->enc_ch);
> +     ipu_idmac_enable_channel(priv->enc_rot_in_ch);
> +     ipu_idmac_enable_channel(priv->enc_rot_out_ch);
> +
> +     /* and finally enable the IC PRPENC task */
> +     ipu_ic_task_enable(priv->ic_enc);
> +
> +     return 0;
> +
> +free_rot1:
> +     imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]);
> +free_rot0:
> +     imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]);
> +     return ret;
> +}
> +
> +static void prpenc_unsetup_rotation(struct prpenc_priv *priv)
> +{
> +     ipu_ic_task_disable(priv->ic_enc);
> +
> +     ipu_idmac_disable_channel(priv->enc_ch);
> +     ipu_idmac_disable_channel(priv->enc_rot_in_ch);
> +     ipu_idmac_disable_channel(priv->enc_rot_out_ch);
> +
> +     ipu_idmac_unlink(priv->enc_ch, priv->enc_rot_in_ch);
> +
> +     ipu_ic_disable(priv->ic_enc);
> +
> +     imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]);
> +     imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]);
> +}
> +
> +static int prpenc_setup_norotation(struct prpenc_priv *priv)
> +{
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     struct v4l2_mbus_framefmt *infmt, *outfmt;
> +     const struct imx_media_pixfmt *outcc, *incc;
> +     struct imx_media_dma_buf *buf0, *buf1;
> +     int ret;
> +
> +     infmt = &priv->format_mbus[priv->input_pad];
> +     outfmt = &priv->format_mbus[priv->output_pad];
> +     incc = priv->cc[priv->input_pad];
> +     outcc = priv->cc[priv->output_pad];
> +
> +     ret = ipu_ic_task_init(priv->ic_enc,
> +                            infmt->width, infmt->height,
> +                            outfmt->width, outfmt->height,
> +                            incc->cs, outcc->cs);
> +     if (ret) {
> +             v4l2_err(&ic_priv->sd, "ipu_ic_task_init failed, %d\n", ret);
> +             return ret;
> +     }
> +
> +     buf0 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> +     imx_media_dma_buf_set_active(buf0);
> +     buf1 = imx_media_dma_buf_get_next_queued(priv->out_ring);
> +     priv->next = buf1;
> +
> +     /* init the IC PRP-->MEM IDMAC channel */
> +     prpenc_setup_channel(priv, priv->enc_ch, priv->rot_mode,
> +                          buf0->phys, buf1->phys,
> +                          false);
> +
> +     ipu_cpmem_dump(priv->enc_ch);
> +     ipu_ic_dump(priv->ic_enc);
> +     ipu_dump(priv->ipu);
> +
> +     ipu_ic_enable(priv->ic_enc);
> +
> +     /* set buffers ready */
> +     ipu_idmac_select_buffer(priv->enc_ch, 0);
> +     ipu_idmac_select_buffer(priv->enc_ch, 1);
> +
> +     /* enable the channels */
> +     ipu_idmac_enable_channel(priv->enc_ch);
> +
> +     /* enable the IC ENCODE task */
> +     ipu_ic_task_enable(priv->ic_enc);
> +
> +     return 0;
> +}
> +
> +static void prpenc_unsetup_norotation(struct prpenc_priv *priv)
> +{
> +     ipu_ic_task_disable(priv->ic_enc);
> +     ipu_idmac_disable_channel(priv->enc_ch);
> +     ipu_ic_disable(priv->ic_enc);
> +}
> +
> +static int prpenc_start(struct prpenc_priv *priv)
> +{
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     int ret;
> +
> +     if (!priv->sensor) {
> +             v4l2_err(&ic_priv->sd, "no sensor attached\n");
> +             return -EINVAL;
> +     }
> +
> +     ret = prpenc_get_ipu_resources(priv);
> +     if (ret)
> +             return ret;
> +
> +     /* set IC to receive from CSI */
> +     ipu_set_ic_src_mux(priv->ipu, priv->csi_id, false);
> +
> +     /* ask the sink for the buffer ring */
> +     ret = v4l2_subdev_call(priv->sink_sd, core, ioctl,
> +                            IMX_MEDIA_REQ_DMA_BUF_SINK_RING,
> +                            &priv->out_ring);
> +     if (ret)
> +             goto out_put_ipu;
> +
> +     priv->ipu_buf_num = 0;
> +
> +     /* init EOF completion waitq */
> +     init_completion(&priv->last_eof_comp);
> +     priv->last_eof = false;
> +
> +     if (ipu_rot_mode_is_irt(priv->rot_mode))
> +             ret = prpenc_setup_rotation(priv);
> +     else
> +             ret = prpenc_setup_norotation(priv);
> +     if (ret)
> +             goto out_put_ipu;
> +
> +     priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
> +                                               priv->enc_ch,
> +                                               IPU_IRQ_NFB4EOF);
> +     ret = devm_request_irq(ic_priv->dev, priv->nfb4eof_irq,
> +                            prpenc_nfb4eof_interrupt, 0,
> +                            "imx-ic-prpenc-nfb4eof", priv);
> +     if (ret) {
> +             v4l2_err(&ic_priv->sd,
> +                      "Error registering NFB4EOF irq: %d\n", ret);
> +             goto out_unsetup;
> +     }
> +
> +     if (ipu_rot_mode_is_irt(priv->rot_mode))
> +             priv->eof_irq = ipu_idmac_channel_irq(
> +                     priv->ipu, priv->enc_rot_out_ch, IPU_IRQ_EOF);
> +     else
> +             priv->eof_irq = ipu_idmac_channel_irq(
> +                     priv->ipu, priv->enc_ch, IPU_IRQ_EOF);
> +
> +     ret = devm_request_irq(ic_priv->dev, priv->eof_irq,
> +                            prpenc_eof_interrupt, 0,
> +                            "imx-ic-prpenc-eof", priv);
> +     if (ret) {
> +             v4l2_err(&ic_priv->sd,
> +                      "Error registering eof irq: %d\n", ret);
> +             goto out_free_nfb4eof_irq;
> +     }
> +
> +     /* start the EOF timeout timer */
> +     mod_timer(&priv->eof_timeout_timer,
> +               jiffies + msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
> +
> +     return 0;
> +
> +out_free_nfb4eof_irq:
> +     devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
> +out_unsetup:
> +     if (ipu_rot_mode_is_irt(priv->rot_mode))
> +             prpenc_unsetup_rotation(priv);
> +     else
> +             prpenc_unsetup_norotation(priv);
> +out_put_ipu:
> +     prpenc_put_ipu_resources(priv);
> +     return ret;
> +}
> +
> +static void prpenc_stop(struct prpenc_priv *priv)
> +{
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     unsigned long flags;
> +     int ret;
> +
> +     /* mark next EOF interrupt as the last before stream off */
> +     spin_lock_irqsave(&priv->irqlock, flags);
> +     priv->last_eof = true;
> +     spin_unlock_irqrestore(&priv->irqlock, flags);
> +
> +     /*
> +      * and then wait for interrupt handler to mark completion.
> +      */
> +     ret = wait_for_completion_timeout(
> +             &priv->last_eof_comp,
> +             msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
> +     if (ret == 0)
> +             v4l2_warn(&ic_priv->sd, "wait last EOF timeout\n");
> +
> +     devm_free_irq(ic_priv->dev, priv->eof_irq, priv);
> +     devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv);
> +
> +     if (ipu_rot_mode_is_irt(priv->rot_mode))
> +             prpenc_unsetup_rotation(priv);
> +     else
> +             prpenc_unsetup_norotation(priv);
> +
> +     prpenc_put_ipu_resources(priv);
> +
> +     /* cancel the EOF timeout timer */
> +     del_timer_sync(&priv->eof_timeout_timer);
> +
> +     priv->out_ring = NULL;
> +
> +     /* inform sink that the buffer ring can now be freed */
> +     v4l2_subdev_call(priv->sink_sd, core, ioctl,
> +                      IMX_MEDIA_REL_DMA_BUF_SINK_RING, 0);
> +}
> +
> +static int prpenc_enum_mbus_code(struct v4l2_subdev *sd,
> +                              struct v4l2_subdev_pad_config *cfg,
> +                              struct v4l2_subdev_mbus_code_enum *code)
> +{
> +     struct prpenc_priv *priv = sd_to_priv(sd);
> +     bool allow_planar;
> +
> +     if (code->pad >= PRPENC_NUM_PADS)
> +             return -EINVAL;
> +
> +     allow_planar = (code->pad == priv->output_pad);
> +
> +     return imx_media_enum_format(&code->code, code->index,
> +                                  true, allow_planar);
> +}
> +
> +static int prpenc_get_fmt(struct v4l2_subdev *sd,
> +                       struct v4l2_subdev_pad_config *cfg,
> +                       struct v4l2_subdev_format *sdformat)
> +{
> +     struct prpenc_priv *priv = sd_to_priv(sd);
> +
> +     if (sdformat->pad >= PRPENC_NUM_PADS)
> +             return -EINVAL;
> +
> +     sdformat->format = priv->format_mbus[sdformat->pad];
> +
> +     return 0;
> +}
> +
> +static int prpenc_set_fmt(struct v4l2_subdev *sd,
> +                       struct v4l2_subdev_pad_config *cfg,
> +                       struct v4l2_subdev_format *sdformat)
> +{
> +     struct prpenc_priv *priv = sd_to_priv(sd);
> +     struct v4l2_mbus_framefmt *infmt, *outfmt;
> +     const struct imx_media_pixfmt *cc;
> +     bool allow_planar;
> +     u32 code;
> +
> +     if (sdformat->pad >= PRPENC_NUM_PADS)
> +             return -EINVAL;
> +
> +     if (priv->stream_on)
> +             return -EBUSY;
> +
> +     infmt = &priv->format_mbus[priv->input_pad];
> +     outfmt = &priv->format_mbus[priv->output_pad];
> +     allow_planar = (sdformat->pad == priv->output_pad);
> +
> +     cc = imx_media_find_format(0, sdformat->format.code,
> +                                true, allow_planar);
> +     if (!cc) {
> +             imx_media_enum_format(&code, 0, true, false);
> +             cc = imx_media_find_format(0, code, true, false);
> +             sdformat->format.code = cc->codes[0];
> +     }
> +
> +     if (sdformat->pad == priv->output_pad) {
> +             sdformat->format.width = min_t(__u32,
> +                                            sdformat->format.width,
> +                                            MAX_W_IC);
> +             sdformat->format.height = min_t(__u32,
> +                                             sdformat->format.height,
> +                                             MAX_H_IC);
> +
> +             if (sdformat->format.field != V4L2_FIELD_NONE)
> +                     sdformat->format.field = infmt->field;
> +
> +             /* IC resizer cannot downsize more than 4:1 */
> +             if (ipu_rot_mode_is_irt(priv->rot_mode)) {
> +                     sdformat->format.width = max_t(__u32,
> +                                                    sdformat->format.width,
> +                                                    infmt->height / 4);
> +                     sdformat->format.height = max_t(__u32,
> +                                                     sdformat->format.height,
> +                                                     infmt->width / 4);
> +             } else {
> +                     sdformat->format.width = max_t(__u32,
> +                                                    sdformat->format.width,
> +                                                    infmt->width / 4);
> +                     sdformat->format.height = max_t(__u32,
> +                                                     sdformat->format.height,
> +                                                     infmt->height / 4);
> +             }
> +     } else {
> +             sdformat->format.width = min_t(__u32,
> +                                            sdformat->format.width,
> +                                            MAX_W_SINK);
> +             sdformat->format.height = min_t(__u32,
> +                                             sdformat->format.height,
> +                                             MAX_H_SINK);
> +     }
> +
> +     if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) {
> +             cfg->try_fmt = sdformat->format;
> +     } else {
> +             priv->format_mbus[sdformat->pad] = sdformat->format;
> +             priv->cc[sdformat->pad] = cc;
> +     }
> +
> +     return 0;
> +}
> +
> +static int prpenc_link_setup(struct media_entity *entity,
> +                          const struct media_pad *local,
> +                          const struct media_pad *remote, u32 flags)
> +{
> +     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> +     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> +     struct prpenc_priv *priv = ic_priv->task_priv;
> +     struct v4l2_subdev *remote_sd;
> +
> +     dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name,
> +             local->entity->name);
> +
> +     remote_sd = media_entity_to_v4l2_subdev(remote->entity);
> +
> +     if (local->flags & MEDIA_PAD_FL_SOURCE) {
> +             if (flags & MEDIA_LNK_FL_ENABLED) {
> +                     if (priv->sink_sd)
> +                             return -EBUSY;
> +                     priv->sink_sd = remote_sd;
> +             } else {
> +                     priv->sink_sd = NULL;
> +             }
> +
> +             return 0;
> +     }
> +
> +     /* this is sink pad */
> +     if (flags & MEDIA_LNK_FL_ENABLED) {
> +             if (priv->src_sd)
> +                     return -EBUSY;
> +             priv->src_sd = remote_sd;
> +     } else {
> +             priv->src_sd = NULL;
> +             return 0;
> +     }
> +
> +     switch (remote_sd->grp_id) {
> +     case IMX_MEDIA_GRP_ID_CSI0:
> +             priv->csi_id = 0;
> +             break;
> +     case IMX_MEDIA_GRP_ID_CSI1:
> +             priv->csi_id = 1;
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static int prpenc_link_validate(struct v4l2_subdev *sd,
> +                             struct media_link *link,
> +                             struct v4l2_subdev_format *source_fmt,
> +                             struct v4l2_subdev_format *sink_fmt)
> +{
> +     struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd);
> +     struct prpenc_priv *priv = ic_priv->task_priv;
> +     struct v4l2_mbus_config sensor_mbus_cfg;
> +     int ret;
> +
> +     ret = v4l2_subdev_link_validate_default(sd, link,
> +                                             source_fmt, sink_fmt);
> +     if (ret)
> +             return ret;
> +
> +     priv->sensor = __imx_media_find_sensor(priv->md, &ic_priv->sd.entity);
> +     if (IS_ERR(priv->sensor)) {
> +             v4l2_err(&ic_priv->sd, "no sensor attached\n");
> +             ret = PTR_ERR(priv->sensor);
> +             priv->sensor = NULL;
> +             return ret;
> +     }
> +
> +     ret = v4l2_subdev_call(priv->sensor->sd, video, g_mbus_config,
> +                            &sensor_mbus_cfg);
> +     if (ret)
> +             return ret;
> +
> +     if (sensor_mbus_cfg.type == V4L2_MBUS_CSI2) {
> +             int vc_num = 0;
> +             /* see NOTE in imx-csi.c */
> +#if 0
> +             vc_num = imx_media_find_mipi_csi2_channel(
> +                     priv->md, &ic_priv->sd.entity);
> +             if (vc_num < 0)
> +                     return vc_num;
> +#endif
> +             /* only virtual channel 0 can be sent to IC */
> +             if (vc_num != 0)
> +                     return -EINVAL;
> +     } else {
> +             /*
> +              * only 8-bit pixels can be sent to IC for parallel
> +              * busses
> +              */
> +             if (priv->sensor->sensor_ep.bus.parallel.bus_width >= 16)
> +                     return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static int prpenc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +     struct prpenc_priv *priv = container_of(ctrl->handler,
> +                                            struct prpenc_priv, ctrl_hdlr);
> +     struct imx_ic_priv *ic_priv = priv->ic_priv;
> +     enum ipu_rotate_mode rot_mode;
> +     bool hflip, vflip;
> +     int rotation, ret;
> +
> +     rotation = priv->rotation;
> +     hflip = priv->hflip;
> +     vflip = priv->vflip;
> +
> +     switch (ctrl->id) {
> +     case V4L2_CID_HFLIP:
> +             hflip = (ctrl->val == 1);
> +             break;
> +     case V4L2_CID_VFLIP:
> +             vflip = (ctrl->val == 1);
> +             break;
> +     case V4L2_CID_ROTATE:
> +             rotation = ctrl->val;
> +             break;
> +     default:
> +             v4l2_err(&ic_priv->sd, "Invalid control\n");
> +             return -EINVAL;
> +     }
> +
> +     ret = ipu_degrees_to_rot_mode(&rot_mode, rotation, hflip, vflip);
> +     if (ret)
> +             return ret;
> +
> +     if (rot_mode != priv->rot_mode) {
> +             /* can't change rotation mid-streaming */
> +             if (priv->stream_on)
> +                     return -EBUSY;
> +
> +             priv->rot_mode = rot_mode;
> +             priv->rotation = rotation;
> +             priv->hflip = hflip;
> +             priv->vflip = vflip;
> +     }
> +
> +     return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops prpenc_ctrl_ops = {
> +     .s_ctrl = prpenc_s_ctrl,
> +};
> +
> +static const struct v4l2_ctrl_config prpenc_std_ctrl[] = {
> +     {
> +             .id = V4L2_CID_HFLIP,
> +             .name = "Horizontal Flip",
> +             .type = V4L2_CTRL_TYPE_BOOLEAN,
> +             .def =  0,
> +             .min =  0,
> +             .max =  1,
> +             .step = 1,
> +     }, {
> +             .id = V4L2_CID_VFLIP,
> +             .name = "Vertical Flip",
> +             .type = V4L2_CTRL_TYPE_BOOLEAN,
> +             .def =  0,
> +             .min =  0,
> +             .max =  1,
> +             .step = 1,
> +     }, {
> +             .id = V4L2_CID_ROTATE,
> +             .name = "Rotation",
> +             .type = V4L2_CTRL_TYPE_INTEGER,
> +             .def =   0,
> +             .min =   0,
> +             .max = 270,
> +             .step = 90,
> +     },
> +};

Use v4l2_ctrl_new_std() instead of this array: this avoids duplicating 
information
like the name and type.

If this is also done elsewhere, then it should be changed there as well.

Regards,

        Hans
_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to