lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <51a1974ebeed5d7fda49f05545ac4c86f7ec85b6.camel@ndufresne.ca>
Date: Fri, 07 Nov 2025 16:18:11 -0500
From: Nicolas Dufresne <nicolas@...fresne.ca>
To: yassine.ouaissa@...egrodvt.com, Mauro Carvalho Chehab
 <mchehab@...nel.org>,  Michael Tretter <m.tretter@...gutronix.de>,
 Pengutronix Kernel Team <kernel@...gutronix.de>, Michal Simek	
 <michal.simek@....com>, Rob Herring <robh@...nel.org>, Krzysztof Kozlowski	
 <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>
Cc: linux-kernel@...r.kernel.org, linux-media@...r.kernel.org, 
	linux-arm-kernel@...ts.infradead.org, devicetree@...r.kernel.org
Subject: Re: [PATCH v4 4/4] media: allegro-dvt: Add Gen 3 IP stateful
 decoder driver

Hi,

took a long time, but I got to it.

Le mercredi 16 juillet 2025 à 14:55 +0000, Yassine Ouaissa via B4 Relay a écrit :
> From: Yassine Ouaissa <yassine.ouaissa@...egrodvt.com>
> 
> This commit introduces a new allegro-dvt V4L2 stateful decoder driverfor

driverfor -> driver for

> the Gen 3 IP with support for:
> - AVC (H.264), HEVC (H.265), and JPEG decoding
> - Output formats: NV12, NV16, I420, and P010 for capture
> 
> Signed-off-by: Yassine Ouaissa <yassine.ouaissa@...egrodvt.com>
> ---
>  drivers/media/platform/allegro-dvt/Kconfig         |    1 +
>  drivers/media/platform/allegro-dvt/Makefile        |    1 +
>  drivers/media/platform/allegro-dvt/al300/Kconfig   |   23 +
>  drivers/media/platform/allegro-dvt/al300/Makefile  |    9 +
>  .../platform/allegro-dvt/al300/al_codec_common.c   |  747 ++++++++++
>  .../platform/allegro-dvt/al300/al_codec_common.h   |  251 ++++
>  .../platform/allegro-dvt/al300/al_codec_dbgfs.c    |  229 +++
>  .../platform/allegro-dvt/al300/al_codec_dbgfs.h    |   71 +
>  .../platform/allegro-dvt/al300/al_codec_util.c     |  180 +++
>  .../platform/allegro-dvt/al300/al_codec_util.h     |  194 +++
>  .../media/platform/allegro-dvt/al300/al_vdec_drv.c | 1515 ++++++++++++++++++++
>  .../media/platform/allegro-dvt/al300/al_vdec_drv.h |   98 ++
>  12 files changed, 3319 insertions(+)
> 
> diff --git a/drivers/media/platform/allegro-dvt/Kconfig b/drivers/media/platform/allegro-dvt/Kconfig
> index e9008614c27b9490d1cd29fab887977a1918ede4..0d01ed0ad08ab3bf63fc6bc60ac6c8ad9b31c9ab 100644
> --- a/drivers/media/platform/allegro-dvt/Kconfig
> +++ b/drivers/media/platform/allegro-dvt/Kconfig
> @@ -2,4 +2,5 @@
>  
>  comment "Allegro DVT media platform drivers"
>  
> +source "drivers/media/platform/allegro-dvt/al300/Kconfig"
>  source "drivers/media/platform/allegro-dvt/zynqmp/Kconfig"
> diff --git a/drivers/media/platform/allegro-dvt/Makefile b/drivers/media/platform/allegro-dvt/Makefile
> index d2aa6875edcf7760901996aac4d5ac98282cce20..c70ca19a47fb7a50a568b37ce519bbedbefe670d 100644
> --- a/drivers/media/platform/allegro-dvt/Makefile
> +++ b/drivers/media/platform/allegro-dvt/Makefile
> @@ -1,3 +1,4 @@
>  # SPDX-License-Identifier: GPL-2.0
>  
> +obj-y += al300/
>  obj-y += zynqmp/
> diff --git a/drivers/media/platform/allegro-dvt/al300/Kconfig b/drivers/media/platform/allegro-dvt/al300/Kconfig
> new file mode 100644
> index 0000000000000000000000000000000000000000..0bc3d7a79f14038a4f497f736b14a7fc7cca0aeb
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/Kconfig
> @@ -0,0 +1,23 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config VIDEO_ALLEGRO_DVT_D300
> +	tristate "Allegro DVT Video IP Decode Gen 3"
> +	depends on V4L_MEM2MEM_DRIVERS
> +	depends on VIDEO_DEV && OF && HAS_DMA
> +	select V4L2_MEM2MEM_DEV
> +	select VIDEOBUF2_DMA_CONTIG
> +	help
> +	  This is a video4linux2 driver for the Allegro DVT IP Decode Gen 3,
> +	  that support codecs : AVC (H.264), HEVC (H.265), and JPEG.
> +
> +	  The driver provides hardware acceleration for video decoding operations,
> +	  enabling efficient processing of compressed video streams on platforms
> +	  featuring this IP block. It handles memory management, buffer allocation,
> +	  and decoder command sequencing to deliver optimized performance.
> +
> +	  The driver integrates with the V4L2 framework and videobuf2 subsystem
> +	  to provide a standard interface for applications requiring video
> +	  decoding capabilities.
> +
> +	  To compile this driver as a module, choose M here. The module
> +	  will be called al300-vdec.
> diff --git a/drivers/media/platform/allegro-dvt/al300/Makefile b/drivers/media/platform/allegro-dvt/al300/Makefile
> new file mode 100644
> index 0000000000000000000000000000000000000000..e5bc317ee2a76963ff8bfe4a584ce5bde1a98fb7
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/Makefile
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +al300-vdec-objs := al_codec_common.o al_codec_util.o al_vdec_drv.o
> +
> +ifneq ($(CONFIG_DEBUG_FS),)
> +al300-vdec-objs += al_codec_dbgfs.o
> +endif
> +
> +obj-$(CONFIG_VIDEO_ALLEGRO_DVT_D300) += al300-vdec.o
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_codec_common.c b/drivers/media/platform/allegro-dvt/al300/al_codec_common.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..cfd0f24e7d0279423573c08816f8c6761b95a691
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_codec_common.c
> @@ -0,0 +1,747 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Core MCU functionality including firmware loading,
> + * memory allocation, and general MCU interaction interfaces
> + *
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + */
> +
> +#include <linux/cleanup.h>
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include "al_codec_common.h"
> +
> +#define AL_CODEC_UID 0x0000
> +#define AL_CODEC_RESET 0x0010
> +#define AL_CODEC_IRQ_MASK 0x0014
> +#define AL_CODEC_IRQ_STATUS_CLEAR 0x0018
> +#define AL_CODEC_MCU_CLK 0x0400
> +#define AL_CODEC_MCU_RST 0x0404
> +#define AL_CODEC_MCU_IRQ 0x040C
> +#define AL_CODEC_MCU_BOOT_ADDR_HI 0x0410
> +#define AL_CODEC_MCU_BOOT_ADDR_LO 0x0414
> +#define AL_CODEC_MCU_IP_START_ADDR_HI 0x0418
> +#define AL_CODEC_MCU_IP_START_ADDR_LO 0x041C
> +#define AL_CODEC_MCU_IP_END_ADDR_HI 0x0420
> +#define AL_CODEC_MCU_IP_END_ADDR_LO 0x0424
> +#define AL_CODEC_MCU_PERIPH_ADDR_HI 0x0428
> +#define AL_CODEC_MCU_PERIPH_ADDR_LO 0x042C
> +#define AL_CODEC_MCU_IRQ_MASK 0x0440
> +#define AL_CODEC_INST_OFFSET_HI 0x0450
> +#define AL_CODEC_INST_OFFSET_LO 0x0454
> +#define AL_CODEC_DATA_OFFSET_HI 0x0458
> +#define AL_CODEC_DATA_OFFSET_LO 0x045C
> +
> +#define AL_CODEC_UID_ID 0x30AB6E51
> +#define AL_CODEC_IRQ_MCU_2_CPU BIT(30)
> +#define AL_CODEC_IP_OFFSET GENMASK(26, 25)
> +#define AL_CODEC_APB_MASK GENMASK(26, 0)
> +
> +#define AL_CODEC_MCU_BOOT_RESET_WAIT 2 /* in ms */
> +#define AL_CODEC_REG_ENABLE BIT(0)
> +#define AL_CODEC_REG_DISABLE 0
> +
> +/*
> + * struct codec_dma_buf - Allocated dma buffer
> + *
> + * @list: list head for buffer queue
> + * @paddr: physical address of the allcated DMA buffer
> + * @vaddr: virtual address of the allocated DMA buffer
> + * @size: Size of allocated dma memory
> + */
> +struct codec_dma_buf {
> +	void *vaddr;
> +	dma_addr_t paddr;
> +	u32 size;
> +	struct list_head list;
> +};
> +
> +struct mb_header {
> +	u64 start;
> +	u64 end;
> +} __packed;
> +
> +struct boot_header {
> +	u32 bh_version;
> +	u32 fw_version;
> +	char model[16];
> +	u64 vaddr_start;
> +	u64 vaddr_end;
> +	u64 vaddr_boot;
> +	struct mb_header h2m;
> +	struct mb_header m2h;
> +	u64 machine_id;
> +	/* fill by driver before fw boot */
> +	u64 ip_start;
> +	u64 ip_end;
> +	u64 mcu_clk_rate;
> +} __packed;
> +
> +static u32 al_common_read(struct al_codec_dev *codec, u32 offset)
> +{
> +	return readl(codec->regs + offset);
> +}
> +
> +static void al_common_write(struct al_codec_dev *codec, u32 offset, u32 val)
> +{
> +	writel(val, codec->regs + offset);
> +}
> +
> +static void al_common_trigger_mcu_irq(void *arg)
> +{
> +	struct al_codec_dev *codec = arg;
> +
> +	al_common_write(codec, AL_CODEC_MCU_IRQ, BIT(0));
> +}
> +
> +static inline void al_common_reset(struct al_codec_dev *codec)
> +{
> +	/* reset ip */
> +	al_common_write(codec, AL_CODEC_RESET, AL_CODEC_REG_ENABLE);
> +
> +	/* reset and stop mcu */
> +	al_common_write(codec, AL_CODEC_MCU_CLK, AL_CODEC_REG_ENABLE);
> +	al_common_write(codec, AL_CODEC_MCU_RST, AL_CODEC_REG_ENABLE);
> +	/* time to reset the mct */
> +	mdelay(AL_CODEC_MCU_BOOT_RESET_WAIT);
> +	al_common_write(codec, AL_CODEC_MCU_CLK, AL_CODEC_REG_DISABLE);
> +
> +	al_common_write(codec, AL_CODEC_MCU_IRQ, AL_CODEC_REG_DISABLE);
> +	al_common_write(codec, AL_CODEC_MCU_IRQ_MASK, AL_CODEC_REG_DISABLE);
> +
> +	mdelay(AL_CODEC_MCU_BOOT_RESET_WAIT * 5);
> +	al_common_write(codec, AL_CODEC_MCU_RST, AL_CODEC_REG_DISABLE);
> +}
> +
> +/**
> + * Check if physical address exceeds 40-bit addressing capability
> + * @param phys_addr: Physical address to check
> + * @return: Offset beyond (40-bit - 1) limit, or 0 if within limit
> + */
> +static inline uint64_t get_phys_offset(uint64_t paddr)
> +{
> +	const uint64_t ADDR_40BIT_MAX = GENMASK(39, 0); // (2^40 - 1)
> +
> +	return (paddr > ADDR_40BIT_MAX) ? (paddr - ADDR_40BIT_MAX - 1) : 0;
> +}
> +
> +static int al_common_setup_hw_regs(struct al_codec_dev *codec)
> +{
> +	u64 reg_start, reg_end;
> +	dma_addr_t boot_addr;
> +	u64 fw_offset;
> +	unsigned int id;
> +
> +	id = al_common_read(codec, AL_CODEC_UID);
> +
> +	if (id != AL_CODEC_UID_ID) {
> +		al_codec_err(codec,
> +			     "bad device id, expected 0x%08x, got 0x%08x",
> +			     AL_CODEC_UID_ID, id);
> +		return -ENODEV;
> +	}
> +
> +	fw_offset = get_phys_offset(codec->firmware.phys);
> +	boot_addr = codec->firmware.phys + codec->firmware.bin_data.offset -
> +		    fw_offset;
> +
> +	/* Reset MCU step */
> +	al_common_reset(codec);
> +
> +	/* Configure the MCU*/
> +	al_common_write(codec, AL_CODEC_IRQ_MASK, AL_CODEC_IRQ_MCU_2_CPU);
> +	/* Set Instruction and data offset */
> +	al_common_write(codec, AL_CODEC_INST_OFFSET_HI,
> +			upper_32_bits(fw_offset));
> +	al_common_write(codec, AL_CODEC_INST_OFFSET_LO,
> +			lower_32_bits(fw_offset));
> +	al_common_write(codec, AL_CODEC_DATA_OFFSET_HI,
> +			upper_32_bits(fw_offset));
> +	al_common_write(codec, AL_CODEC_DATA_OFFSET_LO,
> +			lower_32_bits(fw_offset));
> +
> +	reg_start = codec->regs_info->start;
> +	reg_end = reg_start + resource_size(codec->regs_info);
> +	al_common_write(codec, AL_CODEC_MCU_IP_START_ADDR_HI,
> +			upper_32_bits(reg_start));
> +	al_common_write(codec, AL_CODEC_MCU_IP_START_ADDR_LO,
> +			lower_32_bits(reg_start));
> +	al_common_write(codec, AL_CODEC_MCU_IP_END_ADDR_HI,
> +			upper_32_bits(reg_end));
> +	al_common_write(codec, AL_CODEC_MCU_IP_END_ADDR_HI,
> +			lower_32_bits(reg_end));
> +
> +	al_common_write(codec, AL_CODEC_MCU_PERIPH_ADDR_HI,
> +			upper_32_bits(codec->apb));
> +	al_common_write(codec, AL_CODEC_MCU_PERIPH_ADDR_LO,
> +			lower_32_bits(codec->apb));
> +
> +	al_common_write(codec, AL_CODEC_MCU_BOOT_ADDR_HI,
> +			upper_32_bits(boot_addr));
> +	al_common_write(codec, AL_CODEC_MCU_BOOT_ADDR_LO,
> +			lower_32_bits(boot_addr));
> +
> +	return 0;
> +}
> +
> +static void al_common_dma_buf_insert(struct al_codec_dev *codec,
> +				     struct codec_dma_buf *buf)
> +{
> +	guard(mutex)(&codec->buf_lock);
> +	list_add(&buf->list, &codec->alloc_buffers);
> +}
> +
> +static void al_common_dma_buf_remove(struct al_codec_dev *codec,
> +				     struct codec_dma_buf *buf)
> +{
> +	struct device *dev = &codec->pdev->dev;
> +
> +	guard(mutex)(&codec->buf_lock);
> +	dma_free_coherent(dev, buf->size, buf->vaddr, buf->paddr);
> +	list_del(&buf->list);
> +	kfree(buf);
> +}
> +
> +static struct codec_dma_buf *
> +al_common_dma_buf_lookup(struct al_codec_dev *codec, dma_addr_t buf_paddr)
> +{
> +	struct codec_dma_buf *buf = NULL;
> +
> +	guard(mutex)(&codec->buf_lock);
> +	list_for_each_entry(buf, &codec->alloc_buffers, list)
> +		if (likely(buf->paddr == buf_paddr))
> +			break;
> +
> +	return list_entry_is_head(buf, &codec->alloc_buffers, list) ? NULL :
> +								      buf;
> +}
> +
> +static void al_common_dma_buf_cleanup(struct al_codec_dev *codec)
> +{
> +	struct device *dev = &codec->pdev->dev;
> +	struct codec_dma_buf *buf, *tmp;
> +
> +	guard(mutex)(&codec->buf_lock);
> +	list_for_each_entry_safe(buf, tmp, &codec->alloc_buffers, list) {
> +		dma_free_coherent(dev, buf->size, buf->vaddr, buf->paddr);
> +		list_del(&buf->list);
> +		kfree(buf);
> +	}
> +}
> +
> +static int al_common_setup_dma(struct al_codec_dev *codec)
> +{
> +	struct device *dev = &codec->pdev->dev;
> +	int ret;
> +
> +	/* setup dma memory mask */
> +	if (!dev->coherent_dma_mask) {
> +		ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
> +		if (ret) {
> +			al_codec_err(codec, "Failed to set dma mask %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	/* Try to use reserved memory if we got one */
> +	ret = of_reserved_mem_device_init(dev);
> +	if (ret && ret != ENODEV)
> +		dev_warn(dev, "No reserved memory, use cma instead\n");
> +
> +	return 0;
> +}
> +
> +void al_common_remove(struct al_codec_dev *codec)
> +{
> +	/* Cleanup allocated internal buffers used by the mcu*/
> +	al_common_dma_buf_cleanup(codec);
> +
> +	/* reset codecice */
> +	al_common_reset(codec);
> +	clk_disable_unprepare(codec->clk);
> +	/* Free the allocated memory for the firmware */
> +	dma_free_coherent(&codec->pdev->dev, codec->firmware.size,
> +			  codec->firmware.virt, codec->firmware.phys);
> +
> +	if (codec->firmware.firmware)
> +		release_firmware(codec->firmware.firmware);
> +}
> +
> +static void handle_alloc_memory_req(struct al_codec_dev *codec,
> +				    struct msg_itf_header *hdr)
> +{
> +	struct device *dev = &codec->pdev->dev;
> +	struct msg_itf_alloc_mem_req req;
> +	struct msg_itf_alloc_mem_reply_full reply = {
> +		.reply.phyAddr = 0,
> +		.hdr.type = MSG_ITF_TYPE_ALLOC_MEM_REPLY,
> +		.hdr.drv_ctx_hdl = hdr->drv_ctx_hdl,
> +		.hdr.drv_cmd_hdl = hdr->drv_cmd_hdl,
> +		.hdr.payload_len = sizeof(struct msg_itf_alloc_mem_reply),
> +	};
> +	struct codec_dma_buf *buf;
> +	int ret;
> +
> +	ret = al_common_get_data(codec, (char *)&req, hdr->payload_len);
> +	if (ret) {
> +		al_codec_err(codec, "Unable to get cma req %d", ret);
> +		return;
> +	}
> +
> +	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
> +	if (!buf)
> +		goto send_reply;
> +
> +	buf->size = req.uSize;
> +	buf->vaddr =
> +		dma_alloc_coherent(dev, buf->size, &buf->paddr, GFP_KERNEL);
> +	if (!buf->vaddr) {
> +		dev_warn(dev, "Failed to allocate DMA buffer\n");
> +		goto send_reply;
> +	}
> +
> +	reply.reply.phyAddr = (u64)buf->paddr;
> +	al_common_dma_buf_insert(codec, buf);
> +
> +send_reply:
> +	ret = al_common_send(codec, &reply.hdr);
> +	if (ret) {
> +		al_codec_err(codec, "Unable to reply to cma alloc");
> +		al_common_dma_buf_remove(codec, buf);
> +	}
> +}
> +
> +static void handle_free_memory_req(struct al_codec_dev *codec,
> +				   struct msg_itf_header *hdr)
> +{
> +	struct msg_itf_free_mem_req req;
> +	struct msg_itf_free_mem_reply_full reply = {
> +		.hdr.type = MSG_ITF_TYPE_FREE_MEM_REPLY,
> +		.hdr.drv_ctx_hdl = hdr->drv_ctx_hdl,
> +		.hdr.drv_cmd_hdl = hdr->drv_cmd_hdl,
> +		.hdr.payload_len = sizeof(struct msg_itf_free_mem_reply),
> +		.reply.ret = -1,
> +	};
> +	struct codec_dma_buf *buf;
> +	int ret;
> +
> +	ret = al_common_get_data(codec, (char *)&req, hdr->payload_len);
> +	if (ret) {
> +		al_codec_err(codec, "Unable to put cma req");
> +		return;
> +	}
> +
> +	buf = al_common_dma_buf_lookup(codec, req.phyAddr);
> +	if (!buf) {
> +		al_codec_err(codec, "Unable to get dma handle for %p",
> +			     (void *)(long)req.phyAddr);
> +		reply.reply.ret = -EINVAL;
> +		goto send_reply;
> +	}
> +
> +	al_codec_dbg(codec, "Free memory %p, size %d",
> +		     (void *)(long)req.phyAddr, buf->size);
> +
> +	al_common_dma_buf_remove(codec, buf);
> +	reply.reply.ret = 0;
> +
> +send_reply:
> +	ret = al_common_send(codec, &reply.hdr);
> +	if (ret)
> +		al_codec_err(codec, "Unable to reply to cma free");
> +}
> +
> +static void handle_mcu_console_print(struct al_codec_dev *codec,
> +				     struct msg_itf_header *hdr)
> +{
> +	struct msg_itf_write_req *req;
> +	int ret;
> +
> +	/* one more byte to be sure to have a zero terminated string */
> +	req = kzalloc(hdr->payload_len + 1, GFP_KERNEL);
> +	if (!req) {
> +		al_common_skip_data(codec, hdr->payload_len);
> +		al_codec_err(codec, "Unable to alloc memory");
> +		return;
> +	}
> +
> +	ret = al_codec_msg_get_data(&codec->mb_m2h, (char *)req,
> +				    hdr->payload_len);
> +	if (ret) {
> +		al_codec_err(codec, "Unable to get request");
> +		kfree(req);
> +		return;
> +	}
> +
> +	/* Print the mcu logs */
> +	al_mcu_dbg(codec, "%s", (char *)(req + 1));
> +	kfree(req);
> +}
> +
> +static void process_one_message(struct al_codec_dev *codec,
> +				struct msg_itf_header *hdr)
> +{
> +	switch (hdr->type) {
> +	case MSG_ITF_TYPE_ALLOC_MEM_REQ:
> +		handle_alloc_memory_req(codec, hdr);
> +		break;
> +	case MSG_ITF_TYPE_FREE_MEM_REQ:
> +		handle_free_memory_req(codec, hdr);
> +		break;
> +	case MSG_ITF_TYPE_WRITE_REQ:
> +		handle_mcu_console_print(codec, hdr);
> +		break;
> +	case MSG_ITF_TYPE_MCU_ALIVE:
> +		complete(&codec->completion);
> +		break;
> +	default:
> +		codec->process_msg_cb(codec->cb_arg, hdr);
> +		break;
> +	}
> +}
> +
> +static irqreturn_t al_common_irq_handler(int irq, void *data)
> +{
> +	struct al_codec_dev *codec = data;
> +	struct msg_itf_header hdr;
> +
> +	/* poll all messages */
> +	while (al_codec_msg_get_header(&codec->mb_m2h, &hdr) == 0)
> +		process_one_message(codec, &hdr);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t al_common_hardirq_handler(int irq, void *data)
> +{
> +	struct al_codec_dev *codec = data;
> +
> +	if (al_common_read(codec, AL_CODEC_IRQ_STATUS_CLEAR) == 0)
> +		return IRQ_NONE;
> +
> +	al_common_write(codec, AL_CODEC_IRQ_STATUS_CLEAR,
> +			AL_CODEC_IRQ_MCU_2_CPU);
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static int al_common_start_fw(struct al_codec_dev *codec)
> +{
> +	/* Enable the MCU clock */
> +	al_common_write(codec, AL_CODEC_MCU_CLK, AL_CODEC_REG_ENABLE);
> +
> +	return !wait_for_completion_timeout(&codec->completion, 2 * HZ);
> +}
> +
> +static void al_common_copy_firmware_image(struct al_codec_dev *codec)
> +{
> +	const struct firmware *firmware = codec->firmware.firmware;
> +	u32 *virt = codec->firmware.virt;
> +
> +	/* copy the whole thing taking into account endianness */
> +	for (size_t i = 0; i < firmware->size / sizeof(u32); i++)
> +		virt[i] = le32_to_cpu(((__le32 *)firmware->data)[i]);
> +}
> +
> +static int al_common_read_firmware(struct al_codec_dev *codec, const char *name)
> +{
> +	struct device *dev = &codec->pdev->dev;
> +	const struct boot_header *bh;
> +	int err;
> +
> +	/* request_firmware prints error if it fails */
> +	err = request_firmware(&codec->firmware.firmware, name, dev);
> +	if (err < 0)
> +		return err;
> +
> +	bh = (struct boot_header *)codec->firmware.firmware->data;
> +	codec->firmware.size = bh->vaddr_end - bh->vaddr_start;
> +
> +	return 0;
> +}
> +
> +static int al_common_parse_firmware_image(struct al_codec_dev *codec)
> +{
> +	struct boot_header *bh = (void *)codec->firmware.virt;
> +
> +	if (bh->bh_version < AL_BOOT_VERSION(2, 0, 0) ||
> +	    bh->bh_version >= AL_BOOT_VERSION(3, 0, 0)) {
> +		al_codec_err(codec, "Unsupported firmware version");
> +		return -EINVAL;
> +	}
> +
> +	codec->firmware.bin_data.offset = bh->vaddr_boot - bh->vaddr_start;
> +	codec->firmware.bin_data.size = bh->vaddr_end - bh->vaddr_start;
> +
> +	codec->firmware.mb_h2m.offset = bh->h2m.start - bh->vaddr_start;
> +	codec->firmware.mb_h2m.size = bh->h2m.end - bh->h2m.start;
> +	codec->firmware.mb_m2h.offset = bh->m2h.start - bh->vaddr_start;
> +	codec->firmware.mb_m2h.size = bh->m2h.end - bh->m2h.start;
> +
> +	/* Override some data */
> +	bh->ip_start = codec->apb + AL_CODEC_IP_OFFSET;
> +	bh->ip_end = bh->ip_start + resource_size(codec->regs_info);
> +	bh->mcu_clk_rate = clk_get_rate(codec->clk);
> +
> +	al_codec_dbg(codec, "bh version     = 0x%08x", bh->bh_version);
> +	al_codec_dbg(codec, "fw version     = 0x%08x", bh->fw_version);
> +	al_codec_dbg(codec, "fw model       = %s", bh->model);
> +	al_codec_dbg(codec, "vaddress start = 0x%016llx", bh->vaddr_start);
> +	al_codec_dbg(codec, "vaddress end   = 0x%016llx", bh->vaddr_end);
> +	al_codec_dbg(codec, "boot address   = 0x%016llx", bh->vaddr_boot);
> +	al_codec_dbg(codec, "machineid      = %lld", bh->machine_id);
> +	al_codec_dbg(codec, "periph address = 0x%016llx", codec->apb);
> +	al_codec_dbg(codec, "ip start       = 0x%016llx", bh->ip_start);
> +	al_codec_dbg(codec, "ip end         = 0x%016llx", bh->ip_end);
> +	al_codec_dbg(codec, "mcu clk	      = %llu", bh->mcu_clk_rate);
> +
> +	return 0;
> +}
> +
> +static int al_common_load_firmware_start(struct al_codec_dev *codec,
> +					 const char *name)
> +{
> +	struct device *dev = &codec->pdev->dev;
> +	dma_addr_t phys;
> +	size_t size;
> +	void *virt;
> +	int err;
> +
> +	if (codec->firmware.virt)
> +		return 0;
> +
> +	err = al_common_read_firmware(codec, name);
> +	if (err)
> +		return err;
> +
> +	size = codec->firmware.size;
> +
> +	virt = dma_alloc_coherent(dev, size, &phys, GFP_KERNEL);
> +	err = dma_mapping_error(dev, phys);
> +	if (err < 0)
> +		return err;
> +
> +	codec->firmware.virt = virt;
> +	codec->firmware.phys = phys;
> +
> +	al_common_copy_firmware_image(codec);
> +	err = al_common_parse_firmware_image(codec);
> +	if (err) {
> +		al_codec_err(codec, "failed to parse firmware image");
> +		goto cleanup;
> +	}
> +
> +	err = al_common_setup_hw_regs(codec);
> +	if (err) {
> +		al_codec_err(codec, "Unable to setup hw registers");
> +		goto cleanup;
> +	}
> +
> +	al_codec_mb_init(&codec->mb_h2m, virt + codec->firmware.mb_h2m.offset,
> +			 codec->firmware.mb_h2m.size, MB_IFT_MAGIC_H2M);
> +
> +	al_codec_mb_init(&codec->mb_m2h, virt + codec->firmware.mb_m2h.offset,
> +			 codec->firmware.mb_m2h.size, MB_IFT_MAGIC_M2H);
> +
> +	err = al_common_start_fw(codec);
> +	if (err) {
> +		al_codec_err(codec, "fw start has failed");
> +		goto cleanup;
> +	}
> +
> +	al_codec_dbg(codec, "mcu has boot successfully !");
> +	codec->fw_ready_cb(codec->cb_arg);
> +
> +	release_firmware(codec->firmware.firmware);
> +	codec->firmware.firmware = NULL;
> +
> +	return 0;
> +
> +cleanup:
> +	dma_free_coherent(dev, size, virt, phys);
> +
> +	return err;
> +}
> +
> +static u64 al_common_get_periph_addr(struct al_codec_dev *codec)
> +{
> +	struct resource *res;
> +
> +	res = platform_get_resource_byname(codec->pdev, IORESOURCE_MEM, "apb");
> +	if (!res) {
> +		al_codec_err(codec, "Unable to find APB start address");
> +		return 0;
> +	}
> +
> +	if (res->start & AL_CODEC_APB_MASK) {
> +		al_codec_err(codec, "APB start address is invalid");
> +		return 0;
> +	}
> +
> +	return res->start;
> +}
> +
> +int al_common_probe(struct al_codec_dev *codec, const char *name)
> +{
> +	struct platform_device *pdev = codec->pdev;
> +	int irq;
> +	int ret;
> +
> +	mutex_init(&codec->buf_lock);
> +	INIT_LIST_HEAD(&codec->alloc_buffers);
> +	init_completion(&codec->completion);
> +
> +	/* setup dma memory */
> +	ret = al_common_setup_dma(codec);
> +	if (ret)
> +		return ret;
> +
> +	/* Hw registers */
> +	codec->regs_info =
> +		platform_get_resource_byname(pdev, IORESOURCE_MEM, "top");
> +	if (!codec->regs_info) {
> +		al_codec_err(codec, "regs resource missing from device tree");
> +		return -EINVAL;
> +	}
> +
> +	codec->regs = devm_ioremap_resource(&pdev->dev, codec->regs_info);
> +	if (!codec->regs) {
> +		al_codec_err(codec, "failed to map registers");
> +		return -ENOMEM;
> +	}
> +
> +	codec->apb = al_common_get_periph_addr(codec);
> +	if (!codec->apb)
> +		return -EINVAL;
> +
> +	/* The MCU has already default clock value */
> +	codec->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(codec->clk)) {
> +		al_codec_err(codec, "failed to get MCU core clock");
> +		return PTR_ERR(codec->clk);
> +	}
> +
> +	ret = clk_prepare_enable(codec->clk);
> +	if (ret) {
> +		al_codec_err(codec, "Cannot enable MCU clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		al_codec_err(codec, "Failed to get IRQ");
> +		ret = -EINVAL;
> +		goto disable_clk;
> +	}
> +
> +	ret = devm_request_threaded_irq(&pdev->dev, irq,
> +					al_common_hardirq_handler,
> +					al_common_irq_handler, IRQF_SHARED,
> +					dev_name(&pdev->dev), codec);
> +	if (ret) {
> +		al_codec_err(codec, "Unable to register irq handler");
> +		goto disable_clk;
> +	}
> +
> +	/* ok so request the fw */
> +	ret = al_common_load_firmware_start(codec, name);
> +	if (ret) {
> +		al_codec_err(codec, "failed to load firmware : %s", name);
> +		goto disable_clk;
> +	}
> +
> +	return 0;
> +
> +disable_clk:
> +	clk_disable_unprepare(codec->clk);
> +
> +	return ret;
> +}
> +
> +int al_common_send(struct al_codec_dev *codec, struct msg_itf_header *hdr)
> +{
> +	return al_codec_msg_send(&codec->mb_h2m, hdr, al_common_trigger_mcu_irq,
> +				 codec);
> +}
> +
> +int al_common_send_req_reply(struct al_codec_dev *codec,
> +			     struct list_head *cmd_list,
> +			     struct msg_itf_header *hdr,
> +			     struct al_common_mcu_req *req)
> +{
> +	struct al_codec_cmd *cmd = NULL;
> +	int ret;
> +
> +	hdr->drv_cmd_hdl = 0;
> +
> +	if (req->reply_size && req->reply) {
> +		cmd = al_codec_cmd_create(req->reply_size);
> +		if (!cmd)
> +			return -ENOMEM;
> +
> +		hdr->drv_cmd_hdl = al_virt_to_phys(cmd);
> +	}
> +
> +	hdr->drv_ctx_hdl = req->pCtx;
> +	hdr->type = req->req_type;
> +	hdr->payload_len = req->req_size;
> +
> +	/* Add the list to the cmd list */
> +	if (cmd)
> +		list_add(&cmd->list, cmd_list);
> +
> +	ret = al_common_send(codec, hdr);
> +	if (ret)
> +		goto remove_cmd;
> +
> +	al_codec_dbg(codec, "Send req to mcu %d : %ld ", req->req_type,
> +		     req->req_size);
> +
> +	if (!cmd)
> +		return 0;
> +
> +	ret = wait_for_completion_timeout(&cmd->done, 5 * HZ);
> +	if (ret <= 0) {
> +		al_codec_err(codec, "cmd %p has %d (%s)", cmd, ret,
> +			     (ret == 0) ? "failed" : "timedout");
> +		ret = -ETIMEDOUT;
> +		goto remove_cmd;
> +	}
> +
> +	ret = 0;
> +	memcpy(req->reply, cmd->reply, req->reply_size);
> +
> +remove_cmd:
> +
> +	if (cmd) {
> +		list_del(&cmd->list);
> +		al_codec_cmd_put(cmd);
> +	}
> +	return ret;
> +}
> +
> +bool al_common_mcu_is_alive(struct al_codec_dev *codec)
> +{
> +	static const struct msg_itf_header hdr = {
> +		.type = MSG_ITF_TYPE_MCU_ALIVE,
> +		.payload_len = 0,
> +	};
> +	int ret;
> +
> +	ret = al_common_send(codec, (struct msg_itf_header *)&hdr);
> +	if (ret)
> +		return false;
> +
> +	ret = wait_for_completion_timeout(&codec->completion, 5 * HZ);
> +	if (ret <= 0)
> +		return false;
> +
> +	return true;
> +}
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_codec_common.h b/drivers/media/platform/allegro-dvt/al300/al_codec_common.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..2664ffb659a2e8a6a4cf86b51811119639f2bbef
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_codec_common.h
> @@ -0,0 +1,251 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + */
> +
> +#ifndef __AL_CODEC_COMMON__
> +#define __AL_CODEC_COMMON__
> +
> +#include <linux/cleanup.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <media/v4l2-device.h>
> +
> +#include "al_codec_dbgfs.h"
> +#include "al_codec_util.h"
> +
> +#define fh_to_ctx(ptr, type) container_of(ptr, type, fh)
> +
> +enum {
> +	MSG_ITF_TYPE_CREATE_INST_REQ = MSG_ITF_TYPE_NEXT_REQ,
> +	MSG_ITF_TYPE_DESTROY_INST_REQ,
> +	MSG_ITF_TYPE_PUSH_BITSTREAM_BUFFER_REQ,
> +	MSG_ITF_TYPE_PUT_DISPLAY_PICTURE_REQ,
> +	MSG_ITF_TYPE_FLUSH_REQ,
> +	MSG_ITF_TYPE_INFO_REQ,
> +	MSG_ITF_TYPE_CREATE_INST_REPLY = MSG_ITF_TYPE_NEXT_REPLY,
> +	MSG_ITF_TYPE_DESTROY_INST_REPLY,
> +	MSG_ITF_TYPE_PUSH_BITSTREAM_BUFFER_REPLY,
> +	MSG_ITF_TYPE_PUT_DISPLAY_PICTURE_REPLY,
> +	MSG_ITF_TYPE_FLUSH_REPLY,
> +	MSG_ITF_TYPE_INFO_REPLY,
> +	MSG_ITF_TYPE_EVT_ERROR = MSG_ITF_TYPE_NEXT_EVT,
> +};
> +
> +struct msg_itf_write_req {
> +	u32 fd;
> +	u32 len;
> +	/* payload follow */
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_write_req);
> +
> +struct msg_itf_free_mem_req {
> +	phys_addr_t phyAddr;

No camel please.

> +} __packed;

A bit off to have struct with a single fixes size member, was this wrapper
needed ?

> +DECLARE_FULL_REQ(msg_itf_free_mem_req);
> +
> +struct msg_itf_alloc_mem_req {
> +	u64 uSize;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_alloc_mem_req);
> +
> +struct msg_itf_alloc_mem_reply {
> +	phys_addr_t phyAddr;
> +} __packed;
> +DECLARE_FULL_REPLY(msg_itf_alloc_mem_reply);
> +
> +struct msg_itf_free_mem_reply {
> +	s64 ret;
> +};
> +DECLARE_FULL_REPLY(msg_itf_free_mem_reply);
> +
> +struct msg_itf_create_codec_reply {
> +	phys_addr_t hCodec;

Same, no came. Usually we allow this only if it match a publicly available spec
(such as USB).

> +	s32 ret;
> +} __packed;
> +DECLARE_FULL_REPLY(msg_itf_create_codec_reply);
> +
> +struct msg_itf_destroy_codec_req {
> +	phys_addr_t hCodec;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_destroy_codec_req);
> +
> +/*
> + * Note : no need to know the status of this request
> + * The codec should be destroyed, in case of the mcu
> + * hasn't received any request with the codec handler
> + */
> +struct msg_itf_destroy_codec_reply {
> +	u32 unused;
> +} __packed;
> +DECLARE_FULL_REPLY(msg_itf_destroy_codec_reply);
> +
> +struct al_buffer_meta {
> +	u64 timestamp;
> +	struct v4l2_timecode timecode;
> +	bool last;
> +};
> +
> +struct msg_itf_push_src_buf_req {
> +	phys_addr_t hCodec;
> +	phys_addr_t bufferHandle;
> +	phys_addr_t phyAddr;
> +	u64 size;
> +	struct al_buffer_meta meta;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_push_src_buf_req);
> +
> +struct msg_itf_push_dst_buf_req {
> +	phys_addr_t hCodec;
> +	phys_addr_t bufferHandle;
> +	phys_addr_t phyAddr;
> +	u64 size;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_push_dst_buf_req);
> +
> +struct msg_itf_push_buffer_req {
> +	phys_addr_t hCodec;
> +	phys_addr_t bufferHandle;
> +	phys_addr_t phyAddr;
> +	u64 size;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_push_buffer_req);
> +
> +struct msg_itf_push_buffer_reply {
> +	s32 res;
> +} __packed;
> +DECLARE_FULL_REPLY(msg_itf_push_buffer_reply);
> +
> +struct msg_itf_info_req {
> +	u64 unused;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_info_req);
> +
> +struct msg_itf_flush_req {
> +	phys_addr_t hCodec;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_flush_req);
> +
> +struct msg_itf_flush_reply {
> +	int32_t unused;
> +} __packed;
> +DECLARE_FULL_REPLY(msg_itf_flush_reply);
> +
> +struct msg_itf_evt_error {
> +	uint32_t errno;
> +} __packed;
> +DECLARE_FULL_EVENT(msg_itf_evt_error);
> +
> +struct al_match_data {
> +	const char *fw_name;
> +};
> +
> +struct al_common_mcu_req {
> +	phys_addr_t pCtx;
> +	int req_type;
> +	size_t req_size;
> +	size_t reply_size;
> +	void *reply;
> +} __packed;
> +
> +struct al_firmware_section {
> +	u64 offset;
> +	size_t size;
> +};
> +
> +struct al_firmware {
> +	/* Firmware after it is read but not loaded */
> +	const struct firmware *firmware;
> +
> +	/* Raw firmware data */
> +	dma_addr_t phys;
> +	void *virt;
> +	size_t size;
> +
> +	/* Parsed firmware information */
> +	struct al_firmware_section bin_data;
> +	struct al_firmware_section mb_m2h;
> +	struct al_firmware_section mb_h2m;
> +};
> +
> +struct al_codec_dev {
> +	struct platform_device *pdev;
> +	struct v4l2_device v4l2_dev;
> +	struct v4l2_m2m_dev *m2m_dev;
> +	struct video_device video_dev;
> +
> +	/* Firmware */
> +	struct al_firmware firmware;
> +	dma_addr_t apb;
> +
> +	struct clk *clk;
> +	void __iomem *regs;
> +	struct resource *regs_info;
> +
> +	/* Mailbox structs */
> +	struct al_codec_mb mb_h2m;
> +	struct al_codec_mb mb_m2h;
> +
> +	/* list of buffers used by the MCU */
> +	struct list_head alloc_buffers;
> +	struct mutex buf_lock;
> +
> +	/* mutex protecting vb2_queue structure */
> +	struct mutex lock;
> +
> +	/* list of ctx (aka decoder) */
> +	struct mutex ctx_mlock;
> +	struct list_head ctx_q_list;
> +	struct al_codec_dbgfs dbgfs;
> +	u64 ctx_counter;
> +	bool init_done;
> +
> +	/* list of cap/out supported formats */
> +	struct list_head codec_q_list;
> +	struct al_codec_cmd *codec_info_cmd;
> +
> +	/* Command completion */
> +	struct completion completion;
> +	/* Resolution found completion */
> +	struct completion res_done;
> +
> +	/* callbacks set by client before common_probe */
> +	void *cb_arg;
> +	void (*process_msg_cb)(void *cb_arg, struct msg_itf_header *hdr);
> +	void (*fw_ready_cb)(void *cb_arg);
> +};
> +
> +static inline int al_common_get_header(struct al_codec_dev *codec,
> +				       struct msg_itf_header *hdr)
> +{
> +	return al_codec_msg_get_header(&codec->mb_m2h, hdr);
> +}
> +
> +static inline int al_common_get_data(struct al_codec_dev *codec, char *data,
> +				     int len)
> +{
> +	return al_codec_msg_get_data(&codec->mb_m2h, data, len);
> +}
> +
> +static inline int al_common_skip_data(struct al_codec_dev *codec, int len)
> +{
> +	return al_common_get_data(codec, NULL, len);
> +}
> +
> +int al_common_send(struct al_codec_dev *codec, struct msg_itf_header *hdr);
> +int al_common_send_req_reply(struct al_codec_dev *codec,
> +			     struct list_head *cmd_list,
> +			     struct msg_itf_header *hdr,
> +			     struct al_common_mcu_req *req);
> +bool al_common_mcu_is_alive(struct al_codec_dev *codec);
> +
> +int al_common_probe(struct al_codec_dev *codec, const char *name);
> +void al_common_remove(struct al_codec_dev *codec);
> +
> +#endif /*__AL_CODEC_COMMON__*/
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_codec_dbgfs.c b/drivers/media/platform/allegro-dvt/al300/al_codec_dbgfs.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..9a9264d2aa22ebdc21945e4290c744491974b692
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_codec_dbgfs.c
> @@ -0,0 +1,229 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + */
> +
> +#include <linux/debugfs.h>
> +
> +#include "al_codec_dbgfs.h"
> +#include "al_vdec_drv.h"
> +#include "al_codec_util.h"
> +
> +static void al_vdec_dbgfs_get_format_type(struct al_dec_ctx *ctx, char *buf,
> +					  int *used, int total)
> +{
> +	struct al_frame *frame = &ctx->src;
> +	int curr_len;
> +
> +	switch (frame->fmt->pixelformat) {
> +	case V4L2_PIX_FMT_H264:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\toutput format: h264\n");
> +		break;
> +	case V4L2_PIX_FMT_HEVC:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\toutput format: hevc\n");
> +		break;
> +	case V4L2_PIX_FMT_JPEG:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\toutput format: jpeg\n");
> +		break;
> +	default:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\tunsupported output format: 0x%x\n",
> +				    frame->fmt->pixelformat);
> +	}
> +	*used += curr_len;
> +	frame = &ctx->dst;
> +
> +	switch (frame->fmt->pixelformat) {
> +	case V4L2_PIX_FMT_NV12:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\tcapture format: NV12\n");
> +		break;
> +	case V4L2_PIX_FMT_NV16:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\tcapture format: NV16\n");
> +		break;
> +	case V4L2_PIX_FMT_P010:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\tcapture format: P010\n");
> +		break;
> +	case V4L2_PIX_FMT_YUV420:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\tcapture format: YUV420\n");
> +		break;
> +	case V4L2_PIX_FMT_YVU420:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\tcapture format: YVU420\n");
> +		break;
> +	default:
> +		curr_len = snprintf(buf + *used, total - *used,
> +				    "\tunsupported capture format: 0x%x\n",
> +				    frame->fmt->pixelformat);
> +	}
> +	*used += curr_len;
> +}
> +
> +static void al_vdec_dbgfs_get_help(char *buf, int *used, int total)
> +{
> +	int curr_len;
> +
> +	curr_len = snprintf(buf + *used, total - *used,
> +			    "help: (1: echo -'info' > dec 2: cat dec)\n");
> +	*used += curr_len;
> +
> +	curr_len = snprintf(buf + *used, total - *used,
> +			    "\t-picinfo: get resolution\n");
> +	*used += curr_len;
> +
> +	curr_len = snprintf(buf + *used, total - *used,
> +			    "\t-format: get output & capture queue format\n");
> +	*used += curr_len;
> +}
> +
> +static ssize_t al_vdec_dbgfs_write(struct file *filp, const char __user *ubuf,
> +				   size_t count, loff_t *ppos)
> +{
> +	struct al_codec_dev *codec = filp->private_data;
> +	struct al_codec_dbgfs *dbgfs = &codec->dbgfs;
> +
> +	mutex_lock(&dbgfs->lock);
> +	dbgfs->size = simple_write_to_buffer(dbgfs->buf, sizeof(dbgfs->buf),
> +					     ppos, ubuf, count);
> +	mutex_unlock(&dbgfs->lock);
> +	if (dbgfs->size > 0)
> +		return count;
> +
> +	return dbgfs->size;
> +}
> +
> +static ssize_t al_vdec_dbgfs_read(struct file *filp, char __user *ubuf,
> +				  size_t count, loff_t *ppos)
> +{
> +	struct al_codec_dev *codec = filp->private_data;
> +	struct al_codec_dbgfs *dbgfs = &codec->dbgfs;
> +	struct al_codec_dbgfs_ctx *dbgfs_ctx;
> +	struct al_dec_ctx *ctx;
> +	struct al_frame *frame;
> +	int total_len = 200 * (dbgfs->ctx_count == 0 ? 1 : dbgfs->ctx_count);
> +	int used_len = 0, curr_len, ret;
> +	bool dbgfs_index[AL_VDEC_DBGFS_MAX] = { 0 };
> +	char *buf = kmalloc(total_len, GFP_KERNEL);
> +
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	if (strstr(dbgfs->buf, "-help") || dbgfs->size == 1) {

This parser is a bit weak at first sight. I didn't get introduce to this debug
fs interface, is there some doc ?

> +		al_vdec_dbgfs_get_help(buf, &used_len, total_len);
> +		goto read_buffer;
> +	}
> +
> +	if (strstr(dbgfs->buf, "-picinfo"))
> +		dbgfs_index[AL_VDEC_DBGFS_PICINFO] = true;
> +
> +	if (strstr(dbgfs->buf, "-format"))
> +		dbgfs_index[AL_VDEC_DBGFS_FORMAT] = true;
> +
> +	mutex_lock(&dbgfs->lock);
> +	list_for_each_entry(dbgfs_ctx, &dbgfs->dbgfs_head, node) {
> +		ctx = dbgfs_ctx->dec_ctx;
> +		frame = &ctx->src;
> +
> +		curr_len = snprintf(buf + used_len, total_len - used_len,
> +				    "inst[%lld]:\n ", ctx->id);
> +		used_len += curr_len;
> +
> +		if (dbgfs_index[AL_VDEC_DBGFS_PICINFO]) {
> +			curr_len = snprintf(buf + used_len,
> +					    total_len - used_len,
> +					    "\tOUTPUT: %dx%d\n", frame->width,
> +					    frame->height);
> +			used_len += curr_len;
> +			frame = &ctx->dst;
> +
> +			curr_len = snprintf(buf + used_len,
> +					    total_len - used_len,
> +					    "\tCAPTURE: %dx%d\n", frame->width,
> +					    frame->height);
> +			used_len += curr_len;
> +		}
> +
> +		if (dbgfs_index[AL_VDEC_DBGFS_FORMAT])
> +			al_vdec_dbgfs_get_format_type(ctx, buf, &used_len,
> +						      total_len);
> +	}
> +	mutex_unlock(&dbgfs->lock);
> +read_buffer:
> +	ret = simple_read_from_buffer(ubuf, count, ppos, buf, used_len);
> +	kfree(buf);
> +	return ret;
> +}
> +
> +static const struct file_operations vdec_fops = {
> +	.open = simple_open,
> +	.write = al_vdec_dbgfs_write,
> +	.read = al_vdec_dbgfs_read,
> +};
> +
> +void al_codec_dbgfs_create(struct al_dec_ctx *ctx)
> +{
> +	struct al_codec_dbgfs_ctx *dbgfs_ctx;
> +	struct al_codec_dev *codec = ctx->codec;
> +
> +	dbgfs_ctx = kzalloc(sizeof(*dbgfs_ctx), GFP_KERNEL);
> +	if (!dbgfs_ctx)
> +		return;
> +
> +	list_add_tail(&dbgfs_ctx->node, &codec->dbgfs.dbgfs_head);
> +
> +	codec->dbgfs.ctx_count++;
> +
> +	dbgfs_ctx->ctx_id = ctx->id;
> +	dbgfs_ctx->dec_ctx = ctx;
> +}
> +
> +void al_codec_dbgfs_remove(struct al_codec_dev *codec, int ctx_id)
> +{
> +	struct al_codec_dbgfs_ctx *dbgfs_ctx;
> +
> +	list_for_each_entry(dbgfs_ctx, &codec->dbgfs.dbgfs_head, node) {
> +		if (dbgfs_ctx->ctx_id == ctx_id) {
> +			codec->dbgfs.ctx_count--;
> +			list_del(&dbgfs_ctx->node);
> +			kfree(dbgfs_ctx);
> +			return;
> +		}
> +	}
> +}
> +
> +static void al_codec_dbgfs_vdec_init(struct al_codec_dev *codec)
> +{
> +	struct dentry *vcodec_root;
> +
> +	codec->dbgfs.vcodec_root = debugfs_create_dir("al-vdec", NULL);
> +	if (IS_ERR(codec->dbgfs.vcodec_root))
> +		dev_err(&codec->pdev->dev, "create al-vdec dir err:%ld\n",
> +			PTR_ERR(codec->dbgfs.vcodec_root));
> +
> +	vcodec_root = codec->dbgfs.vcodec_root;
> +	debugfs_create_x32("al_v4l2_dbg_level", 0644, vcodec_root,
> +			   &al_v4l2_dbg_level);
> +	debugfs_create_x32("al_codec_dbg", 0644, vcodec_root, &al_codec_dbg);
> +
> +	codec->dbgfs.ctx_count = 0;
> +	INIT_LIST_HEAD(&codec->dbgfs.dbgfs_head);
> +	debugfs_create_file("vdec", 0200, vcodec_root, codec, &vdec_fops);
> +	mutex_init(&codec->dbgfs.lock);
> +}
> +
> +void al_codec_dbgfs_init(void *codec)
> +{
> +	al_codec_dbgfs_vdec_init(codec);
> +}
> +
> +void al_codec_dbgfs_deinit(struct al_codec_dbgfs *dbgfs)
> +{
> +	debugfs_remove_recursive(dbgfs->vcodec_root);
> +}
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_codec_dbgfs.h b/drivers/media/platform/allegro-dvt/al300/al_codec_dbgfs.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..0f205e50530822371203126d1d3632f8429a6de9
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_codec_dbgfs.h
> @@ -0,0 +1,71 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + */
> +
> +#ifndef __AL_CODEC_DBGFS_H__
> +#define __AL_CODEC_DBGFS_H__
> +
> +struct al_codec_dev;
> +struct al_dec_ctx;
> +
> +/*
> + * enum al_vdec_dbgfs_log_index  - used to get different debug information
> + */
> +enum al_vdec_dbgfs_log_index {
> +	AL_VDEC_DBGFS_PICINFO,
> +	AL_VDEC_DBGFS_FORMAT,
> +	AL_VDEC_DBGFS_MAX,
> +};
> +
> +/**
> + * struct al_codec_dbgfs_ctx  - debugfs information for each context
> + * @node:       list node for each inst
> + * @dec_ctx:	struct al_dec_ctx
> + * @ctx_id:     index of the context that the same with ctx->id
> + */
> +struct al_codec_dbgfs_ctx {
> +	struct list_head node;
> +	struct al_dec_ctx *dec_ctx;
> +	int ctx_id;
> +};
> +
> +/**
> + * struct al_codec_dbgfs  - dbgfs information
> + * @dbgfs_head:  list head used to link each context
> + * @vcodec_root: codec dbgfs entry
> + * @lock:	 lock used to protect dbgfs_buf
> + * @buf:	 dbgfs buf used to store dbgfs cmd
> + * @size:	 dbgfs buffer size
> + * @ctx_count:   the count of total context
> + */
> +struct al_codec_dbgfs {
> +	struct list_head dbgfs_head;
> +	struct dentry *vcodec_root;
> +	struct mutex lock;
> +	char buf[1024];
> +	int size;
> +	int ctx_count;
> +};
> +
> +#if defined(CONFIG_DEBUG_FS)
> +void al_codec_dbgfs_create(struct al_dec_ctx *ctx);
> +void al_codec_dbgfs_remove(struct al_codec_dev *codec, int ctx_id);
> +void al_codec_dbgfs_init(void *codec);
> +void al_codec_dbgfs_deinit(struct al_codec_dbgfs *dbgfs);
> +#else
> +static inline void al_codec_dbgfs_create(struct al_dec_ctx *ctx)
> +{
> +}
> +static inline void al_codec_dbgfs_remove(struct al_codec_dev *codec, int ctx_id)
> +{
> +}
> +static inline void al_codec_dbgfs_init(void *codec)
> +{
> +}
> +static inline void al_codec_dbgfs_deinit(struct al_codec_dbgfs *dbgfs)
> +{
> +}
> +#endif /* CONFIG_DEBUG_FS */
> +#endif /* __AL_CODEC_DBGFS_H__ */
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_codec_util.c b/drivers/media/platform/allegro-dvt/al300/al_codec_util.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..f59fedae0491f9384052b80f9bc19a22a3efe2a3
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_codec_util.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Mailbox communication utilities for command creation
> + * and message exchange with the MCU
> + *
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + */
> +
> +#include <asm-generic/errno.h>
> +#include <linux/errno.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +
> +#include "al_codec_util.h"
> +
> +#if defined(CONFIG_DEBUG_FS)
> +/* Log level */
> +int al_v4l2_dbg_level;
> +int al_codec_dbg;
> +#endif
> +
> +static int al_get_used_space(struct al_codec_mb *mb)
> +{
> +	u32 head = mb->hdr->head;
> +	u32 tail = mb->hdr->tail;
> +
> +	return head >= tail ? head - tail : mb->size - (tail - head);
> +}
> +
> +static int al_get_free_space(struct al_codec_mb *mb)
> +{
> +	return mb->size - al_get_used_space(mb) - 1;
> +}
> +
> +static int al_has_enough_space(struct al_codec_mb *mb, int len)
> +{
> +	return al_get_free_space(mb) >= len;
> +}
> +
> +static inline void al_copy_to_mb(struct al_codec_mb *mb, char *data, int len)
> +{
> +	u32 head = mb->hdr->head;
> +	int copy_len = min(mb->size - head, (unsigned int)len);
> +	int copied_len = len;
> +
> +	memcpy(&mb->data[head], data, copy_len);
> +	len -= copy_len;
> +	if (len)
> +		memcpy(&mb->data[0], &data[copy_len], len);
> +
> +	/* Make sure that all messages are written before updating the head */
> +	dma_wmb();
> +	mb->hdr->head = (head + copied_len) % mb->size;
> +	/* Make sure that the head is updated in DDR instead of cache */
> +	dma_wmb();
> +}
> +
> +static inline void al_copy_from_mb(struct al_codec_mb *mb, char *data, int len)
> +{
> +	u32 tail = mb->hdr->tail;
> +	int copy_len = min(mb->size - tail, (unsigned int)len);
> +	int copied_len = len;
> +
> +	if (!data)
> +		goto update_tail;
> +
> +	memcpy(data, &mb->data[tail], copy_len);
> +	len -= copy_len;
> +	if (len)
> +		memcpy(&data[copy_len], &mb->data[0], len);
> +
> +update_tail:
> +	mb->hdr->tail = (tail + copied_len) % mb->size;
> +	/* Make sure that the head is updated in DDR instead of cache */
> +	dma_wmb();
> +}
> +
> +static int al_codec_mb_send(struct al_codec_mb *mb, char *data, int len)
> +{
> +	if (!al_has_enough_space(mb, len))
> +		return -ENOMEM;
> +
> +	al_copy_to_mb(mb, data, len);
> +
> +	return 0;
> +}
> +
> +static int al_codec_mb_receive(struct al_codec_mb *mb, char *data, int len)
> +{
> +	if (al_get_used_space(mb) < len)
> +		return -ENOMEM;
> +
> +	al_copy_from_mb(mb, data, len);
> +
> +	return 0;
> +}
> +
> +void al_codec_mb_init(struct al_codec_mb *mb, char *addr, int size, u32 magic)
> +{
> +	mb->hdr = (struct al_mb_itf *)addr;
> +	mb->hdr->magic = magic;
> +	mb->hdr->version = MB_IFT_VERSION;
> +	mb->hdr->head = 0;
> +	mb->hdr->tail = 0;
> +	mb->data = addr + sizeof(struct al_mb_itf);
> +	mb->size = size - sizeof(struct al_mb_itf);
> +	mutex_init(&mb->lock);
> +}
> +
> +int al_codec_msg_get_header(struct al_codec_mb *mb, struct msg_itf_header *hdr)
> +{
> +	return al_codec_mb_receive(mb, (char *)hdr, sizeof(*hdr));
> +}
> +
> +int al_codec_msg_get_data(struct al_codec_mb *mb, char *data, int len)
> +{
> +	return al_codec_mb_receive(mb, data, len);
> +}
> +
> +int al_codec_msg_send(struct al_codec_mb *mb, struct msg_itf_header *hdr,
> +		      void (*trigger)(void *), void *trigger_arg)
> +{
> +	const unsigned long timeout = jiffies + HZ;
> +	int ret;
> +
> +	guard(mutex)(&mb->lock);
> +	do {
> +		if (time_after(jiffies, timeout))
> +			return -ETIMEDOUT;
> +
> +		ret = al_codec_mb_send(mb, (char *)hdr,
> +				       hdr->payload_len +
> +					       sizeof(struct msg_itf_header));
> +
> +	} while (ret);
> +
> +	trigger(trigger_arg);
> +
> +	return 0;
> +}
> +
> +static void al_codec_cmd_cleanup(struct kref *ref)
> +{
> +	struct al_codec_cmd *cmd = container_of(ref, typeof(*cmd), refcount);
> +
> +	kfree(cmd->reply);
> +	kfree(cmd);
> +}
> +
> +void al_codec_cmd_put(struct al_codec_cmd *cmd)
> +{
> +	if (WARN_ON(!cmd))
> +		return;
> +
> +	kref_put(&cmd->refcount, al_codec_cmd_cleanup);
> +}
> +
> +struct al_codec_cmd *al_codec_cmd_create(int reply_size)
> +{
> +	struct al_codec_cmd *cmd;
> +
> +	cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
> +	if (!cmd)
> +		return NULL;
> +
> +	cmd->reply = kmalloc(reply_size, GFP_KERNEL);
> +	if (!cmd->reply) {
> +		kfree(cmd);
> +		return NULL;
> +	}
> +
> +	kref_init(&cmd->refcount);
> +	cmd->reply_size = reply_size;
> +	init_completion(&cmd->done);
> +
> +	return cmd;
> +}
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_codec_util.h b/drivers/media/platform/allegro-dvt/al300/al_codec_util.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..e5541dd11c4cc46b6ef0ea22d10d08c85fa6c75a
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_codec_util.h
> @@ -0,0 +1,194 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + */
> +
> +#ifndef __AL_CODEC_UTIL__
> +#define __AL_CODEC_UTIL__
> +
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +#include <linux/v4l2-common.h>
> +
> +#define MB_IFT_MAGIC_H2M 0xabcd1230
> +#define MB_IFT_MAGIC_M2H 0xabcd1231
> +#define MB_IFT_VERSION 0x00010000
> +
> +#define MAJOR_SHIFT 20
> +#define MAJOR_MASK 0xfff
> +#define MINOR_SHIFT 8
> +#define MINOR_MASK 0xfff
> +#define PATCH_SHIFT 0
> +#define PATCH_MASK 0xff
> +
> +/*
> + * AL_BOOT_VERSION() - Version format 32-bit, 12 bits for the major,
> + * the same for minor, 8bits for the patch
> + */
> +#define AL_BOOT_VERSION(major, minor, patch)       \
> +	((((major) & MAJOR_MASK) << MAJOR_SHIFT) | \
> +	 (((minor) & MINOR_MASK) << MINOR_SHIFT) | \
> +	 (((patch) & PATCH_MASK) << PATCH_SHIFT))
> +
> +#define al_phys_to_virt(x) ((void *)(uintptr_t)x)
> +#define al_virt_to_phys(x) ((phys_addr_t)(uintptr_t)x)
> +
> +#define DECLARE_FULL_REQ(s)                \
> +	struct s##_full {                  \
> +		struct msg_itf_header hdr; \
> +		struct s req;              \
> +	} __packed
> +
> +#define DECLARE_FULL_REPLY(s)              \
> +	struct s##_full {                  \
> +		struct msg_itf_header hdr; \
> +		struct s reply;            \
> +	} __packed
> +
> +#define DECLARE_FULL_EVENT(s)              \
> +	struct s##_full {                  \
> +		struct msg_itf_header hdr; \
> +		struct s event;            \
> +	} __packed
> +
> +struct al_mb_itf {
> +	u32 magic;
> +	u32 version;
> +	u32 head;
> +	u32 tail;
> +} __packed;
> +
> +struct al_codec_mb {
> +	struct al_mb_itf *hdr;
> +	struct mutex lock;
> +	char *data;
> +	int size;
> +};
> +
> +struct al_codec_cmd {
> +	struct kref refcount;
> +	struct list_head list;
> +	struct completion done;
> +	int reply_size;
> +	void *reply;
> +};
> +
> +#define al_codec_err(codec, fmt, args...)                                     \
> +	dev_err(&(codec)->pdev->dev, "[ALG_CODEC][ERROR] %s():%d: " fmt "\n", \
> +		__func__, __LINE__, ##args)
> +
> +#define al_v4l2_err(codec, fmt, args...)                                     \
> +	dev_err(&(codec)->pdev->dev, "[ALG_V4L2][ERROR] %s():%d: " fmt "\n", \
> +		__func__, __LINE__, ##args)
> +
> +#if defined(CONFIG_DEBUG_FS)
> +/* Log level */
> +extern int al_v4l2_dbg_level;
> +extern int al_codec_dbg;
> +
> +/* V4L2 logs */
> +#define al_v4l2_dbg(codec, level, fmt, args...)                           \
> +	do {                                                              \
> +		if (al_v4l2_dbg_level >= level)                           \
> +			dev_dbg(&(codec)->pdev->dev,                      \
> +				"[ALG_V4L2] level=%d %s(),%d: " fmt "\n", \
> +				level, __func__, __LINE__, ##args);       \
> +	} while (0)
> +
> +/* Codec logs */
> +#define al_codec_dbg(codec, fmt, args...)                                   \
> +	do {                                                                \
> +		if (al_codec_dbg)                                           \
> +			dev_dbg(&(codec)->pdev->dev,                        \
> +				"[ALG_CODEC] %s(),%d: " fmt "\n", __func__, \
> +				__LINE__, ##args);                          \
> +	} while (0)
> +
> +#define al_mcu_dbg(codec, fmt, args...)                                   \
> +	do {                                                              \
> +		if (al_codec_dbg)                                         \
> +			dev_dbg(&(codec)->pdev->dev,                      \
> +				"[ALG_MCU] %s(),%d: " fmt "\n", __func__, \
> +				__LINE__, ##args);                        \
> +	} while (0)
> +
> +#else
> +
> +#define al_v4l2_dbg(codec, level, fmt, args...)                                \
> +	do {                                                                   \
> +		(void)level;                                                   \
> +		dev_dbg(&(codec)->pdev->dev, "[ALG_V4L2]: " fmt "\n", ##args); \
> +	} while (0)
> +
> +#define al_codec_dbg(codec, fmt, args...) \
> +	dev_dbg(&(codec)->pdev->dev, "[ALG_CODEC]: " fmt "\n", ##args)
> +
> +#define al_mcu_dbg(codec, fmt, args...) \
> +	dev_dbg(&(codec)->pdev->dev, "[ALG_MCU]: " fmt "\n", ##args)
> +
> +#endif
> +
> +#define MSG_ITF_TYPE_LIMIT BIT(10)
> +
> +/* Message types host <-> mcu */
> +enum {
> +	MSG_ITF_TYPE_MCU_ALIVE = 0,
> +	MSG_ITF_TYPE_WRITE_REQ = 2,
> +	MSG_ITF_TYPE_FIRST_REQ = 1024,
> +	MSG_ITF_TYPE_NEXT_REQ,
> +	MSG_ITF_TYPE_FIRST_REPLY = 2048,
> +	MSG_ITF_TYPE_NEXT_REPLY,
> +	MSG_ITF_TYPE_ALLOC_MEM_REQ = 3072,
> +	MSG_ITF_TYPE_FREE_MEM_REQ,
> +	MSG_ITF_TYPE_ALLOC_MEM_REPLY = 4096,
> +	MSG_ITF_TYPE_FREE_MEM_REPLY,
> +	MSG_ITF_TYPE_FIRST_EVT = 5120,
> +	MSG_ITF_TYPE_NEXT_EVT = MSG_ITF_TYPE_FIRST_EVT
> +};
> +
> +struct msg_itf_header {
> +	u64 drv_ctx_hdl;
> +	u64 drv_cmd_hdl;
> +	u16 type;
> +	u16 payload_len;
> +	u16 padding[2];
> +} __packed;
> +
> +void al_codec_mb_init(struct al_codec_mb *mb, char *addr, int size, u32 magic);
> +int al_codec_msg_get_header(struct al_codec_mb *mb, struct msg_itf_header *hdr);
> +int al_codec_msg_get_data(struct al_codec_mb *mb, char *data, int len);
> +int al_codec_msg_send(struct al_codec_mb *mb, struct msg_itf_header *hdr,
> +		      void (*trigger)(void *), void *trigger_arg);
> +
> +static inline bool is_type_reply(uint16_t type)
> +{
> +	return type >= MSG_ITF_TYPE_FIRST_REPLY &&
> +	       type < MSG_ITF_TYPE_FIRST_REPLY + MSG_ITF_TYPE_LIMIT;
> +}
> +
> +static inline bool is_type_event(uint16_t type)
> +{
> +	return type >= MSG_ITF_TYPE_FIRST_EVT &&
> +	       type < MSG_ITF_TYPE_FIRST_EVT + MSG_ITF_TYPE_LIMIT;
> +}
> +
> +void al_codec_cmd_put(struct al_codec_cmd *cmd);
> +
> +struct al_codec_cmd *al_codec_cmd_create(int reply_size);
> +
> +static inline struct al_codec_cmd *al_codec_cmd_get(struct list_head *cmd_list,
> +						    uint64_t hdl)
> +{
> +	struct al_codec_cmd *cmd = NULL;
> +
> +	list_for_each_entry(cmd, cmd_list, list) {
> +		if (likely(cmd == al_phys_to_virt(hdl))) {
> +			kref_get(&cmd->refcount);
> +			break;
> +		}
> +	}
> +	return list_entry_is_head(cmd, cmd_list, list) ? NULL : cmd;
> +}
> +
> +#endif /* __AL_CODEC_UTIL__ */
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_vdec_drv.c b/drivers/media/platform/allegro-dvt/al300/al_vdec_drv.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..eed037b7e8ca920c26928a6ca4cf012f0fed0563
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_vdec_drv.c
> @@ -0,0 +1,1515 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + *
> + * Allegro DVT stateful video decoder driver for the IP Gen 3
> + */
> +
> +#include <asm-generic/errno-base.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/string.h>
> +#include <linux/v4l2-controls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "al_codec_dbgfs.h"
> +#include "al_vdec_drv.h"
> +
> +/* default decoder params */
> +#define DECODER_WIDTH_DEFAULT 640
> +#define DECODER_HEIGHT_DEFAULT 480
> +#define DECODER_WIDTH_MAX 3840
> +#define DECODER_HEIGHT_MAX 2160
> +#define DECODER_WIDTH_MIN 16
> +#define DECODER_HEIGHT_MIN 16
> +#define DEC_REQ_TIMEOUT msecs_to_jiffies(1000)
> +#define DEC_RES_EVT_TIMEOUT DEC_REQ_TIMEOUT
> +
> +/* Supported formats */
> +static const struct al_fmt al_src_formats[] = {
> +	{
> +		.pixelformat = V4L2_PIX_FMT_H264,
> +		.bpp = 20,

bpp applied to H.264 ? Isn't zero a better value ?

> +	},
> +	{
> +		.pixelformat = V4L2_PIX_FMT_HEVC,
> +		.bpp = 20,
> +	},
> +	{
> +		.pixelformat = V4L2_PIX_FMT_JPEG,
> +		.bpp = 8,
> +	}
> +};
> +
> +static const struct al_fmt al_dst_formats[] = {
> +	{
> +		.pixelformat = V4L2_PIX_FMT_NV12,
> +		.bpp = 12,
> +	},
> +	{
> +		.pixelformat = V4L2_PIX_FMT_P010,
> +		.bpp = 12,
> +	},
> +	{
> +		.pixelformat = V4L2_PIX_FMT_NV16,
> +		.bpp = 16,
> +	},
> +	{
> +		.pixelformat = V4L2_PIX_FMT_YUV420, /* YUV 4:2:0 */
> +		.bpp = 12,
> +	},
> +	{
> +		.pixelformat = V4L2_PIX_FMT_YVU420, /* YVU 4:2:0 */
> +		.bpp = 12,
> +	},
> +};
> +
> +/* Default format */
> +static const struct al_frame al_default_fmt = {
> +
> +	.width = DECODER_WIDTH_DEFAULT,
> +	.height = DECODER_HEIGHT_DEFAULT,
> +	.bytesperline = DECODER_WIDTH_MAX * 4,
> +	.sizeimage = DECODER_WIDTH_DEFAULT * DECODER_HEIGHT_DEFAULT * 4,
> +	.nbuffers = 1,
> +	.fmt = &al_dst_formats[0],
> +	.field = V4L2_FIELD_NONE,
> +	.colorspace = V4L2_COLORSPACE_REC709,
> +	.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
> +	.quantization = V4L2_QUANTIZATION_DEFAULT,
> +	.xfer_func = V4L2_XFER_FUNC_DEFAULT
> +};
> +
> +static struct al_frame *al_get_frame(struct al_dec_ctx *ctx,
> +				     enum v4l2_buf_type type)
> +{
> +	if (WARN_ON(!ctx))
> +		return ERR_PTR(-EINVAL);
> +
> +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> +		return &ctx->src;
> +	else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return &ctx->dst;
> +
> +	al_v4l2_err(ctx->codec, "Unsupported type (%d)", type);
> +
> +	return ERR_PTR(-EINVAL);
> +}
> +
> +static const struct al_fmt *al_find_fmt(u32 pixelformat)
> +{
> +	const struct al_fmt *fmt;
> +	unsigned int i;
> +
> +	/* check if the pixelformat exist in the src formats list */
> +	for (i = 0; i < ARRAY_SIZE(al_src_formats); i++) {
> +		fmt = &al_src_formats[i];
> +		if (fmt->pixelformat == pixelformat)
> +			return fmt;
> +	}
> +
> +	/* check if the pixelformat exist in the dst formats list */
> +	for (i = 0; i < ARRAY_SIZE(al_dst_formats); i++) {
> +		fmt = &al_dst_formats[i];
> +		if (fmt->pixelformat == pixelformat)
> +			return fmt;
> +	}
> +
> +	return NULL;
> +}
> +
> +static int dec_fw_create_decoder(struct al_dec_ctx *ctx)
> +{
> +	struct msg_itf_create_decoder_req_full req;
> +	struct msg_itf_create_codec_reply reply;
> +	const struct al_frame *frame = &ctx->src;
> +	struct al_common_mcu_req mreq = {
> +		.pCtx = al_virt_to_phys(ctx),
> +		.req_type = MSG_ITF_TYPE_CREATE_INST_REQ,
> +		.req_size = sizeof(req.req),
> +		.reply_size = sizeof(reply),
> +		.reply = &reply,
> +	};
> +	int ret;
> +
> +	if (ctx->hDec) {
> +		al_v4l2_dbg(ctx->codec, 3, "fw decoder already exist\n");
> +		return 0;
> +	}
> +
> +	req.req.codec = frame->fmt->pixelformat;
> +
> +	ret = al_common_send_req_reply(ctx->codec, &ctx->cmd_q_list, &req.hdr,
> +				       &mreq);
> +
> +	if (!ret && !reply.ret)
> +		ctx->hDec = reply.hCodec;
> +	else if (reply.ret)
> +		ret = -ENODEV;
> +
> +	return ret;
> +}
> +
> +static void dec_fw_destroy_decoder(struct al_dec_ctx *ctx)
> +{
> +	struct msg_itf_destroy_codec_req_full req;
> +	struct msg_itf_destroy_codec_reply reply;
> +	struct al_common_mcu_req mreq = {
> +		.req_type = MSG_ITF_TYPE_DESTROY_INST_REQ,
> +		.pCtx = al_virt_to_phys(ctx),
> +		.req_size = sizeof(req.req),
> +		.reply_size = sizeof(reply),
> +		.reply = &reply,
> +	};
> +	int ret;
> +
> +	if (WARN(!ctx->hDec, "NULL Decoder to destroy !"))
> +		return;
> +
> +	al_v4l2_dbg(ctx->codec, 3, "Destroy decoder %lld ", ctx->hDec);
> +
> +	req.req.hCodec = ctx->hDec;
> +
> +	ret = al_common_send_req_reply(ctx->codec, &ctx->cmd_q_list, &req.hdr,
> +				       &mreq);
> +
> +	if (!ret)
> +		ctx->hDec = 0;
> +}
> +
> +static int al_dec_fw_push_frame_buf(struct al_dec_ctx *ctx,
> +				    struct vb2_v4l2_buffer *vbuf)
> +{
> +	struct msg_itf_push_dst_buf_req_full req;
> +	struct v4l2_m2m_buffer *m2m_buf;
> +	struct al_common_mcu_req mreq = {
> +		.req_type = MSG_ITF_TYPE_PUT_DISPLAY_PICTURE_REQ,
> +		.pCtx = al_virt_to_phys(ctx),
> +		.req_size = sizeof(req.req),
> +	};
> +	int ret;
> +
> +	if (WARN(!vbuf, "NULL frame Buffer to push!!"))
> +		return -EINVAL;
> +
> +	m2m_buf = container_of(vbuf, struct v4l2_m2m_buffer, vb);
> +	req.req.hCodec = ctx->hDec;
> +	req.req.bufferHandle = al_virt_to_phys(m2m_buf);
> +	req.req.phyAddr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
> +	req.req.size = vb2_plane_size(&vbuf->vb2_buf, 0);
> +
> +	ret = al_common_send_req_reply(ctx->codec, &ctx->cmd_q_list, &req.hdr,
> +				       &mreq);
> +	if (ret)
> +		al_v4l2_err(ctx->codec, "Failed to push frame buffer %p %d",
> +			    m2m_buf, ret);
> +
> +	return ret;
> +}
> +
> +static int al_dec_fw_push_bitstream_buf(struct al_dec_ctx *ctx,
> +					struct vb2_v4l2_buffer *vbuf)
> +{
> +	struct msg_itf_push_src_buf_req_full req;
> +	struct v4l2_m2m_buffer *m2m_buf;
> +	struct al_common_mcu_req mreq = {
> +		.req_type = MSG_ITF_TYPE_PUSH_BITSTREAM_BUFFER_REQ,
> +		.pCtx = al_virt_to_phys(ctx),
> +		.req_size = sizeof(req.req),
> +	};
> +	int ret;
> +
> +	if (WARN(!vbuf, "NULL Buffer to push!!"))
> +		return -EINVAL;
> +
> +	m2m_buf = container_of(vbuf, struct v4l2_m2m_buffer, vb);
> +	req.req.hCodec = ctx->hDec;
> +	req.req.bufferHandle = al_virt_to_phys(m2m_buf);
> +	req.req.phyAddr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
> +	req.req.size = vb2_plane_size(&vbuf->vb2_buf, 0);
> +
> +	/* Fill the v4l2 metadata*/
> +	req.req.meta.timestamp = vbuf->vb2_buf.timestamp;
> +	req.req.meta.timecode = vbuf->timecode;
> +	req.req.meta.last = vbuf->flags & V4L2_BUF_FLAG_LAST;
> +
> +	ret = al_common_send_req_reply(ctx->codec, &ctx->cmd_q_list, &req.hdr,
> +				       &mreq);
> +	if (ret)
> +		al_v4l2_err(ctx->codec, "Failed to push bitstream buffer %p %d",
> +			    m2m_buf, ret);
> +
> +	return ret;
> +}
> +
> +static int dec_fw_flush_req(struct al_dec_ctx *ctx)
> +{
> +	struct msg_itf_flush_req_full req;
> +	struct msg_itf_flush_reply reply;
> +	struct al_common_mcu_req mreq = {
> +		.req_type = MSG_ITF_TYPE_FLUSH_REQ,
> +		.pCtx = al_virt_to_phys(ctx),
> +		.req_size = sizeof(req.req),
> +		.reply_size = sizeof(reply),
> +		.reply = &reply,
> +	};
> +	int ret;
> +
> +	req.req.hCodec = ctx->hDec;
> +
> +	ret = al_common_send_req_reply(ctx->codec, &ctx->cmd_q_list, &req.hdr,
> +				       &mreq);
> +
> +	if (ret)
> +		al_v4l2_err(ctx->codec, "Failed to flush the decoder %d", ret);
> +
> +	return ret;
> +}
> +
> +static struct vb2_v4l2_buffer *al_dec_dequeue_buf(struct al_dec_ctx *ctx,
> +						  uint64_t hdl,
> +						  struct list_head *buffer_list)
> +{
> +	struct v4l2_m2m_buffer *buf, *tmp;
> +	struct vb2_v4l2_buffer *ret = NULL;
> +
> +	guard(mutex)(&ctx->buf_q_mlock);
> +	list_for_each_entry_safe(buf, tmp, buffer_list, list) {
> +		if (buf == al_phys_to_virt(hdl)) {
> +			list_del(&buf->list);
> +			ret = &buf->vb;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static struct vb2_v4l2_buffer *al_dec_dequeue_src_buf(struct al_dec_ctx *ctx,
> +						      uint64_t hdl)
> +{
> +	return al_dec_dequeue_buf(ctx, hdl, &ctx->stream_q_list);
> +}
> +
> +static struct vb2_v4l2_buffer *al_dec_dequeue_dst_buf(struct al_dec_ctx *ctx,
> +						      uint64_t hdl)
> +{
> +	return al_dec_dequeue_buf(ctx, hdl, &ctx->frame_q_list);
> +}
> +
> +static void al_ctx_cleanup(struct kref *ref)
> +{
> +	struct al_dec_ctx *ctx = container_of(ref, struct al_dec_ctx, refcount);
> +
> +	kfree(ctx);
> +}
> +
> +static struct al_dec_ctx *al_ctx_get(struct al_codec_dev *codec, uint64_t hdl)
> +{
> +	struct al_dec_ctx *ctx;
> +	struct al_dec_ctx *ret = NULL;
> +
> +	guard(mutex)(&codec->ctx_mlock);
> +	list_for_each_entry(ctx, &codec->ctx_q_list, list) {
> +		if (ctx == al_phys_to_virt(hdl)) {
> +			kref_get(&ctx->refcount);
> +			ret = ctx;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void al_ctx_put(struct al_dec_ctx *ctx)
> +{
> +	kref_put(&ctx->refcount, al_ctx_cleanup);
> +}
> +
> +static int al_dec_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> +	struct al_dec_ctx *ctx = vb2_get_drv_priv(q);
> +	struct al_codec_dev *codec = ctx->codec;
> +
> +	v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q);
> +
> +	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
> +		struct v4l2_m2m_buffer *buf;
> +		int ret;
> +
> +		if (list_empty(&ctx->stream_q_list)) {
> +			al_v4l2_err(codec, "Empty stream list.");
> +			return -EINVAL;
> +		}
> +
> +		if (!al_common_mcu_is_alive(codec)) {
> +			al_v4l2_err(codec, "Unable to ping the mcu");
> +			return -ENODEV;
> +		}
> +
> +		ret = dec_fw_create_decoder(ctx);
> +		if (ret) {
> +			al_v4l2_err(codec, "Unable to create the fw decoder %d",
> +				    ret);
> +			return ret;
> +		}
> +
> +		/* Get the first vid-out queued buffer */
> +		buf = list_first_entry(&ctx->stream_q_list,
> +				       struct v4l2_m2m_buffer, list);
> +
> +		if (al_dec_fw_push_bitstream_buf(ctx, &buf->vb)) {
> +			al_v4l2_err(codec,
> +				    "Unable to push the bitstream buffer");
> +			return -EINVAL;
> +		}
> +
> +		/* Wait until the mcu detect the resolution of the stream */
> +		ret = wait_for_completion_timeout(&ctx->res_done,
> +						  DEC_RES_EVT_TIMEOUT);
> +		if (!ret) {
> +			al_v4l2_err(codec, "unsupported stream");
> +			ctx->aborting = true;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void al_dec_stop_streaming_cap(struct al_dec_ctx *ctx)
> +{
> +	struct vb2_v4l2_buffer *vbuf;
> +	struct v4l2_m2m_buffer *entry, *tmp;
> +
> +	mutex_lock(&ctx->buf_q_mlock);
> +	if (!list_empty(&ctx->frame_q_list))
> +		list_for_each_entry_safe(entry, tmp, &ctx->frame_q_list, list) {
> +			list_del(&entry->list);
> +			vbuf = &entry->vb;
> +			vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0);
> +			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +		}
> +	mutex_unlock(&ctx->buf_q_mlock);
> +
> +	while (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) {
> +		vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +		if (vbuf) {
> +			vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0);
> +			v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +		}
> +	}
> +
> +	v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx);
> +}
> +
> +static void al_dec_stop_streaming_out(struct al_dec_ctx *ctx)
> +{
> +	struct vb2_v4l2_buffer *vbuf;
> +	struct v4l2_m2m_buffer *entry, *tmp;
> +
> +	mutex_lock(&ctx->buf_q_mlock);
> +	if (!list_empty(&ctx->stream_q_list))
> +		list_for_each_entry_safe(entry, tmp, &ctx->stream_q_list,
> +					 list) {
> +			list_del(&entry->list);
> +			v4l2_m2m_buf_done(&entry->vb, VB2_BUF_STATE_ERROR);
> +		}
> +	mutex_unlock(&ctx->buf_q_mlock);
> +
> +	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
> +		while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
> +			if (vbuf->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
> +				v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> +	}
> +
> +	dec_fw_destroy_decoder(ctx);
> +}
> +
> +static void al_dec_stop_streaming(struct vb2_queue *q)
> +{
> +	struct al_dec_ctx *ctx = vb2_get_drv_priv(q);
> +
> +	v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q);
> +
> +	/* Releasing the dst and src buffers */
> +	ctx->stopped = true;
> +
> +	if (V4L2_TYPE_IS_OUTPUT(q->type))
> +		al_dec_stop_streaming_out(ctx);
> +	else
> +		al_dec_stop_streaming_cap(ctx);
> +}
> +
> +static int al_dec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> +			      unsigned int *nplanes, unsigned int sizes[],
> +			      struct device *alloc_devs[])
> +{
> +	struct al_dec_ctx *ctx = vb2_get_drv_priv(vq);
> +	struct al_frame *format = al_get_frame(ctx, vq->type);
> +
> +	if (IS_ERR(format)) {
> +		al_v4l2_err(ctx->codec, "Invalid format %p", format);
> +		return PTR_ERR(format);
> +	}
> +
> +	if (*nplanes)
> +		return ((sizes[0] < format->sizeimage) ? -EINVAL : 0);
> +
> +	/* update queue num buffers */
> +	format->nbuffers = max(*nbuffers, format->nbuffers);
> +
> +	*nplanes = 1;
> +	sizes[0] = format->sizeimage;
> +	*nbuffers = format->nbuffers;
> +
> +	al_v4l2_dbg(ctx->codec, 2, "%s: Get %d buffers of size %d each ",
> +		    (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? "OUT" : "CAP",
> +		    *nbuffers, sizes[0]);
> +
> +	return 0;
> +}
> +
> +static int al_dec_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct al_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	if (ctx->aborting)
> +		return -EINVAL;
> +
> +	if (V4L2_TYPE_IS_CAPTURE(vb->type)) {
> +		if (vbuf->field == V4L2_FIELD_ANY)
> +			vbuf->field = V4L2_FIELD_NONE;
> +		if (vbuf->field != V4L2_FIELD_NONE)
> +			return -EINVAL;
> +	}
> +
> +	al_v4l2_dbg(ctx->codec, 3, "%s : Buffer (%p) prepared ",
> +		    (V4L2_TYPE_IS_OUTPUT(vb->type) ? "OUT" : "CAP"), vbuf);
> +
> +	return 0;
> +}
> +
> +static inline void al_dec_fill_bitstream(struct al_dec_ctx *ctx)
> +{
> +	struct vb2_v4l2_buffer *src_buf;
> +	struct v4l2_m2m_buffer *m2m_buf;
> +	struct vb2_queue *src_vq;
> +
> +	lockdep_assert_held(&ctx->buf_q_mlock);
> +
> +	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
> +		src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> +		if (!src_buf)
> +			return;
> +
> +		/* Dump empty buffers */
> +		if (!vb2_get_plane_payload(&src_buf->vb2_buf, 0)) {
> +			src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +			v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> +			return;
> +		}
> +
> +		src_vq = v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx);
> +		src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> +
> +		if (src_buf) {
> +			src_buf->sequence = ctx->osequence++;
> +
> +			if (vb2_is_streaming(src_vq) &&
> +			    al_dec_fw_push_bitstream_buf(ctx, src_buf)) {
> +				v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> +				return;
> +			}
> +
> +			m2m_buf = container_of(src_buf, struct v4l2_m2m_buffer,
> +					       vb);
> +			list_add_tail(&m2m_buf->list, &ctx->stream_q_list);
> +		}
> +	}
> +}
> +
> +static void al_dec_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct al_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +
> +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +
> +	if (V4L2_TYPE_IS_OUTPUT(vb->type)) {
> +		mutex_lock(&ctx->buf_q_mlock);
> +		al_dec_fill_bitstream(ctx);
> +		mutex_unlock(&ctx->buf_q_mlock);
> +	}
> +
> +	al_v4l2_dbg(ctx->codec, 3, "%s queued (%p) - (%d)",
> +		    V4L2_TYPE_IS_OUTPUT(vb->type) ? "OUT" : "CAP", vbuf,
> +		    vb->num_planes);
> +}
> +
> +static const struct vb2_ops dec_queue_ops = {
> +	.queue_setup = al_dec_queue_setup,
> +	.buf_prepare = al_dec_buf_prepare,
> +	.buf_queue = al_dec_buf_queue,
> +	.start_streaming = al_dec_start_streaming,
> +	.stop_streaming = al_dec_stop_streaming,
> +	.wait_prepare = vb2_ops_wait_prepare,
> +	.wait_finish = vb2_ops_wait_finish,
> +};
> +
> +static int al_dec_queue_init(void *priv, struct vb2_queue *src_vq,
> +			     struct vb2_queue *dst_vq)
> +{
> +	struct al_dec_ctx *ctx = priv;
> +	struct al_codec_dev *codec = ctx->codec;
> +	int ret;
> +
> +	src_vq->dev = &codec->pdev->dev;
> +	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> +	src_vq->non_coherent_mem = false;
> +	src_vq->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;

I don't think you need that with the dma contig ops. And if someone integrate an
IOMMU, this will get in the way.

> +	src_vq->mem_ops = &vb2_dma_contig_memops;
> +	src_vq->drv_priv = ctx;
> +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +	src_vq->ops = &dec_queue_ops;
> +	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> +	src_vq->lock = &ctx->codec->lock;
> +	ret = vb2_queue_init(src_vq);
> +	if (ret)
> +		return ret;
> +
> +	dst_vq->dev = &codec->pdev->dev;
> +	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> +	dst_vq->non_coherent_mem = false;
> +	dst_vq->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
> +	dst_vq->mem_ops = &vb2_dma_contig_memops;
> +	dst_vq->drv_priv = ctx;
> +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> +	dst_vq->ops = &dec_queue_ops;
> +	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> +	dst_vq->lock = &ctx->codec->lock;
> +	ret = vb2_queue_init(dst_vq);
> +	if (ret) {
> +		vb2_queue_release(src_vq);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int al_dec_querycap(struct file *file, void *fh,
> +			   struct v4l2_capability *cap)
> +{
> +	struct al_codec_dev *codec = video_drvdata(file);
> +
> +	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strscpy(cap->card, "Allegro DVT Video Decoder", sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> +		 dev_name(&codec->pdev->dev));
> +
> +	return 0;
> +}
> +
> +static int al_dec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> +{
> +	const struct al_fmt *fmt;
> +
> +	if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
> +	    f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
> +		if (f->index >= ARRAY_SIZE(al_src_formats))
> +			return -EINVAL;
> +
> +		fmt = &al_src_formats[f->index];
> +	} else {
> +		if (f->index >= ARRAY_SIZE(al_dst_formats))
> +			return -EINVAL;
> +
> +		fmt = &al_dst_formats[f->index];
> +	}
> +
> +	f->pixelformat = fmt->pixelformat;
> +	return 0;
> +}
> +
> +static int al_dec_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct al_dec_ctx *ctx = fh_to_ctx(fh, struct al_dec_ctx);
> +	struct v4l2_pix_format *pix = &f->fmt.pix;
> +	struct al_frame *pix_fmt;
> +
> +	pix_fmt = al_get_frame(ctx, f->type);
> +	if (IS_ERR(pix_fmt)) {
> +		al_v4l2_err(ctx->codec, "Invalid frame (%p)", pix_fmt);
> +		return PTR_ERR(pix_fmt);
> +	}
> +
> +	pix_fmt->fmt = al_find_fmt(pix->pixelformat);
> +	if (!pix_fmt->fmt) {
> +		al_v4l2_err(ctx->codec, "Unknown format 0x%x",
> +			    pix->pixelformat);
> +		return -EINVAL;
> +	}
> +	pix->field = V4L2_FIELD_NONE;
> +	pix->width = clamp_t(__u32, pix->width, DECODER_WIDTH_MIN,
> +			     DECODER_WIDTH_MAX);
> +	pix->height = clamp_t(__u32, pix->height, DECODER_HEIGHT_MIN,
> +			      DECODER_HEIGHT_MAX);
> +
> +	pix->bytesperline = pix->width;
> +	pix->sizeimage = (pix->width * pix->height * pix_fmt->fmt->bpp) / 8;

Please, use v4l2_apply_frmsize_constraints() from v4l2-common, and aremove hand
made alignement, bytesperline and sizeimage calculation. Check other drivers for
example, you can integrate the struct v4l2_fmtdesc to you static table above.

> +
> +	if (V4L2_TYPE_IS_CAPTURE(f->type))
> +		if (pix->sizeimage < pix_fmt->sizeimage)
> +			pix->sizeimage = pix_fmt->sizeimage;
> +
> +	al_v4l2_dbg(
> +		ctx->codec, 3,
> +		"%s : width (%d) , height (%d), bytesperline (%d), sizeimage (%d) ",
> +		(f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ? "CAP" : "OUT",
> +		pix->width, pix->height, pix->bytesperline, pix->sizeimage);

There is also logging of the VIDOC_S_FMT output, that seems redundant, remove.

	echo 0xff  > /sys/class/video4linux2/videoN/dev_debug

> +
> +	return 0;
> +}
> +
> +static int al_dec_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct al_dec_ctx *ctx = fh_to_ctx(fh, struct al_dec_ctx);
> +	struct al_frame *pix_fmt = al_get_frame(ctx, f->type);
> +	struct v4l2_pix_format *pix;
> +
> +	if (IS_ERR(pix_fmt)) {
> +		al_v4l2_err(ctx->codec, "Invalid pixel format %p", pix_fmt);
> +		return PTR_ERR(pix_fmt);
> +	}
> +
> +	if (!pix_fmt->fmt) {
> +		al_v4l2_err(ctx->codec, "Unknown format for %d", f->type);
> +		return -EINVAL;
> +	}
> +
> +	pix = &f->fmt.pix;
> +	pix->width = pix_fmt->width;
> +	pix->height = pix_fmt->height;
> +	pix->bytesperline = pix_fmt->bytesperline;
> +	pix->sizeimage = pix_fmt->sizeimage;
> +	pix->pixelformat = pix_fmt->fmt->pixelformat;
> +	pix->field = V4L2_FIELD_NONE;
> +
> +	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> +		pix->bytesperline = 0;
> +
> +	pix->ycbcr_enc = pix_fmt->ycbcr_enc;
> +	pix->quantization = pix_fmt->quantization;
> +	pix->xfer_func = pix_fmt->xfer_func;
> +	pix->colorspace = pix_fmt->colorspace;
> +
> +	al_v4l2_dbg(
> +		ctx->codec, 3,
> +		"%s : width (%d) , height (%d), bytesperline (%d) , sizeimage (%d)",
> +		(f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ? "CAP" : "OUT",
> +		pix->width, pix->height, pix->bytesperline, pix->sizeimage);

Drop.

> +
> +	return 0;
> +}
> +
> +static int al_dec_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
> +{
> +	struct al_dec_ctx *ctx = fh_to_ctx(fh, struct al_dec_ctx);
> +	struct v4l2_pix_format *pix;
> +	struct al_frame *frame;
> +	struct vb2_queue *vq;
> +	int ret;
> +
> +	ret = al_dec_try_fmt(file, fh, f);
> +	if (ret) {
> +		al_v4l2_err(ctx->codec, "Cannot set format (%d)", f->type);
> +		return ret;
> +	}
> +
> +	frame = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? &ctx->src : &ctx->dst;
> +
> +	pix = &f->fmt.pix;
> +	frame->fmt = al_find_fmt(pix->pixelformat);
> +	if (!frame->fmt) {
> +		al_v4l2_err(ctx->codec, "Unknown format for %d",
> +			    pix->pixelformat);
> +		return -EINVAL;
> +	}

This cannot happen, it was already checked in try_fmt.

> +
> +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> +	if (vb2_is_streaming(vq)) {
> +		al_v4l2_err(ctx->codec, "queue %d busy", f->type);
> +		return -EBUSY;
> +	}
> +
> +	frame->width = pix->width;
> +	frame->height = pix->height;
> +	frame->bytesperline = pix->bytesperline;
> +	frame->sizeimage = pix->sizeimage;
> +	frame->field = pix->field;
> +
> +	frame->ycbcr_enc = pix->ycbcr_enc;
> +	frame->quantization = pix->quantization;
> +	frame->xfer_func = pix->xfer_func;
> +	frame->colorspace = pix->colorspace;
> +
> +	al_v4l2_dbg(
> +		ctx->codec, 3,
> +		" %s : width (%d) , height (%d), bytesperline (%d), sizeimage (%d)",
> +		(f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ? "CAP" : "OUT",
> +		pix->width, pix->height, pix->bytesperline, pix->sizeimage);

Drop.

> +
> +	return 0;
> +}
> +
> +static void al_queue_eos_event(struct al_dec_ctx *ctx)
> +{
> +	const struct v4l2_event eos_event = {
> +		.id = 0,
> +		.type = V4L2_EVENT_EOS,
> +	};
> +
> +	v4l2_event_queue_fh(&ctx->fh, &eos_event);
> +}
> +
> +static void al_queue_res_chg_event(struct al_dec_ctx *ctx)
> +{
> +	static const struct v4l2_event ev_src_ch = {
> +		.id = 0,
> +		.type = V4L2_EVENT_SOURCE_CHANGE,
> +		.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
> +	};
> +
> +	v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
> +}
> +
> +static int al_dec_decoder_cmd(struct file *file, void *fh,
> +			      struct v4l2_decoder_cmd *dcmd)
> +{
> +	struct al_dec_ctx *ctx = fh_to_ctx(fh, struct al_dec_ctx);
> +	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +	struct vb2_v4l2_buffer *vbuf;
> +	struct vb2_queue *dst_vq;
> +	int ret;
> +
> +	ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dcmd);
> +	if (ret)
> +		return ret;
> +
> +	/* Get the vb2 queue for the Capture */
> +	dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> +
> +	switch (dcmd->cmd) {
> +	case V4L2_DEC_CMD_START:
> +		vb2_clear_last_buffer_dequeued(dst_vq);
> +		break;
> +	case V4L2_DEC_CMD_STOP:
> +		vbuf = v4l2_m2m_last_src_buf(m2m_ctx);
> +		if (vbuf) {
> +			al_v4l2_dbg(ctx->codec, 1,
> +				    "marking last pending buffer");
> +
> +			vbuf->flags |= V4L2_BUF_FLAG_LAST;
> +			if (v4l2_m2m_num_src_bufs_ready(m2m_ctx) == 0) {
> +				al_v4l2_dbg(ctx->codec, 1,
> +					    "all remaining buffers queued");
> +				v4l2_m2m_try_schedule(m2m_ctx);
> +			}
> +		}
> +		dec_fw_flush_req(ctx);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int al_dec_enum_framesizes(struct file *file, void *fh,
> +				  struct v4l2_frmsizeenum *fsize)
> +{
> +	if (!al_find_fmt(fsize->pixel_format))
> +		return -EINVAL;
> +
> +	/* FIXME : check step size */
> +	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
> +	fsize->stepwise.min_width = DECODER_WIDTH_MIN;
> +	fsize->stepwise.max_width = DECODER_WIDTH_MAX;
> +	fsize->stepwise.step_width = 8;
> +	fsize->stepwise.min_height = DECODER_HEIGHT_MIN;
> +	fsize->stepwise.max_height = DECODER_HEIGHT_MAX;
> +	fsize->stepwise.step_height = 8;
> +
> +	return 0;
> +}
> +
> +static int al_dec_subscribe_event(struct v4l2_fh *fh,
> +				  const struct v4l2_event_subscription *sub)
> +{
> +	switch (sub->type) {
> +	case V4L2_EVENT_EOS:
> +		return v4l2_event_subscribe(fh, sub, 0, NULL);
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		return v4l2_src_change_event_subscribe(fh, sub);
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int al_dec_log_status(struct file *file, void *fh)
> +{
> +	struct al_codec_dev *codec = video_drvdata(file);
> +
> +	v4l2_device_call_all(&codec->v4l2_dev, 0, core, log_status);
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops al_dec_ioctl_ops = {
> +	.vidioc_querycap = al_dec_querycap,
> +	.vidioc_enum_fmt_vid_cap = al_dec_enum_fmt,
> +	.vidioc_enum_fmt_vid_out = al_dec_enum_fmt,
> +	.vidioc_g_fmt_vid_cap = al_dec_g_fmt,
> +	.vidioc_g_fmt_vid_out = al_dec_g_fmt,
> +	.vidioc_try_fmt_vid_cap = al_dec_try_fmt,
> +	.vidioc_try_fmt_vid_out = al_dec_try_fmt,
> +	.vidioc_s_fmt_vid_cap = al_dec_s_fmt,
> +	.vidioc_s_fmt_vid_out = al_dec_s_fmt,
> +
> +	.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> +	.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> +
> +	.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> +	.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> +	.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> +	.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> +	.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> +
> +	.vidioc_streamon = v4l2_m2m_ioctl_streamon,
> +	.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> +	.vidioc_log_status = al_dec_log_status,
> +
> +	.vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
> +	.vidioc_decoder_cmd = al_dec_decoder_cmd,
> +	.vidioc_enum_framesizes = al_dec_enum_framesizes,
> +
> +	.vidioc_subscribe_event = al_dec_subscribe_event,
> +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static void al_device_run(void *priv)
> +{
> +	struct al_dec_ctx *ctx = priv;
> +	struct vb2_v4l2_buffer *dst_buf;
> +	struct v4l2_m2m_buffer *m2m_buf;
> +
> +	if (unlikely(!ctx))
> +		return;
> +
> +	if (ctx->aborting) {
> +		vb2_queue_error(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx));
> +		vb2_queue_error(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx));
> +		return;
> +	}
> +
> +	if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx))
> +		goto job_finish;
> +
> +	dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +	if (!dst_buf)
> +		goto job_finish;
> +
> +	if (!al_common_mcu_is_alive(ctx->codec) ||
> +	    al_dec_fw_push_frame_buf(ctx, dst_buf)) {
> +		vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
> +		v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
> +		goto job_finish;
> +	}
> +
> +	mutex_lock(&ctx->buf_q_mlock);
> +	m2m_buf = container_of(dst_buf, struct v4l2_m2m_buffer, vb);
> +	list_add_tail(&m2m_buf->list, &ctx->frame_q_list);
> +	mutex_unlock(&ctx->buf_q_mlock);
> +
> +job_finish:
> +	v4l2_m2m_job_finish(ctx->codec->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +
> +static const struct v4l2_m2m_ops al_dec_m2m_ops = {
> +	.device_run = al_device_run,
> +};
> +
> +static int al_dec_open(struct file *file)
> +{
> +	struct video_device *vdev = video_devdata(file);
> +	struct al_codec_dev *codec = video_get_drvdata(vdev);
> +	struct al_dec_ctx *ctx = NULL;
> +	int ret;
> +
> +	if (mutex_lock_interruptible(&codec->ctx_mlock))
> +		return -ERESTARTSYS;

This is a bit of over-locking ...

> +
> +	/* Aloocate memory for the dec ctx */
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx) {
> +		ret = -ENOMEM;
> +		goto unlock;
> +	}
> +
> +	ctx->codec = codec;
> +	/* Init ctx mutex */
> +	mutex_init(&ctx->buf_q_mlock);
> +	/* Init ctx LISTHEADs*/
> +	INIT_LIST_HEAD(&ctx->cmd_q_list);
> +	INIT_LIST_HEAD(&ctx->frame_q_list);
> +	INIT_LIST_HEAD(&ctx->stream_q_list);
> +
> +	/* Init the irq queue */
> +	init_completion(&ctx->res_done);
> +
> +	v4l2_fh_init(&ctx->fh, vdev);
> +
> +	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 0);
> +	if (ctx->ctrl_handler.error) {
> +		ret = ctx->ctrl_handler.error;
> +		al_v4l2_err(codec, "Failed to create control %d", ret);
> +		goto handler_error;
> +	}
> +
> +	ctx->fh.ctrl_handler = &ctx->ctrl_handler;
> +	v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
> +
> +	file->private_data = &ctx->fh;
> +	v4l2_fh_add(&ctx->fh);
> +
> +	/* Set default formats */
> +	ctx->src = ctx->dst = al_default_fmt;
> +	ctx->csequence = ctx->osequence = 0;
> +
> +	ctx->stopped = false;
> +	ctx->aborting = false;
> +	ctx->id = codec->ctx_counter++;
> +
> +	/* Setup the ctx for m2m mode */
> +	ctx->fh.m2m_ctx =
> +		v4l2_m2m_ctx_init(codec->m2m_dev, ctx, al_dec_queue_init);
> +	if (IS_ERR(ctx->fh.m2m_ctx)) {
> +		ret = PTR_ERR(ctx->fh.m2m_ctx);
> +		al_v4l2_err(codec, "Failed to initialize m2m mode %d", ret);
> +		goto error_ctrls;
> +	}
> +
> +	v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
> +
> +	/* Add ctx to the LIST */
> +	kref_init(&ctx->refcount);

You really just need to lock this call. Be aware you can use scoped_guard() too.

> +	list_add(&ctx->list, &codec->ctx_q_list);
> +	al_codec_dbgfs_create(ctx);
> +
> +	mutex_unlock(&codec->ctx_mlock);
> +
> +	return 0;
> +
> +error_ctrls:
> +	v4l2_fh_del(&ctx->fh);
> +handler_error:
> +	v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> +	v4l2_fh_exit(&ctx->fh);
> +	kfree(ctx);
> +
> +unlock:
> +	mutex_unlock(&codec->ctx_mlock);
> +	return ret;
> +}
> +
> +static int al_dec_release(struct file *file)
> +{
> +	struct al_dec_ctx *ctx =
> +		fh_to_ctx(file->private_data, struct al_dec_ctx);
> +	struct al_codec_dev *codec = ctx->codec;
> +
> +	guard(mutex)(&codec->ctx_mlock);
> +
> +	/* It is important to do this before removing ctx from dev list.
> +	 * Those commands will trigger some traffic towards fw and so we
> +	 * need completion to avoid deadlock if cmds can't find 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);
> +
> +	list_del(&ctx->list);
> +	al_codec_dbgfs_remove(codec, ctx->id);
> +	al_ctx_put(ctx);

I'd scope the lock to the list_del, and move it first so that ctx is completely
detached.

> +
> +	return 0;
> +}
> +
> +static inline bool al_mark_last_dst_buf(struct al_dec_ctx *ctx)
> +{
> +	struct vb2_v4l2_buffer *buf;
> +	struct vb2_buffer *dst_vb;
> +	struct vb2_queue *dst_vq;
> +	unsigned long flags;
> +
> +	al_v4l2_dbg(ctx->codec, 1, "marking last capture buffer");
> +
> +	dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +	spin_lock_irqsave(&dst_vq->done_lock, flags);
> +	if (list_empty(&dst_vq->done_list)) {
> +		spin_unlock_irqrestore(&dst_vq->done_lock, flags);
> +		return false;
> +	}
> +
> +	dst_vb = list_last_entry(&dst_vq->done_list, struct vb2_buffer,
> +				 done_entry);
> +	buf = to_vb2_v4l2_buffer(dst_vb);
> +	buf->flags |= V4L2_BUF_FLAG_LAST;

In my experience, this approach isn't reliable. Other driver simply add some
hook into queue_buf, and flag that buffer when userspace queue back a free
buffer.

> +
> +	spin_unlock_irqrestore(&dst_vq->done_lock, flags);
> +	return true;
> +}
> +
> +static const struct v4l2_file_operations al_dec_file_ops = {
> +	.owner = THIS_MODULE,
> +	.open = al_dec_open,
> +	.release = al_dec_release,
> +	.poll = v4l2_m2m_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = v4l2_m2m_fop_mmap,
> +};
> +
> +static void handle_error_evt(struct al_dec_ctx *ctx, struct msg_itf_header *hdr)
> +{
> +	struct al_codec_dev *codec = ctx->codec;
> +	struct msg_itf_evt_error evt;
> +	struct v4l2_m2m_buffer *vbuf;
> +
> +	if (al_common_get_data(codec, (char *)&evt, hdr->payload_len)) {
> +		al_v4l2_err(codec, "Unable to get resolution found event");
> +		return;
> +	}
> +
> +	al_v4l2_err(codec, "Decoding error  %d", evt.errno);
> +
> +	guard(mutex)(&ctx->buf_q_mlock);
> +	if (!list_empty(&ctx->stream_q_list)) {
> +		vbuf = list_last_entry(&ctx->frame_q_list,
> +				       struct v4l2_m2m_buffer, list);
> +		list_del(&vbuf->list);
> +		v4l2_m2m_buf_done(&vbuf->vb, VB2_BUF_STATE_ERROR);
> +	}
> +}
> +
> +static void handle_resolution_found_evt(struct al_dec_ctx *ctx,
> +					struct msg_itf_header *hdr)
> +{
> +	struct msg_itf_evt_resolution_found evt;
> +	struct al_codec_dev *codec = ctx->codec;
> +	struct al_frame *frame;
> +	struct vb2_queue *dst_vq;
> +
> +	if (al_common_get_data(codec, (char *)&evt, hdr->payload_len)) {
> +		al_v4l2_err(codec, "Unable to get resolution found event");
> +		return;
> +	}
> +
> +	frame = &ctx->dst;
> +
> +	if (frame->width != evt.width || frame->height != evt.height ||
> +	    frame->nbuffers < evt.buffer_nb) {
> +		/* Update frame properties */
> +		frame->width = evt.width;
> +		frame->height = evt.height;
> +		frame->bytesperline = evt.bytesperline;
> +		frame->sizeimage = evt.sizeimage;
> +		frame->nbuffers = evt.buffer_nb;
> +		frame->fmt = al_find_fmt(evt.pixelformat);
> +
> +		/* This has to be changed */
> +		if (!frame->fmt)
> +			return;
> +
> +		al_queue_res_chg_event(ctx);
> +	}
> +
> +	dst_vq = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx);
> +	if (!vb2_is_streaming(dst_vq))
> +		complete(&ctx->res_done);
> +
> +	al_v4l2_dbg(
> +		codec, 3,
> +		"width(%d) , height(%d), bytesperline(%d), sizeimage(%d), n_bufs(%d)",
> +		frame->width, frame->height, frame->bytesperline,
> +		frame->sizeimage, frame->nbuffers);
> +}
> +
> +static void handle_bitstream_buffer_release_evt(struct al_dec_ctx *ctx,
> +						struct msg_itf_header *hdr)
> +{
> +	struct msg_itf_evt_bitstream_buffer_release evt;
> +	struct al_codec_dev *codec = ctx->codec;
> +	struct vb2_v4l2_buffer *vbuf;
> +
> +	if (al_common_get_data(codec, (char *)&evt, hdr->payload_len)) {
> +		al_v4l2_err(codec, "Unable to get buffer release event");
> +		return;
> +	}
> +
> +	if (ctx->stopped)
> +		return;
> +
> +	vbuf = al_dec_dequeue_src_buf(ctx, evt.bufferHandle);
> +	if (!vbuf) {
> +		al_v4l2_err(codec, "Unable to find bitsream buffer 0x%llx",
> +			    evt.bufferHandle);
> +		return;
> +	}
> +
> +	al_v4l2_dbg(codec, 3, "Release bitstream buffer %p", vbuf);
> +	v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
> +}
> +
> +static void handle_eos_evt(struct al_dec_ctx *ctx, struct msg_itf_header *hdr)
> +{
> +	struct msg_itf_evt_frame_buffer_decode evt;
> +	struct al_codec_dev *codec = ctx->codec;
> +
> +	if (al_common_get_data(codec, (char *)&evt, hdr->payload_len)) {
> +		al_v4l2_err(codec, "Unable to get frame buffer event");
> +		return;
> +	}
> +
> +	/* set LAST_FLAG to the last done CAPTURE buffer*/
> +	al_mark_last_dst_buf(ctx);
> +	/* Set eos event */
> +	al_queue_eos_event(ctx);
> +}
> +
> +static void handle_frame_buffer_decode_evt(struct al_dec_ctx *ctx,
> +					   struct msg_itf_header *hdr)
> +{
> +	struct msg_itf_evt_frame_buffer_decode evt;
> +	struct al_codec_dev *codec = ctx->codec;
> +	struct vb2_v4l2_buffer *vbuf;
> +	struct al_buffer_meta *meta;
> +
> +	if (al_common_get_data(codec, (char *)&evt, hdr->payload_len)) {
> +		al_v4l2_err(codec, "Unable to get frame buffer event");
> +		return;
> +	}
> +
> +	vbuf = al_dec_dequeue_dst_buf(ctx, evt.bufferHandle);
> +	if (!vbuf) {
> +		al_v4l2_err(codec, "Unable to find frame buffer 0x%llx",
> +			    evt.bufferHandle);
> +		return;
> +	}
> +
> +	meta = &evt.meta;
> +	al_v4l2_dbg(codec, 3, "Decoded frame done for buffer %p (%d) (%lld)",
> +		    vbuf, meta->last, evt.size);
> +
> +	vb2_set_plane_payload(&vbuf->vb2_buf, 0, evt.size);
> +	vbuf->field = V4L2_FIELD_NONE;
> +	vbuf->sequence = ctx->csequence++;
> +	vbuf->timecode = meta->timecode;
> +	vbuf->vb2_buf.timestamp = meta->timestamp;
> +
> +	if (meta->last || (vbuf->flags & V4L2_BUF_FLAG_LAST)) {
> +		vbuf->flags |= V4L2_BUF_FLAG_LAST;
> +		v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx);
> +	}
> +
> +	v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
> +}
> +
> +static int al_handle_cmd_reply(struct al_codec_dev *codec,
> +			       struct msg_itf_header *hdr)
> +{
> +	struct al_dec_ctx *ctx;
> +	struct al_codec_cmd *cmd = NULL;
> +	int ret = 0;
> +
> +	ctx = al_ctx_get(codec, hdr->drv_ctx_hdl);
> +	if (IS_ERR_OR_NULL(ctx)) {
> +		al_v4l2_err(codec, "Unable to find ctx %p for reply %d",
> +			    al_phys_to_virt(hdr->drv_ctx_hdl), hdr->type);
> +		return -EINVAL;
> +	}
> +
> +	cmd = al_codec_cmd_get(&ctx->cmd_q_list, hdr->drv_cmd_hdl);
> +	if (!cmd) {
> +		al_v4l2_err(codec, "Unable to find command %p for reply %d",
> +			    al_phys_to_virt(hdr->drv_cmd_hdl), hdr->type);
> +		ret = -EINVAL;
> +		goto ctx_put;
> +	}
> +
> +	if (cmd->reply_size != hdr->payload_len) {
> +		al_v4l2_err(codec, "mismatch size %d %d", cmd->reply_size,
> +			    hdr->payload_len);
> +		ret = -EINVAL;
> +		goto cmd_put;
> +	}
> +
> +	ret = al_common_get_data(codec, cmd->reply, hdr->payload_len);
> +	if (ret)
> +		al_v4l2_err(codec, "Unable to copy reply");
> +
> +	complete(&cmd->done);
> +	ret = 0;
> +
> +cmd_put:
> +	al_codec_cmd_put(cmd);
> +ctx_put:
> +	al_ctx_put(ctx);
> +
> +	return ret;
> +}
> +
> +static int al_handle_cmd_evt(struct al_codec_dev *dev,
> +			     struct msg_itf_header *hdr, int type)
> +{
> +	static u32 evt_sizes[] = {
> +		sizeof(struct msg_itf_evt_error),
> +		sizeof(struct msg_itf_evt_resolution_found),
> +		sizeof(struct msg_itf_evt_bitstream_buffer_release),
> +		sizeof(struct msg_itf_evt_frame_buffer_decode),
> +		sizeof(struct msg_itf_evt_eos),
> +	};
> +
> +	u32 evt_size;
> +	struct al_dec_ctx *ctx = NULL;
> +	int ret = 0;
> +
> +	if (type < MSG_ITF_TYPE_NEXT_EVT || type > MSG_ITF_TYPE_END_EVT) {
> +		al_v4l2_err(dev, "Unsupporting event type %d", type);
> +		return -EINVAL;
> +	}
> +
> +	ctx = al_ctx_get(dev, hdr->drv_ctx_hdl);
> +	if (!ctx) {
> +		al_v4l2_err(dev, "Unable to find ctx %p for evt %d",
> +			    al_phys_to_virt(hdr->drv_ctx_hdl), type);
> +		return -EINVAL;
> +	}
> +
> +	// Check the received event size and the expected one
> +	evt_size = evt_sizes[type - MSG_ITF_TYPE_NEXT_EVT];
> +	if (hdr->payload_len != evt_size) {
> +		al_v4l2_err(
> +			dev,
> +			"Invalid event size for client (%p) for evt (%d) : Got (%d), expected (%d)",
> +			al_phys_to_virt(hdr->drv_ctx_hdl), type,
> +			hdr->payload_len, evt_size);
> +		ret = -EINVAL;
> +		goto clean_ctx;
> +	}
> +
> +	al_v4l2_dbg(dev, 3, "Event received from MCU (%d)", type);
> +
> +	switch (type) {
> +	case MSG_ITF_TYPE_EVT_ERROR:
> +		handle_error_evt(ctx, hdr);
> +		break;
> +	case MSG_ITF_TYPE_EVT_RESOLUTION_FOUND:
> +		handle_resolution_found_evt(ctx, hdr);
> +		break;
> +	case MSG_ITF_TYPE_EVT_BITSTREAM_BUFFER_RELEASE:
> +		handle_bitstream_buffer_release_evt(ctx, hdr);
> +		break;
> +	case MSG_ITF_TYPE_EVT_FRAME_BUFFER_DECODE:
> +		handle_frame_buffer_decode_evt(ctx, hdr);
> +		break;
> +	case MSG_ITF_TYPE_EVT_EOS:
> +		handle_eos_evt(ctx, hdr);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +clean_ctx:
> +	al_ctx_put(ctx);
> +	return ret;
> +}
> +
> +static void al_dec_process_msg(void *cb_arg, struct msg_itf_header *hdr)
> +{
> +	struct al_codec_dev *dev = cb_arg;
> +	int ret;
> +
> +	if (is_type_reply(hdr->type))
> +		ret = al_handle_cmd_reply(dev, hdr);
> +	else if (is_type_event(hdr->type))
> +		ret = al_handle_cmd_evt(dev, hdr, hdr->type);
> +	else {
> +		al_v4l2_err(dev, "Unsupported message type %d", hdr->type);
> +		ret = -EINVAL;
> +	}
> +
> +	if (ret) {
> +		al_v4l2_err(dev, "Skip received data");
> +		al_common_skip_data(dev, hdr->payload_len);
> +	}
> +}
> +
> +static const struct video_device al_videodev = {
> +	.name = "allegro-decoder",
> +	.fops = &al_dec_file_ops,
> +	.ioctl_ops = &al_dec_ioctl_ops,
> +	.minor = -1,
> +	.release = video_device_release_empty,
> +	.vfl_dir = VFL_DIR_M2M,
> +	.device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
> +};
> +
> +static void al_dec_register_v4l2(void *cb_arg)
> +{
> +	struct al_codec_dev *dev = cb_arg;
> +	struct video_device *video_dev = NULL;
> +	int ret;
> +
> +	ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_dev);
> +	if (ret) {
> +		al_v4l2_err(dev, "Unable to register v4l2 device %d", ret);
> +		return;
> +	}
> +
> +	dev->m2m_dev = v4l2_m2m_init(&al_dec_m2m_ops);
> +	if (IS_ERR(dev->m2m_dev)) {
> +		ret = PTR_ERR(dev->m2m_dev);
> +		al_v4l2_err(dev, "failed to init mem2mem device %d", ret);
> +		goto v4l2_m2m_init_error;
> +	}
> +
> +	video_dev = &dev->video_dev;
> +	*video_dev = al_videodev;
> +
> +	video_dev->lock = &dev->lock;
> +	video_dev->v4l2_dev = &dev->v4l2_dev;
> +
> +	video_set_drvdata(video_dev, dev);
> +	ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1);
> +	if (ret) {
> +		al_v4l2_err(dev, "failed to register video device %d", ret);
> +		goto video_register_device_error;
> +	}
> +
> +	v4l2_info(&dev->v4l2_dev, "registered as /dev/video%d\n",
> +		  dev->video_dev.num);
> +
> +	dev->init_done = true;
> +
> +	return;
> +
> +video_register_device_error:
> +	v4l2_m2m_release(dev->m2m_dev);
> +v4l2_m2m_init_error:
> +	v4l2_device_unregister(&dev->v4l2_dev);
> +}
> +
> +static int al_dec_probe(struct platform_device *pdev)
> +{
> +	struct al_codec_dev *codec;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	const struct al_match_data *match_data;
> +	const char *firmware;
> +	int ret;
> +
> +	dev_info(dev, "Probing ...\n");

Drop, this provide no information.

> +
> +	match_data = device_get_match_data(dev);
> +	if (!match_data) {
> +		dev_err(dev, "Missing device match data\n");
> +		return -EINVAL;
> +	}
> +
> +	codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
> +	if (!codec)
> +		return -ENOMEM;
> +
> +	codec->pdev = pdev;
> +	codec->init_done = false;
> +	codec->ctx_counter = 0;
> +	mutex_init(&codec->lock);
> +	mutex_init(&codec->ctx_mlock);
> +	INIT_LIST_HEAD(&codec->ctx_q_list);
> +
> +	codec->cb_arg = codec;
> +	codec->process_msg_cb = al_dec_process_msg;
> +	codec->fw_ready_cb = al_dec_register_v4l2;
> +
> +	/* firmware-name is optional in DT */
> +	of_property_read_string(np, "firmware-name", &firmware);
> +	if (!firmware)
> +		firmware = match_data->fw_name;
> +
> +	ret = al_common_probe(codec, firmware);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, codec);
> +	al_codec_dbgfs_init(codec);
> +	dev_info(dev, "Probing done successfully %p\n", codec);

Drop, its not really successful until fw_ready_cb is called. This one nicely
give the video node in the trace.

> +
> +	return 0;
> +}
> +
> +static void al_dec_remove(struct platform_device *pdev)
> +{
> +	struct al_codec_dev *dev = platform_get_drvdata(pdev);
> +
> +	dev_info(&pdev->dev, "remove %p\n", dev);

At best debug, this isn't valuable info.

> +
> +	if (dev->init_done) {
> +		video_unregister_device(&dev->video_dev);
> +		if (dev->m2m_dev)
> +			v4l2_m2m_release(dev->m2m_dev);
> +		v4l2_device_unregister(&dev->v4l2_dev);
> +	}
> +	al_codec_dbgfs_deinit(&dev->dbgfs);
> +	al_common_remove(dev);
> +}
> +
> +static const struct al_match_data ald300_data = {
> +	.fw_name = "al300-vdec.fw",
> +};
> +
> +static const struct of_device_id v4l2_al_dec_dt_match[] = {
> +	{ .compatible = "allegro,al300-vdec", .data = &ald300_data },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, v4l2_al_dec_dt_match);
> +
> +static struct platform_driver al300_vdec_drv = {
> +	.probe = al_dec_probe,
> +	.remove = al_dec_remove,
> +	.driver = {
> +		.name = "al300_vdec",
> +		.of_match_table = of_match_ptr(v4l2_al_dec_dt_match),
> +	},
> +};
> +
> +module_platform_driver(al300_vdec_drv);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:al300-vdec");
> +MODULE_AUTHOR("Yassine OUAISSA <yassine.ouaissa@...egrodvt.com>");
> +MODULE_DESCRIPTION("Allegro DVT V4l2 decoder driver gen 3");
> diff --git a/drivers/media/platform/allegro-dvt/al300/al_vdec_drv.h b/drivers/media/platform/allegro-dvt/al300/al_vdec_drv.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..a9f4818bcc94114c02614328c6c6edec33702556
> --- /dev/null
> +++ b/drivers/media/platform/allegro-dvt/al300/al_vdec_drv.h
> @@ -0,0 +1,98 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (c) 2025 Allegro DVT.
> + * Author: Yassine OUAISSA <yassine.ouaissa@...egrodvt.fr>
> + */
> +
> +#ifndef __AL_VDEC_DRV__
> +#define __AL_VDEC_DRV__
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/videobuf2-core.h>
> +#include <media/v4l2-mem2mem.h>
> +
> +#include "al_codec_common.h"
> +
> +enum {
> +	MSG_ITF_TYPE_EVT_RESOLUTION_FOUND = MSG_ITF_TYPE_NEXT_EVT + 1,
> +	MSG_ITF_TYPE_EVT_BITSTREAM_BUFFER_RELEASE,
> +	MSG_ITF_TYPE_EVT_FRAME_BUFFER_DECODE,
> +	MSG_ITF_TYPE_EVT_EOS,
> +	/* Mark the end of the events list.*/
> +	MSG_ITF_TYPE_END_EVT,
> +};
> +
> +struct msg_itf_create_decoder_req {
> +	unsigned int codec;
> +} __packed;
> +DECLARE_FULL_REQ(msg_itf_create_decoder_req);
> +
> +struct msg_itf_evt_resolution_found {
> +	u16 buffer_nb;
> +	u16 width;
> +	u16 height;
> +	u32 pixelformat;
> +	u32 sizeimage;
> +	u32 bytesperline;
> +} __packed;
> +DECLARE_FULL_EVENT(msg_itf_evt_resolution_found);
> +
> +struct msg_itf_evt_bitstream_buffer_release {
> +	u64 bufferHandle;
> +} __packed;
> +DECLARE_FULL_EVENT(msg_itf_evt_bitstream_buffer_release);
> +
> +struct msg_itf_evt_frame_buffer_decode {
> +	u64 bufferHandle;
> +	u64 size;
> +	struct al_buffer_meta meta;
> +} __packed;
> +DECLARE_FULL_EVENT(msg_itf_evt_frame_buffer_decode);
> +
> +struct msg_itf_evt_eos {
> +	u32 unused;
> +} __packed;
> +DECLARE_FULL_EVENT(msg_itf_evt_eos);
> +
> +struct al_fmt {
> +	u32 pixelformat;
> +	u8 bpp;
> +};
> +
> +struct al_frame {
> +	u32 width;
> +	u32 height;
> +	u32 bytesperline;
> +	u32 sizeimage;
> +	u32 nbuffers;
> +	const struct al_fmt *fmt;
> +	enum v4l2_field field;
> +	enum v4l2_colorspace colorspace;
> +	enum v4l2_ycbcr_encoding ycbcr_enc;
> +	enum v4l2_quantization quantization;
> +	enum v4l2_xfer_func xfer_func;
> +};
> +
> +struct al_dec_ctx {
> +	struct al_codec_dev *codec;
> +	struct v4l2_fh fh;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct kref refcount;
> +	struct list_head list;
> +	/* CAP and OUT frames */
> +	struct al_frame src;
> +	struct al_frame dst;
> +	struct completion res_done; /* Resolution found event */
> +	struct list_head cmd_q_list; /* Store active commands */
> +	struct mutex buf_q_mlock;
> +	struct list_head frame_q_list;
> +	struct list_head stream_q_list;
> +	u64 hDec;
> +	u32 csequence;
> +	u32 osequence;
> +	u64 id;
> +	bool stopped;
> +	bool aborting;
> +};
> +
> +#endif /*__AL_VDEC_DRV__*/

This is getting into good shape, looking forward v4.

Nicolas

Download attachment "signature.asc" of type "application/pgp-signature" (229 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ