Audio memory to memory virtual driver use video memory to memory
virtual driver vim2m.c as example. The main difference is
device type is VFL_TYPE_AUDIO and device cap type is V4L2_CAP_AUDIO_M2M.
The device_run function is a dummy function, which is simply
copy the data from input buffer to output buffer.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
drivers/media/test-drivers/Kconfig | 9 +
drivers/media/test-drivers/Makefile | 1 +
drivers/media/test-drivers/vim2m_audio.c | 680 +++++++++++++++++++++++
3 files changed, 690 insertions(+)
create mode 100644 drivers/media/test-drivers/vim2m_audio.c
diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig
index 459b433e9fae..c280e192d43a 100644
--- a/drivers/media/test-drivers/Kconfig
+++ b/drivers/media/test-drivers/Kconfig
@@ -17,6 +17,15 @@ config VIDEO_VIM2M
This is a virtual test device for the memory-to-memory driver
framework.
+config VIDEO_VIM2M_AUDIO
+ tristate "Virtual Memory-to-Memory Driver For Audio"
+ depends on VIDEO_DEV
+ select VIDEOBUF2_VMALLOC
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a virtual audio test device for the memory-to-memory driver
+ framework.
+
source "drivers/media/test-drivers/vicodec/Kconfig"
source "drivers/media/test-drivers/vimc/Kconfig"
source "drivers/media/test-drivers/vivid/Kconfig"
diff --git a/drivers/media/test-drivers/Makefile b/drivers/media/test-drivers/Makefile
index 740714a4584d..c2c33282bf96 100644
--- a/drivers/media/test-drivers/Makefile
+++ b/drivers/media/test-drivers/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DVB_VIDTV) += vidtv/
obj-$(CONFIG_VIDEO_VICODEC) += vicodec/
obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o
+obj-$(CONFIG_VIDEO_VIM2M_AUDIO) += vim2m_audio.o
obj-$(CONFIG_VIDEO_VIMC) += vimc/
obj-$(CONFIG_VIDEO_VIVID) += vivid/
obj-$(CONFIG_VIDEO_VISL) += visl/
diff --git a/drivers/media/test-drivers/vim2m_audio.c b/drivers/media/test-drivers/vim2m_audio.c
new file mode 100644
index 000000000000..2134e8338417
--- /dev/null
+++ b/drivers/media/test-drivers/vim2m_audio.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * A virtual v4l2-mem2mem example for audio device.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+#include <sound/dmaengine_pcm.h>
+
+MODULE_DESCRIPTION("Virtual device for audio mem2mem testing");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "debug level");
+
+#define MEM2MEM_NAME "vim2m-audio"
+
+#define dprintk(dev, lvl, fmt, arg...) \
+ v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+#define SAMPLE_NUM 4096
+
+static void audm2m_dev_release(struct device *dev)
+{}
+
+static struct platform_device audm2m_pdev = {
+ .name = MEM2MEM_NAME,
+ .dev.release = audm2m_dev_release,
+};
+
+static u32 formats[] = {
+ V4L2_AUDIO_FMT_S8,
+ V4L2_AUDIO_FMT_S16_LE,
+ V4L2_AUDIO_FMT_U16_LE,
+ V4L2_AUDIO_FMT_S24_LE,
+ V4L2_AUDIO_FMT_S24_3LE,
+ V4L2_AUDIO_FMT_U24_LE,
+ V4L2_AUDIO_FMT_U24_3LE,
+ V4L2_AUDIO_FMT_S32_LE,
+ V4L2_AUDIO_FMT_U32_LE,
+ V4L2_AUDIO_FMT_S20_3LE,
+ V4L2_AUDIO_FMT_U20_3LE,
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+/* Per-queue, driver-specific private data */
+struct audm2m_q_data {
+ unsigned int rate;
+ unsigned int channels;
+ unsigned int buffersize;
+ u32 fourcc;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+static snd_pcm_format_t find_format(u32 fourcc)
+{
+ snd_pcm_format_t fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ if (formats[k] == fourcc)
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return 0;
+
+ fmt = v4l2_fourcc_to_audfmt(formats[k]);
+
+ return fmt;
+}
+
+struct audm2m_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vfd;
+
+ struct mutex dev_mutex;
+
+ struct v4l2_m2m_dev *m2m_dev;
+};
+
+struct audm2m_ctx {
+ struct v4l2_fh fh;
+ struct audm2m_dev *dev;
+
+ struct mutex vb_mutex;
+
+ /* Source and destination queue data */
+ struct audm2m_q_data q_data[2];
+};
+
+static inline struct audm2m_ctx *file2ctx(struct file *file)
+{
+ return container_of(file->private_data, struct audm2m_ctx, fh);
+}
+
+static struct audm2m_q_data *get_q_data(struct audm2m_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (type == V4L2_BUF_TYPE_AUDIO_OUTPUT)
+ return &ctx->q_data[V4L2_M2M_SRC];
+ return &ctx->q_data[V4L2_M2M_DST];
+}
+
+static const char *type_name(enum v4l2_buf_type type)
+{
+ if (type == V4L2_BUF_TYPE_AUDIO_OUTPUT)
+ return "Output";
+ return "Capture";
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/*
+ * device_run() - prepares and starts the device
+ */
+static void device_run(void *priv)
+{
+ struct audm2m_ctx *ctx = priv;
+ struct audm2m_dev *audm2m_dev;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ struct audm2m_q_data *q_data_src, *q_data_dst;
+ int src_size, dst_size;
+
+ audm2m_dev = ctx->dev;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_AUDIO_OUTPUT);
+ if (!q_data_src)
+ return;
+
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_AUDIO_CAPTURE);
+ if (!q_data_dst)
+ return;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ /* Process the conversion */
+ src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+ if (src_size > q_data_dst->buffersize)
+ dst_size = q_data_dst->buffersize;
+ else
+ dst_size = src_size;
+
+ memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0),
+ vb2_plane_vaddr(&src_buf->vb2_buf, 0),
+ dst_size);
+
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, dst_size);
+
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+ v4l2_m2m_job_finish(audm2m_dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static int audm2m_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver));
+ strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", MEM2MEM_NAME);
+
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f)
+{
+ int i, num;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (num == f->index)
+ break;
+ /*
+ * Correct type but haven't reached our index yet,
+ * just increment per-type index
+ */
+ ++num;
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ f->pixelformat = formats[i];
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int audm2m_enum_fmt_audio_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f);
+}
+
+static int audm2m_enum_fmt_audio_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f);
+}
+
+static int audm2m_g_fmt(struct audm2m_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct audm2m_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ f->fmt.audio.audioformat = q_data->fourcc;
+ f->fmt.audio.channels = q_data->channels;
+ f->fmt.audio.buffersize = q_data->buffersize;
+
+ return 0;
+}
+
+static int audm2m_g_fmt_audio_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return audm2m_g_fmt(file2ctx(file), f);
+}
+
+static int audm2m_g_fmt_audio_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return audm2m_g_fmt(file2ctx(file), f);
+}
+
+static int audm2m_try_fmt(struct v4l2_format *f, snd_pcm_format_t fmt)
+{
+ if (f->fmt.audio.channels < 1)
+ f->fmt.audio.channels = 1;
+ else if (f->fmt.audio.channels > 8)
+ f->fmt.audio.channels = 8;
+
+ f->fmt.audio.buffersize = f->fmt.audio.channels *
+ snd_pcm_format_physical_width(fmt) *
+ SAMPLE_NUM;
+ return 0;
+}
+
+static int audm2m_try_fmt_audio_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ snd_pcm_format_t fmt;
+
+ fmt = find_format(f->fmt.audio.audioformat);
+ if (!fmt) {
+ f->fmt.audio.audioformat = formats[0];
+ fmt = find_format(f->fmt.audio.audioformat);
+ }
+
+ return audm2m_try_fmt(f, fmt);
+}
+
+static int audm2m_try_fmt_audio_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ snd_pcm_format_t fmt;
+
+ fmt = find_format(f->fmt.audio.audioformat);
+ if (!fmt) {
+ f->fmt.audio.audioformat = formats[0];
+ fmt = find_format(f->fmt.audio.audioformat);
+ }
+
+ return audm2m_try_fmt(f, fmt);
+}
+
+static int audm2m_s_fmt(struct audm2m_ctx *ctx, struct v4l2_format *f)
+{
+ struct audm2m_q_data *q_data;
+ struct vb2_queue *vq;
+ snd_pcm_format_t fmt;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ q_data->fourcc = f->fmt.audio.audioformat;
+ q_data->channels = f->fmt.audio.channels;
+
+ fmt = find_format(f->fmt.audio.audioformat);
+ q_data->buffersize = q_data->channels *
+ snd_pcm_format_physical_width(fmt) *
+ SAMPLE_NUM;
+
+ dprintk(ctx->dev, 1,
+ "Format for type %s: %d/%d, fmt: %c%c%c%c\n",
+ type_name(f->type), q_data->rate,
+ q_data->channels,
+ (q_data->fourcc & 0xff),
+ (q_data->fourcc >> 8) & 0xff,
+ (q_data->fourcc >> 16) & 0xff,
+ (q_data->fourcc >> 24) & 0xff);
+
+ return 0;
+}
+
+static int audm2m_s_fmt_audio_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = audm2m_try_fmt_audio_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return audm2m_s_fmt(file2ctx(file), f);
+}
+
+static int audm2m_s_fmt_audio_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = audm2m_try_fmt_audio_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return audm2m_s_fmt(file2ctx(file), f);
+}
+
+static const struct v4l2_ioctl_ops audm2m_ioctl_ops = {
+ .vidioc_querycap = audm2m_querycap,
+
+ .vidioc_enum_fmt_audio_cap = audm2m_enum_fmt_audio_cap,
+ .vidioc_g_fmt_audio_cap = audm2m_g_fmt_audio_cap,
+ .vidioc_try_fmt_audio_cap = audm2m_try_fmt_audio_cap,
+ .vidioc_s_fmt_audio_cap = audm2m_s_fmt_audio_cap,
+
+ .vidioc_enum_fmt_audio_out = audm2m_enum_fmt_audio_out,
+ .vidioc_g_fmt_audio_out = audm2m_g_fmt_audio_out,
+ .vidioc_try_fmt_audio_out = audm2m_try_fmt_audio_out,
+ .vidioc_s_fmt_audio_out = audm2m_s_fmt_audio_out,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .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_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * Queue operations
+ */
+static int audm2m_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct audm2m_ctx *ctx = vb2_get_drv_priv(vq);
+ struct audm2m_q_data *q_data;
+
+ q_data = get_q_data(ctx, vq->type);
+
+ if (*nplanes)
+ return sizes[0] < q_data->buffersize ? -EINVAL : 0;
+
+ *nplanes = 1;
+ sizes[0] = q_data->buffersize;
+
+ dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n",
+ type_name(vq->type), *nplanes, sizes[0]);
+
+ return 0;
+}
+
+static void audm2m_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct audm2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static void audm2m_stop_streaming(struct vb2_queue *q)
+{
+ struct audm2m_ctx *ctx = vb2_get_drv_priv(q);
+ struct vb2_v4l2_buffer *vbuf;
+
+ for (;;) {
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (!vbuf)
+ return;
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops audm2m_qops = {
+ .queue_setup = audm2m_queue_setup,
+ .buf_queue = audm2m_buf_queue,
+ .stop_streaming = audm2m_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct audm2m_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_AUDIO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &audm2m_qops;
+ src_vq->mem_ops = &vb2_vmalloc_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->vb_mutex;
+ src_vq->min_buffers_needed = 1;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_AUDIO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &audm2m_qops;
+ dst_vq->mem_ops = &vb2_vmalloc_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->vb_mutex;
+ dst_vq->min_buffers_needed = 1;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int audm2m_open(struct file *file)
+{
+ struct audm2m_dev *dev = video_drvdata(file);
+ struct audm2m_ctx *ctx = NULL;
+ snd_pcm_format_t fmt;
+ int width;
+ int rc = 0;
+
+ if (mutex_lock_interruptible(&dev->dev_mutex))
+ return -ERESTARTSYS;
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto open_unlock;
+ }
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ ctx->dev = dev;
+
+ ctx->q_data[V4L2_M2M_SRC].fourcc = formats[0];
+ ctx->q_data[V4L2_M2M_SRC].rate = 8000;
+ ctx->q_data[V4L2_M2M_SRC].channels = 2;
+
+ /* Fix to 4096 samples */
+ fmt = find_format(formats[0]);
+ width = snd_pcm_format_physical_width(fmt);
+ ctx->q_data[V4L2_M2M_SRC].buffersize = SAMPLE_NUM * 2 * width;
+ ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
+
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
+ mutex_init(&ctx->vb_mutex);
+
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ rc = PTR_ERR(ctx->fh.m2m_ctx);
+
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ goto open_unlock;
+ }
+
+ v4l2_fh_add(&ctx->fh);
+
+ dprintk(dev, 1, "Created instance: %p, m2m_ctx: %p\n",
+ ctx, ctx->fh.m2m_ctx);
+
+open_unlock:
+ mutex_unlock(&dev->dev_mutex);
+ return rc;
+}
+
+static int audm2m_release(struct file *file)
+{
+ struct audm2m_dev *dev = video_drvdata(file);
+ struct audm2m_ctx *ctx = file2ctx(file);
+
+ dprintk(dev, 1, "Releasing instance %p\n", ctx);
+
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ mutex_lock(&dev->dev_mutex);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ mutex_unlock(&dev->dev_mutex);
+ kfree(ctx);
+
+ return 0;
+}
+
+static void audm2m_device_release(struct video_device *vdev)
+{
+ struct audm2m_dev *dev = container_of(vdev, struct audm2m_dev, vfd);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+ v4l2_m2m_release(dev->m2m_dev);
+
+ kfree(dev);
+}
+
+static const struct v4l2_file_operations audm2m_fops = {
+ .owner = THIS_MODULE,
+ .open = audm2m_open,
+ .release = audm2m_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct video_device audm2m_videodev = {
+ .name = MEM2MEM_NAME,
+ .vfl_dir = VFL_DIR_M2M,
+ .fops = &audm2m_fops,
+ .ioctl_ops = &audm2m_ioctl_ops,
+ .minor = -1,
+ .release = audm2m_device_release,
+ .device_caps = V4L2_CAP_AUDIO_M2M | V4L2_CAP_STREAMING,
+};
+
+static const struct v4l2_m2m_ops m2m_ops = {
+ .device_run = device_run,
+};
+
+static int audm2m_probe(struct platform_device *pdev)
+{
+ struct audm2m_dev *dev;
+ struct video_device *vfd;
+ int ret;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto error_free;
+
+ mutex_init(&dev->dev_mutex);
+
+ dev->vfd = audm2m_videodev;
+ vfd = &dev->vfd;
+ vfd->lock = &dev->dev_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+
+ video_set_drvdata(vfd, dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+ dev->m2m_dev = NULL;
+ goto error_dev;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_AUDIO, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto error_m2m;
+ }
+
+ v4l2_info(&dev->v4l2_dev,
+ "Device registered as /dev/v4l-audio%d\n", vfd->num);
+
+ return 0;
+
+error_m2m:
+ v4l2_m2m_release(dev->m2m_dev);
+error_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+error_free:
+ kfree(dev);
+
+ return ret;
+}
+
+static void audm2m_remove(struct platform_device *pdev)
+{
+ struct audm2m_dev *dev = platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME);
+
+ video_unregister_device(&dev->vfd);
+}
+
+static struct platform_driver audm2m_pdrv = {
+ .probe = audm2m_probe,
+ .remove_new = audm2m_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ },
+};
+
+static void __exit audm2m_exit(void)
+{
+ platform_driver_unregister(&audm2m_pdrv);
+ platform_device_unregister(&audm2m_pdev);
+}
+
+static int __init audm2m_init(void)
+{
+ int ret;
+
+ ret = platform_device_register(&audm2m_pdev);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&audm2m_pdrv);
+ if (ret)
+ platform_device_unregister(&audm2m_pdev);
+
+ return ret;
+}
+
+module_init(audm2m_init);
+module_exit(audm2m_exit);
--
2.34.1
On 27/10/2023 12:35, Shengjiu Wang wrote:
> Audio memory to memory virtual driver use video memory to memory
> virtual driver vim2m.c as example. The main difference is
> device type is VFL_TYPE_AUDIO and device cap type is V4L2_CAP_AUDIO_M2M.
>
> The device_run function is a dummy function, which is simply
> copy the data from input buffer to output buffer.
I started work on the v4l-utils part of this, using this driver.
I noticed that this driver doesn't expose the V4L2_CID_M2M_AUDIO_SOURCE/SINK_RATE
controls, and it really should, otherwise it is not representative of this
type of device.
It is enough to start with just a single fixed rate listed for each control.
It would be even nicer if you can have two rates such as 24000 and 48000 and
do the actual rate conversion, i.e. dropping every other sample or duplicating
each sample depending on whether you're halving or doubling the rate. That
should be easy to implement, and it makes this driver much more realistic.
Regards,
Hans
>
> Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
> ---
> drivers/media/test-drivers/Kconfig | 9 +
> drivers/media/test-drivers/Makefile | 1 +
> drivers/media/test-drivers/vim2m_audio.c | 680 +++++++++++++++++++++++
> 3 files changed, 690 insertions(+)
> create mode 100644 drivers/media/test-drivers/vim2m_audio.c
>
> diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig
> index 459b433e9fae..c280e192d43a 100644
> --- a/drivers/media/test-drivers/Kconfig
> +++ b/drivers/media/test-drivers/Kconfig
> @@ -17,6 +17,15 @@ config VIDEO_VIM2M
> This is a virtual test device for the memory-to-memory driver
> framework.
>
> +config VIDEO_VIM2M_AUDIO
> + tristate "Virtual Memory-to-Memory Driver For Audio"
> + depends on VIDEO_DEV
> + select VIDEOBUF2_VMALLOC
> + select V4L2_MEM2MEM_DEV
> + help
> + This is a virtual audio test device for the memory-to-memory driver
> + framework.
> +
> source "drivers/media/test-drivers/vicodec/Kconfig"
> source "drivers/media/test-drivers/vimc/Kconfig"
> source "drivers/media/test-drivers/vivid/Kconfig"
> diff --git a/drivers/media/test-drivers/Makefile b/drivers/media/test-drivers/Makefile
> index 740714a4584d..c2c33282bf96 100644
> --- a/drivers/media/test-drivers/Makefile
> +++ b/drivers/media/test-drivers/Makefile
> @@ -10,6 +10,7 @@ obj-$(CONFIG_DVB_VIDTV) += vidtv/
>
> obj-$(CONFIG_VIDEO_VICODEC) += vicodec/
> obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o
> +obj-$(CONFIG_VIDEO_VIM2M_AUDIO) += vim2m_audio.o
> obj-$(CONFIG_VIDEO_VIMC) += vimc/
> obj-$(CONFIG_VIDEO_VIVID) += vivid/
> obj-$(CONFIG_VIDEO_VISL) += visl/
> diff --git a/drivers/media/test-drivers/vim2m_audio.c b/drivers/media/test-drivers/vim2m_audio.c
> new file mode 100644
> index 000000000000..2134e8338417
> --- /dev/null
> +++ b/drivers/media/test-drivers/vim2m_audio.c
> @@ -0,0 +1,680 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * A virtual v4l2-mem2mem example for audio device.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +
> +#include <linux/platform_device.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-vmalloc.h>
> +#include <sound/dmaengine_pcm.h>
> +
> +MODULE_DESCRIPTION("Virtual device for audio mem2mem testing");
> +MODULE_LICENSE("GPL");
> +
> +static unsigned int debug;
> +module_param(debug, uint, 0644);
> +MODULE_PARM_DESC(debug, "debug level");
> +
> +#define MEM2MEM_NAME "vim2m-audio"
> +
> +#define dprintk(dev, lvl, fmt, arg...) \
> + v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg)
> +
> +#define SAMPLE_NUM 4096
> +
> +static void audm2m_dev_release(struct device *dev)
> +{}
> +
> +static struct platform_device audm2m_pdev = {
> + .name = MEM2MEM_NAME,
> + .dev.release = audm2m_dev_release,
> +};
> +
> +static u32 formats[] = {
> + V4L2_AUDIO_FMT_S8,
> + V4L2_AUDIO_FMT_S16_LE,
> + V4L2_AUDIO_FMT_U16_LE,
> + V4L2_AUDIO_FMT_S24_LE,
> + V4L2_AUDIO_FMT_S24_3LE,
> + V4L2_AUDIO_FMT_U24_LE,
> + V4L2_AUDIO_FMT_U24_3LE,
> + V4L2_AUDIO_FMT_S32_LE,
> + V4L2_AUDIO_FMT_U32_LE,
> + V4L2_AUDIO_FMT_S20_3LE,
> + V4L2_AUDIO_FMT_U20_3LE,
> +};
> +
> +#define NUM_FORMATS ARRAY_SIZE(formats)
> +
> +/* Per-queue, driver-specific private data */
> +struct audm2m_q_data {
> + unsigned int rate;
> + unsigned int channels;
> + unsigned int buffersize;
> + u32 fourcc;
> +};
> +
> +enum {
> + V4L2_M2M_SRC = 0,
> + V4L2_M2M_DST = 1,
> +};
> +
> +static snd_pcm_format_t find_format(u32 fourcc)
> +{
> + snd_pcm_format_t fmt;
> + unsigned int k;
> +
> + for (k = 0; k < NUM_FORMATS; k++) {
> + if (formats[k] == fourcc)
> + break;
> + }
> +
> + if (k == NUM_FORMATS)
> + return 0;
> +
> + fmt = v4l2_fourcc_to_audfmt(formats[k]);
> +
> + return fmt;
> +}
> +
> +struct audm2m_dev {
> + struct v4l2_device v4l2_dev;
> + struct video_device vfd;
> +
> + struct mutex dev_mutex;
> +
> + struct v4l2_m2m_dev *m2m_dev;
> +};
> +
> +struct audm2m_ctx {
> + struct v4l2_fh fh;
> + struct audm2m_dev *dev;
> +
> + struct mutex vb_mutex;
> +
> + /* Source and destination queue data */
> + struct audm2m_q_data q_data[2];
> +};
> +
> +static inline struct audm2m_ctx *file2ctx(struct file *file)
> +{
> + return container_of(file->private_data, struct audm2m_ctx, fh);
> +}
> +
> +static struct audm2m_q_data *get_q_data(struct audm2m_ctx *ctx,
> + enum v4l2_buf_type type)
> +{
> + if (type == V4L2_BUF_TYPE_AUDIO_OUTPUT)
> + return &ctx->q_data[V4L2_M2M_SRC];
> + return &ctx->q_data[V4L2_M2M_DST];
> +}
> +
> +static const char *type_name(enum v4l2_buf_type type)
> +{
> + if (type == V4L2_BUF_TYPE_AUDIO_OUTPUT)
> + return "Output";
> + return "Capture";
> +}
> +
> +/*
> + * mem2mem callbacks
> + */
> +
> +/*
> + * device_run() - prepares and starts the device
> + */
> +static void device_run(void *priv)
> +{
> + struct audm2m_ctx *ctx = priv;
> + struct audm2m_dev *audm2m_dev;
> + struct vb2_v4l2_buffer *src_buf, *dst_buf;
> + struct audm2m_q_data *q_data_src, *q_data_dst;
> + int src_size, dst_size;
> +
> + audm2m_dev = ctx->dev;
> +
> + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_AUDIO_OUTPUT);
> + if (!q_data_src)
> + return;
> +
> + q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_AUDIO_CAPTURE);
> + if (!q_data_dst)
> + return;
> +
> + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
> + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
> +
> + /* Process the conversion */
> + src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
> +
> + if (src_size > q_data_dst->buffersize)
> + dst_size = q_data_dst->buffersize;
> + else
> + dst_size = src_size;
> +
> + memcpy(vb2_plane_vaddr(&dst_buf->vb2_buf, 0),
> + vb2_plane_vaddr(&src_buf->vb2_buf, 0),
> + dst_size);
> +
> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, dst_size);
> +
> + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> +
> + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
> + v4l2_m2m_job_finish(audm2m_dev->m2m_dev, ctx->fh.m2m_ctx);
> +}
> +
> +static int audm2m_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + strscpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver));
> + strscpy(cap->card, MEM2MEM_NAME, sizeof(cap->card));
> + snprintf(cap->bus_info, sizeof(cap->bus_info),
> + "platform:%s", MEM2MEM_NAME);
> +
> + return 0;
> +}
> +
> +static int enum_fmt(struct v4l2_fmtdesc *f)
> +{
> + int i, num;
> +
> + num = 0;
> +
> + for (i = 0; i < NUM_FORMATS; ++i) {
> + if (num == f->index)
> + break;
> + /*
> + * Correct type but haven't reached our index yet,
> + * just increment per-type index
> + */
> + ++num;
> + }
> +
> + if (i < NUM_FORMATS) {
> + /* Format found */
> + f->pixelformat = formats[i];
> + return 0;
> + }
> +
> + /* Format not found */
> + return -EINVAL;
> +}
> +
> +static int audm2m_enum_fmt_audio_cap(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + return enum_fmt(f);
> +}
> +
> +static int audm2m_enum_fmt_audio_out(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + return enum_fmt(f);
> +}
> +
> +static int audm2m_g_fmt(struct audm2m_ctx *ctx, struct v4l2_format *f)
> +{
> + struct vb2_queue *vq;
> + struct audm2m_q_data *q_data;
> +
> + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> + if (!vq)
> + return -EINVAL;
> +
> + q_data = get_q_data(ctx, f->type);
> + if (!q_data)
> + return -EINVAL;
> +
> + f->fmt.audio.audioformat = q_data->fourcc;
> + f->fmt.audio.channels = q_data->channels;
> + f->fmt.audio.buffersize = q_data->buffersize;
> +
> + return 0;
> +}
> +
> +static int audm2m_g_fmt_audio_out(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + return audm2m_g_fmt(file2ctx(file), f);
> +}
> +
> +static int audm2m_g_fmt_audio_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + return audm2m_g_fmt(file2ctx(file), f);
> +}
> +
> +static int audm2m_try_fmt(struct v4l2_format *f, snd_pcm_format_t fmt)
> +{
> + if (f->fmt.audio.channels < 1)
> + f->fmt.audio.channels = 1;
> + else if (f->fmt.audio.channels > 8)
> + f->fmt.audio.channels = 8;
> +
> + f->fmt.audio.buffersize = f->fmt.audio.channels *
> + snd_pcm_format_physical_width(fmt) *
> + SAMPLE_NUM;
> + return 0;
> +}
> +
> +static int audm2m_try_fmt_audio_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + snd_pcm_format_t fmt;
> +
> + fmt = find_format(f->fmt.audio.audioformat);
> + if (!fmt) {
> + f->fmt.audio.audioformat = formats[0];
> + fmt = find_format(f->fmt.audio.audioformat);
> + }
> +
> + return audm2m_try_fmt(f, fmt);
> +}
> +
> +static int audm2m_try_fmt_audio_out(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + snd_pcm_format_t fmt;
> +
> + fmt = find_format(f->fmt.audio.audioformat);
> + if (!fmt) {
> + f->fmt.audio.audioformat = formats[0];
> + fmt = find_format(f->fmt.audio.audioformat);
> + }
> +
> + return audm2m_try_fmt(f, fmt);
> +}
> +
> +static int audm2m_s_fmt(struct audm2m_ctx *ctx, struct v4l2_format *f)
> +{
> + struct audm2m_q_data *q_data;
> + struct vb2_queue *vq;
> + snd_pcm_format_t fmt;
> +
> + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
> + if (!vq)
> + return -EINVAL;
> +
> + q_data = get_q_data(ctx, f->type);
> + if (!q_data)
> + return -EINVAL;
> +
> + if (vb2_is_busy(vq)) {
> + v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + q_data->fourcc = f->fmt.audio.audioformat;
> + q_data->channels = f->fmt.audio.channels;
> +
> + fmt = find_format(f->fmt.audio.audioformat);
> + q_data->buffersize = q_data->channels *
> + snd_pcm_format_physical_width(fmt) *
> + SAMPLE_NUM;
> +
> + dprintk(ctx->dev, 1,
> + "Format for type %s: %d/%d, fmt: %c%c%c%c\n",
> + type_name(f->type), q_data->rate,
> + q_data->channels,
> + (q_data->fourcc & 0xff),
> + (q_data->fourcc >> 8) & 0xff,
> + (q_data->fourcc >> 16) & 0xff,
> + (q_data->fourcc >> 24) & 0xff);
> +
> + return 0;
> +}
> +
> +static int audm2m_s_fmt_audio_cap(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + int ret;
> +
> + ret = audm2m_try_fmt_audio_cap(file, priv, f);
> + if (ret)
> + return ret;
> +
> + return audm2m_s_fmt(file2ctx(file), f);
> +}
> +
> +static int audm2m_s_fmt_audio_out(struct file *file, void *priv,
> + struct v4l2_format *f)
> +{
> + int ret;
> +
> + ret = audm2m_try_fmt_audio_out(file, priv, f);
> + if (ret)
> + return ret;
> +
> + return audm2m_s_fmt(file2ctx(file), f);
> +}
> +
> +static const struct v4l2_ioctl_ops audm2m_ioctl_ops = {
> + .vidioc_querycap = audm2m_querycap,
> +
> + .vidioc_enum_fmt_audio_cap = audm2m_enum_fmt_audio_cap,
> + .vidioc_g_fmt_audio_cap = audm2m_g_fmt_audio_cap,
> + .vidioc_try_fmt_audio_cap = audm2m_try_fmt_audio_cap,
> + .vidioc_s_fmt_audio_cap = audm2m_s_fmt_audio_cap,
> +
> + .vidioc_enum_fmt_audio_out = audm2m_enum_fmt_audio_out,
> + .vidioc_g_fmt_audio_out = audm2m_g_fmt_audio_out,
> + .vidioc_try_fmt_audio_out = audm2m_try_fmt_audio_out,
> + .vidioc_s_fmt_audio_out = audm2m_s_fmt_audio_out,
> +
> + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> + .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_create_bufs = v4l2_m2m_ioctl_create_bufs,
> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> +
> + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> +
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +/*
> + * Queue operations
> + */
> +static int audm2m_queue_setup(struct vb2_queue *vq,
> + unsigned int *nbuffers,
> + unsigned int *nplanes,
> + unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + struct audm2m_ctx *ctx = vb2_get_drv_priv(vq);
> + struct audm2m_q_data *q_data;
> +
> + q_data = get_q_data(ctx, vq->type);
> +
> + if (*nplanes)
> + return sizes[0] < q_data->buffersize ? -EINVAL : 0;
> +
> + *nplanes = 1;
> + sizes[0] = q_data->buffersize;
> +
> + dprintk(ctx->dev, 1, "%s: get %d buffer(s) of size %d each.\n",
> + type_name(vq->type), *nplanes, sizes[0]);
> +
> + return 0;
> +}
> +
> +static void audm2m_buf_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct audm2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
> +
> + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> +}
> +
> +static void audm2m_stop_streaming(struct vb2_queue *q)
> +{
> + struct audm2m_ctx *ctx = vb2_get_drv_priv(q);
> + struct vb2_v4l2_buffer *vbuf;
> +
> + for (;;) {
> + if (V4L2_TYPE_IS_OUTPUT(q->type))
> + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> + else
> + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> + if (!vbuf)
> + return;
> + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> + }
> +}
> +
> +static const struct vb2_ops audm2m_qops = {
> + .queue_setup = audm2m_queue_setup,
> + .buf_queue = audm2m_buf_queue,
> + .stop_streaming = audm2m_stop_streaming,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> +};
> +
> +static int queue_init(void *priv, struct vb2_queue *src_vq,
> + struct vb2_queue *dst_vq)
> +{
> + struct audm2m_ctx *ctx = priv;
> + int ret;
> +
> + src_vq->type = V4L2_BUF_TYPE_AUDIO_OUTPUT;
> + src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> + src_vq->drv_priv = ctx;
> + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> + src_vq->ops = &audm2m_qops;
> + src_vq->mem_ops = &vb2_vmalloc_memops;
> + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + src_vq->lock = &ctx->vb_mutex;
> + src_vq->min_buffers_needed = 1;
> +
> + ret = vb2_queue_init(src_vq);
> + if (ret)
> + return ret;
> +
> + dst_vq->type = V4L2_BUF_TYPE_AUDIO_CAPTURE;
> + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> + dst_vq->drv_priv = ctx;
> + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
> + dst_vq->ops = &audm2m_qops;
> + dst_vq->mem_ops = &vb2_vmalloc_memops;
> + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + dst_vq->lock = &ctx->vb_mutex;
> + dst_vq->min_buffers_needed = 1;
> +
> + return vb2_queue_init(dst_vq);
> +}
> +
> +/*
> + * File operations
> + */
> +static int audm2m_open(struct file *file)
> +{
> + struct audm2m_dev *dev = video_drvdata(file);
> + struct audm2m_ctx *ctx = NULL;
> + snd_pcm_format_t fmt;
> + int width;
> + int rc = 0;
> +
> + if (mutex_lock_interruptible(&dev->dev_mutex))
> + return -ERESTARTSYS;
> + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> + if (!ctx) {
> + rc = -ENOMEM;
> + goto open_unlock;
> + }
> +
> + v4l2_fh_init(&ctx->fh, video_devdata(file));
> + file->private_data = &ctx->fh;
> + ctx->dev = dev;
> +
> + ctx->q_data[V4L2_M2M_SRC].fourcc = formats[0];
> + ctx->q_data[V4L2_M2M_SRC].rate = 8000;
> + ctx->q_data[V4L2_M2M_SRC].channels = 2;
> +
> + /* Fix to 4096 samples */
> + fmt = find_format(formats[0]);
> + width = snd_pcm_format_physical_width(fmt);
> + ctx->q_data[V4L2_M2M_SRC].buffersize = SAMPLE_NUM * 2 * width;
> + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
> +
> + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
> +
> + mutex_init(&ctx->vb_mutex);
> +
> + if (IS_ERR(ctx->fh.m2m_ctx)) {
> + rc = PTR_ERR(ctx->fh.m2m_ctx);
> +
> + v4l2_fh_exit(&ctx->fh);
> + kfree(ctx);
> + goto open_unlock;
> + }
> +
> + v4l2_fh_add(&ctx->fh);
> +
> + dprintk(dev, 1, "Created instance: %p, m2m_ctx: %p\n",
> + ctx, ctx->fh.m2m_ctx);
> +
> +open_unlock:
> + mutex_unlock(&dev->dev_mutex);
> + return rc;
> +}
> +
> +static int audm2m_release(struct file *file)
> +{
> + struct audm2m_dev *dev = video_drvdata(file);
> + struct audm2m_ctx *ctx = file2ctx(file);
> +
> + dprintk(dev, 1, "Releasing instance %p\n", ctx);
> +
> + v4l2_fh_del(&ctx->fh);
> + v4l2_fh_exit(&ctx->fh);
> + mutex_lock(&dev->dev_mutex);
> + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> + mutex_unlock(&dev->dev_mutex);
> + kfree(ctx);
> +
> + return 0;
> +}
> +
> +static void audm2m_device_release(struct video_device *vdev)
> +{
> + struct audm2m_dev *dev = container_of(vdev, struct audm2m_dev, vfd);
> +
> + v4l2_device_unregister(&dev->v4l2_dev);
> + v4l2_m2m_release(dev->m2m_dev);
> +
> + kfree(dev);
> +}
> +
> +static const struct v4l2_file_operations audm2m_fops = {
> + .owner = THIS_MODULE,
> + .open = audm2m_open,
> + .release = audm2m_release,
> + .poll = v4l2_m2m_fop_poll,
> + .unlocked_ioctl = video_ioctl2,
> + .mmap = v4l2_m2m_fop_mmap,
> +};
> +
> +static const struct video_device audm2m_videodev = {
> + .name = MEM2MEM_NAME,
> + .vfl_dir = VFL_DIR_M2M,
> + .fops = &audm2m_fops,
> + .ioctl_ops = &audm2m_ioctl_ops,
> + .minor = -1,
> + .release = audm2m_device_release,
> + .device_caps = V4L2_CAP_AUDIO_M2M | V4L2_CAP_STREAMING,
> +};
> +
> +static const struct v4l2_m2m_ops m2m_ops = {
> + .device_run = device_run,
> +};
> +
> +static int audm2m_probe(struct platform_device *pdev)
> +{
> + struct audm2m_dev *dev;
> + struct video_device *vfd;
> + int ret;
> +
> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> + if (ret)
> + goto error_free;
> +
> + mutex_init(&dev->dev_mutex);
> +
> + dev->vfd = audm2m_videodev;
> + vfd = &dev->vfd;
> + vfd->lock = &dev->dev_mutex;
> + vfd->v4l2_dev = &dev->v4l2_dev;
> +
> + video_set_drvdata(vfd, dev);
> + platform_set_drvdata(pdev, dev);
> +
> + dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
> + if (IS_ERR(dev->m2m_dev)) {
> + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
> + ret = PTR_ERR(dev->m2m_dev);
> + dev->m2m_dev = NULL;
> + goto error_dev;
> + }
> +
> + ret = video_register_device(vfd, VFL_TYPE_AUDIO, 0);
> + if (ret) {
> + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
> + goto error_m2m;
> + }
> +
> + v4l2_info(&dev->v4l2_dev,
> + "Device registered as /dev/v4l-audio%d\n", vfd->num);
> +
> + return 0;
> +
> +error_m2m:
> + v4l2_m2m_release(dev->m2m_dev);
> +error_dev:
> + v4l2_device_unregister(&dev->v4l2_dev);
> +error_free:
> + kfree(dev);
> +
> + return ret;
> +}
> +
> +static void audm2m_remove(struct platform_device *pdev)
> +{
> + struct audm2m_dev *dev = platform_get_drvdata(pdev);
> +
> + v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME);
> +
> + video_unregister_device(&dev->vfd);
> +}
> +
> +static struct platform_driver audm2m_pdrv = {
> + .probe = audm2m_probe,
> + .remove_new = audm2m_remove,
> + .driver = {
> + .name = MEM2MEM_NAME,
> + },
> +};
> +
> +static void __exit audm2m_exit(void)
> +{
> + platform_driver_unregister(&audm2m_pdrv);
> + platform_device_unregister(&audm2m_pdev);
> +}
> +
> +static int __init audm2m_init(void)
> +{
> + int ret;
> +
> + ret = platform_device_register(&audm2m_pdev);
> + if (ret)
> + return ret;
> +
> + ret = platform_driver_register(&audm2m_pdrv);
> + if (ret)
> + platform_device_unregister(&audm2m_pdev);
> +
> + return ret;
> +}
> +
> +module_init(audm2m_init);
> +module_exit(audm2m_exit);
On 06/11/2023 14:58, Hans Verkuil wrote:
> On 27/10/2023 12:35, Shengjiu Wang wrote:
>> Audio memory to memory virtual driver use video memory to memory
>> virtual driver vim2m.c as example. The main difference is
>> device type is VFL_TYPE_AUDIO and device cap type is V4L2_CAP_AUDIO_M2M.
>>
>> The device_run function is a dummy function, which is simply
>> copy the data from input buffer to output buffer.
>
> I started work on the v4l-utils part of this, using this driver.
>
> I noticed that this driver doesn't expose the V4L2_CID_M2M_AUDIO_SOURCE/SINK_RATE
> controls, and it really should, otherwise it is not representative of this
> type of device.
>
> It is enough to start with just a single fixed rate listed for each control.
>
> It would be even nicer if you can have two rates such as 24000 and 48000 and
> do the actual rate conversion, i.e. dropping every other sample or duplicating
> each sample depending on whether you're halving or doubling the rate. That
> should be easy to implement, and it makes this driver much more realistic.
Update: I have finished the v4l-utils update (I'll post a patch for that later).
But while testing I noticed that this driver does not set up the sequence number
and it doesn't copy the timestamp. So the patch below needs to be applied.
Just squash it together with your patch. Note that you need to do the same for
your alsa driver.
Also, please rename the source name from vim2m_audio.c to vim2m-audio.c. That is
consistent with the naming elsewhere in test-drivers.
I also want to have support for the MEDIA_CONTROLLER here. See vim2m, search for
CONFIG_MEDIA_CONTROLLER. Both in this test driver and also in your audio driver.
This will require adding a new media entity (MEDIA_ENT_F_PROC_AUDIO_RESAMPLER?).
And you also need to add a new MEDIA_INTF_T_V4L_AUDIO interface type that will be
used by v4l2_m2m_register_media_controller(). That function can check vdev->vfl_type
to see if it needs to use MEDIA_INTF_T_V4L_VIDEO or MEDIA_INTF_T_V4L_AUDIO.
Remember to update the documentation as well!
The reason for using the media controller here is that it turns out to be very useful
for application to detect what sort of m2m device it is dealing with: it has proven
it worth for video codecs, and I think it should be standard for new m2m devices, and
especially for a completely new type of m2m device.
Regards,
Hans
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
diff --git a/drivers/media/test-drivers/vim2m_audio.c b/drivers/media/test-drivers/vim2m_audio.c
index 2134e8338417..e8aa2bb0aa77 100644
--- a/drivers/media/test-drivers/vim2m_audio.c
+++ b/drivers/media/test-drivers/vim2m_audio.c
@@ -62,6 +62,7 @@ struct audm2m_q_data {
unsigned int channels;
unsigned int buffersize;
u32 fourcc;
+ unsigned int sequence;
};
enum {
@@ -170,6 +171,9 @@ static void device_run(void *priv)
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ src_buf->sequence = q_data_src->sequence++;
+ dst_buf->sequence = q_data_dst->sequence++;
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
@@ -423,6 +427,15 @@ static void audm2m_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
+static int audm2m_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct audm2m_ctx *ctx = vb2_get_drv_priv(q);
+ struct audm2m_q_data *q_data = get_q_data(ctx, q->type);
+
+ q_data->sequence = 0;
+ return 0;
+}
+
static void audm2m_stop_streaming(struct vb2_queue *q)
{
struct audm2m_ctx *ctx = vb2_get_drv_priv(q);
@@ -442,6 +455,7 @@ static void audm2m_stop_streaming(struct vb2_queue *q)
static const struct vb2_ops audm2m_qops = {
.queue_setup = audm2m_queue_setup,
.buf_queue = audm2m_buf_queue,
+ .start_streaming = audm2m_start_streaming,
.stop_streaming = audm2m_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
This patch adds support for v4l-audio devices to v4l2-ctl, v4l2-compliance
and the test-media script.
This is RFC quality: when media controller support is added to the m2m audio
drivers, this patch will need to be updated. And it also need to be split into
smaller pieces, but I will wait with that until vim2m-audio uses the media
controller.
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
diff --git a/contrib/test/test-media b/contrib/test/test-media
index afe20760..c2a71b89 100755
--- a/contrib/test/test-media
+++ b/contrib/test/test-media
@@ -7,6 +7,7 @@
vidtv=0
vivid=0
vim2m=0
+vim2m_audio=0
vimc=0
vicodec=0
cec=0
@@ -53,13 +54,14 @@ if [ -z "$1" ]; then
echo Test Targets:
echo "vivid: test the vivid driver"
echo "vim2m: test the vim2m driver"
+ echo "vim2m-audio: test the vim2m-audio driver"
echo "vimc: test the vimc driver"
echo "vicodec: test the vicodec driver"
echo "vidtv: test the vidtv driver"
echo "cec: adds the vivid CEC compliance tests, except for the CEC standby/wakeup tests."
echo "cec-pwr: adds the vivid CEC compliance tests, including the CEC standby/wakeup tests."
- echo "all: equals 'vivid vim2m vimc vicodec vidtv cec cec-pwr'"
- echo "mc: equals 'vivid vim2m vimc vicodec vidtv'"
+ echo "all: equals 'vivid vim2m vim2m-audio vimc vicodec vidtv cec cec-pwr'"
+ echo "mc: equals 'vivid vim2m vim2m-audio vimc vicodec vidtv'"
exit 0
fi
@@ -116,6 +118,7 @@ while [ ! -z "$1" ]; do
vidtv=1
vivid=1
vim2m=1
+ vim2m_audio=1
vimc=1
vicodec=1
cec=1
@@ -124,6 +127,7 @@ while [ ! -z "$1" ]; do
mc)
vivid=1
vim2m=1
+ vim2m_audio=1
vimc=1
vicodec=1
vidtv=1
@@ -137,6 +141,9 @@ while [ ! -z "$1" ]; do
vim2m)
vim2m=1
;;
+ vim2m-audio)
+ vim2m_audio=1
+ ;;
vimc)
vimc=1
;;
@@ -421,6 +428,83 @@ if [ $vim2m -eq 1 -a $setup -eq 0 ]; then
echo
fi
+
+if [ $vim2m_audio -eq 1 ]; then
+ rmmod vim2m-audio 2&>/dev/null
+ modprobe vim2m-audio
+ sleep $modprobe_time
+ dmesg -n notice
+
+ if ! $v4l2_ctl -z platform:vim2m-audio ; then
+ echo "FAIL: the vim2m-audio module failed to load" | tee -a $tmp
+ echo "Grand Total for vim2m-audio: Succeeded: 0, Failed: 1, Warnings: 0" | tee -a $tmp
+ echo "Final Summary: 1, Succeeded: 0, Failed: 1, Warnings: 0"
+ rmmod vivid
+ exit 0
+ fi
+fi
+
+if [ $vim2m_audio -eq 1 -a $setup -eq 0 ]; then
+ echo
+ echo vim2m-audio compliance tests | tee /dev/kmsg
+ echo
+ date
+ stdbuf -oL $v4l2_compliance -A0 -z platform:vivid-002 -e vivid-002-vid-cap -s10 -P -a 2>&1 | tee -a $tmp
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo unbind vim2m-audio | tee /dev/kmsg
+ echo
+ echo -n vim2m-audio.0 >/sys/bus/platform/drivers/vim2m-audio/unbind
+ sleep $unbind_time
+ echo
+ echo rebind vim2m-audio | tee /dev/kmsg
+ echo
+ echo -n vim2m-audio.0 >/sys/bus/platform/drivers/vim2m-audio/bind
+ sleep 1
+ echo
+ echo second unbind vim2m-audio | tee /dev/kmsg
+ echo
+ for i in `$v4l2_ctl -z platform:vim2m-audio --list-devices`; do
+ let "t = 1 + $RANDOM / 4096"
+ echo $i: sleep ${t}s
+ sleep $t <$i &
+ done
+ sleep 1
+ echo
+ echo -n vim2m-audio.0 >/sys/bus/platform/drivers/vim2m-audio/unbind
+ sleep $reunbind_time
+ echo
+ echo rmmod vim2m-audio | tee /dev/kmsg
+ echo
+ rmmod vim2m-audio
+ sleep $rmmod_time
+ if [ $kmemleak -eq 1 ]; then
+ echo
+ echo kmemleak results for vim2m-audio:
+ echo
+ echo scan >/sys/kernel/debug/kmemleak
+ cat /sys/kernel/debug/kmemleak
+ echo
+ echo end of kmemleak results
+ echo clear >/sys/kernel/debug/kmemleak
+ fi
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo
+ echo
+fi
+
if [ $vimc -eq 1 ]; then
rmmod vimc 2&>/dev/null
modprobe vimc
diff --git a/utils/common/cv4l-helpers.h b/utils/common/cv4l-helpers.h
index 91a04146..235368ec 100644
--- a/utils/common/cv4l-helpers.h
+++ b/utils/common/cv4l-helpers.h
@@ -78,6 +78,13 @@ public:
bool has_rds_out() const { return v4l_has_rds_out(this); }
bool has_sdr_cap() const { return v4l_has_sdr_cap(this); }
bool has_sdr_out() const { return v4l_has_sdr_out(this); }
+ bool has_touch() const { return v4l_has_touch(this); }
+ bool has_meta_cap() const { return v4l_has_meta_cap(this); }
+ bool has_meta_out() const { return v4l_has_meta_out(this); }
+ bool has_audio_cap() const { return v4l_has_audio_cap(this); }
+ bool has_audio_out() const { return v4l_has_audio_out(this); }
+ bool has_audio_m2m() const { return v4l_has_audio_m2m(this); }
+ bool has_m2m() const { return v4l_has_m2m(this); }
bool has_hwseek() const { return v4l_has_hwseek(this); }
bool has_rw() const { return v4l_has_rw(this); }
bool has_streaming() const { return v4l_has_streaming(this); }
diff --git a/utils/common/v4l-helpers.h b/utils/common/v4l-helpers.h
index 5a256603..a01b3e48 100644
--- a/utils/common/v4l-helpers.h
+++ b/utils/common/v4l-helpers.h
@@ -404,11 +404,26 @@ static inline bool v4l_has_touch(const struct v4l_fd *f)
return v4l_g_caps(f) & V4L2_CAP_TOUCH;
}
+static inline bool v4l_has_audio_cap(const struct v4l_fd *f)
+{
+ return v4l_g_caps(f) & V4L2_CAP_AUDIO_M2M;
+}
+
+static inline bool v4l_has_audio_out(const struct v4l_fd *f)
+{
+ return v4l_g_caps(f) & V4L2_CAP_AUDIO_M2M;
+}
+
static inline bool v4l_has_audio_m2m(const struct v4l_fd *f)
{
return v4l_g_caps(f) & V4L2_CAP_AUDIO_M2M;
}
+static inline bool v4l_has_m2m(const struct v4l_fd *f)
+{
+ return v4l_has_vid_m2m(f) || v4l_has_audio_m2m(f);
+}
+
static inline bool v4l_has_hwseek(const struct v4l_fd *f)
{
return v4l_g_caps(f) & V4L2_CAP_HW_FREQ_SEEK;
@@ -454,6 +469,10 @@ static inline __u32 v4l_determine_type(const struct v4l_fd *f)
return V4L2_BUF_TYPE_META_CAPTURE;
if (v4l_has_meta_out(f))
return V4L2_BUF_TYPE_META_OUTPUT;
+ if (v4l_has_audio_cap(f))
+ return V4L2_BUF_TYPE_AUDIO_CAPTURE;
+ if (v4l_has_audio_out(f))
+ return V4L2_BUF_TYPE_AUDIO_OUTPUT;
return 0;
}
@@ -706,6 +725,10 @@ static inline void v4l_format_s_pixelformat(struct v4l2_format *fmt, __u32 pixel
case V4L2_BUF_TYPE_META_OUTPUT:
fmt->fmt.meta.dataformat = pixelformat;
break;
+ case V4L2_BUF_TYPE_AUDIO_CAPTURE:
+ case V4L2_BUF_TYPE_AUDIO_OUTPUT:
+ fmt->fmt.audio.audioformat = pixelformat;
+ break;
}
}
@@ -727,6 +750,9 @@ static inline __u32 v4l_format_g_pixelformat(const struct v4l2_format *fmt)
case V4L2_BUF_TYPE_META_CAPTURE:
case V4L2_BUF_TYPE_META_OUTPUT:
return fmt->fmt.meta.dataformat;
+ case V4L2_BUF_TYPE_AUDIO_CAPTURE:
+ case V4L2_BUF_TYPE_AUDIO_OUTPUT:
+ return fmt->fmt.audio.audioformat;
default:
return 0;
}
@@ -1068,6 +1094,9 @@ v4l_format_g_sizeimage(const struct v4l2_format *fmt, unsigned plane)
case V4L2_BUF_TYPE_META_CAPTURE:
case V4L2_BUF_TYPE_META_OUTPUT:
return plane ? 0 : fmt->fmt.meta.buffersize;
+ case V4L2_BUF_TYPE_AUDIO_CAPTURE:
+ case V4L2_BUF_TYPE_AUDIO_OUTPUT:
+ return plane ? 0 : fmt->fmt.audio.buffersize;
default:
return 0;
}
@@ -1192,12 +1221,22 @@ static inline bool v4l_type_is_meta(unsigned type)
type == V4L2_BUF_TYPE_META_OUTPUT;
}
+static inline bool v4l_type_is_audio(unsigned type)
+{
+ return type == V4L2_BUF_TYPE_AUDIO_CAPTURE ||
+ type == V4L2_BUF_TYPE_AUDIO_OUTPUT;
+}
+
static inline unsigned v4l_type_invert(unsigned type)
{
if (v4l_type_is_planar(type))
return v4l_type_is_output(type) ?
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ if (v4l_type_is_audio(type))
+ return v4l_type_is_output(type) ?
+ V4L2_BUF_TYPE_AUDIO_CAPTURE :
+ V4L2_BUF_TYPE_AUDIO_OUTPUT;
return v4l_type_is_output(type) ?
V4L2_BUF_TYPE_VIDEO_CAPTURE :
V4L2_BUF_TYPE_VIDEO_OUTPUT;
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
index bba56b12..1e83fba3 100644
--- a/utils/v4l2-compliance/v4l2-compliance.h
+++ b/utils/v4l2-compliance/v4l2-compliance.h
@@ -102,7 +102,7 @@ using frmsizes_count_map = std::map<__u32, unsigned>;
struct base_node;
-#define V4L2_BUF_TYPE_LAST V4L2_BUF_TYPE_META_OUTPUT
+#define V4L2_BUF_TYPE_LAST V4L2_BUF_TYPE_AUDIO_OUTPUT
struct base_node {
bool is_video;
diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp
index 6d592c9b..d5c8d17c 100644
--- a/utils/v4l2-compliance/v4l2-test-buffers.cpp
+++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp
@@ -235,12 +235,14 @@ public:
if (v4l_type_is_output(g_type()))
fill_output_buf(fill_bytesused);
err = node->qbuf(*this);
- if (err == 0 &&
- v4l_type_is_video(g_type()) && v4l_type_is_output(g_type())) {
- fail_on_test(g_field() == V4L2_FIELD_ANY);
+ if (err)
+ return err;
+ if (v4l_type_is_output(g_type())) {
+ if (v4l_type_is_video(g_type()))
+ fail_on_test(g_field() == V4L2_FIELD_ANY);
buffer_info[g_timestamp()] = buf;
}
- return err;
+ return 0;
}
int qbuf(node *node, const cv4l_queue &q)
{
diff --git a/utils/v4l2-compliance/v4l2-test-formats.cpp b/utils/v4l2-compliance/v4l2-test-formats.cpp
index c92e9658..adec678a 100644
--- a/utils/v4l2-compliance/v4l2-test-formats.cpp
+++ b/utils/v4l2-compliance/v4l2-test-formats.cpp
@@ -451,6 +451,7 @@ static int testFormatsType(struct node *node, int ret, unsigned type, struct v4
struct v4l2_sliced_vbi_format &sliced = fmt.fmt.sliced;
struct v4l2_sdr_format &sdr = fmt.fmt.sdr;
struct v4l2_meta_format &meta = fmt.fmt.meta;
+ struct v4l2_audio_format &audio = fmt.fmt.audio;
unsigned min_data_samples;
unsigned min_sampling_rate;
v4l2_std_id std;
@@ -595,6 +596,13 @@ static int testFormatsType(struct node *node, int ret, unsigned type, struct v4
meta.dataformat, fcc2s(meta.dataformat).c_str(), type);
fail_on_test(meta.buffersize == 0);
break;
+ case V4L2_BUF_TYPE_AUDIO_CAPTURE:
+ case V4L2_BUF_TYPE_AUDIO_OUTPUT:
+ if (map.find(audio.audioformat) == map.end())
+ return fail("audioformat %08x (%s) for buftype %d not reported by ENUM_FMT\n",
+ audio.audioformat, fcc2s(audio.audioformat).c_str(), type);
+ fail_on_test(audio.buffersize == 0);
+ break;
case V4L2_BUF_TYPE_PRIVATE:
break;
}
@@ -709,6 +717,9 @@ static bool matchFormats(const struct v4l2_format &f1, const struct v4l2_format
case V4L2_BUF_TYPE_META_CAPTURE:
case V4L2_BUF_TYPE_META_OUTPUT:
return !memcmp(&f1.fmt.meta, &f2.fmt.meta, sizeof(f1.fmt.meta));
+ case V4L2_BUF_TYPE_AUDIO_CAPTURE:
+ case V4L2_BUF_TYPE_AUDIO_OUTPUT:
+ return !memcmp(&f1.fmt.audio, &f2.fmt.audio, sizeof(f1.fmt.audio));
}
return false;
@@ -788,6 +799,10 @@ int testTryFormats(struct node *node)
case V4L2_BUF_TYPE_META_OUTPUT:
pixelformat = fmt.fmt.meta.dataformat;
break;
+ case V4L2_BUF_TYPE_AUDIO_CAPTURE:
+ case V4L2_BUF_TYPE_AUDIO_OUTPUT:
+ pixelformat = fmt.fmt.audio.audioformat;
+ break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
pixelformat = fmt.fmt.pix_mp.pixelformat;
@@ -866,6 +881,9 @@ static int testM2MFormats(struct node *node)
fail_on_test(node->g_fmt(fmt_out, out_type));
fail_on_test(node->g_fmt(fmt_cap, cap_type));
+ if (node->has_audio_m2m())
+ return 0;
+
/*
* JPEG codec have fixed colorspace, so these tests
* are different compared to other m2m devices.
@@ -1138,6 +1156,10 @@ int testSetFormats(struct node *node)
case V4L2_BUF_TYPE_META_OUTPUT:
pixelformat = fmt_set.fmt.meta.dataformat;
break;
+ case V4L2_BUF_TYPE_AUDIO_CAPTURE:
+ case V4L2_BUF_TYPE_AUDIO_OUTPUT:
+ pixelformat = fmt_set.fmt.audio.audioformat;
+ break;
case V4L2_BUF_TYPE_SDR_CAPTURE:
case V4L2_BUF_TYPE_SDR_OUTPUT:
pixelformat = fmt_set.fmt.sdr.pixelformat;
diff --git a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
index ffa36164..18dd2c9b 100644
--- a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
@@ -2156,7 +2156,7 @@ static FILE *open_input_file(cv4l_fd &fd, __u32 type)
static void streaming_set_out(cv4l_fd &fd, cv4l_fd &exp_fd)
{
- __u32 type = fd.has_vid_m2m() ? v4l_type_invert(fd.g_type()) : fd.g_type();
+ __u32 type = fd.has_m2m() ? v4l_type_invert(fd.g_type()) : fd.g_type();
cv4l_queue q(type, out_memory);
cv4l_queue exp_q(exp_fd.g_type(), V4L2_MEMORY_MMAP);
int fd_flags = fcntl(fd.g_fd(), F_GETFL);
@@ -2713,7 +2713,7 @@ static void streaming_set_m2m(cv4l_fd &fd, cv4l_fd &exp_fd)
fd.g_fmt(fmt[OUT], out.g_type());
fd.g_fmt(fmt[CAP], in.g_type());
- if (!fd.has_vid_m2m()) {
+ if (!fd.has_m2m()) {
fprintf(stderr, "unsupported m2m stream type\n");
return;
}
@@ -2763,7 +2763,7 @@ static void streaming_set_cap2out(cv4l_fd &fd, cv4l_fd &out_fd)
bool use_poll = options[OptStreamPoll];
bool use_dmabuf = options[OptStreamDmaBuf] || options[OptStreamOutDmaBuf];
bool use_userptr = options[OptStreamUser] && options[OptStreamOutUser];
- __u32 out_type = out_fd.has_vid_m2m() ? v4l_type_invert(out_fd.g_type()) : out_fd.g_type();
+ __u32 out_type = out_fd.has_m2m() ? v4l_type_invert(out_fd.g_type()) : out_fd.g_type();
cv4l_queue in(fd.g_type(), memory);
cv4l_queue out(out_type, out_memory);
fps_timestamps fps_ts[2];
@@ -3002,7 +3002,7 @@ void streaming_list(cv4l_fd &fd, cv4l_fd &out_fd)
list_buffers(fd, fd.g_type());
if (options[OptListBuffersOut])
- list_buffers(*p_out_fd, p_out_fd->has_vid_m2m() ?
+ list_buffers(*p_out_fd, p_out_fd->has_m2m() ?
v4l_type_invert(p_out_fd->g_type()) : p_out_fd->g_type());
if (options[OptStreamBufCaps])
© 2016 - 2025 Red Hat, Inc.