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@xxxxxx> > Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@xxxxxx> > --- > 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@xxxxxx> > + * Hugues Fruchet <hugues.fruchet@xxxxxx> > + * 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@xxxxxx> > + * Hugues Fruchet <hugues.fruchet@xxxxxx> > + * 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@xxxxxx> > + * Hugues Fruchet <hugues.fruchet@xxxxxx> > + * 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@xxxxxx> > + * Hugues Fruchet <hugues.fruchet@xxxxxx> > + * 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@xxxxxx> > + * Hugues Fruchet <hugues.fruchet@xxxxxx> > + * 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@xxxxxx>"); > +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@xxxxxx> > + * Hugues Fruchet <hugues.fruchet@xxxxxx> > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#ifndef HVA_H > +#define HVA_H > + > +#include > +#include > +#include > +#include > + > +#define fh_to_ctx(f) (container_of(f, struct hva_ctx, fh)) > + > +#define hva_to_dev(h) (h->dev) > + > +#define ctx_to_dev(c) (c->hva_dev->dev) > + > +#define ctx_to_hdev(c) (c->hva_dev) > + > +#define HVA_PREFIX "[---:----]" > + > +/** > + * struct hva_frameinfo - information about hva frame > + * > + * @pixelformat: fourcc code for uncompressed video format > + * @width: width of frame > + * @height: height of frame > + * @aligned_width: width of frame (with encoder alignment constraint) > + * @aligned_height: height of frame (with encoder alignment constraint) > + * @size: maximum size in bytes required for data > +*/ > +struct hva_frameinfo { > + u32 pixelformat; > + u32 width; > + u32 height; > + u32 aligned_width; > + u32 aligned_height; > + u32 size; > +}; > + > +/** > + * struct hva_streaminfo - information about hva stream > + * > + * @streamformat: fourcc code of compressed video format (H.264...) > + * @width: width of stream > + * @height: height of stream > + * @profile: profile string > + * @level: level string > + */ > +struct hva_streaminfo { > + u32 streamformat; > + u32 width; > + u32 height; > + u8 profile[32]; > + u8 level[32]; > +}; > + > +/** > + * struct hva_controls - hva controls set > + * > + * @time_per_frame: time per frame in seconds > + * @bitrate_mode: bitrate mode (constant bitrate or variable bitrate) > + * @gop_size: groupe of picture size > + * @bitrate: bitrate (in kbps) > + * @aspect: video aspect > + */ > +struct hva_controls { > + struct v4l2_fract time_per_frame; > + enum v4l2_mpeg_video_bitrate_mode bitrate_mode; > + u32 gop_size; > + u32 bitrate; > + enum v4l2_mpeg_video_aspect aspect; > +}; > + > +/** > + * struct hva_frame - hva frame buffer (output) > + * > + * @vbuf: video buffer information for V4L2 > + * @list: V4L2 m2m list that the frame belongs to > + * @info: frame information (width, height, format, alignment...) > + * @paddr: physical address (for hardware) > + * @vaddr: virtual address (kernel can read/write) > + * @prepared: true if vaddr/paddr are resolved > + */ > +struct hva_frame { > + struct vb2_v4l2_buffer vbuf; > + struct list_head list; > + struct hva_frameinfo info; > + dma_addr_t paddr; > + void *vaddr; > + bool prepared; > +}; > + > +/* > + * to_hva_frame() - cast struct vb2_v4l2_buffer * to struct hva_frame * > + */ > +#define to_hva_frame(vb) \ > + container_of(vb, struct hva_frame, vbuf) > + > +/** > + * struct hva_stream - hva stream buffer (capture) > + * > + * @v4l2: video buffer information for V4L2 > + * @list: V4L2 m2m list that the frame belongs to > + * @paddr: physical address (for hardware) > + * @vaddr: virtual address (kernel can read/write) > + * @prepared: true if vaddr/paddr are resolved > + * @size: size of the buffer in bytes > + * @bytesused: number of bytes occupied by data in the buffer > + */ > +struct hva_stream { > + struct vb2_v4l2_buffer vbuf; > + struct list_head list; > + dma_addr_t paddr; > + void *vaddr; > + int prepared; > + unsigned int size; > + unsigned int bytesused; > +}; > + > +/* > + * to_hva_stream() - cast struct vb2_v4l2_buffer * to struct hva_stream * > + */ > +#define to_hva_stream(vb) \ > + container_of(vb, struct hva_stream, vbuf) > + > +struct hva_dev; > +struct hva_enc; > + > +/** > + * struct hva_ctx - context of hva instance > + * > + * @hva_dev: the device that this instance is associated with > + * @fh: V4L2 file handle > + * @ctrl_handler: V4L2 controls handler > + * @ctrls: hva controls set > + * @id: instance identifier > + * @aborting: true if current job aborted > + * @name: instance name (debug purpose) > + * @run_work: encode work > + * @lock: mutex used to lock access of this context > + * @flags: validity of streaminfo and frameinfo fields > + * @frame_num: frame number > + * @stream_num: stream number > + * @max_stream_size: maximum size in bytes required for stream data > + * @streaminfo: stream properties > + * @frameinfo: frame properties > + * @enc: current encoder > + * @priv: private codec data for this instance, allocated > + * by encoder @open time > + * @hw_err: true if hardware error detected > + */ > +struct hva_ctx { > + struct hva_dev *hva_dev; > + struct v4l2_fh fh; > + struct v4l2_ctrl_handler ctrl_handler; > + struct hva_controls ctrls; > + u8 id; > + bool aborting; > + char name[100]; > + struct work_struct run_work; > + /* mutex protecting this data structure */ > + struct mutex lock; > + u32 flags; > + u32 frame_num; > + u32 stream_num; > + u32 max_stream_size; > + struct hva_streaminfo streaminfo; > + struct hva_frameinfo frameinfo; > + struct hva_enc *enc; > + void *priv; > + bool hw_err; > +}; > + > +#define HVA_FLAG_STREAMINFO 0x0001 > +#define HVA_FLAG_FRAMEINFO 0x0002 > + > +#define HVA_MAX_INSTANCES 16 > +#define HVA_MAX_ENCODERS 10 > +#define HVA_MAX_FORMATS HVA_MAX_ENCODERS > + > +/** > + * struct hva_dev - abstraction for hva entity > + * > + * @v4l2_dev: V4L2 device > + * @vdev: video device > + * @pdev: platform device > + * @dev: device > + * @lock: mutex used for critical sections & V4L2 ops > + * serialization > + * @m2m_dev: memory-to-memory V4L2 device informatio > + * @alloc_ctx: videobuf2 memory allocator context > + * @instances: opened instances > + * @nb_of_instances: number of opened instances > + * @instance_id: rolling counter identifying an instance (debug purpose) > + * @regs: register io memory access > + * @esram_addr: esram address > + * @esram_size: esram size > + * @clk: hva clock > + * @irq_its: status interruption > + * @irq_err: error interruption > + * @work_queue: work queue to handle the encode jobs > + * @protect_mutex: mutex used to lock access of hardware > + * @interrupt: completion interrupt > + * @ip_version: IP hardware version > + * @encoders: registered encoders > + * @nb_of_encoders: number of registered encoders > + * @pixelformats: supported uncompressed video formats > + * @nb_of_pixelformats: number of supported umcompressed video formats > + * @streamformats: supported compressed video formats > + * @nb_of_streamformats: number of supported compressed video formats > + * @sfl_reg: status fifo level register value > + * @sts_reg: status register value > + * @lmi_err_reg: local memory interface error register value > + * @emi_err_reg: external memory interface error register value > + * @hec_mif_err_reg: HEC memory interface error register value > + */ > +struct hva_dev { > + struct v4l2_device v4l2_dev; > + struct video_device *vdev; > + struct platform_device *pdev; > + struct device *dev; > + /* mutex protecting vb2_queue structure */ > + struct mutex lock; > + struct v4l2_m2m_dev *m2m_dev; > + struct vb2_alloc_ctx *alloc_ctx; > + struct hva_ctx *instances[HVA_MAX_INSTANCES]; > + unsigned int nb_of_instances; > + unsigned int instance_id; > + void __iomem *regs; > + u32 esram_addr; > + u32 esram_size; > + struct clk *clk; > + int irq_its; > + int irq_err; > + struct workqueue_struct *work_queue; > + /* mutex protecting hardware access */ > + struct mutex protect_mutex; > + struct completion interrupt; > + unsigned long int ip_version; > + const struct hva_enc *encoders[HVA_MAX_ENCODERS]; > + u32 nb_of_encoders; > + u32 pixelformats[HVA_MAX_FORMATS]; > + u32 nb_of_pixelformats; > + u32 streamformats[HVA_MAX_FORMATS]; > + u32 nb_of_streamformats; > + u32 sfl_reg; > + u32 sts_reg; > + u32 lmi_err_reg; > + u32 emi_err_reg; > + u32 hec_mif_err_reg; > +}; > + > +/** > + * struct hva_enc - hva encoder > + * > + * @name: encoder name > + * @streamformat: fourcc code for compressed video format (H.264...) > + * @pixelformat: fourcc code for uncompressed video format > + * @max_width: maximum width of frame for this encoder > + * @max_height: maximum height of frame for this encoder > + * @open: open encoder > + * @close: close encoder > + * @encode: encode a frame (struct hva_frame) in a stream > + * (struct hva_stream) > + */ > + > +struct hva_enc { > + const char *name; > + u32 streamformat; > + u32 pixelformat; > + u32 max_width; > + u32 max_height; > + int (*open)(struct hva_ctx *ctx); > + int (*close)(struct hva_ctx *ctx); > + int (*encode)(struct hva_ctx *ctx, struct hva_frame *frame, > + struct hva_stream *stream); > +}; > + > +#endif /* HVA_H */
Attachment:
signature.asc
Description: This is a digitally signed message part