Le lundi 11 juillet 2016 à 17:14 +0200, Jean-Christophe Trotin a écrit :
> This patch adds V4L2 HVA (Hardware Video Accelerator) video encoder
> driver for STMicroelectronics SoC. It uses the V4L2 mem2mem framework.
> 
> This patch only contains the core parts of the driver:
> - the V4L2 interface with the userland (hva-v4l2.c)
> - the hardware services (hva-hw.c)
> - the memory management utilities (hva-mem.c)
> 
> This patch doesn't include the support of specific codec (e.g. H.264)
> video encoding: this support is part of subsequent patches.
> 
> Signed-off-by: Yannick Fertre <yannick.fer...@st.com>
> Signed-off-by: Jean-Christophe Trotin <jean-christophe.tro...@st.com>
> ---
>  drivers/media/platform/Kconfig            |   14 +
>  drivers/media/platform/Makefile           |    1 +
>  drivers/media/platform/sti/hva/Makefile   |    2 +
>  drivers/media/platform/sti/hva/hva-hw.c   |  534 ++++++++++++
>  drivers/media/platform/sti/hva/hva-hw.h   |   42 +
>  drivers/media/platform/sti/hva/hva-mem.c  |   60 ++
>  drivers/media/platform/sti/hva/hva-mem.h  |   36 +
>  drivers/media/platform/sti/hva/hva-v4l2.c | 1299 
> +++++++++++++++++++++++++++++
>  drivers/media/platform/sti/hva/hva.h      |  284 +++++++
>  9 files changed, 2272 insertions(+)
>  create mode 100644 drivers/media/platform/sti/hva/Makefile
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-hw.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.c
>  create mode 100644 drivers/media/platform/sti/hva/hva-mem.h
>  create mode 100644 drivers/media/platform/sti/hva/hva-v4l2.c
>  create mode 100644 drivers/media/platform/sti/hva/hva.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 382f393..182d63f 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -227,6 +227,20 @@ config VIDEO_STI_BDISP
>       help
>         This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
>  
> +config VIDEO_STI_HVA
> +     tristate "STMicroelectronics STiH41x HVA multi-format video encoder 
> V4L2 driver"
> +     depends on VIDEO_DEV && VIDEO_V4L2
> +     depends on ARCH_STI || COMPILE_TEST
> +     select VIDEOBUF2_DMA_CONTIG
> +     select V4L2_MEM2MEM_DEV
> +     help
> +       This V4L2 driver enables HVA multi-format video encoder of
> +       STMicroelectronics SoC STiH41x series, allowing hardware encoding of 
> raw
> +       uncompressed formats in various compressed video bitstreams format.
> +
> +       To compile this driver as a module, choose M here:
> +       the module will be called hva.
> +
>  config VIDEO_SH_VEU
>       tristate "SuperH VEU mem2mem video processing driver"
>       depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 99cf315..784dcd4 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
>  obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC)       += exynos-gsc/
>  
>  obj-$(CONFIG_VIDEO_STI_BDISP)                += sti/bdisp/
> +obj-$(CONFIG_VIDEO_STI_HVA)          += sti/hva/
>  obj-$(CONFIG_DVB_C8SECTPFE)          += sti/c8sectpfe/
>  
>  obj-$(CONFIG_BLACKFIN)                  += blackfin/
> diff --git a/drivers/media/platform/sti/hva/Makefile 
> b/drivers/media/platform/sti/hva/Makefile
> new file mode 100644
> index 0000000..7022a33
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_VIDEO_STI_HVA) := hva.o
> +hva-y := hva-v4l2.o hva-hw.o hva-mem.o
> diff --git a/drivers/media/platform/sti/hva/hva-hw.c 
> b/drivers/media/platform/sti/hva/hva-hw.c
> new file mode 100644
> index 0000000..fa293c7
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-hw.c
> @@ -0,0 +1,534 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fer...@st.com>
> + *          Hugues Fruchet <hugues.fruc...@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "hva.h"
> +#include "hva-hw.h"
> +
> +/* HVA register offsets */
> +#define HVA_HIF_REG_RST                 0x0100U
> +#define HVA_HIF_REG_RST_ACK             0x0104U
> +#define HVA_HIF_REG_MIF_CFG             0x0108U
> +#define HVA_HIF_REG_HEC_MIF_CFG         0x010CU
> +#define HVA_HIF_REG_CFL                 0x0110U
> +#define HVA_HIF_FIFO_CMD                0x0114U
> +#define HVA_HIF_FIFO_STS                0x0118U
> +#define HVA_HIF_REG_SFL                 0x011CU
> +#define HVA_HIF_REG_IT_ACK              0x0120U
> +#define HVA_HIF_REG_ERR_IT_ACK          0x0124U
> +#define HVA_HIF_REG_LMI_ERR             0x0128U
> +#define HVA_HIF_REG_EMI_ERR             0x012CU
> +#define HVA_HIF_REG_HEC_MIF_ERR         0x0130U
> +#define HVA_HIF_REG_HEC_STS             0x0134U
> +#define HVA_HIF_REG_HVC_STS             0x0138U
> +#define HVA_HIF_REG_HJE_STS             0x013CU
> +#define HVA_HIF_REG_CNT                 0x0140U
> +#define HVA_HIF_REG_HEC_CHKSYN_DIS      0x0144U
> +#define HVA_HIF_REG_CLK_GATING          0x0148U
> +#define HVA_HIF_REG_VERSION             0x014CU
> +#define HVA_HIF_REG_BSM                 0x0150U
> +
> +/* define value for version id register (HVA_HIF_REG_VERSION) */
> +#define VERSION_ID_MASK      0x0000FFFF
> +
> +/* define values for BSM register (HVA_HIF_REG_BSM) */
> +#define BSM_CFG_VAL1 0x0003F000
> +#define BSM_CFG_VAL2 0x003F0000
> +
> +/* define values for memory interface register (HVA_HIF_REG_MIF_CFG) */
> +#define MIF_CFG_VAL1 0x04460446
> +#define MIF_CFG_VAL2 0x04460806
> +#define MIF_CFG_VAL3 0x00000000
> +
> +/* define value for HEC memory interface register (HVA_HIF_REG_MIF_CFG) */
> +#define HEC_MIF_CFG_VAL      0x000000C4
> +
> +/*  Bits definition for clock gating register (HVA_HIF_REG_CLK_GATING) */
> +#define CLK_GATING_HVC       BIT(0)
> +#define CLK_GATING_HEC       BIT(1)
> +#define CLK_GATING_HJE       BIT(2)
> +
> +/* fix hva clock rate */
> +#define CLK_RATE             300000000
> +
> +/* fix delay for pmruntime */
> +#define AUTOSUSPEND_DELAY_MS 3
> +
> +/**
> + * hw encode error values
> + * NO_ERROR: Success, Task OK
> + * H264_BITSTREAM_OVERSIZE: VECH264 Bitstream size > bitstream buffer
> + * H264_FRAME_SKIPPED: VECH264 Frame skipped (refers to CPB Buffer Size)
> + * H264_SLICE_LIMIT_SIZE: VECH264 MB > slice limit size
> + * H264_MAX_SLICE_NUMBER: VECH264 max slice number reached
> + * H264_SLICE_READY: VECH264 Slice ready
> + * TASK_LIST_FULL: HVA/FPC task list full
> +                (discard latest transform command)
> + * UNKNOWN_COMMAND: Transform command not known by HVA/FPC
> + * WRONG_CODEC_OR_RESOLUTION: Wrong Codec or Resolution Selection
> + * NO_INT_COMPLETION: Time-out on interrupt completion
> + * LMI_ERR: Local Memory Interface Error
> + * EMI_ERR: External Memory Interface Error
> + * HECMI_ERR: HEC Memory Interface Error
> + */
> +enum hva_hw_error {
> +     NO_ERROR = 0x0,
> +     H264_BITSTREAM_OVERSIZE = 0x2,
> +     H264_FRAME_SKIPPED = 0x4,
> +     H264_SLICE_LIMIT_SIZE = 0x5,
> +     H264_MAX_SLICE_NUMBER = 0x7,
> +     H264_SLICE_READY = 0x8,
> +     TASK_LIST_FULL = 0xF0,
> +     UNKNOWN_COMMAND = 0xF1,
> +     WRONG_CODEC_OR_RESOLUTION = 0xF4,
> +     NO_INT_COMPLETION = 0x100,
> +     LMI_ERR = 0x101,
> +     EMI_ERR = 0x102,
> +     HECMI_ERR = 0x103,
> +};
> +
> +static irqreturn_t hva_hw_its_interrupt(int irq, void *data)
> +{
> +     struct hva_dev *hva = data;
> +
> +     /* read status registers */
> +     hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
> +     hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
> +
> +     /* acknowledge interruption */
> +     writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
> +
> +     return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t hva_hw_its_irq_thread(int irq, void *arg)
> +{
> +     struct hva_dev *hva = arg;
> +     struct device *dev = hva_to_dev(hva);
> +     u32 status = hva->sts_reg & 0xFF;
> +     u8 ctx_id = 0;
> +     struct hva_ctx *ctx = NULL;
> +
> +     dev_dbg(dev, "%s     %s: status: 0x%02x fifo level: 0x%02x\n",
> +             HVA_PREFIX, __func__, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
> +
> +     /*
> +      * status: task_id[31:16] client_id[15:8] status[7:0]
> +      * the context identifier is retrieved from the client identifier
> +      */
> +     ctx_id = (hva->sts_reg & 0xFF00) >> 8;
> +     if (ctx_id >= HVA_MAX_INSTANCES) {
> +             dev_err(dev, "%s     %s: bad context identifier: %d\n",
> +                     ctx->name, __func__, ctx_id);
> +             ctx->hw_err = true;
> +             goto out;
> +     }
> +
> +     ctx = hva->instances[ctx_id];
> +
> +     switch (status) {
> +     case NO_ERROR:
> +             dev_dbg(dev, "%s     %s: no error\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = false;
> +             break;
> +     case H264_SLICE_READY:
> +             dev_dbg(dev, "%s     %s: h264 slice ready\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = false;
> +             break;
> +     case H264_FRAME_SKIPPED:
> +             dev_dbg(dev, "%s     %s: h264 frame skipped\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = false;
> +             break;
> +     case H264_BITSTREAM_OVERSIZE:
> +             dev_err(dev, "%s     %s:h264 bitstream oversize\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = true;
> +             break;
> +     case H264_SLICE_LIMIT_SIZE:
> +             dev_err(dev, "%s     %s: h264 slice limit size is reached\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = true;
> +             break;
> +     case H264_MAX_SLICE_NUMBER:
> +             dev_err(dev, "%s     %s: h264 max slice number is reached\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = true;
> +             break;
> +     case TASK_LIST_FULL:
> +             dev_err(dev, "%s     %s:task list full\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = true;
> +             break;
> +     case UNKNOWN_COMMAND:
> +             dev_err(dev, "%s     %s: command not known\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = true;
> +             break;
> +     case WRONG_CODEC_OR_RESOLUTION:
> +             dev_err(dev, "%s     %s: wrong codec or resolution\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = true;
> +             break;
> +     default:
> +             dev_err(dev, "%s     %s: status not recognized\n",
> +                     ctx->name, __func__);
> +             ctx->hw_err = true;
> +             break;
> +     }
> +out:
> +     complete(&hva->interrupt);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t hva_hw_err_interrupt(int irq, void *data)
> +{
> +     struct hva_dev *hva = data;
> +
> +     /* read status registers */
> +     hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
> +     hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
> +
> +     /* read error registers */
> +     hva->lmi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_LMI_ERR);
> +     hva->emi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_EMI_ERR);
> +     hva->hec_mif_err_reg = readl_relaxed(hva->regs +
> +                                          HVA_HIF_REG_HEC_MIF_ERR);
> +
> +     /* acknowledge interruption */
> +     writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
> +
> +     return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t hva_hw_err_irq_thread(int irq, void *arg)
> +{
> +     struct hva_dev *hva = arg;
> +     struct device *dev = hva_to_dev(hva);
> +     u8 ctx_id = 0;
> +     struct hva_ctx *ctx;
> +
> +     dev_dbg(dev, "%s     status: 0x%02x fifo level: 0x%02x\n",
> +             HVA_PREFIX, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
> +
> +     /*
> +      * status: task_id[31:16] client_id[15:8] status[7:0]
> +      * the context identifier is retrieved from the client identifier
> +      */
> +     ctx_id = (hva->sts_reg & 0xFF00) >> 8;
> +     if (ctx_id >= HVA_MAX_INSTANCES) {
> +             dev_err(dev, "%s     bad context identifier: %d\n", HVA_PREFIX,
> +                     ctx_id);
> +             goto out;
> +     }
> +
> +     ctx = hva->instances[ctx_id];
> +
> +     if (hva->lmi_err_reg) {
> +             dev_err(dev, "%s     local memory interface error: 0x%08x\n",
> +                     ctx->name, hva->lmi_err_reg);
> +             ctx->hw_err = true;
> +     }
> +
> +     if (hva->lmi_err_reg) {
> +             dev_err(dev, "%s     external memory interface error: 0x%08x\n",
> +                     ctx->name, hva->emi_err_reg);
> +             ctx->hw_err = true;
> +     }
> +
> +     if (hva->hec_mif_err_reg) {
> +             dev_err(dev, "%s     hec memory interface error: 0x%08x\n",
> +                     ctx->name, hva->hec_mif_err_reg);
> +             ctx->hw_err = true;
> +     }
> +out:
> +     complete(&hva->interrupt);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static unsigned long int hva_hw_get_ip_version(struct hva_dev *hva)
> +{
> +     struct device *dev = hva_to_dev(hva);
> +     unsigned long int version;
> +
> +     if (pm_runtime_get_sync(dev) < 0) {
> +             dev_err(dev, "%s     failed to get pm_runtime\n", HVA_PREFIX);
> +             mutex_unlock(&hva->protect_mutex);
> +             return -EFAULT;
> +     }
> +
> +     version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) &
> +                             VERSION_ID_MASK;
> +
> +     pm_runtime_put_autosuspend(dev);
> +
> +     switch (version) {
> +     case HVA_VERSION_V400:
> +             dev_dbg(dev, "%s     IP hardware version 0x%lx\n",
> +                     HVA_PREFIX, version);
> +             break;
> +     default:
> +             dev_err(dev, "%s     unknown IP hardware version 0x%lx\n",
> +                     HVA_PREFIX, version);
> +             version = HVA_VERSION_UNKNOWN;
> +             break;
> +     }
> +
> +     return version;
> +}
> +
> +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva)
> +{
> +     struct device *dev = &pdev->dev;
> +     struct resource *regs;
> +     struct resource *esram;
> +     int ret;
> +
> +     WARN_ON(!hva);
> +
> +     /* get memory for registers */
> +     regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     hva->regs = devm_ioremap_resource(dev, regs);
> +     if (IS_ERR_OR_NULL(hva->regs)) {
> +             dev_err(dev, "%s     failed to get regs\n", HVA_PREFIX);
> +             return PTR_ERR(hva->regs);
> +     }
> +
> +     /* get memory for esram */
> +     esram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +     if (IS_ERR_OR_NULL(esram)) {
> +             dev_err(dev, "%s     failed to get esram\n", HVA_PREFIX);
> +             return PTR_ERR(esram);
> +     }
> +     hva->esram_addr = esram->start;
> +     hva->esram_size = esram->end - esram->start + 1;
> +
> +     dev_info(dev, "%s     esram reserved for address: 0x%x size:%d\n",
> +              HVA_PREFIX, hva->esram_addr, hva->esram_size);
> +
> +     /* get clock resource */
> +     hva->clk = devm_clk_get(dev, "clk_hva");
> +     if (IS_ERR(hva->clk)) {
> +             dev_err(dev, "%s     failed to get clock\n", HVA_PREFIX);
> +             return PTR_ERR(hva->clk);
> +     }
> +
> +     ret = clk_prepare(hva->clk);
> +     if (ret < 0) {
> +             dev_err(dev, "%s     failed to prepare clock\n", HVA_PREFIX);
> +             hva->clk = ERR_PTR(-EINVAL);
> +             return ret;
> +     }
> +
> +     /* get status interruption resource */
> +     ret  = platform_get_irq(pdev, 0);
> +     if (ret < 0) {
> +             dev_err(dev, "%s     failed to get status IRQ\n", HVA_PREFIX);
> +             goto err_clk;
> +     }
> +     hva->irq_its = ret;
> +
> +     ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt,
> +                                     hva_hw_its_irq_thread,
> +                                     IRQF_ONESHOT,
> +                                     "hva_its_irq", hva);
> +     if (ret) {
> +             dev_err(dev, "%s     failed to install status IRQ 0x%x\n",
> +                     HVA_PREFIX, hva->irq_its);
> +             goto err_clk;
> +     }
> +     disable_irq(hva->irq_its);
> +
> +     /* get error interruption resource */
> +     ret = platform_get_irq(pdev, 1);
> +     if (ret < 0) {
> +             dev_err(dev, "%s     failed to get error IRQ\n", HVA_PREFIX);
> +             goto err_clk;
> +     }
> +     hva->irq_err = ret;
> +
> +     ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt,
> +                                     hva_hw_err_irq_thread,
> +                                     IRQF_ONESHOT,
> +                                     "hva_err_irq", hva);
> +     if (ret) {
> +             dev_err(dev, "%s     failed to install error IRQ 0x%x\n",
> +                     HVA_PREFIX, hva->irq_err);
> +             goto err_clk;
> +     }
> +     disable_irq(hva->irq_err);
> +
> +     /* initialise protection mutex */
> +     mutex_init(&hva->protect_mutex);
> +
> +     /* initialise completion signal */
> +     init_completion(&hva->interrupt);
> +
> +     /* initialise runtime power management */
> +     pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY_MS);
> +     pm_runtime_use_autosuspend(dev);
> +     pm_runtime_set_suspended(dev);
> +     pm_runtime_enable(dev);
> +
> +     ret = pm_runtime_get_sync(dev);
> +     if (ret < 0) {
> +             dev_err(dev, "%s     failed to set PM\n", HVA_PREFIX);
> +             goto err_clk;
> +     }
> +
> +     /* check IP hardware version */
> +     hva->ip_version = hva_hw_get_ip_version(hva);
> +
> +     if (hva->ip_version == HVA_VERSION_UNKNOWN) {
> +             ret = -EINVAL;
> +             goto err_pm;
> +     }
> +
> +     dev_info(dev, "%s     found hva device (version 0x%lx)\n", HVA_PREFIX,
> +              hva->ip_version);
> +
> +     return 0;
> +
> +err_pm:
> +     pm_runtime_put(dev);
> +err_clk:
> +     if (hva->clk)
> +             clk_unprepare(hva->clk);
> +
> +     return ret;
> +}
> +
> +void hva_hw_remove(struct hva_dev *hva)
> +{
> +     struct device *dev = hva_to_dev(hva);
> +
> +     disable_irq(hva->irq_its);
> +     disable_irq(hva->irq_err);
> +
> +     pm_runtime_put_autosuspend(dev);
> +     pm_runtime_disable(dev);
> +}
> +
> +int hva_hw_runtime_suspend(struct device *dev)
> +{
> +     struct hva_dev *hva = dev_get_drvdata(dev);
> +
> +     clk_disable_unprepare(hva->clk);
> +
> +     return 0;
> +}
> +
> +int hva_hw_runtime_resume(struct device *dev)
> +{
> +     struct hva_dev *hva = dev_get_drvdata(dev);
> +
> +     if (clk_prepare_enable(hva->clk)) {
> +             dev_err(hva->dev, "%s     failed to prepare hva clk\n",
> +                     HVA_PREFIX);
> +             return -EINVAL;
> +     }
> +
> +     if (clk_set_rate(hva->clk, CLK_RATE)) {
> +             dev_err(dev, "%s     failed to set clock frequency\n",
> +                     HVA_PREFIX);
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
> +                     struct hva_buffer *task)
> +{
> +     struct hva_dev *hva = ctx_to_hdev(ctx);
> +     struct device *dev = hva_to_dev(hva);
> +     u8 client_id = ctx->id;
> +     int ret;
> +     u32 reg = 0;
> +
> +     mutex_lock(&hva->protect_mutex);
> +
> +     /* enable irqs */
> +     enable_irq(hva->irq_its);
> +     enable_irq(hva->irq_err);
> +
> +     if (pm_runtime_get_sync(dev) < 0) {
> +             dev_err(dev, "%s     failed to get pm_runtime\n", ctx->name);
> +             ret = -EFAULT;
> +             goto out;
> +     }
> +
> +     reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING);
> +     switch (cmd) {
> +     case H264_ENC:
> +             reg |= CLK_GATING_HVC;
> +             break;
> +     default:
> +             dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
> +             ret = -EFAULT;
> +             goto out;
> +     }
> +     writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
> +
> +     dev_dbg(dev, "%s     %s: write configuration registers\n", ctx->name,
> +             __func__);
> +
> +     /* byte swap config */
> +     writel_relaxed(BSM_CFG_VAL1, hva->regs + HVA_HIF_REG_BSM);
> +
> +     /* define Max Opcode Size and Max Message Size for LMI and EMI */
> +     writel_relaxed(MIF_CFG_VAL3, hva->regs + HVA_HIF_REG_MIF_CFG);
> +     writel_relaxed(HEC_MIF_CFG_VAL, hva->regs + HVA_HIF_REG_HEC_MIF_CFG);
> +
> +     /*
> +      * command FIFO: task_id[31:16] client_id[15:8] command_type[7:0]
> +      * the context identifier is provided as client identifier to the
> +      * hardware, and is retrieved in the interrupt functions from the
> +      * status register
> +      */
> +     dev_dbg(dev, "%s     %s: send task (cmd: %d, task_desc: %pad)\n",
> +             ctx->name, __func__, cmd + (client_id << 8), &task->paddr);
> +     writel_relaxed(cmd + (client_id << 8), hva->regs + HVA_HIF_FIFO_CMD);
> +     writel_relaxed(task->paddr, hva->regs + HVA_HIF_FIFO_CMD);
> +
> +     if (!wait_for_completion_timeout(&hva->interrupt,
> +                                      msecs_to_jiffies(2000))) {
> +             dev_err(dev, "%s     %s: time out on completion\n", ctx->name,
> +                     __func__);
> +             ret = -EFAULT;
> +             goto out;
> +     }
> +
> +     /* get encoding status */
> +     ret = ctx->hw_err ? -EFAULT : 0;
> +
> +out:
> +     disable_irq(hva->irq_its);
> +     disable_irq(hva->irq_err);
> +
> +     switch (cmd) {
> +     case H264_ENC:
> +             reg &= ~CLK_GATING_HVC;
> +             writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
> +             break;
> +     default:
> +             dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
> +     }
> +
> +     pm_runtime_put_autosuspend(dev);
> +     mutex_unlock(&hva->protect_mutex);
> +
> +     return ret;
> +}
> diff --git a/drivers/media/platform/sti/hva/hva-hw.h 
> b/drivers/media/platform/sti/hva/hva-hw.h
> new file mode 100644
> index 0000000..efb45b9
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-hw.h
> @@ -0,0 +1,42 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fer...@st.com>
> + *          Hugues Fruchet <hugues.fruc...@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef HVA_HW_H
> +#define HVA_HW_H
> +
> +#include "hva-mem.h"
> +
> +/* HVA Versions */
> +#define HVA_VERSION_UNKNOWN    0x000
> +#define HVA_VERSION_V400       0x400
> +
> +/* HVA command types */
> +enum hva_hw_cmd_type {
> +     /* RESERVED = 0x00 */
> +     /* RESERVED = 0x01 */
> +     H264_ENC = 0x02,
> +     /* RESERVED = 0x03 */
> +     /* RESERVED = 0x04 */
> +     /* RESERVED = 0x05 */
> +     /* RESERVED = 0x06 */
> +     /* RESERVED = 0x07 */
> +     REMOVE_CLIENT = 0x08,
> +     FREEZE_CLIENT = 0x09,
> +     START_CLIENT = 0x0A,
> +     FREEZE_ALL = 0x0B,
> +     START_ALL = 0x0C,
> +     REMOVE_ALL = 0x0D
> +};
> +
> +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva);
> +void hva_hw_remove(struct hva_dev *hva);
> +int hva_hw_runtime_suspend(struct device *dev);
> +int hva_hw_runtime_resume(struct device *dev);
> +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
> +                     struct hva_buffer *task);
> +
> +#endif /* HVA_HW_H */
> diff --git a/drivers/media/platform/sti/hva/hva-mem.c 
> b/drivers/media/platform/sti/hva/hva-mem.c
> new file mode 100644
> index 0000000..759c873
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-mem.c
> @@ -0,0 +1,60 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fer...@st.com>
> + *          Hugues Fruchet <hugues.fruc...@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include "hva.h"
> +#include "hva-mem.h"
> +
> +int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name,
> +               struct hva_buffer **buf)
> +{
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct hva_buffer *b;
> +     dma_addr_t paddr;
> +     void *base;
> +     DEFINE_DMA_ATTRS(attrs);
> +
> +     b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
> +     if (!b)
> +             return -ENOMEM;
> +
> +     dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
> +     base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, &attrs);
> +     if (!base) {
> +             dev_err(dev, "%s %s : dma_alloc_attrs failed for %s 
> (size=%d)\n",
> +                     ctx->name, __func__, name, size);
> +             devm_kfree(dev, b);
> +             return -ENOMEM;
> +     }
> +
> +     b->size = size;
> +     b->paddr = paddr;
> +     b->vaddr = base;
> +     b->attrs = attrs;
> +     b->name = name;
> +
> +     dev_dbg(dev,
> +             "%s allocate %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
> +             ctx->name, size, b->vaddr, &b->paddr, b->name);
> +
> +     /* return  hva buffer to user */
> +     *buf = b;
> +
> +     return 0;
> +}
> +
> +void hva_mem_free(struct hva_ctx *ctx, struct hva_buffer *buf)
> +{
> +     struct device *dev = ctx_to_dev(ctx);
> +
> +     dev_dbg(dev,
> +             "%s free %d bytes of HW memory @(virt=%p, phy=%pad): %s\n",
> +             ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name);
> +
> +     dma_free_attrs(dev, buf->size, buf->vaddr, buf->paddr, &buf->attrs);
> +
> +     devm_kfree(dev, buf);
> +}
> diff --git a/drivers/media/platform/sti/hva/hva-mem.h 
> b/drivers/media/platform/sti/hva/hva-mem.h
> new file mode 100644
> index 0000000..e8a3f7e
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-mem.h
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fer...@st.com>
> + *          Hugues Fruchet <hugues.fruc...@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef HVA_MEM_H
> +#define HVA_MEM_H
> +
> +/**
> + * struct hva_buffer - hva buffer
> + *
> + * @name:  name of requester
> + * @attrs: dma attributes
> + * @paddr: physical address (for hardware)
> + * @vaddr: virtual address (kernel can read/write)
> + * @size:  size of buffer
> + */
> +struct hva_buffer {
> +     const char              *name;
> +     struct dma_attrs        attrs;
> +     dma_addr_t              paddr;
> +     void                    *vaddr;
> +     u32                     size;
> +};
> +
> +int hva_mem_alloc(struct hva_ctx *ctx,
> +               __u32 size,
> +               const char *name,
> +               struct hva_buffer **buf);
> +
> +void hva_mem_free(struct hva_ctx *ctx,
> +               struct hva_buffer *buf);
> +
> +#endif /* HVA_MEM_H */
> diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c 
> b/drivers/media/platform/sti/hva/hva-v4l2.c
> new file mode 100644
> index 0000000..bacc9ff
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva-v4l2.c
> @@ -0,0 +1,1299 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fer...@st.com>
> + *          Hugues Fruchet <hugues.fruc...@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "hva.h"
> +#include "hva-hw.h"
> +
> +#define HVA_NAME "hva"
> +
> +#define MIN_FRAMES   1
> +#define MIN_STREAMS  1
> +
> +#define HVA_MIN_WIDTH        32
> +#define HVA_MAX_WIDTH        1920
> +#define HVA_MIN_HEIGHT       32
> +#define HVA_MAX_HEIGHT       1920
> +
> +/* HVA requires a 16x16 pixels alignment for frames */
> +#define HVA_WIDTH_ALIGNMENT  16
> +#define HVA_HEIGHT_ALIGNMENT 16
> +
> +#define DEFAULT_WIDTH                HVA_MIN_WIDTH
> +#define      DEFAULT_HEIGHT          HVA_MIN_HEIGHT
> +#define DEFAULT_FRAME_NUM    1
> +#define DEFAULT_FRAME_DEN    30
> +
> +#define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \
> +                        "frame" : "stream")
> +
> +#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
> +
> +/* registry of available encoders */
> +const struct hva_enc *hva_encoders[] = {
> +};
> +
> +static inline int frame_size(u32 w, u32 h, u32 fmt)
> +{
> +     switch (fmt) {
> +     case V4L2_PIX_FMT_NV12:
> +     case V4L2_PIX_FMT_NV21:
> +             return (w * h * 3) / 2;
> +     default:
> +             return 0;
> +     }
> +}
> +
> +static inline int frame_stride(u32 w, u32 fmt)
> +{
> +     switch (fmt) {
> +     case V4L2_PIX_FMT_NV12:
> +     case V4L2_PIX_FMT_NV21:
> +             return w;
> +     default:
> +             return 0;
> +     }
> +}
> +
> +static inline int frame_alignment(u32 fmt)
> +{
> +     switch (fmt) {
> +     case V4L2_PIX_FMT_NV12:
> +     case V4L2_PIX_FMT_NV21:
> +             /* multiple of 2 */
> +             return 2;
> +     default:
> +             return 1;
> +     }
> +}
> +
> +static inline int estimated_stream_size(u32 w, u32 h)
> +{
> +     /*
> +      * HVA only encodes in YUV420 format, whatever the frame format.
> +      * A compression ratio of 2 is assumed: thus, the maximum size
> +      * of a stream is estimated to ((width x height x 3 / 2) / 2)
> +      */
> +     return (w * h * 3) / 4;
> +}
> +
> +static void set_default_params(struct hva_ctx *ctx)
> +{
> +     struct hva_frameinfo *frameinfo = &ctx->frameinfo;
> +     struct hva_streaminfo *streaminfo = &ctx->streaminfo;
> +
> +     frameinfo->pixelformat = V4L2_PIX_FMT_NV12;
> +     frameinfo->width = DEFAULT_WIDTH;
> +     frameinfo->height = DEFAULT_HEIGHT;
> +     frameinfo->aligned_width = DEFAULT_WIDTH;
> +     frameinfo->aligned_height = DEFAULT_HEIGHT;
> +     frameinfo->size = frame_size(frameinfo->aligned_width,
> +                                  frameinfo->aligned_height,
> +                                  frameinfo->pixelformat);
> +
> +     streaminfo->streamformat = V4L2_PIX_FMT_H264;
> +     streaminfo->width = DEFAULT_WIDTH;
> +     streaminfo->height = DEFAULT_HEIGHT;
> +
> +     ctx->max_stream_size = estimated_stream_size(streaminfo->width,
> +                                                  streaminfo->height);
> +}
> +
> +static const struct hva_enc *hva_find_encoder(struct hva_ctx *ctx,
> +                                           u32 pixelformat,
> +                                           u32 streamformat)
> +{
> +     struct hva_dev *hva = ctx_to_hdev(ctx);
> +     const struct hva_enc *enc;
> +     unsigned int i;
> +
> +     for (i = 0; i < hva->nb_of_encoders; i++) {
> +             enc = hva->encoders[i];
> +             if ((enc->pixelformat == pixelformat) &&
> +                 (enc->streamformat == streamformat))
> +                     return enc;
> +     }
> +
> +     return NULL;
> +}
> +
> +static void register_format(u32 format, u32 formats[], u32 *nb_of_formats)
> +{
> +     u32 i;
> +     bool found = false;
> +
> +     for (i = 0; i < *nb_of_formats; i++) {
> +             if (format == formats[i]) {
> +                     found = true;
> +                     break;
> +             }
> +     }
> +
> +     if (!found)
> +             formats[(*nb_of_formats)++] = format;
> +}
> +
> +static void register_formats(struct hva_dev *hva)
> +{
> +     unsigned int i;
> +
> +     for (i = 0; i < hva->nb_of_encoders; i++) {
> +             register_format(hva->encoders[i]->pixelformat,
> +                             hva->pixelformats,
> +                             &hva->nb_of_pixelformats);
> +
> +             register_format(hva->encoders[i]->streamformat,
> +                             hva->streamformats,
> +                             &hva->nb_of_streamformats);
> +     }
> +}
> +
> +static void register_encoders(struct hva_dev *hva)
> +{
> +     struct device *dev = hva_to_dev(hva);
> +     unsigned int i;
> +
> +     for (i = 0; i < ARRAY_SIZE(hva_encoders); i++) {
> +             if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) {
> +                     dev_dbg(dev,
> +                             "%s failed to register encoder (%d maximum 
> reached)\n",
> +                             hva_encoders[i]->name, HVA_MAX_ENCODERS);
> +                     return;
> +             }
> +
> +             hva->encoders[hva->nb_of_encoders++] = hva_encoders[i];
> +             dev_info(dev, "%s encoder registered\n", hva_encoders[i]->name);
> +     }
> +}
> +
> +static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat,
> +                         u32 pixelformat, struct hva_enc **penc)
> +{
> +     struct hva_dev *hva = ctx_to_hdev(ctx);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct hva_enc *enc;
> +     unsigned int i;
> +     int ret;
> +     bool found = false;
> +
> +     /* find an encoder which can deal with these formats */
> +     for (i = 0; i < hva->nb_of_encoders; i++) {
> +             enc = (struct hva_enc *)hva->encoders[i];
> +             if ((enc->streamformat == streamformat) &&
> +                 (enc->pixelformat == pixelformat)) {
> +                     found = true;
> +                     break;
> +             }
> +     }
> +
> +     if (!found) {
> +             dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n",
> +                     ctx->name, (char *)&pixelformat, (char *)&streamformat);
> +             return -EINVAL;
> +     }
> +
> +     dev_dbg(dev, "%s one encoder matching %4.4s => %4.4s\n",
> +             ctx->name, (char *)&pixelformat, (char *)&streamformat);
> +
> +     /* update instance name */
> +     snprintf(ctx->name, sizeof(ctx->name), "[%3d:%4.4s]",
> +              hva->instance_id, (char *)&streamformat);
> +
> +     /* open encoder instance */
> +     ret = enc->open(ctx);
> +     if (ret) {
> +             dev_err(dev, "%s failed to open encoder instance (%d)\n",
> +                     ctx->name, ret);
> +             return ret;
> +     }
> +
> +     dev_dbg(dev, "%s %s encoder opened\n", ctx->name, enc->name);
> +
> +     *penc = enc;
> +
> +     return ret;
> +}
> +
> +/*
> + * V4L2 ioctl operations
> + */
> +
> +static int hva_querycap(struct file *file, void *priv,
> +                     struct v4l2_capability *cap)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +     strlcpy(cap->driver, hva->pdev->name, sizeof(cap->driver));
> +     strlcpy(cap->card, hva->pdev->name, sizeof(cap->card));
> +     snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> +              HVA_NAME);
> +
> +     cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
> +     cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +
> +     return 0;
> +}
> +
> +static int hva_enum_fmt_stream(struct file *file, void *priv,
> +                            struct v4l2_fmtdesc *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +     if (unlikely(f->index >= hva->nb_of_streamformats))
> +             return -EINVAL;
> +
> +     f->pixelformat = hva->streamformats[f->index];
> +     snprintf(f->description, sizeof(f->description), "%4.4s",
> +              (char *)&f->pixelformat);
> +     f->flags = V4L2_FMT_FLAG_COMPRESSED;
> +
> +     return 0;
> +}
> +
> +static int hva_enum_fmt_frame(struct file *file, void *priv,
> +                           struct v4l2_fmtdesc *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +     if (unlikely(f->index >= hva->nb_of_pixelformats))
> +             return -EINVAL;
> +
> +     f->pixelformat = hva->pixelformats[f->index];
> +     snprintf(f->description, sizeof(f->description), "%4.4s",
> +              (char *)&f->pixelformat);
> +
> +     return 0;
> +}
> +
> +static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format 
> *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct hva_streaminfo *streaminfo = &ctx->streaminfo;
> +
> +     f->fmt.pix.width = streaminfo->width;
> +     f->fmt.pix.height = streaminfo->height;
> +     f->fmt.pix.field = V4L2_FIELD_NONE;
> +     f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;

Hard coding this is not great.Ideally the colorimetry (if not modified) should 
be copied from OUTPUT to CAPTURE, you may also set this to 
V4L2_COLORSPACE_DEFAULT.

> +     f->fmt.pix.pixelformat = streaminfo->streamformat;
> +     f->fmt.pix.bytesperline = 0;
> +     f->fmt.pix.sizeimage = ctx->max_stream_size;
> +
> +     dev_dbg(dev, "%s V4L2 G_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
> +             ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +             (u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +     return 0;
> +}
> +
> +static int hva_g_fmt_frame(struct file *file, void *fh, struct v4l2_format 
> *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct hva_frameinfo *frameinfo = &ctx->frameinfo;
> +
> +     f->fmt.pix.width = frameinfo->width;
> +     f->fmt.pix.height = frameinfo->height;
> +     f->fmt.pix.field = V4L2_FIELD_NONE;
> +     f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
> +     f->fmt.pix.pixelformat = frameinfo->pixelformat;
> +     f->fmt.pix.bytesperline = frame_stride(frameinfo->aligned_width,
> +                                            frameinfo->pixelformat);
> +     f->fmt.pix.sizeimage = frameinfo->size;
> +
> +     dev_dbg(dev, "%s V4L2 G_FMT (OUTPUT): %dx%d fmt:%.4s size:%d\n",
> +             ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +             (u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +
> +     return 0;
> +}
> +
> +static int hva_try_fmt_stream(struct file *file, void *priv,
> +                           struct v4l2_format *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct v4l2_pix_format *pix = &f->fmt.pix;
> +     u32 streamformat = pix->pixelformat;
> +     const struct hva_enc *enc;
> +     u32 width, height;
> +     u32 stream_size;
> +
> +     enc = hva_find_encoder(ctx, ctx->frameinfo.pixelformat, streamformat);
> +     if (!enc) {
> +             dev_dbg(dev,
> +                     "%s V4L2 TRY_FMT (CAPTURE): unsupported format %.4s\n",
> +                     ctx->name, (char *)&pix->pixelformat);
> +             return -EINVAL;
> +     }
> +
> +     width = pix->width;
> +     height = pix->height;
> +     if (ctx->flags & HVA_FLAG_FRAMEINFO) {
> +             /*
> +              * if the frame resolution is already fixed, only allow the
> +              * same stream resolution
> +              */
> +             pix->width = ctx->frameinfo.width;
> +             pix->height = ctx->frameinfo.height;
> +             if ((pix->width != width) || (pix->height != height))
> +                     dev_dbg(dev,
> +                             "%s V4L2 TRY_FMT (CAPTURE): resolution updated 
> %dx%d -> %dx%d to fit frame resolution\n",
> +                             ctx->name, width, height,
> +                             pix->width, pix->height);
> +     } else {
> +             /* adjust width & height */
> +             v4l_bound_align_image(&pix->width,
> +                                   HVA_MIN_WIDTH, enc->max_width,
> +                                   0,
> +                                   &pix->height,
> +                                   HVA_MIN_HEIGHT, enc->max_height,
> +                                   0,
> +                                   0);
> +
> +             if ((pix->width != width) || (pix->height != height))
> +                     dev_dbg(dev,
> +                             "%s V4L2 TRY_FMT (CAPTURE): resolution updated 
> %dx%d -> %dx%d to fit min/max/alignment\n",
> +                             ctx->name, width, height,
> +                             pix->width, pix->height);
> +     }
> +
> +     stream_size = estimated_stream_size(pix->width, pix->height);
> +     if (pix->sizeimage < stream_size)
> +             pix->sizeimage = stream_size;
> +
> +     pix->bytesperline = 0;
> +     pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +     pix->field = V4L2_FIELD_NONE;
> +
> +     return 0;
> +}
> +
> +static int hva_try_fmt_frame(struct file *file, void *priv,
> +                          struct v4l2_format *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct v4l2_pix_format *pix = &f->fmt.pix;
> +     u32 pixelformat = pix->pixelformat;
> +     const struct hva_enc *enc;
> +     u32 width, height;
> +
> +     enc = hva_find_encoder(ctx, pixelformat, ctx->streaminfo.streamformat);
> +     if (!enc) {
> +             dev_dbg(dev,
> +                     "%s V4L2 TRY_FMT (OUTPUT): unsupported format %.4s\n",
> +                     ctx->name, (char *)&pixelformat);
> +             return -EINVAL;
> +     }
> +
> +     /* adjust width & height */
> +     width = pix->width;
> +     height = pix->height;
> +     v4l_bound_align_image(&pix->width,
> +                           HVA_MIN_WIDTH, HVA_MAX_WIDTH,
> +                           frame_alignment(pixelformat) - 1,
> +                           &pix->height,
> +                           HVA_MIN_HEIGHT, HVA_MAX_HEIGHT,
> +                           frame_alignment(pixelformat) - 1,
> +                           0);
> +
> +     if ((pix->width != width) || (pix->height != height))
> +             dev_dbg(dev,
> +                     "%s V4L2 TRY_FMT (OUTPUT): resolution updated %dx%d -> 
> %dx%d to fit min/max/alignment\n",
> +                     ctx->name, width, height, pix->width, pix->height);
> +
> +     width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT);
> +     height = ALIGN(pix->height, HVA_HEIGHT_ALIGNMENT);
> +
> +     pix->bytesperline = frame_stride(width, pixelformat);
> +     pix->sizeimage = frame_size(width, height, pixelformat);
> +     pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +     pix->field = V4L2_FIELD_NONE;
> +
> +     return 0;
> +}
> +
> +static int hva_s_fmt_stream(struct file *file, void *fh, struct v4l2_format 
> *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct vb2_queue *vq;
> +     int ret;
> +
> +     dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): %dx%d fmt:%.4s size:%d\n",
> +             ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +             (u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +
> +     ret = hva_try_fmt_stream(file, fh, f);
> +     if (ret) {
> +             dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): unsupported format 
> %.4s\n",
> +                     ctx->name, (char *)&f->fmt.pix.pixelformat);
> +             return ret;
> +     }
> +
> +     vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> +     if (vb2_is_streaming(vq)) {
> +             dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): queue busy\n",
> +                     ctx->name);
> +             return -EBUSY;
> +     }
> +
> +     ctx->max_stream_size = f->fmt.pix.sizeimage;
> +     ctx->streaminfo.width = f->fmt.pix.width;
> +     ctx->streaminfo.height = f->fmt.pix.height;
> +     ctx->streaminfo.streamformat = f->fmt.pix.pixelformat;
> +     ctx->flags |= HVA_FLAG_STREAMINFO;
> +
> +     return 0;
> +}
> +
> +static int hva_s_fmt_frame(struct file *file, void *fh, struct v4l2_format 
> *f)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct v4l2_pix_format *pix = &f->fmt.pix;
> +     struct vb2_queue *vq;
> +     int ret;
> +
> +     dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): %dx%d fmt %.4s size %d\n",
> +             ctx->name, f->fmt.pix.width, f->fmt.pix.height,
> +             (u8 *)&f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
> +
> +     ret = hva_try_fmt_frame(file, fh, f);
> +     if (ret) {
> +             dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): unsupported format 
> %.4s\n",
> +                     ctx->name, (char *)&f->fmt.pix.pixelformat);
> +             return ret;
> +     }
> +
> +     vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> +     if (vb2_is_streaming(vq)) {
> +             dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): queue busy\n", ctx->name);
> +             return -EBUSY;
> +     }
> +
> +     ctx->frameinfo.aligned_width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT);
> +     ctx->frameinfo.aligned_height = ALIGN(pix->height,
> +                                           HVA_HEIGHT_ALIGNMENT);
> +
> +     ctx->frameinfo.size = pix->sizeimage;
> +     ctx->frameinfo.pixelformat = pix->pixelformat;
> +     ctx->frameinfo.width = pix->width;
> +     ctx->frameinfo.height = pix->height;
> +     ctx->flags |= HVA_FLAG_FRAMEINFO;
> +
> +     return 0;
> +}
> +
> +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm 
> *sp)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
> +
> +     time_per_frame->numerator = sp->parm.capture.timeperframe.numerator;
> +     time_per_frame->denominator =
> +             sp->parm.capture.timeperframe.denominator;
> +
> +     dev_dbg(dev, "%s set parameters %d/%d\n", ctx->name,
> +             time_per_frame->numerator, time_per_frame->denominator);
> +
> +     return 0;
> +}
> +
> +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm 
> *sp)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame;
> +
> +     sp->parm.capture.timeperframe.numerator = time_per_frame->numerator;
> +     sp->parm.capture.timeperframe.denominator =
> +             time_per_frame->denominator;
> +
> +     dev_dbg(dev, "%s get parameters %d/%d\n", ctx->name,
> +             time_per_frame->numerator, time_per_frame->denominator);
> +
> +     return 0;
> +}
> +
> +static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
> +{
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +
> +     if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +             /*
> +              * depending on the targeted compressed video format, the
> +              * capture buffer might contain headers (e.g. H.264 SPS/PPS)
> +              * filled in by the driver client; the size of these data is
> +              * copied from the bytesused field of the V4L2 buffer in the
> +              * payload field of the hva stream buffer
> +              */
> +             struct vb2_queue *vq;
> +             struct hva_stream *stream;
> +
> +             vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type);
> +
> +             if (buf->index >= vq->num_buffers) {
> +                     dev_dbg(dev, "%s buffer index %d out of range (%d)\n",
> +                             ctx->name, buf->index, vq->num_buffers);
> +                     return -EINVAL;
> +             }
> +
> +             stream = (struct hva_stream *)vq->bufs[buf->index];
> +             stream->bytesused = buf->bytesused;
> +     }
> +
> +     return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
> +}
> +
> +/* V4L2 ioctl ops */
> +static const struct v4l2_ioctl_ops hva_ioctl_ops = {
> +     .vidioc_querycap                = hva_querycap,
> +     .vidioc_enum_fmt_vid_cap        = hva_enum_fmt_stream,
> +     .vidioc_enum_fmt_vid_out        = hva_enum_fmt_frame,
> +     .vidioc_g_fmt_vid_cap           = hva_g_fmt_stream,
> +     .vidioc_g_fmt_vid_out           = hva_g_fmt_frame,
> +     .vidioc_try_fmt_vid_cap         = hva_try_fmt_stream,
> +     .vidioc_try_fmt_vid_out         = hva_try_fmt_frame,
> +     .vidioc_s_fmt_vid_cap           = hva_s_fmt_stream,
> +     .vidioc_s_fmt_vid_out           = hva_s_fmt_frame,
> +     .vidioc_g_parm                  = hva_g_parm,
> +     .vidioc_s_parm                  = hva_s_parm,
> +     .vidioc_reqbufs                 = v4l2_m2m_ioctl_reqbufs,
> +     .vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
> +     .vidioc_querybuf                = v4l2_m2m_ioctl_querybuf,
> +     .vidioc_expbuf                  = v4l2_m2m_ioctl_expbuf,
> +     .vidioc_qbuf                    = hva_qbuf,
> +     .vidioc_dqbuf                   = v4l2_m2m_ioctl_dqbuf,
> +     .vidioc_streamon                = v4l2_m2m_ioctl_streamon,
> +     .vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,
> +     .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
> +     .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
> +};
> +
> +/*
> + * V4L2 control operations
> + */
> +
> +static int hva_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +     struct hva_ctx *ctx = container_of(ctrl->handler, struct hva_ctx,
> +                                        ctrl_handler);
> +     struct device *dev = ctx_to_dev(ctx);
> +
> +     dev_dbg(dev, "%s S_CTRL: id = %d, val = %d\n", ctx->name,
> +             ctrl->id, ctrl->val);
> +
> +     switch (ctrl->id) {
> +     case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
> +             ctx->ctrls.bitrate_mode = ctrl->val;
> +             break;
> +     case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
> +             ctx->ctrls.gop_size = ctrl->val;
> +             break;
> +     case V4L2_CID_MPEG_VIDEO_BITRATE:
> +             ctx->ctrls.bitrate = ctrl->val;
> +             break;
> +     case V4L2_CID_MPEG_VIDEO_ASPECT:
> +             ctx->ctrls.aspect = ctrl->val;
> +             break;
> +     default:
> +             dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n",
> +                     ctx->name, ctrl->id);
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +/* V4L2 control ops */
> +static const struct v4l2_ctrl_ops hva_ctrl_ops = {
> +     .s_ctrl = hva_s_ctrl,
> +};
> +
> +static int hva_ctrls_setup(struct hva_ctx *ctx)
> +{
> +     struct device *dev = ctx_to_dev(ctx);
> +     u64 mask;
> +
> +     v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
> +
> +     v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
> +                            V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
> +                            V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
> +                            0,
> +                            V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
> +
> +     v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
> +                       V4L2_CID_MPEG_VIDEO_GOP_SIZE,
> +                       1, 60, 1, 16);
> +
> +     v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops,
> +                       V4L2_CID_MPEG_VIDEO_BITRATE,
> +                       1, 50000, 1, 20000);
> +
> +     mask = ~(1 << V4L2_MPEG_VIDEO_ASPECT_1x1);
> +     v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops,
> +                            V4L2_CID_MPEG_VIDEO_ASPECT,
> +                            V4L2_MPEG_VIDEO_ASPECT_1x1,
> +                            mask,
> +                            V4L2_MPEG_VIDEO_ASPECT_1x1);
> +
> +     if (ctx->ctrl_handler.error) {
> +             int err = ctx->ctrl_handler.error;
> +
> +             dev_dbg(dev, "%s controls setup failed (%d)\n",
> +                     ctx->name, err);
> +             v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +             return err;
> +     }
> +
> +     v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
> +
> +     /* set default time per frame */
> +     ctx->ctrls.time_per_frame.numerator = DEFAULT_FRAME_NUM;
> +     ctx->ctrls.time_per_frame.denominator = DEFAULT_FRAME_DEN;
> +
> +     return 0;
> +}
> +
> +/*
> + * mem-to-mem operations
> + */
> +
> +static void hva_run_work(struct work_struct *work)
> +{
> +     struct hva_ctx *ctx = container_of(work, struct hva_ctx, run_work);
> +     struct vb2_v4l2_buffer *src_buf, *dst_buf;
> +     const struct hva_enc *enc = ctx->enc;
> +     struct hva_frame *frame;
> +     struct hva_stream *stream;
> +     int ret;
> +
> +     /* protect instance against reentrancy */
> +     mutex_lock(&ctx->lock);
> +
> +     src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +     dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> +     frame = to_hva_frame(src_buf);
> +     stream = to_hva_stream(dst_buf);
> +     frame->vbuf.sequence = ctx->frame_num++;
> +
> +     ret = enc->encode(ctx, frame, stream);
> +
> +     if (ret) {
> +             v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> +             v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
> +     } else {
> +             /* propagate frame timestamp */
> +             dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
> +             dst_buf->field = V4L2_FIELD_NONE;
> +             dst_buf->sequence = ctx->stream_num - 1;
> +
> +             v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> +             v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
> +     }
> +
> +     mutex_unlock(&ctx->lock);
> +
> +     v4l2_m2m_job_finish(ctx->hva_dev->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +
> +static void hva_device_run(void *priv)
> +{
> +     struct hva_ctx *ctx = priv;
> +     struct hva_dev *hva = ctx_to_hdev(ctx);
> +
> +     queue_work(hva->work_queue, &ctx->run_work);
> +}
> +
> +static void hva_job_abort(void *priv)
> +{
> +     struct hva_ctx *ctx = priv;
> +     struct device *dev = ctx_to_dev(ctx);
> +
> +     dev_dbg(dev, "%s aborting job\n", ctx->name);
> +
> +     ctx->aborting = true;
> +}
> +
> +static int hva_job_ready(void *priv)
> +{
> +     struct hva_ctx *ctx = priv;
> +     struct device *dev = ctx_to_dev(ctx);
> +
> +     if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
> +             dev_dbg(dev, "%s job not ready: no frame buffers\n",
> +                     ctx->name);
> +             return 0;
> +     }
> +
> +     if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
> +             dev_dbg(dev, "%s job not ready: no stream buffers\n",
> +                     ctx->name);
> +             return 0;
> +     }
> +
> +     if (ctx->aborting) {
> +             dev_dbg(dev, "%s job not ready: aborting\n", ctx->name);
> +             return 0;
> +     }
> +
> +     return 1;
> +}
> +
> +/* mem-to-mem ops */
> +static const struct v4l2_m2m_ops hva_m2m_ops = {
> +     .device_run     = hva_device_run,
> +     .job_abort      = hva_job_abort,
> +     .job_ready      = hva_job_ready,
> +};
> +
> +/*
> + * VB2 queue operations
> + */
> +
> +static int hva_queue_setup(struct vb2_queue *vq,
> +                        unsigned int *num_buffers, unsigned int *num_planes,
> +                        unsigned int sizes[], void *alloc_ctxs[])
> +{
> +     struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +     struct device *dev = ctx_to_dev(ctx);
> +     unsigned int size;
> +
> +     dev_dbg(dev, "%s %s queue setup: num_buffers %d\n", ctx->name,
> +             to_type_str(vq->type), *num_buffers);
> +
> +     size = vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ?
> +             ctx->frameinfo.size : ctx->max_stream_size;
> +
> +     alloc_ctxs[0] = ctx->hva_dev->alloc_ctx;
> +
> +     if (*num_planes)
> +             return sizes[0] < size ? -EINVAL : 0;
> +
> +     /* only one plane supported */
> +     *num_planes = 1;
> +     sizes[0] = size;
> +
> +     return 0;
> +}
> +
> +static int hva_buf_prepare(struct vb2_buffer *vb)
> +{
> +     struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +     struct device *dev = ctx_to_dev(ctx);
> +     struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +     if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> +             struct hva_frame *frame = to_hva_frame(vbuf);
> +
> +             if (vbuf->field == V4L2_FIELD_ANY)
> +                     vbuf->field = V4L2_FIELD_NONE;
> +             if (vbuf->field != V4L2_FIELD_NONE) {
> +                     dev_dbg(dev,
> +                             "%s frame[%d] prepare: %d field not 
> supported\n",
> +                             ctx->name, vb->index, vbuf->field);
> +                     return -EINVAL;
> +             }
> +
> +             if (!frame->prepared) {
> +                     /* get memory addresses */
> +                     frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> +                     frame->paddr = vb2_dma_contig_plane_dma_addr(
> +                                     &vbuf->vb2_buf, 0);
> +                     frame->info = ctx->frameinfo;
> +                     frame->prepared = true;
> +
> +                     dev_dbg(dev,
> +                             "%s frame[%d] prepared; virt=%p, phy=%pad\n",
> +                             ctx->name, vb->index,
> +                             frame->vaddr, &frame->paddr);
> +             }
> +     } else {
> +             struct hva_stream *stream = to_hva_stream(vbuf);
> +
> +             if (!stream->prepared) {
> +                     /* get memory addresses */
> +                     stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> +                     stream->paddr = vb2_dma_contig_plane_dma_addr(
> +                                     &vbuf->vb2_buf, 0);
> +                     stream->size = vb2_plane_size(&vbuf->vb2_buf, 0);
> +                     stream->prepared = true;
> +
> +                     dev_dbg(dev,
> +                             "%s stream[%d] prepared; virt=%p, phy=%pad\n",
> +                             ctx->name, vb->index,
> +                             stream->vaddr, &stream->paddr);
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +static void hva_buf_queue(struct vb2_buffer *vb)
> +{
> +     struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +     struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +     if (ctx->fh.m2m_ctx)
> +             v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static int hva_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +     struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +     struct device *dev = ctx_to_dev(ctx);
> +     int ret = 0;
> +
> +     dev_dbg(dev, "%s %s start streaming\n", ctx->name,
> +             to_type_str(vq->type));
> +
> +     /* open encoder when both start_streaming have been called */
> +     if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
> +             if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->cap_q_ctx.q))
> +                     return 0;
> +     } else {
> +             if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->out_q_ctx.q))
> +                     return 0;
> +     }
> +
> +     if (!ctx->enc)
> +             ret = hva_open_encoder(ctx,
> +                                    ctx->streaminfo.streamformat,
> +                                    ctx->frameinfo.pixelformat,
> +                                    &ctx->enc);
> +
> +     return ret;
> +}
> +
> +static void hva_stop_streaming(struct vb2_queue *vq)
> +{
> +     struct hva_ctx *ctx = vb2_get_drv_priv(vq);
> +     struct device *dev = ctx_to_dev(ctx);
> +     const struct hva_enc *enc = ctx->enc;
> +     struct vb2_v4l2_buffer *vbuf;
> +
> +     dev_dbg(dev, "%s %s stop streaming\n", ctx->name,
> +             to_type_str(vq->type));
> +
> +     if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> +             /* return of all pending buffers to vb2 (in error state) */
> +             ctx->frame_num = 0;
> +             while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
> +                     v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +     } else {
> +             /* return of all pending buffers to vb2 (in error state) */
> +             ctx->stream_num = 0;
> +             while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
> +                     v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +     }
> +
> +     if ((V4L2_TYPE_IS_OUTPUT(vq->type) &&
> +          vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) ||
> +         (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
> +          vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) {
> +             dev_dbg(dev, "%s %s out=%d cap=%d\n",
> +                     ctx->name, to_type_str(vq->type),
> +                     vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q),
> +                     vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q));
> +             return;
> +     }
> +
> +     /* close encoder when both stop_streaming have been called */
> +     if (enc) {
> +             dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name);
> +             enc->close(ctx);
> +             ctx->enc = NULL;
> +     }
> +
> +     ctx->aborting = false;
> +}
> +
> +/* VB2 queue ops */
> +static const struct vb2_ops hva_qops = {
> +     .queue_setup            = hva_queue_setup,
> +     .buf_prepare            = hva_buf_prepare,
> +     .buf_queue              = hva_buf_queue,
> +     .start_streaming        = hva_start_streaming,
> +     .stop_streaming         = hva_stop_streaming,
> +     .wait_prepare           = vb2_ops_wait_prepare,
> +     .wait_finish            = vb2_ops_wait_finish,
> +};
> +
> +/*
> + * V4L2 file operations
> + */
> +
> +static int queue_init(struct hva_ctx *ctx, struct vb2_queue *vq)
> +{
> +     vq->io_modes = VB2_MMAP | VB2_DMABUF;
> +     vq->drv_priv = ctx;
> +     vq->ops = &hva_qops;
> +     vq->mem_ops = &vb2_dma_contig_memops;
> +     vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +     vq->lock = &ctx->hva_dev->lock;
> +
> +     return vb2_queue_init(vq);
> +}
> +
> +static int hva_queue_init(void *priv, struct vb2_queue *src_vq,
> +                       struct vb2_queue *dst_vq)
> +{
> +     struct hva_ctx *ctx = priv;
> +     int ret;
> +
> +     src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +     src_vq->buf_struct_size = sizeof(struct hva_frame);
> +     src_vq->min_buffers_needed = MIN_FRAMES;
> +
> +     ret = queue_init(ctx, src_vq);
> +     if (ret)
> +             return ret;
> +
> +     dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +     dst_vq->buf_struct_size = sizeof(struct hva_stream);
> +     dst_vq->min_buffers_needed = MIN_STREAMS;
> +
> +     return queue_init(ctx, dst_vq);
> +}
> +
> +static int hva_open(struct file *file)
> +{
> +     struct hva_dev *hva = video_drvdata(file);
> +     struct device *dev = hva_to_dev(hva);
> +     struct hva_ctx *ctx;
> +     int ret;
> +     unsigned int i;
> +     bool found = false;
> +
> +     ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +     if (!ctx) {
> +             ret = -ENOMEM;
> +             goto out;
> +     }
> +     ctx->hva_dev = hva;
> +
> +     mutex_lock(&hva->lock);
> +
> +     /* store the instance context in the instances array */
> +     for (i = 0; i < HVA_MAX_INSTANCES; i++) {
> +             if (!hva->instances[i]) {
> +                     hva->instances[i] = ctx;
> +                     /* save the context identifier in the context */
> +                     ctx->id = i;
> +                     found = true;
> +                     break;
> +             }
> +     }
> +
> +     if (!found) {
> +             dev_err(dev, "%s [x:x] maximum instances reached\n",
> +                     HVA_PREFIX);
> +             ret = -ENOMEM;
> +             goto mem_ctx;
> +     }
> +
> +     INIT_WORK(&ctx->run_work, hva_run_work);
> +     v4l2_fh_init(&ctx->fh, video_devdata(file));
> +     file->private_data = &ctx->fh;
> +     v4l2_fh_add(&ctx->fh);
> +
> +     ret = hva_ctrls_setup(ctx);
> +     if (ret) {
> +             dev_err(dev, "%s [x:x] failed to setup controls\n",
> +                     HVA_PREFIX);
> +             goto err_fh;
> +     }
> +     ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> +
> +     mutex_init(&ctx->lock);
> +
> +     ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(hva->m2m_dev, ctx,
> +                                         &hva_queue_init);
> +     if (IS_ERR(ctx->fh.m2m_ctx)) {
> +             ret = PTR_ERR(ctx->fh.m2m_ctx);
> +             dev_err(dev, "%s [x:x] failed to initialize m2m context (%d)\n",
> +                     HVA_PREFIX, ret);
> +             goto err_ctrls;
> +     }
> +
> +     /* set the instance name */
> +     hva->instance_id++;
> +     snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]",
> +              hva->instance_id);
> +
> +     hva->nb_of_instances++;
> +
> +     mutex_unlock(&hva->lock);
> +
> +     /* default parameters for frame and stream */
> +     set_default_params(ctx);
> +
> +     dev_info(dev, "%s encoder instance created (id %d)\n",
> +              ctx->name, ctx->id);
> +
> +     return 0;
> +
> +err_ctrls:
> +     v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +err_fh:
> +     v4l2_fh_del(&ctx->fh);
> +     v4l2_fh_exit(&ctx->fh);
> +     hva->instances[ctx->id] = NULL;
> +mem_ctx:
> +     kfree(ctx);
> +     mutex_unlock(&hva->lock);
> +out:
> +     return ret;
> +}
> +
> +static int hva_release(struct file *file)
> +{
> +     struct hva_dev *hva = video_drvdata(file);
> +     struct hva_ctx *ctx = fh_to_ctx(file->private_data);
> +     struct device *dev = ctx_to_dev(ctx);
> +
> +     v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> +
> +     v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +
> +     v4l2_fh_del(&ctx->fh);
> +     v4l2_fh_exit(&ctx->fh);
> +
> +     mutex_lock(&hva->lock);
> +
> +     /* clear instance context in instances array */
> +     hva->instances[ctx->id] = NULL;
> +
> +     hva->nb_of_instances--;
> +
> +     mutex_unlock(&hva->lock);
> +
> +     dev_info(dev, "%s encoder instance released (id %d)\n",
> +              ctx->name, ctx->id);
> +
> +     kfree(ctx);
> +
> +     return 0;
> +}
> +
> +/* V4L2 file ops */
> +static const struct v4l2_file_operations hva_fops = {
> +     .owner                  = THIS_MODULE,
> +     .open                   = hva_open,
> +     .release                = hva_release,
> +     .unlocked_ioctl         = video_ioctl2,
> +     .mmap                   = v4l2_m2m_fop_mmap,
> +     .poll                   = v4l2_m2m_fop_poll,
> +};
> +
> +/*
> + * Platform device operations
> + */
> +
> +static int hva_register_device(struct hva_dev *hva)
> +{
> +     int ret;
> +     struct video_device *vdev;
> +     struct device *dev;
> +
> +     if (!hva)
> +             return -ENODEV;
> +     dev = hva_to_dev(hva);
> +
> +     hva->m2m_dev = v4l2_m2m_init(&hva_m2m_ops);
> +     if (IS_ERR(hva->m2m_dev)) {
> +             dev_err(dev, "%s %s failed to initialize v4l2-m2m device\n",
> +                     HVA_PREFIX, HVA_NAME);
> +             ret = PTR_ERR(hva->m2m_dev);
> +             goto err;
> +     }
> +
> +     vdev = video_device_alloc();
> +     if (!vdev) {
> +             dev_err(dev, "%s %s failed to allocate video device\n",
> +                     HVA_PREFIX, HVA_NAME);
> +             ret = -ENOMEM;
> +             goto err_m2m_release;
> +     }
> +
> +     vdev->fops = &hva_fops;
> +     vdev->ioctl_ops = &hva_ioctl_ops;
> +     vdev->release = video_device_release;
> +     vdev->lock = &hva->lock;
> +     vdev->vfl_dir = VFL_DIR_M2M;
> +     vdev->v4l2_dev = &hva->v4l2_dev;
> +     snprintf(vdev->name, sizeof(vdev->name), "%s", HVA_NAME);
> +
> +     ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +     if (ret) {
> +             dev_err(dev, "%s %s failed to register video device\n",
> +                     HVA_PREFIX, HVA_NAME);
> +             goto err_vdev_release;
> +     }
> +
> +     hva->vdev = vdev;
> +     video_set_drvdata(vdev, hva);
> +     return 0;
> +
> +err_vdev_release:
> +     video_device_release(vdev);
> +err_m2m_release:
> +     v4l2_m2m_release(hva->m2m_dev);
> +err:
> +     return ret;
> +}
> +
> +static void hva_unregister_device(struct hva_dev *hva)
> +{
> +     if (!hva)
> +             return;
> +
> +     if (hva->m2m_dev)
> +             v4l2_m2m_release(hva->m2m_dev);
> +
> +     video_unregister_device(hva->vdev);
> +}
> +
> +static int hva_probe(struct platform_device *pdev)
> +{
> +     struct hva_dev *hva;
> +     struct device *dev = &pdev->dev;
> +     int ret;
> +
> +     hva = devm_kzalloc(dev, sizeof(*hva), GFP_KERNEL);
> +     if (!hva) {
> +             ret = -ENOMEM;
> +             goto err;
> +     }
> +
> +     hva->dev = dev;
> +     hva->pdev = pdev;
> +     platform_set_drvdata(pdev, hva);
> +
> +     mutex_init(&hva->lock);
> +
> +     /* probe hardware */
> +     ret = hva_hw_probe(pdev, hva);
> +     if (ret)
> +             goto err;
> +
> +     /* register all available encoders */
> +     register_encoders(hva);
> +
> +     /* register all supported formats */
> +     register_formats(hva);
> +
> +     /* register on V4L2 */
> +     ret = v4l2_device_register(dev, &hva->v4l2_dev);
> +     if (ret) {
> +             dev_err(dev, "%s %s failed to register V4L2 device\n",
> +                     HVA_PREFIX, HVA_NAME);
> +             goto err_hw;
> +     }
> +
> +     /* continuous memory allocator */
> +     hva->alloc_ctx = vb2_dma_contig_init_ctx(dev);
> +     if (IS_ERR(hva->alloc_ctx)) {
> +             ret = PTR_ERR(hva->alloc_ctx);
> +             goto err_v4l2;
> +     }
> +
> +     hva->work_queue = create_workqueue(HVA_NAME);
> +     if (!hva->work_queue) {
> +             dev_err(dev, "%s %s failed to allocate work queue\n",
> +                     HVA_PREFIX, HVA_NAME);
> +             ret = -ENOMEM;
> +             goto err_vb2_dma;
> +     }
> +
> +     /* register device */
> +     ret = hva_register_device(hva);
> +     if (ret)
> +             goto err_work_queue;
> +
> +     dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX,
> +              HVA_NAME, hva->vdev->num);
> +
> +     return 0;
> +
> +err_work_queue:
> +     destroy_workqueue(hva->work_queue);
> +err_vb2_dma:
> +     vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
> +err_v4l2:
> +     v4l2_device_unregister(&hva->v4l2_dev);
> +err_hw:
> +     hva_hw_remove(hva);
> +err:
> +     return ret;
> +}
> +
> +static int hva_remove(struct platform_device *pdev)
> +{
> +     struct hva_dev *hva = platform_get_drvdata(pdev);
> +     struct device *dev = hva_to_dev(hva);
> +
> +     hva_unregister_device(hva);
> +
> +     destroy_workqueue(hva->work_queue);
> +
> +     vb2_dma_contig_cleanup_ctx(hva->alloc_ctx);
> +
> +     hva_hw_remove(hva);
> +
> +     v4l2_device_unregister(&hva->v4l2_dev);
> +
> +     dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
> +
> +     return 0;
> +}
> +
> +/* PM ops */
> +static const struct dev_pm_ops hva_pm_ops = {
> +     .runtime_suspend        = hva_hw_runtime_suspend,
> +     .runtime_resume         = hva_hw_runtime_resume,
> +};
> +
> +static const struct of_device_id hva_match_types[] = {
> +     {
> +      .compatible = "st,sti-hva",
> +     },
> +     { /* end node */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, hva_match_types);
> +
> +struct platform_driver hva_driver = {
> +     .probe  = hva_probe,
> +     .remove = hva_remove,
> +     .driver = {
> +             .name           = HVA_NAME,
> +             .owner          = THIS_MODULE,
> +             .of_match_table = hva_match_types,
> +             .pm             = &hva_pm_ops,
> +             },
> +};
> +
> +module_platform_driver(hva_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Yannick Fertre <yannick.fer...@st.com>");
> +MODULE_DESCRIPTION("HVA video encoder V4L2 driver");
> diff --git a/drivers/media/platform/sti/hva/hva.h 
> b/drivers/media/platform/sti/hva/hva.h
> new file mode 100644
> index 0000000..9a1b503b
> --- /dev/null
> +++ b/drivers/media/platform/sti/hva/hva.h
> @@ -0,0 +1,284 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2015
> + * Authors: Yannick Fertre <yannick.fer...@st.com>
> + *          Hugues Fruchet <hugues.fruc...@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef HVA_H
> +#define HVA_H
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#define fh_to_ctx(f)    (container_of(f, struct hva_ctx, fh))
> +
> +#define hva_to_dev(h)   (h->dev)
> +
> +#define ctx_to_dev(c)   (c->hva_dev->dev)
> +
> +#define ctx_to_hdev(c)  (c->hva_dev)
> +
> +#define HVA_PREFIX "[---:----]"
> +
> +/**
> + * struct hva_frameinfo - information about hva frame
> + *
> + * @pixelformat:    fourcc code for uncompressed video format
> + * @width:          width of frame
> + * @height:         height of frame
> + * @aligned_width:  width of frame (with encoder alignment constraint)
> + * @aligned_height: height of frame (with encoder alignment constraint)
> + * @size:           maximum size in bytes required for data
> +*/
> +struct hva_frameinfo {
> +     u32     pixelformat;
> +     u32     width;
> +     u32     height;
> +     u32     aligned_width;
> +     u32     aligned_height;
> +     u32     size;
> +};
> +
> +/**
> + * struct hva_streaminfo - information about hva stream
> + *
> + * @streamformat: fourcc code of compressed video format (H.264...)
> + * @width:        width of stream
> + * @height:       height of stream
> + * @profile:      profile string
> + * @level:        level string
> + */
> +struct hva_streaminfo {
> +     u32     streamformat;
> +     u32     width;
> +     u32     height;
> +     u8      profile[32];
> +     u8      level[32];
> +};
> +
> +/**
> + * struct hva_controls - hva controls set
> + *
> + * @time_per_frame: time per frame in seconds
> + * @bitrate_mode:   bitrate mode (constant bitrate or variable bitrate)
> + * @gop_size:       groupe of picture size
> + * @bitrate:        bitrate (in kbps)
> + * @aspect:         video aspect
> + */
> +struct hva_controls {
> +     struct v4l2_fract                       time_per_frame;
> +     enum v4l2_mpeg_video_bitrate_mode       bitrate_mode;
> +     u32                                     gop_size;
> +     u32                                     bitrate;
> +     enum v4l2_mpeg_video_aspect             aspect;
> +};
> +
> +/**
> + * struct hva_frame - hva frame buffer (output)
> + *
> + * @vbuf:     video buffer information for V4L2
> + * @list:     V4L2 m2m list that the frame belongs to
> + * @info:     frame information (width, height, format, alignment...)
> + * @paddr:    physical address (for hardware)
> + * @vaddr:    virtual address (kernel can read/write)
> + * @prepared: true if vaddr/paddr are resolved
> + */
> +struct hva_frame {
> +     struct vb2_v4l2_buffer  vbuf;
> +     struct list_head        list;
> +     struct hva_frameinfo    info;
> +     dma_addr_t              paddr;
> +     void                    *vaddr;
> +     bool                    prepared;
> +};
> +
> +/*
> + * to_hva_frame() - cast struct vb2_v4l2_buffer * to struct hva_frame *
> + */
> +#define to_hva_frame(vb) \
> +     container_of(vb, struct hva_frame, vbuf)
> +
> +/**
> + * struct hva_stream - hva stream buffer (capture)
> + *
> + * @v4l2:       video buffer information for V4L2
> + * @list:       V4L2 m2m list that the frame belongs to
> + * @paddr:      physical address (for hardware)
> + * @vaddr:      virtual address (kernel can read/write)
> + * @prepared:   true if vaddr/paddr are resolved
> + * @size:       size of the buffer in bytes
> + * @bytesused:  number of bytes occupied by data in the buffer
> + */
> +struct hva_stream {
> +     struct vb2_v4l2_buffer  vbuf;
> +     struct list_head        list;
> +     dma_addr_t              paddr;
> +     void                    *vaddr;
> +     int                     prepared;
> +     unsigned int            size;
> +     unsigned int            bytesused;
> +};
> +
> +/*
> + * to_hva_stream() - cast struct vb2_v4l2_buffer * to struct hva_stream *
> + */
> +#define to_hva_stream(vb) \
> +     container_of(vb, struct hva_stream, vbuf)
> +
> +struct hva_dev;
> +struct hva_enc;
> +
> +/**
> + * struct hva_ctx - context of hva instance
> + *
> + * @hva_dev:         the device that this instance is associated with
> + * @fh:              V4L2 file handle
> + * @ctrl_handler:    V4L2 controls handler
> + * @ctrls:           hva controls set
> + * @id:              instance identifier
> + * @aborting:        true if current job aborted
> + * @name:            instance name (debug purpose)
> + * @run_work:        encode work
> + * @lock:            mutex used to lock access of this context
> + * @flags:           validity of streaminfo and frameinfo fields
> + * @frame_num:       frame number
> + * @stream_num:      stream number
> + * @max_stream_size: maximum size in bytes required for stream data
> + * @streaminfo:      stream properties
> + * @frameinfo:       frame properties
> + * @enc:             current encoder
> + * @priv:            private codec data for this instance, allocated
> + *                   by encoder @open time
> + * @hw_err:          true if hardware error detected
> + */
> +struct hva_ctx {
> +     struct hva_dev                  *hva_dev;
> +     struct v4l2_fh                  fh;
> +     struct v4l2_ctrl_handler        ctrl_handler;
> +     struct hva_controls             ctrls;
> +     u8                              id;
> +     bool                            aborting;
> +     char                            name[100];
> +     struct work_struct              run_work;
> +     /* mutex protecting this data structure */
> +     struct mutex                    lock;
> +     u32                             flags;
> +     u32                             frame_num;
> +     u32                             stream_num;
> +     u32                             max_stream_size;
> +     struct hva_streaminfo           streaminfo;
> +     struct hva_frameinfo            frameinfo;
> +     struct hva_enc                  *enc;
> +     void                            *priv;
> +     bool                            hw_err;
> +};
> +
> +#define HVA_FLAG_STREAMINFO  0x0001
> +#define HVA_FLAG_FRAMEINFO   0x0002
> +
> +#define HVA_MAX_INSTANCES    16
> +#define HVA_MAX_ENCODERS     10
> +#define HVA_MAX_FORMATS              HVA_MAX_ENCODERS
> +
> +/**
> + * struct hva_dev - abstraction for hva entity
> + *
> + * @v4l2_dev:            V4L2 device
> + * @vdev:                video device
> + * @pdev:                platform device
> + * @dev:                 device
> + * @lock:                mutex used for critical sections & V4L2 ops
> + *                       serialization
> + * @m2m_dev:             memory-to-memory V4L2 device informatio
> + * @alloc_ctx:           videobuf2 memory allocator context
> + * @instances:           opened instances
> + * @nb_of_instances:     number of opened instances
> + * @instance_id:         rolling counter identifying an instance (debug 
> purpose)
> + * @regs:                register io memory access
> + * @esram_addr:          esram address
> + * @esram_size:          esram size
> + * @clk:                 hva clock
> + * @irq_its:             status interruption
> + * @irq_err:             error interruption
> + * @work_queue:          work queue to handle the encode jobs
> + * @protect_mutex:       mutex used to lock access of hardware
> + * @interrupt:           completion interrupt
> + * @ip_version:          IP hardware version
> + * @encoders:            registered encoders
> + * @nb_of_encoders:      number of registered encoders
> + * @pixelformats:        supported uncompressed video formats
> + * @nb_of_pixelformats:  number of supported umcompressed video formats
> + * @streamformats:       supported compressed video formats
> + * @nb_of_streamformats: number of supported compressed video formats
> + * @sfl_reg:             status fifo level register value
> + * @sts_reg:             status register value
> + * @lmi_err_reg:         local memory interface error register value
> + * @emi_err_reg:         external memory interface error register value
> + * @hec_mif_err_reg:     HEC memory interface error register value
> + */
> +struct hva_dev {
> +     struct v4l2_device      v4l2_dev;
> +     struct video_device     *vdev;
> +     struct platform_device  *pdev;
> +     struct device           *dev;
> +     /* mutex protecting vb2_queue structure */
> +     struct mutex            lock;
> +     struct v4l2_m2m_dev     *m2m_dev;
> +     struct vb2_alloc_ctx    *alloc_ctx;
> +     struct hva_ctx          *instances[HVA_MAX_INSTANCES];
> +     unsigned int            nb_of_instances;
> +     unsigned int            instance_id;
> +     void __iomem            *regs;
> +     u32                     esram_addr;
> +     u32                     esram_size;
> +     struct clk              *clk;
> +     int                     irq_its;
> +     int                     irq_err;
> +     struct workqueue_struct *work_queue;
> +     /* mutex protecting hardware access */
> +     struct mutex            protect_mutex;
> +     struct completion       interrupt;
> +     unsigned long int       ip_version;
> +     const struct hva_enc    *encoders[HVA_MAX_ENCODERS];
> +     u32                     nb_of_encoders;
> +     u32                     pixelformats[HVA_MAX_FORMATS];
> +     u32                     nb_of_pixelformats;
> +     u32                     streamformats[HVA_MAX_FORMATS];
> +     u32                     nb_of_streamformats;
> +     u32                     sfl_reg;
> +     u32                     sts_reg;
> +     u32                     lmi_err_reg;
> +     u32                     emi_err_reg;
> +     u32                     hec_mif_err_reg;
> +};
> +
> +/**
> + * struct hva_enc - hva encoder
> + *
> + * @name:         encoder name
> + * @streamformat: fourcc code for compressed video format (H.264...)
> + * @pixelformat:  fourcc code for uncompressed video format
> + * @max_width:    maximum width of frame for this encoder
> + * @max_height:   maximum height of frame for this encoder
> + * @open:         open encoder
> + * @close:        close encoder
> + * @encode:       encode a frame (struct hva_frame) in a stream
> + *                (struct hva_stream)
> + */
> +
> +struct hva_enc {
> +     const char      *name;
> +     u32             streamformat;
> +     u32             pixelformat;
> +     u32             max_width;
> +     u32             max_height;
> +     int             (*open)(struct hva_ctx *ctx);
> +     int             (*close)(struct hva_ctx *ctx);
> +     int             (*encode)(struct hva_ctx *ctx, struct hva_frame *frame,
> +                               struct hva_stream *stream);
> +};
> +
> +#endif /* HVA_H */

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to