diff mbox

[v2,2/3,media] hva: multi-format video encoder V4L2 driver

Message ID 1468250057-16395-3-git-send-email-jean-christophe.trotin@st.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jean-Christophe Trotin July 11, 2016, 3:14 p.m. UTC
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.fertre@st.com>
Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@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

Comments

Nicolas Dufresne July 11, 2016, 6 p.m. UTC | #1
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.fertre@st.com>
> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@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.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@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.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@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.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@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.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@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.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@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.fertre@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.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@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 */
Hans Verkuil July 18, 2016, 11:45 a.m. UTC | #2
Hi Jean-Christophe,

See my review comments below. Nothing really major, but I do need to know more
about the g/s_parm and the restriction on the number of open()s has to be lifted.
That's not allowed.

On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
> 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.fertre@st.com>
> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@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

Please mention here what HVA stands for.

> +	  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.

How about sti-hva as the module name? 'hva' is a bit too generic.

> +
>  config VIDEO_SH_VEU
>  	tristate "SuperH VEU mem2mem video processing driver"
>  	depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA

<snip>

> 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.fertre@st.com>
> + *          Hugues Fruchet <hugues.fruchet@st.com>
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#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;

Set the new device_caps field of struct video_device to
V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M and drop these two lines.

The v4l2 core will set device_caps and capabilities for you based on the
video_device struct device_caps field. New drivers should use this.

The advantage is that the v4l2 core now knows the caps of the video node.

> +
> +	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;

Drop these two lines. The v4l2 code fills in the description and flags for
you.

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

Ditto.

> +
> +	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;

This is a mem2mem device, so the colorspace comes from the colorspace that
the application specified for the video output format. That's copied to the
video capture format. See e.g. the vim2m.c example driver.

The default colorspace should be REC709 as well, rather than SMPTE170M (that's
for SDTV).

I've added checks to v4l2-compliance to improve testing for this.

See also the vim2m patch I just posted where I patch that m2m driver so it
passes the compliance test.

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

No need for these debug messages. You can always debug the ioctls by:

echo 2 >/sys/class/video4linux/video0/dev_debug.

> +	return 0;
> +}

<snip>

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

This is strange. Normally codecs don't need this. You give them a buffer and
it will be encoded/decoded and then you give it the next one if it is available.
There is normally no frame rate involved.

How does this work in this SoC? I need to know a bit more about this to be
certain there isn't a misunderstanding somewhere.

> +
> +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[])

This patch needs to be rebased: the way allocation contexts are set up has
changed. You now set the new 'dev' field in vb2_queue to the struct device,
and there is no longer any need to fill in alloc_ctxs here or init/free the
allocation context. The prototype of this queue_setup function has
changed as well.

> +{
> +	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;

Anything other than FIELD_NONE should result in an error since no interlaced is supported.
FIELD_ANY is an incorrect value as well.

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

On error all pending buffers for queue vq should be returned to vb2 in the QUEUED state.
Similar to what happens in stop_streaming, but with state QUEUED instead of state ERROR.

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

This is wrong. It should always be possible to open the device node and
e.g. query the format or control settings, or whatever.

In this case there is apparently a hardware restriction with regards to
the number of codec instances. It *is* a hardware restriction, right?
If it is a driver restriction, then that's certainly wrong since there
is normally no reason for that.

Assuming it is a HW restriction, then this restriction is normally
checked in start_streaming or in queue_setup. I.e. at the point where
the HW resource reservation actually takes place.

> +
> +	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,
> +		},

Wrong indentation?

> +};
> +
> +module_platform_driver(hva_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
> +MODULE_DESCRIPTION("HVA video encoder V4L2 driver");

Regards,

	Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jean-Christophe Trotin July 19, 2016, 3:55 p.m. UTC | #3
Hi Hans,

Thank you for your comments.
I've started to take them into account.
I've got a question about V4L2_FIELD_ANY in buf_prepare (please see below).

[snip]

 >> +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;
 >
 > Anything other than FIELD_NONE should result in an error since no interlaced 
is supported.
 > FIELD_ANY is an incorrect value as well.
 >

In videodev2.h, V4L2_FIELD_ANY is commented as "driver can choose from none, 
top, bottom, interlaced depending on whatever it thinks is approximate ...": I 
understand this comment as if vbuf->field is equal to V4L2_FIELD_ANY, then the 
driver can choose to force it to V4L2_FIELD_NONE. Furthermore, it's coded in the 
same way in vim2m.c (vim2m_buf_prepare).
Finally, if I remove these 2 lines, I've got the following error with the 
v4l2-compliance:
Streaming ioctls:
		VIDIOC_G_INPUT returned -1 (Inappropriate ioctl for device)
		VIDIOC_ENUMINPUT returned -1 (Inappropriate ioctl for device)
	test read/write: OK (Not Supported)
		VIDIOC_QUERYCAP returned 0 (Success)
		[snip]
		VIDIOC_QUERYBUF returned 0 (Success)
		VIDIOC_QBUF returned -1 (Invalid argument)
		fail: 
/local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(773): 
buf.qbuf(node)
		fail: 
/local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(971): 
setupM2M(node, m2m_q)
	test MMAP: FAIL

Don't you think that I could keep these two lines?

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

[snip]

Regards,
Jean-Christophe.
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hans Verkuil July 19, 2016, 4:45 p.m. UTC | #4
On 07/19/2016 05:55 PM, Jean Christophe TROTIN wrote:
> Hi Hans,
> 
> Thank you for your comments.
> I've started to take them into account.
> I've got a question about V4L2_FIELD_ANY in buf_prepare (please see below).
> 
> [snip]
> 
>  >> +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;
>  >
>  > Anything other than FIELD_NONE should result in an error since no interlaced 
> is supported.
>  > FIELD_ANY is an incorrect value as well.
>  >
> 
> In videodev2.h, V4L2_FIELD_ANY is commented as "driver can choose from none, 
> top, bottom, interlaced depending on whatever it thinks is approximate ...": I 
> understand this comment as if vbuf->field is equal to V4L2_FIELD_ANY, then the 
> driver can choose to force it to V4L2_FIELD_NONE. Furthermore, it's coded in the 
> same way in vim2m.c (vim2m_buf_prepare).
> Finally, if I remove these 2 lines, I've got the following error with the 
> v4l2-compliance:
> Streaming ioctls:
> 		VIDIOC_G_INPUT returned -1 (Inappropriate ioctl for device)
> 		VIDIOC_ENUMINPUT returned -1 (Inappropriate ioctl for device)
> 	test read/write: OK (Not Supported)
> 		VIDIOC_QUERYCAP returned 0 (Success)
> 		[snip]
> 		VIDIOC_QUERYBUF returned 0 (Success)
> 		VIDIOC_QBUF returned -1 (Invalid argument)
> 		fail: 
> /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(773): 
> buf.qbuf(node)
> 		fail: 
> /local/home/frq08988/views/opensdk-2.1.4.1/sources/v4l-utils/utils/v4l2-compliance/v4l2-test-buffers.cpp(971): 
> setupM2M(node, m2m_q)
> 	test MMAP: FAIL
> 
> Don't you think that I could keep these two lines?

Keep it for now, I dug into this a bit further and it is really a workaround for
poorly written applications that can't be bothered to set the field value to a
proper value. I think the documentation needs to be updated for this.

I might change my mind, though :-)

Regards,

	Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jean-Christophe Trotin July 21, 2016, 7:30 a.m. UTC | #5
On 07/18/2016 01:45 PM, Hans Verkuil wrote:
> Hi Jean-Christophe,
>
> See my review comments below. Nothing really major, but I do need to know more
> about the g/s_parm and the restriction on the number of open()s has to be lifted.
> That's not allowed.
>

Hi Hans,

Thank you for your comments.
I've explained below why I would like to keep 'hva' as driver's name and why the
frame rate is needed (g/s_parm).
I've followed your advice for managing the hardware restriction with regards to
the number of codec instances (see also below).
Finally, I've taken into account all the other comments.
All these modifications will be reflected in the version 3.

Best regards,
Jean-Christophe.

> On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
>> 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.fertre@st.com>
>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@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
>
> Please mention here what HVA stands for.
>

Done in version 3.
HVA stands for "Hardware Video Accelerator".

>> +       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.
>
> How about sti-hva as the module name? 'hva' is a bit too generic.
>

'hva' is a generic IP which could be used on different STMicroelectronics SoCs.
That's the reason why I would like to keep this name. It's not specific to  the
STiH41x series: thus, I've reworked the Kconfig's comment.

>> +
>>   config VIDEO_SH_VEU
>>        tristate "SuperH VEU mem2mem video processing driver"
>>        depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
>
> <snip>
>
>> 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.fertre@st.com>
>> + *          Hugues Fruchet <hugues.fruchet@st.com>
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <media/v4l2-event.h>
>> +#include <media/v4l2-ioctl.h>
>> +#include <media/videobuf2-dma-contig.h>
>> +
>> +#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;
>
> Set the new device_caps field of struct video_device to
> V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M and drop these two lines.
>
> The v4l2 core will set device_caps and capabilities for you based on the
> video_device struct device_caps field. New drivers should use this.
>
> The advantage is that the v4l2 core now knows the caps of the video node.
>

Done in version 3.

>> +
>> +     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;
>
> Drop these two lines. The v4l2 code fills in the description and flags for
> you.
>

Done in version 3.

>> +
>> +     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);
>
> Ditto.
>

Done in version 3.

>> +
>> +     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;
>
> This is a mem2mem device, so the colorspace comes from the colorspace that
> the application specified for the video output format. That's copied to the
> video capture format. See e.g. the vim2m.c example driver.
>
> The default colorspace should be REC709 as well, rather than SMPTE170M (that's
> for SDTV).
>
> I've added checks to v4l2-compliance to improve testing for this.
>
> See also the vim2m patch I just posted where I patch that m2m driver so it
> passes the compliance test.
>

Done in version 3.
I've aligned the code in version 3 on the colorspace management made in the
vim2m and mtk-vcodec drivers.

>> +     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);
>
> No need for these debug messages. You can always debug the ioctls by:
>
> echo 2 >/sys/class/video4linux/video0/dev_debug.
>

Done in version 3 in hva_g_fmt_stream, hva_g_fmt_frame, hva_s_fmt_stream,
hva_s_fmt_frame, hva_s_parm and hva_g_parm functions.

>> +     return 0;
>> +}
>
> <snip>
>
>> +
>> +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;
>> +}
>
> This is strange. Normally codecs don't need this. You give them a buffer and
> it will be encoded/decoded and then you give it the next one if it is available.
> There is normally no frame rate involved.
>
> How does this work in this SoC? I need to know a bit more about this to be
> certain there isn't a misunderstanding somewhere.
>

Among the parameters dimensioning its buffer model, the 'hva' HW IP needs to
calculate the depletion that is the quantity of bits at the output of the
encoder in each time slot, basically bitrate/framerate. That's the reason for
these 2 functions.
Furthermore, I've seen that mtk-vcodec and coda encoders also get the frame rate
to configure their HW IPs (vidioc_venc_s_parm & coda_s_parm).

>> +
>> +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[])
>
> This patch needs to be rebased: the way allocation contexts are set up has
> changed. You now set the new 'dev' field in vb2_queue to the struct device,
> and there is no longer any need to fill in alloc_ctxs here or init/free the
> allocation context. The prototype of this queue_setup function has
> changed as well.
>

I apologize for forgetting this rebase.
Done in version 3.

>> +{
>> +     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;
>
> Anything other than FIELD_NONE should result in an error since no interlaced is supported.
> FIELD_ANY is an incorrect value as well.
>

These 2 lines are kept as discussed in a previous email.

>> +             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);
>
> On error all pending buffers for queue vq should be returned to vb2 in the QUEUED state.
> Similar to what happens in stop_streaming, but with state QUEUED instead of state ERROR.
>

Done in version 3.

>> +
>> +     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;
>> +     }
>
> This is wrong. It should always be possible to open the device node and
> e.g. query the format or control settings, or whatever.
>
> In this case there is apparently a hardware restriction with regards to
> the number of codec instances. It *is* a hardware restriction, right?
> If it is a driver restriction, then that's certainly wrong since there
> is normally no reason for that.
>
> Assuming it is a HW restriction, then this restriction is normally
> checked in start_streaming or in queue_setup. I.e. at the point where
> the HW resource reservation actually takes place.
>

I confirm that there's a hardware restriction with regards to the number of
codec instances. I agree with you that it shall be checked in start_streaming.
Done in version 3.

>> +
>> +     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,
>> +             },
>
> Wrong indentation?
>

Done in version 3.

>> +};
>> +
>> +module_platform_driver(hva_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
>> +MODULE_DESCRIPTION("HVA video encoder V4L2 driver");
>
> Regards,
>
>          Hans
>
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hans Verkuil July 21, 2016, 9:49 a.m. UTC | #6
On 07/21/2016 09:30 AM, Jean Christophe TROTIN wrote:
> 
> On 07/18/2016 01:45 PM, Hans Verkuil wrote:
>> Hi Jean-Christophe,
>>
>> See my review comments below. Nothing really major, but I do need to know more
>> about the g/s_parm and the restriction on the number of open()s has to be lifted.
>> That's not allowed.
>>
> 
> Hi Hans,
> 
> Thank you for your comments.
> I've explained below why I would like to keep 'hva' as driver's name and why the
> frame rate is needed (g/s_parm).
> I've followed your advice for managing the hardware restriction with regards to
> the number of codec instances (see also below).
> Finally, I've taken into account all the other comments.
> All these modifications will be reflected in the version 3.
> 
> Best regards,
> Jean-Christophe.
> 
>> On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
>>> 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.fertre@st.com>
>>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@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
>>
>> Please mention here what HVA stands for.
>>
> 
> Done in version 3.
> HVA stands for "Hardware Video Accelerator".
> 
>>> +       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.
>>
>> How about sti-hva as the module name? 'hva' is a bit too generic.
>>
> 
> 'hva' is a generic IP which could be used on different STMicroelectronics SoCs.
> That's the reason why I would like to keep this name. It's not specific to  the
> STiH41x series: thus, I've reworked the Kconfig's comment.

How about st-hva? I really like it to be a bit more specific.

>>> +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;
>>> +}
>>
>> This is strange. Normally codecs don't need this. You give them a buffer and
>> it will be encoded/decoded and then you give it the next one if it is available.
>> There is normally no frame rate involved.
>>
>> How does this work in this SoC? I need to know a bit more about this to be
>> certain there isn't a misunderstanding somewhere.
>>
> 
> Among the parameters dimensioning its buffer model, the 'hva' HW IP needs to
> calculate the depletion that is the quantity of bits at the output of the
> encoder in each time slot, basically bitrate/framerate. That's the reason for
> these 2 functions.
> Furthermore, I've seen that mtk-vcodec and coda encoders also get the frame rate
> to configure their HW IPs (vidioc_venc_s_parm & coda_s_parm).

Ah, OK. That makes sense. So this is *only* used in calculating the bitrate(s),
not in actual scheduling of threads or something like that, right?

Regards,

	Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jean-Christophe Trotin July 25, 2016, 2:09 p.m. UTC | #7
On 07/21/2016 11:49 AM, Hans Verkuil wrote:
>
>
> On 07/21/2016 09:30 AM, Jean Christophe TROTIN wrote:
>>
>> On 07/18/2016 01:45 PM, Hans Verkuil wrote:
>>> Hi Jean-Christophe,
>>>
>>> See my review comments below. Nothing really major, but I do need to know more
>>> about the g/s_parm and the restriction on the number of open()s has to be lifted.
>>> That's not allowed.
>>>
>>
>> Hi Hans,
>>
>> Thank you for your comments.
>> I've explained below why I would like to keep 'hva' as driver's name and why the
>> frame rate is needed (g/s_parm).
>> I've followed your advice for managing the hardware restriction with regards to
>> the number of codec instances (see also below).
>> Finally, I've taken into account all the other comments.
>> All these modifications will be reflected in the version 3.
>>
>> Best regards,
>> Jean-Christophe.
>>
>>> On 07/11/2016 05:14 PM, Jean-Christophe Trotin wrote:
>>>> 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.fertre@st.com>
>>>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@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
>>>
>>> Please mention here what HVA stands for.
>>>
>>
>> Done in version 3.
>> HVA stands for "Hardware Video Accelerator".
>>
>>>> +       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.
>>>
>>> How about sti-hva as the module name? 'hva' is a bit too generic.
>>>
>>
>> 'hva' is a generic IP which could be used on different STMicroelectronics SoCs.
>> That's the reason why I would like to keep this name. It's not specific to  the
>> STiH41x series: thus, I've reworked the Kconfig's comment.
>
> How about st-hva? I really like it to be a bit more specific.
>

Hi Hans,

Thank you for your comments.
As discussed through IRC with you and Benjamin Gaignard last week, I will rename 
the module "st-hva" in the version 4.

>>>> +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;
>>>> +}
>>>
>>> This is strange. Normally codecs don't need this. You give them a buffer and
>>> it will be encoded/decoded and then you give it the next one if it is available.
>>> There is normally no frame rate involved.
>>>
>>> How does this work in this SoC? I need to know a bit more about this to be
>>> certain there isn't a misunderstanding somewhere.
>>>
>>
>> Among the parameters dimensioning its buffer model, the 'hva' HW IP needs to
>> calculate the depletion that is the quantity of bits at the output of the
>> encoder in each time slot, basically bitrate/framerate. That's the reason for
>> these 2 functions.
>> Furthermore, I've seen that mtk-vcodec and coda encoders also get the frame rate
>> to configure their HW IPs (vidioc_venc_s_parm & coda_s_parm).
>
> Ah, OK. That makes sense. So this is *only* used in calculating the bitrate(s),
> not in actual scheduling of threads or something like that, right?
>
> Regards,
>
> 	Hans
>

About the frame rate (G/S-PARM), I confirm that it's not used for any scheduling 
of threads or anything like that: it's only for bitrate(s) calculation.

Best regards,
Jean-Christophe.--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#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.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@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.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@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.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@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.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#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;
+	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.fertre@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.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef HVA_H
+#define HVA_H
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-mem2mem.h>
+
+#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 */