summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-22 02:01:51 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-22 02:01:51 +0000
commitd3b0deb5f720416f2b1c985bf768b729ed4654e4 (patch)
tree1c4787ae954ef270b7a0e77cc86441a14816895a
parente69b992b2f9b3c05b05976ff71e50cac0ec73870 (diff)
parent497785e139ab44e917cf4e9bf7d74ae03ac8ca33 (diff)
downloadvirtual-device-common-android14-6.1-2023-05-exp-release.tar.gz
Snap for 11203489 from 497785e139ab44e917cf4e9bf7d74ae03ac8ca33 to common-android14-6.1-2023-05-exp-releasecommon-android14-6.1-2023-05-exp-release
Change-Id: Ic1e3b4cc1aa851e5cf45ccb6ddb7498a557428ae Signed-off-by: Coastguard Worker <android-build-coastguard-worker@google.com>
-rw-r--r--BUILD.bazel218
-rw-r--r--build.config.crashdump13
-rw-r--r--build.config.crashdump.aarch647
-rw-r--r--build.config.crashdump.x86_647
-rw-r--r--build.config.microdroid21
-rw-r--r--build.config.microdroid.aarch647
-rw-r--r--build.config.microdroid.x86_647
-rw-r--r--configs/crashdump_arm64.defconfig81
-rw-r--r--configs/crashdump_x86_64.defconfig87
-rw-r--r--configs/microdroid_arm64.defconfig215
-rw-r--r--configs/microdroid_x86_64.defconfig291
-rw-r--r--modules.options4
-rw-r--r--uapi/linux/virtio_video.h495
-rw-r--r--virtio_video/Kbuild22
-rw-r--r--virtio_video/virtio_video.h446
-rw-r--r--virtio_video/virtio_video_cam.c359
-rw-r--r--virtio_video/virtio_video_caps.c460
-rw-r--r--virtio_video/virtio_video_dec.c424
-rw-r--r--virtio_video/virtio_video_device.c1282
-rw-r--r--virtio_video/virtio_video_driver.c246
-rw-r--r--virtio_video/virtio_video_enc.c601
-rw-r--r--virtio_video/virtio_video_helpers.c289
-rw-r--r--virtio_video/virtio_video_vq.c980
23 files changed, 5747 insertions, 815 deletions
diff --git a/BUILD.bazel b/BUILD.bazel
index aad2cc7..91cd4cf 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -14,6 +14,8 @@
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_skylib//lib:sets.bzl", "sets")
+load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir")
+load("//build/bazel_common_rules/test_mappings:test_mappings.bzl", "test_mappings_dist")
load(
"//build/kernel/kleaf:kernel.bzl",
"ddk_headers",
@@ -28,10 +30,7 @@ load(
"kernel_unstripped_modules_archive",
"merged_kernel_uapi_headers",
)
-load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir")
-load("//build/bazel_common_rules/test_mappings:test_mappings.bzl", "test_mappings_dist")
-load("//build/bazel_common_rules/exec:exec.bzl", "exec")
-load("//build/kernel/kleaf:constants.bzl", "X86_64_OUTS")
+load("//build/kernel/kleaf:hermetic_tools.bzl", "hermetic_exec")
load("//common:modules.bzl", "COMMON_GKI_MODULES_LIST")
filegroup(
@@ -77,6 +76,21 @@ filegroup(
)
filegroup(
+ name = "virtio_video_sources",
+ srcs = [
+ "virtio_video/virtio_video.h",
+ "virtio_video/virtio_video_cam.c",
+ "virtio_video/virtio_video_caps.c",
+ "virtio_video/virtio_video_dec.c",
+ "virtio_video/virtio_video_device.c",
+ "virtio_video/virtio_video_driver.c",
+ "virtio_video/virtio_video_enc.c",
+ "virtio_video/virtio_video_helpers.c",
+ "virtio_video/virtio_video_vq.c",
+ ],
+)
+
+filegroup(
name = "virtual_device_aarch64_common_sources",
srcs = [
":common_sources",
@@ -270,12 +284,22 @@ ddk_module(
deps = [":common_headers_x86_64"],
)
+ddk_module(
+ name = "x86_64/virtio_video",
+ srcs = [":virtio_video_sources"],
+ out = "virtio_video.ko",
+ kernel_build = ":virtual_device_x86_64",
+ local_defines = ["DRIVER_VERSION=\"android14-6.1\""],
+ deps = [":common_headers_x86_64"],
+)
+
kernel_module_group(
name = "virtual_device_x86_64_external_modules",
srcs = [
":x86_64/goldfish_drivers/goldfish_address_space",
":x86_64/goldfish_drivers/goldfish_pipe",
":x86_64/goldfish_drivers/goldfish_sync",
+ ":x86_64/virtio_video",
],
)
@@ -301,6 +325,7 @@ kernel_images(
kernel_build = ":virtual_device_x86_64",
kernel_modules_install = ":virtual_device_x86_64_modules_install",
modules_blocklist = "modules.blocklist",
+ modules_options = "modules.options",
)
# Workaround for b/201687176.
@@ -325,7 +350,7 @@ copy_to_dist_dir(
log = "info",
)
-exec(
+hermetic_exec(
name = "virtual_device_x86_64_dist",
data = [
":virtual_device_x86_64_copy_to_dist_dir",
@@ -408,12 +433,22 @@ ddk_module(
deps = [":common_headers_aarch64"],
)
+ddk_module(
+ name = "aarch64/virtio_video",
+ srcs = [":virtio_video_sources"],
+ out = "virtio_video.ko",
+ kernel_build = ":virtual_device_aarch64",
+ local_defines = ["DRIVER_VERSION=\"android14-6.1\""],
+ deps = [":common_headers_aarch64"],
+)
+
kernel_module_group(
name = "virtual_device_aarch64_external_modules",
srcs = [
":aarch64/goldfish_drivers/goldfish_address_space",
":aarch64/goldfish_drivers/goldfish_pipe",
":aarch64/goldfish_drivers/goldfish_sync",
+ ":aarch64/virtio_video",
],
)
@@ -447,6 +482,7 @@ kernel_images(
kernel_build = ":virtual_device_aarch64",
kernel_modules_install = ":virtual_device_aarch64_modules_install",
modules_blocklist = "modules.blocklist",
+ modules_options = "modules.options",
)
# Workaround for b/201687176.
@@ -472,7 +508,7 @@ copy_to_dist_dir(
log = "info",
)
-exec(
+hermetic_exec(
name = "virtual_device_aarch64_dist",
data = [
":virtual_device_aarch64_copy_to_dist_dir",
@@ -523,12 +559,22 @@ ddk_module(
deps = [":common_headers_aarch64"],
)
+ddk_module(
+ name = "aarch64_16k/virtio_video",
+ srcs = [":virtio_video_sources"],
+ out = "virtio_video.ko",
+ kernel_build = ":virtual_device_aarch64_16k",
+ local_defines = ["DRIVER_VERSION=\"android14-6.1\""],
+ deps = [":common_headers_aarch64"],
+)
+
kernel_module_group(
name = "virtual_device_aarch64_16k_external_modules",
srcs = [
":aarch64_16k/goldfish_drivers/goldfish_address_space",
":aarch64_16k/goldfish_drivers/goldfish_pipe",
":aarch64_16k/goldfish_drivers/goldfish_sync",
+ ":aarch64_16k/virtio_video",
],
)
@@ -546,6 +592,7 @@ kernel_images(
kernel_build = ":virtual_device_aarch64_16k",
kernel_modules_install = ":virtual_device_aarch64_16k_modules_install",
modules_blocklist = "modules.blocklist",
+ modules_options = "modules.options",
)
# Workaround for b/201687176.
@@ -569,7 +616,7 @@ copy_to_dist_dir(
log = "info",
)
-exec(
+hermetic_exec(
name = "virtual_device_aarch64_16k_dist",
data = [
":virtual_device_aarch64_16k_copy_to_dist_dir",
@@ -618,12 +665,22 @@ ddk_module(
deps = [":common_headers_riscv64"],
)
+ddk_module(
+ name = "riscv64/virtio_video",
+ srcs = [":virtio_video_sources"],
+ out = "virtio_video.ko",
+ kernel_build = ":virtual_device_riscv64",
+ local_defines = ["DRIVER_VERSION=\"android14-6.1\""],
+ deps = [":common_headers_riscv64"],
+)
+
kernel_module_group(
name = "virtual_device_riscv64_external_modules",
srcs = [
":riscv64/goldfish_drivers/goldfish_address_space",
":riscv64/goldfish_drivers/goldfish_pipe",
":riscv64/goldfish_drivers/goldfish_sync",
+ ":riscv64/virtio_video",
],
)
@@ -641,6 +698,7 @@ kernel_images(
kernel_build = ":virtual_device_riscv64",
kernel_modules_install = ":virtual_device_riscv64_modules_install",
modules_blocklist = "modules.blocklist",
+ modules_options = "modules.options",
)
copy_to_dist_dir(
@@ -700,12 +758,22 @@ ddk_module(
deps = [":common_headers_arm"],
)
+ddk_module(
+ name = "arm/virtio_video",
+ srcs = [":virtio_video_sources"],
+ out = "virtio_video.ko",
+ kernel_build = ":virtual_device_arm",
+ local_defines = ["DRIVER_VERSION=\"android14-6.1\""],
+ deps = [":common_headers_arm"],
+)
+
kernel_module_group(
name = "virtual_device_arm_external_modules",
srcs = [
":arm/goldfish_drivers/goldfish_address_space",
":arm/goldfish_drivers/goldfish_pipe",
":arm/goldfish_drivers/goldfish_sync",
+ ":arm/virtio_video",
],
)
@@ -721,6 +789,7 @@ kernel_images(
kernel_build = "//common-modules/virtual-device:virtual_device_arm",
kernel_modules_install = "//common-modules/virtual-device:virtual_device_arm_modules_install",
modules_blocklist = "modules.blocklist",
+ modules_options = "modules.options",
)
copy_to_dist_dir(
@@ -745,7 +814,7 @@ kernel_build(
"vmlinux",
"vmlinux.symvers",
],
- arch = "x86_64",
+ arch = "i386",
base_kernel = None, # We don't have i686 GKI builds, so not using mixed build
build_config = "build.config.virtual_device.i686",
module_outs = sets.to_list(sets.make(
@@ -784,12 +853,22 @@ ddk_module(
deps = [":common_headers_x86_64"],
)
+ddk_module(
+ name = "i686/virtio_video",
+ srcs = [":virtio_video_sources"],
+ out = "virtio_video.ko",
+ kernel_build = ":virtual_device_i686",
+ local_defines = ["DRIVER_VERSION=\"android14-6.1\""],
+ deps = [":common_headers_x86_64"],
+)
+
kernel_module_group(
name = "virtual_device_i686_external_modules",
srcs = [
":i686/goldfish_drivers/goldfish_address_space",
":i686/goldfish_drivers/goldfish_pipe",
":i686/goldfish_drivers/goldfish_sync",
+ ":i686/virtio_video",
],
)
@@ -805,6 +884,7 @@ kernel_images(
kernel_build = "//common-modules/virtual-device:virtual_device_i686",
kernel_modules_install = "//common-modules/virtual-device:virtual_device_i686_modules_install",
modules_blocklist = "modules.blocklist",
+ modules_options = "modules.options",
)
copy_to_dist_dir(
@@ -942,97 +1022,77 @@ kernel_dtstree(
visibility = ["//common:__pkg__"],
)
-# Microdroid is not a real device. The kernel image is built with special
-# configs to reduce the size. Hence, not using mixed build.
-kernel_build(
+# Don't use these aliases for microdroid and crashdump kernels.
+# Use the actual names. These aliases will get removed in the future.
+
+alias(
name = "microdroid_aarch64",
- srcs = [":virtual_device_aarch64_common_sources"],
- outs = [
- "Image",
- "System.map",
- "modules.builtin",
- "modules.builtin.modinfo",
- "vmlinux",
- "vmlinux.symvers",
- ],
- build_config = "build.config.microdroid.aarch64",
- make_goals = [
- "Image",
- ],
+ actual = "//common:kernel_aarch64_microdroid",
+ deprecation = "Use //common:kernel_aarch64_microdroid",
)
-copy_to_dist_dir(
+alias(
name = "microdroid_aarch64_dist",
- data = [
- ":microdroid_aarch64",
- ],
- dist_dir = "out/microdroid_aarch64/dist",
- flat = True,
- log = "info",
+ actual = "//common:kernel_aarch64_microdroid_dist",
+ deprecation = "Use //common:kernel_aarch64_microdroid_dist",
)
-# Microdroid is not a real device. The kernel image is built with special
-# configs to reduce the size. Hence, not using mixed build.
-kernel_build(
+alias(
name = "microdroid_x86_64",
- srcs = [":virtual_device_x86_64_common_sources"],
- outs = X86_64_OUTS,
- arch = "x86_64",
- build_config = "build.config.microdroid.x86_64",
- make_goals = [
- "bzImage",
- ],
+ actual = "//common:kernel_x86_64_microdroid",
+ deprecation = "Use //common:kernel_x86_64_microdroid",
)
-copy_to_dist_dir(
+alias(
name = "microdroid_x86_64_dist",
- data = [
- ":microdroid_x86_64",
- ],
- dist_dir = "out/microdroid_x86_64/dist",
- flat = True,
- log = "info",
+ actual = "//common:kernel_x86_64_microdroid_dist",
+ deprecation = "Use //common:kernel_x86_64_microdroid_dist",
)
-kernel_build(
+alias(
name = "crashdump_aarch64",
- srcs = [":virtual_device_aarch64_common_sources"],
- outs = [
- "Image",
- ],
- build_config = "build.config.crashdump.aarch64",
- make_goals = [
- "Image",
- ],
+ actual = "//common:kernel_aarch64_crashdump",
+ deprecation = "Use //common:kernel_aarch64_crashdump",
)
-copy_to_dist_dir(
+alias(
name = "crashdump_aarch64_dist",
- data = [
- ":crashdump_aarch64",
- ],
- dist_dir = "out/crashdump_aarch64/dist",
- flat = True,
- log = "info",
+ actual = "//common:kernel_aarch64_crashdump_dist",
+ deprecation = "Use //common:kernel_aarch64_crashdump_dist",
)
-kernel_build(
+alias(
name = "crashdump_x86_64",
- srcs = [":virtual_device_x86_64_common_sources"],
- outs = X86_64_OUTS,
- arch = "x86_64",
- build_config = "build.config.crashdump.x86_64",
- make_goals = [
- "bzImage",
- ],
+ actual = "//common:kernel_x86_64_crashdump",
+ deprecation = "Use //common:kernel_x86_64_crashdump",
)
-copy_to_dist_dir(
+alias(
name = "crashdump_x86_64_dist",
- data = [
- ":crashdump_x86_64",
- ],
- dist_dir = "out/crashdump_x86_64/dist",
- flat = True,
- log = "info",
+ actual = "//common:kernel_x86_64_crashdump_dist",
+ deprecation = "Use //common:kernel_x86_64_crashdump_dist",
+)
+
+alias(
+ name = "microdroid_aarch64_config",
+ actual = "//common:kernel_aarch64_microdroid_config",
+ deprecation = "Use //common:kernel_aarch64_microdroid_config",
+)
+
+alias(
+ name = "microdroid_x86_64_config",
+ actual = "//common:kernel_x86_64_microdroid_config",
+ deprecation = "Use //common:kernel_x86_64_microdroid_config",
+)
+
+alias(
+ name = "crashdump_aarch64_config",
+ actual = "//common:kernel_aarch64_crashdump_config",
+ deprecation = "Use //common:kernel_aarch64_crashdump_config",
+)
+
+alias(
+ name = "crashdump_x86_64_config",
+ actual = "//common:kernel_x86_64_crashdump_config",
+ deprecation = "Use //common:kernel_x86_64_crashdump_config",
)
diff --git a/build.config.crashdump b/build.config.crashdump
deleted file mode 100644
index 00ac6a1..0000000
--- a/build.config.crashdump
+++ /dev/null
@@ -1,13 +0,0 @@
-SRCARCH=${ARCH}
-if [ "${ARCH}" = x86_64 ]; then
- SRCARCH=x86
-fi
-
-DEFCONFIG=crashdump_defconfig
-
-SRC_CONFIG=${ROOT_DIR}/common-modules/virtual-device/configs/crashdump_${ARCH}.defconfig
-COPIED_CONFIG=\${OUT_DIR}/arch/${SRCARCH}/configs/${DEFCONFIG}
-UPDATED_CONFIG=${ROOT_DIR}/${KERNEL_DIR}/arch/${SRCARCH}/configs/${DEFCONFIG}
-
-PRE_DEFCONFIG_CMDS="mkdir -p $(dirname ${COPIED_CONFIG}) && cp ${SRC_CONFIG} ${COPIED_CONFIG}"
-POST_DEFCONFIG_CMDS="if [ -f ${UPDATED_CONFIG} ]; then mv ${UPDATED_CONFIG} ${SRC_CONFIG}; fi"
diff --git a/build.config.crashdump.aarch64 b/build.config.crashdump.aarch64
deleted file mode 100644
index 86f7237..0000000
--- a/build.config.crashdump.aarch64
+++ /dev/null
@@ -1,7 +0,0 @@
-KERNEL_DIR=common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.aarch64
-. ${ROOT_DIR}/common-modules/virtual-device/build.config.crashdump
-
-# No modules
-IN_KERNEL_MODULES=
diff --git a/build.config.crashdump.x86_64 b/build.config.crashdump.x86_64
deleted file mode 100644
index 03fd8f8..0000000
--- a/build.config.crashdump.x86_64
+++ /dev/null
@@ -1,7 +0,0 @@
-KERNEL_DIR=common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.x86_64
-. ${ROOT_DIR}/common-modules/virtual-device/build.config.crashdump
-
-# No modules
-IN_KERNEL_MODULES=
diff --git a/build.config.microdroid b/build.config.microdroid
deleted file mode 100644
index fed5714..0000000
--- a/build.config.microdroid
+++ /dev/null
@@ -1,21 +0,0 @@
-SRCARCH=${ARCH}
-if [ "${ARCH}" = x86_64 ]; then
- SRCARCH=x86
-fi
-
-DEFCONFIG=microdroid_defconfig
-
-SRC_CONFIG=${ROOT_DIR}/common-modules/virtual-device/configs/microdroid_${ARCH}.defconfig
-COPIED_CONFIG=\${OUT_DIR}/arch/${SRCARCH}/configs/${DEFCONFIG}
-UPDATED_CONFIG=${ROOT_DIR}/${KERNEL_DIR}/arch/${SRCARCH}/configs/${DEFCONFIG}
-
-if [ -n ${BUILD_WORKSPACE_DIRECTORY} ]; then
- UPDATE_CONFIG_DEST=${BUILD_WORKSPACE_DIRECTORY}/common-modules/virtual-device/configs/microdroid_${ARCH}.defconfig
-fi
-
-# HACK: bug in bash 5.2 that if the last command of the eval in a subshell is not a built-in
-# command, the subshell exits prematurely. This is fixed in 5.2.15 but we leave
-# this hack in until bash becomes hermetic (unlikely).
-# See b/275468906#comment8
-PRE_DEFCONFIG_CMDS="mkdir -p $(dirname ${COPIED_CONFIG}) && cp ${SRC_CONFIG} ${COPIED_CONFIG} && true"
-POST_DEFCONFIG_CMDS="if [ -f ${UPDATED_CONFIG} ] && [ -n ${UPDATE_CONFIG_DEST} ]; then mv ${UPDATED_CONFIG} ${UPDATE_CONFIG_DEST}; echo Updated ${UPDATE_CONFIG_DEST}; fi"
diff --git a/build.config.microdroid.aarch64 b/build.config.microdroid.aarch64
deleted file mode 100644
index d4fbbcf..0000000
--- a/build.config.microdroid.aarch64
+++ /dev/null
@@ -1,7 +0,0 @@
-KERNEL_DIR=common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.aarch64
-. ${ROOT_DIR}/common-modules/virtual-device/build.config.microdroid
-
-# No modules
-IN_KERNEL_MODULES=
diff --git a/build.config.microdroid.x86_64 b/build.config.microdroid.x86_64
deleted file mode 100644
index 7b421de..0000000
--- a/build.config.microdroid.x86_64
+++ /dev/null
@@ -1,7 +0,0 @@
-KERNEL_DIR=common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.x86_64
-. ${ROOT_DIR}/common-modules/virtual-device/build.config.microdroid
-
-# No modules
-IN_KERNEL_MODULES=
diff --git a/configs/crashdump_arm64.defconfig b/configs/crashdump_arm64.defconfig
deleted file mode 100644
index 75866b0..0000000
--- a/configs/crashdump_arm64.defconfig
+++ /dev/null
@@ -1,81 +0,0 @@
-# CONFIG_WERROR is not set
-# CONFIG_LOCALVERSION_AUTO is not set
-# CONFIG_SWAP is not set
-# CONFIG_CROSS_MEMORY_ATTACH is not set
-CONFIG_NO_HZ_IDLE=y
-CONFIG_PREEMPT=y
-# CONFIG_CPU_ISOLATION is not set
-CONFIG_LOG_BUF_SHIFT=15
-CONFIG_LOG_CPU_MAX_BUF_SHIFT=10
-# CONFIG_UTS_NS is not set
-# CONFIG_TIME_NS is not set
-# CONFIG_PID_NS is not set
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_RD_GZIP is not set
-# CONFIG_RD_BZIP2 is not set
-# CONFIG_RD_LZMA is not set
-# CONFIG_RD_XZ is not set
-# CONFIG_RD_LZ4 is not set
-# CONFIG_RD_ZSTD is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
-# CONFIG_COMPAT_BRK is not set
-# CONFIG_SLAB_MERGE_DEFAULT is not set
-# CONFIG_SLUB_CPU_PARTIAL is not set
-CONFIG_ARM64_VA_BITS_48=y
-CONFIG_NR_CPUS=2
-CONFIG_CRASH_DUMP=y
-# CONFIG_RODATA_FULL_DEFAULT_ENABLED is not set
-# CONFIG_ARM64_TAGGED_ADDR_ABI is not set
-# CONFIG_ARM64_SVE is not set
-# CONFIG_EFI is not set
-# CONFIG_SUSPEND is not set
-CONFIG_JUMP_LABEL=y
-# CONFIG_SECCOMP is not set
-# CONFIG_STACKPROTECTOR is not set
-# CONFIG_VMAP_STACK is not set
-# CONFIG_MQ_IOSCHED_DEADLINE is not set
-# CONFIG_MQ_IOSCHED_KYBER is not set
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_BINFMT_SCRIPT is not set
-# CONFIG_SPECULATIVE_PAGE_FAULT is not set
-CONFIG_PCI=y
-CONFIG_PCI_HOST_GENERIC=y
-CONFIG_PCI_ENDPOINT=y
-CONFIG_DEVTMPFS=y
-# CONFIG_STANDALONE is not set
-# CONFIG_PREVENT_FIRMWARE_BUILD is not set
-CONFIG_ARM_SCMI_PROTOCOL=y
-# CONFIG_ARM_SMCCC_SOC_ID is not set
-# CONFIG_BLK_DEV is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_SERIO is not set
-# CONFIG_LEGACY_PTYS is not set
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_NR_UARTS=1
-CONFIG_SERIAL_8250_RUNTIME_UARTS=1
-CONFIG_SERIAL_8250_EXTENDED=y
-CONFIG_SERIAL_OF_PLATFORM=y
-CONFIG_VIRTIO_CONSOLE=y
-# CONFIG_HW_RANDOM is not set
-# CONFIG_DEVMEM is not set
-# CONFIG_HWMON is not set
-# CONFIG_HID is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_VIRTIO_PCI=y
-# CONFIG_VIRTIO_PCI_LEGACY is not set
-# CONFIG_VHOST_MENU is not set
-# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set
-# CONFIG_FSL_ERRATUM_A008585 is not set
-# CONFIG_HISILICON_ERRATUM_161010101 is not set
-# CONFIG_ARM64_ERRATUM_858921 is not set
-# CONFIG_IOMMU_SUPPORT is not set
-# CONFIG_DNOTIFY is not set
-# CONFIG_INOTIFY_USER is not set
-CONFIG_TMPFS=y
-CONFIG_TMPFS_XATTR=y
-# CONFIG_MISC_FILESYSTEMS is not set
-CONFIG_XZ_DEC=y
-# CONFIG_SYMBOLIC_ERRNAME is not set
-# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/configs/crashdump_x86_64.defconfig b/configs/crashdump_x86_64.defconfig
deleted file mode 100644
index 5177d6e..0000000
--- a/configs/crashdump_x86_64.defconfig
+++ /dev/null
@@ -1,87 +0,0 @@
-# CONFIG_WERROR is not set
-# CONFIG_LOCALVERSION_AUTO is not set
-CONFIG_KERNEL_LZ4=y
-# CONFIG_SWAP is not set
-# CONFIG_CROSS_MEMORY_ATTACH is not set
-CONFIG_NO_HZ_IDLE=y
-CONFIG_PREEMPT=y
-CONFIG_LOG_BUF_SHIFT=12
-CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=12
-# CONFIG_UTS_NS is not set
-# CONFIG_TIME_NS is not set
-# CONFIG_PID_NS is not set
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_RD_GZIP is not set
-# CONFIG_RD_BZIP2 is not set
-# CONFIG_RD_LZMA is not set
-# CONFIG_RD_XZ is not set
-# CONFIG_RD_LZO is not set
-# CONFIG_RD_LZ4 is not set
-# CONFIG_RD_ZSTD is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
-# CONFIG_COMPAT_BRK is not set
-# CONFIG_SLAB_MERGE_DEFAULT is not set
-# CONFIG_RETPOLINE is not set
-# CONFIG_X86_EXTENDED_PLATFORM is not set
-# CONFIG_SCHED_OMIT_FRAME_POINTER is not set
-# CONFIG_X86_MCE is not set
-# CONFIG_PERF_EVENTS_AMD_UNCORE is not set
-# CONFIG_X86_IOPL_IOPERM is not set
-# CONFIG_MICROCODE is not set
-# CONFIG_X86_5LEVEL is not set
-# CONFIG_MTRR_SANITIZER is not set
-# CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS is not set
-CONFIG_CRASH_DUMP=y
-CONFIG_PHYSICAL_START=0x100000
-# CONFIG_RANDOMIZE_BASE is not set
-CONFIG_LEGACY_VSYSCALL_NONE=y
-# CONFIG_SUSPEND is not set
-# CONFIG_ACPI is not set
-# CONFIG_VIRTUALIZATION is not set
-CONFIG_JUMP_LABEL=y
-# CONFIG_SECCOMP is not set
-# CONFIG_STACKPROTECTOR is not set
-# CONFIG_VMAP_STACK is not set
-# CONFIG_MQ_IOSCHED_DEADLINE is not set
-# CONFIG_MQ_IOSCHED_KYBER is not set
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_BINFMT_SCRIPT is not set
-# CONFIG_SPARSEMEM_VMEMMAP is not set
-# CONFIG_COMPACTION is not set
-CONFIG_PCI=y
-CONFIG_PCI_ENDPOINT=y
-CONFIG_DEVTMPFS=y
-# CONFIG_STANDALONE is not set
-# CONFIG_PREVENT_FIRMWARE_BUILD is not set
-# CONFIG_DMIID is not set
-# CONFIG_BLK_DEV is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_SERIO_I8042 is not set
-# CONFIG_LEGACY_PTYS is not set
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_NR_UARTS=1
-CONFIG_SERIAL_8250_RUNTIME_UARTS=1
-CONFIG_SERIAL_8250_EXTENDED=y
-CONFIG_SERIAL_DEV_BUS=y
-# CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
-CONFIG_VIRTIO_CONSOLE=y
-# CONFIG_HW_RANDOM is not set
-# CONFIG_DEVMEM is not set
-# CONFIG_HWMON is not set
-# CONFIG_HID is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_VIRTIO_PCI=y
-# CONFIG_VIRTIO_PCI_LEGACY is not set
-# CONFIG_VHOST_MENU is not set
-# CONFIG_X86_PLATFORM_DEVICES is not set
-# CONFIG_IOMMU_SUPPORT is not set
-# CONFIG_DNOTIFY is not set
-# CONFIG_INOTIFY_USER is not set
-CONFIG_TMPFS=y
-# CONFIG_MISC_FILESYSTEMS is not set
-# CONFIG_PAGE_TABLE_ISOLATION is not set
-# CONFIG_SYMBOLIC_ERRNAME is not set
-# CONFIG_X86_VERBOSE_BOOTUP is not set
-# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/configs/microdroid_arm64.defconfig b/configs/microdroid_arm64.defconfig
deleted file mode 100644
index 4882f5e..0000000
--- a/configs/microdroid_arm64.defconfig
+++ /dev/null
@@ -1,215 +0,0 @@
-CONFIG_AUDIT=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_PREEMPT=y
-CONFIG_IRQ_TIME_ACCOUNTING=y
-CONFIG_PSI=y
-CONFIG_RCU_EXPERT=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_NOCB_CPU=y
-# CONFIG_UTS_NS is not set
-# CONFIG_TIME_NS is not set
-# CONFIG_PID_NS is not set
-# CONFIG_NET_NS is not set
-# CONFIG_RD_GZIP is not set
-# CONFIG_RD_BZIP2 is not set
-# CONFIG_RD_LZMA is not set
-# CONFIG_RD_XZ is not set
-# CONFIG_RD_LZO is not set
-# CONFIG_RD_ZSTD is not set
-CONFIG_BOOT_CONFIG=y
-CONFIG_PROFILING=y
-CONFIG_SCHED_MC=y
-CONFIG_NR_CPUS=32
-CONFIG_PARAVIRT_TIME_ACCOUNTING=y
-CONFIG_KEXEC_FILE=y
-CONFIG_ARM64_SW_TTBR0_PAN=y
-CONFIG_RANDOMIZE_BASE=y
-# CONFIG_RANDOMIZE_MODULE_REGION_FULL is not set
-CONFIG_CMDLINE="stack_depot_disable=on kasan.stacktrace=off cgroup_disable=pressure ioremap_guard panic=-1 bootconfig"
-CONFIG_CMDLINE_EXTEND=y
-# CONFIG_EFI is not set
-CONFIG_PM_WAKELOCKS=y
-CONFIG_PM_WAKELOCKS_LIMIT=0
-# CONFIG_PM_WAKELOCKS_GC is not set
-CONFIG_VIRTUALIZATION=y
-CONFIG_JUMP_LABEL=y
-CONFIG_SHADOW_CALL_STACK=y
-CONFIG_LTO_CLANG_FULL=y
-CONFIG_CFI_CLANG=y
-CONFIG_MODULES=y
-CONFIG_BLK_DEV_ZONED=y
-CONFIG_BLK_INLINE_ENCRYPTION=y
-CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y
-CONFIG_PARTITION_ADVANCED=y
-# CONFIG_MSDOS_PARTITION is not set
-CONFIG_IOSCHED_BFQ=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_BINFMT_MISC=y
-# CONFIG_SLAB_MERGE_DEFAULT is not set
-CONFIG_SLAB_FREELIST_RANDOM=y
-CONFIG_SLAB_FREELIST_HARDENED=y
-CONFIG_SHUFFLE_PAGE_ALLOCATOR=y
-# CONFIG_COMPAT_BRK is not set
-CONFIG_MEMORY_HOTPLUG=y
-CONFIG_MEMORY_HOTREMOVE=y
-CONFIG_DEFAULT_MMAP_MIN_ADDR=32768
-CONFIG_TRANSPARENT_HUGEPAGE=y
-CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
-CONFIG_ANON_VMA_NAME=y
-CONFIG_USERFAULTFD=y
-CONFIG_LRU_GEN=y
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_XFRM_USER=y
-CONFIG_XFRM_INTERFACE=y
-CONFIG_XFRM_MIGRATE=y
-CONFIG_XFRM_STATISTICS=y
-CONFIG_NET_KEY=y
-CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
-CONFIG_IP_ADVANCED_ROUTER=y
-CONFIG_IP_MULTIPLE_TABLES=y
-CONFIG_NET_IPIP=y
-CONFIG_NET_IPGRE_DEMUX=y
-CONFIG_NET_IPGRE=y
-CONFIG_NET_IPVTI=y
-CONFIG_INET_ESP=y
-CONFIG_INET_UDP_DIAG=y
-CONFIG_INET_DIAG_DESTROY=y
-CONFIG_IPV6_ROUTER_PREF=y
-CONFIG_IPV6_ROUTE_INFO=y
-CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_INET6_ESP=y
-CONFIG_INET6_IPCOMP=y
-CONFIG_IPV6_MIP6=y
-CONFIG_IPV6_VTI=y
-CONFIG_IPV6_GRE=y
-CONFIG_IPV6_MULTIPLE_TABLES=y
-CONFIG_VSOCKETS=y
-CONFIG_VIRTIO_VSOCKETS=y
-# CONFIG_WIRELESS is not set
-CONFIG_PCI=y
-CONFIG_PCIEPORTBUS=y
-CONFIG_PCIEAER=y
-CONFIG_PCI_IOV=y
-CONFIG_PCI_HOST_GENERIC=y
-CONFIG_PCIE_DW_PLAT_EP=y
-CONFIG_PCIE_KIRIN=y
-CONFIG_PCI_ENDPOINT=y
-CONFIG_FW_LOADER_USER_HELPER=y
-# CONFIG_FW_CACHE is not set
-CONFIG_ARM_SCMI_PROTOCOL=y
-# CONFIG_ARM_SCMI_POWER_DOMAIN is not set
-CONFIG_ZRAM=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_LOOP_MIN_COUNT=16
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_VIRTIO_BLK=y
-CONFIG_OPEN_DICE=y
-CONFIG_VCPU_STALL_DETECTOR=y
-CONFIG_MD=y
-CONFIG_BLK_DEV_DM=y
-CONFIG_DM_CRYPT=y
-CONFIG_DM_INIT=y
-CONFIG_DM_UEVENT=y
-CONFIG_DM_VERITY=y
-CONFIG_DM_VERITY_FEC=y
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_LEGACY_PTYS is not set
-CONFIG_SERIAL_8250=y
-# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
-CONFIG_SERIAL_8250_CONSOLE=y
-# CONFIG_SERIAL_8250_PCI is not set
-CONFIG_SERIAL_8250_RUNTIME_UARTS=0
-CONFIG_SERIAL_OF_PLATFORM=y
-CONFIG_NULL_TTY=y
-CONFIG_VIRTIO_CONSOLE=y
-CONFIG_HW_RANDOM=y
-CONFIG_HW_RANDOM_CCTRNG=y
-# CONFIG_DEVMEM is not set
-# CONFIG_DEVPORT is not set
-CONFIG_POWER_RESET_SYSCON=y
-# CONFIG_HWMON is not set
-CONFIG_WATCHDOG=y
-CONFIG_WATCHDOG_CORE=y
-# CONFIG_HID is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_EDAC=y
-CONFIG_RTC_CLASS=y
-# CONFIG_RTC_NVMEM is not set
-CONFIG_RTC_DRV_PL030=y
-CONFIG_RTC_DRV_PL031=y
-CONFIG_DMABUF_HEAPS=y
-CONFIG_DMABUF_SYSFS_STATS=y
-CONFIG_UIO=y
-CONFIG_VIRTIO_PCI=y
-CONFIG_VIRTIO_BALLOON=y
-CONFIG_STAGING=y
-CONFIG_HWSPINLOCK=y
-CONFIG_EXT4_FS=y
-# CONFIG_EXT4_USE_FOR_EXT2 is not set
-CONFIG_EXT4_FS_POSIX_ACL=y
-CONFIG_EXT4_FS_SECURITY=y
-# CONFIG_DNOTIFY is not set
-CONFIG_FUSE_FS=y
-CONFIG_TMPFS=y
-CONFIG_EROFS_FS=y
-# CONFIG_NETWORK_FILESYSTEMS is not set
-CONFIG_UNICODE=y
-CONFIG_SECURITY=y
-CONFIG_SECURITYFS=y
-CONFIG_SECURITY_NETWORK=y
-CONFIG_HARDENED_USERCOPY=y
-CONFIG_STATIC_USERMODEHELPER=y
-CONFIG_STATIC_USERMODEHELPER_PATH=""
-CONFIG_SECURITY_SELINUX=y
-CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y
-CONFIG_CRYPTO_DES=y
-CONFIG_CRYPTO_ADIANTUM=y
-CONFIG_CRYPTO_HCTR2=y
-CONFIG_CRYPTO_CHACHA20POLY1305=y
-CONFIG_CRYPTO_BLAKE2B=y
-CONFIG_CRYPTO_MD5=y
-CONFIG_CRYPTO_SHA1=y
-CONFIG_CRYPTO_XCBC=y
-CONFIG_CRYPTO_LZO=y
-CONFIG_CRYPTO_LZ4=y
-CONFIG_CRYPTO_ZSTD=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
-CONFIG_TRACE_MMIO_ACCESS=y
-CONFIG_XZ_DEC=y
-CONFIG_DMA_RESTRICTED_POOL=y
-CONFIG_PRINTK_TIME=y
-CONFIG_PRINTK_CALLER=y
-CONFIG_DYNAMIC_DEBUG_CORE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_INFO_DWARF5=y
-CONFIG_DEBUG_INFO_REDUCED=y
-CONFIG_DEBUG_INFO_COMPRESSED=y
-CONFIG_HEADERS_INSTALL=y
-# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_UBSAN=y
-CONFIG_UBSAN_TRAP=y
-CONFIG_UBSAN_LOCAL_BOUNDS=y
-# CONFIG_UBSAN_SHIFT is not set
-# CONFIG_UBSAN_BOOL is not set
-# CONFIG_UBSAN_ENUM is not set
-CONFIG_PAGE_OWNER=y
-CONFIG_DEBUG_STACK_USAGE=y
-CONFIG_KASAN=y
-CONFIG_KASAN_HW_TAGS=y
-CONFIG_PANIC_ON_OOPS=y
-CONFIG_PANIC_TIMEOUT=-1
-CONFIG_SOFTLOCKUP_DETECTOR=y
-CONFIG_WQ_WATCHDOG=y
-CONFIG_SCHEDSTATS=y
-# CONFIG_DEBUG_PREEMPT is not set
-CONFIG_BUG_ON_DATA_CORRUPTION=y
-CONFIG_HIST_TRIGGERS=y
-CONFIG_PID_IN_CONTEXTIDR=y
-# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/configs/microdroid_x86_64.defconfig b/configs/microdroid_x86_64.defconfig
deleted file mode 100644
index e52a9f5..0000000
--- a/configs/microdroid_x86_64.defconfig
+++ /dev/null
@@ -1,291 +0,0 @@
-CONFIG_KERNEL_LZ4=y
-CONFIG_AUDIT=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_PREEMPT=y
-CONFIG_IRQ_TIME_ACCOUNTING=y
-CONFIG_TASKSTATS=y
-CONFIG_TASK_XACCT=y
-CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_PSI=y
-CONFIG_RCU_EXPERT=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_NOCB_CPU=y
-CONFIG_UCLAMP_TASK=y
-CONFIG_UCLAMP_BUCKETS_COUNT=20
-CONFIG_CGROUPS=y
-CONFIG_MEMCG=y
-CONFIG_BLK_CGROUP=y
-CONFIG_CGROUP_SCHED=y
-CONFIG_UCLAMP_TASK_GROUP=y
-CONFIG_CGROUP_FREEZER=y
-CONFIG_CPUSETS=y
-CONFIG_CGROUP_CPUACCT=y
-# CONFIG_UTS_NS is not set
-# CONFIG_TIME_NS is not set
-# CONFIG_PID_NS is not set
-# CONFIG_NET_NS is not set
-# CONFIG_RD_BZIP2 is not set
-# CONFIG_RD_LZMA is not set
-# CONFIG_RD_XZ is not set
-# CONFIG_RD_LZO is not set
-CONFIG_BOOT_CONFIG=y
-CONFIG_PROFILING=y
-CONFIG_SMP=y
-CONFIG_X86_X2APIC=y
-CONFIG_HYPERVISOR_GUEST=y
-CONFIG_PARAVIRT=y
-CONFIG_PARAVIRT_TIME_ACCOUNTING=y
-CONFIG_NR_CPUS=32
-# CONFIG_X86_MCE is not set
-CONFIG_EFI=y
-CONFIG_KEXEC_FILE=y
-CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE="stack_depot_disable=on cgroup_disable=pressure ioremap_guard panic=-1 bootconfig acpi=noirq"
-CONFIG_PM_WAKELOCKS=y
-CONFIG_PM_WAKELOCKS_LIMIT=0
-# CONFIG_PM_WAKELOCKS_GC is not set
-CONFIG_CPU_FREQ_STAT=y
-CONFIG_CPU_FREQ_TIMES=y
-CONFIG_CPU_FREQ_GOV_POWERSAVE=y
-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
-CONFIG_JUMP_LABEL=y
-CONFIG_LTO_CLANG_FULL=y
-CONFIG_MODULES=y
-CONFIG_BLK_DEV_ZONED=y
-CONFIG_BLK_CGROUP_IOCOST=y
-CONFIG_BLK_INLINE_ENCRYPTION=y
-CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y
-CONFIG_PARTITION_ADVANCED=y
-# CONFIG_MSDOS_PARTITION is not set
-CONFIG_IOSCHED_BFQ=y
-CONFIG_BFQ_GROUP_IOSCHED=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_BINFMT_MISC=y
-# CONFIG_SLAB_MERGE_DEFAULT is not set
-CONFIG_SLAB_FREELIST_RANDOM=y
-CONFIG_SLAB_FREELIST_HARDENED=y
-CONFIG_SHUFFLE_PAGE_ALLOCATOR=y
-# CONFIG_COMPAT_BRK is not set
-CONFIG_MEMORY_HOTPLUG=y
-CONFIG_MEMORY_HOTREMOVE=y
-CONFIG_DEFAULT_MMAP_MIN_ADDR=32768
-CONFIG_TRANSPARENT_HUGEPAGE=y
-CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
-CONFIG_ANON_VMA_NAME=y
-CONFIG_USERFAULTFD=y
-CONFIG_LRU_GEN=y
-CONFIG_DAMON=y
-CONFIG_DAMON_PADDR=y
-CONFIG_DAMON_RECLAIM=y
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_XFRM_USER=y
-CONFIG_XFRM_INTERFACE=y
-CONFIG_XFRM_MIGRATE=y
-CONFIG_XFRM_STATISTICS=y
-CONFIG_NET_KEY=y
-CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
-CONFIG_IP_ADVANCED_ROUTER=y
-CONFIG_IP_MULTIPLE_TABLES=y
-CONFIG_NET_IPIP=y
-CONFIG_NET_IPGRE_DEMUX=y
-CONFIG_NET_IPGRE=y
-CONFIG_NET_IPVTI=y
-CONFIG_INET_ESP=y
-CONFIG_INET_UDP_DIAG=y
-CONFIG_INET_DIAG_DESTROY=y
-CONFIG_IPV6_ROUTER_PREF=y
-CONFIG_IPV6_ROUTE_INFO=y
-CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_INET6_ESP=y
-CONFIG_INET6_IPCOMP=y
-CONFIG_IPV6_MIP6=y
-CONFIG_IPV6_VTI=y
-CONFIG_IPV6_GRE=y
-CONFIG_IPV6_MULTIPLE_TABLES=y
-CONFIG_VSOCKETS=y
-CONFIG_VIRTIO_VSOCKETS=y
-CONFIG_CGROUP_NET_PRIO=y
-# CONFIG_WIRELESS is not set
-CONFIG_PCI=y
-CONFIG_PCIEPORTBUS=y
-CONFIG_PCIEAER=y
-CONFIG_PCI_MSI=y
-CONFIG_PCI_IOV=y
-CONFIG_PCIE_DW_PLAT_EP=y
-CONFIG_PCI_ENDPOINT=y
-CONFIG_FW_LOADER_USER_HELPER=y
-# CONFIG_FW_CACHE is not set
-CONFIG_OF=y
-CONFIG_ZRAM=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_LOOP_MIN_COUNT=16
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_VIRTIO_BLK=y
-CONFIG_MD=y
-CONFIG_BLK_DEV_DM=y
-CONFIG_DM_CRYPT=y
-CONFIG_DM_INIT=y
-CONFIG_DM_UEVENT=y
-CONFIG_DM_VERITY=y
-CONFIG_DM_VERITY_FEC=y
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_LEGACY_PTYS is not set
-CONFIG_SERIAL_8250=y
-# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
-CONFIG_SERIAL_8250_CONSOLE=y
-# CONFIG_SERIAL_8250_PCI is not set
-CONFIG_SERIAL_8250_RUNTIME_UARTS=0
-CONFIG_SERIAL_OF_PLATFORM=y
-CONFIG_NULL_TTY=y
-CONFIG_VIRTIO_CONSOLE=y
-CONFIG_HW_RANDOM=y
-CONFIG_HW_RANDOM_VIRTIO=y
-# CONFIG_DEVMEM is not set
-# CONFIG_DEVPORT is not set
-CONFIG_HPET=y
-CONFIG_GPIOLIB=y
-CONFIG_GPIO_GENERIC_PLATFORM=y
-# CONFIG_HWMON is not set
-CONFIG_THERMAL_NETLINK=y
-CONFIG_THERMAL_STATISTICS=y
-CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=100
-CONFIG_THERMAL_WRITABLE_TRIPS=y
-CONFIG_THERMAL_GOV_USER_SPACE=y
-CONFIG_CPU_THERMAL=y
-CONFIG_THERMAL_EMULATION=y
-# CONFIG_X86_PKG_TEMP_THERMAL is not set
-CONFIG_WATCHDOG=y
-CONFIG_WATCHDOG_CORE=y
-CONFIG_MFD_SYSCON=y
-# CONFIG_HID is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_EDAC=y
-CONFIG_RTC_CLASS=y
-CONFIG_DMABUF_HEAPS=y
-CONFIG_DMABUF_SYSFS_STATS=y
-CONFIG_UIO=y
-CONFIG_VIRTIO_PCI=y
-CONFIG_VIRTIO_BALLOON=y
-CONFIG_STAGING=y
-CONFIG_LIBNVDIMM=y
-CONFIG_EXT4_FS=y
-# CONFIG_EXT4_USE_FOR_EXT2 is not set
-CONFIG_EXT4_FS_POSIX_ACL=y
-CONFIG_EXT4_FS_SECURITY=y
-# CONFIG_DNOTIFY is not set
-CONFIG_FUSE_FS=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-# CONFIG_EFIVAR_FS is not set
-CONFIG_EROFS_FS=y
-# CONFIG_NETWORK_FILESYSTEMS is not set
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_CODEPAGE_737=y
-CONFIG_NLS_CODEPAGE_775=y
-CONFIG_NLS_CODEPAGE_850=y
-CONFIG_NLS_CODEPAGE_852=y
-CONFIG_NLS_CODEPAGE_855=y
-CONFIG_NLS_CODEPAGE_857=y
-CONFIG_NLS_CODEPAGE_860=y
-CONFIG_NLS_CODEPAGE_861=y
-CONFIG_NLS_CODEPAGE_862=y
-CONFIG_NLS_CODEPAGE_863=y
-CONFIG_NLS_CODEPAGE_864=y
-CONFIG_NLS_CODEPAGE_865=y
-CONFIG_NLS_CODEPAGE_866=y
-CONFIG_NLS_CODEPAGE_869=y
-CONFIG_NLS_CODEPAGE_936=y
-CONFIG_NLS_CODEPAGE_950=y
-CONFIG_NLS_CODEPAGE_932=y
-CONFIG_NLS_CODEPAGE_949=y
-CONFIG_NLS_CODEPAGE_874=y
-CONFIG_NLS_ISO8859_8=y
-CONFIG_NLS_CODEPAGE_1250=y
-CONFIG_NLS_CODEPAGE_1251=y
-CONFIG_NLS_ASCII=y
-CONFIG_NLS_ISO8859_1=y
-CONFIG_NLS_ISO8859_2=y
-CONFIG_NLS_ISO8859_3=y
-CONFIG_NLS_ISO8859_4=y
-CONFIG_NLS_ISO8859_5=y
-CONFIG_NLS_ISO8859_6=y
-CONFIG_NLS_ISO8859_7=y
-CONFIG_NLS_ISO8859_9=y
-CONFIG_NLS_ISO8859_13=y
-CONFIG_NLS_ISO8859_14=y
-CONFIG_NLS_ISO8859_15=y
-CONFIG_NLS_KOI8_R=y
-CONFIG_NLS_KOI8_U=y
-CONFIG_NLS_MAC_ROMAN=y
-CONFIG_NLS_MAC_CELTIC=y
-CONFIG_NLS_MAC_CENTEURO=y
-CONFIG_NLS_MAC_CROATIAN=y
-CONFIG_NLS_MAC_CYRILLIC=y
-CONFIG_NLS_MAC_GAELIC=y
-CONFIG_NLS_MAC_GREEK=y
-CONFIG_NLS_MAC_ICELAND=y
-CONFIG_NLS_MAC_INUIT=y
-CONFIG_NLS_MAC_ROMANIAN=y
-CONFIG_NLS_MAC_TURKISH=y
-CONFIG_NLS_UTF8=y
-CONFIG_UNICODE=y
-CONFIG_SECURITY=y
-CONFIG_SECURITYFS=y
-CONFIG_SECURITY_NETWORK=y
-CONFIG_HARDENED_USERCOPY=y
-CONFIG_STATIC_USERMODEHELPER=y
-CONFIG_STATIC_USERMODEHELPER_PATH=""
-CONFIG_SECURITY_SELINUX=y
-CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y
-CONFIG_CRYPTO_DES=y
-CONFIG_CRYPTO_ADIANTUM=y
-CONFIG_CRYPTO_HCTR2=y
-CONFIG_CRYPTO_CHACHA20POLY1305=y
-CONFIG_CRYPTO_BLAKE2B=y
-CONFIG_CRYPTO_MD5=y
-CONFIG_CRYPTO_SHA1=y
-CONFIG_CRYPTO_XCBC=y
-CONFIG_CRYPTO_LZO=y
-CONFIG_CRYPTO_LZ4=y
-CONFIG_CRYPTO_ZSTD=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
-CONFIG_CRYPTO_AES_NI_INTEL=y
-CONFIG_CRYPTO_POLYVAL_CLMUL_NI=y
-CONFIG_CRYPTO_SHA256_SSSE3=y
-CONFIG_CRYPTO_SHA512_SSSE3=y
-CONFIG_CRC8=y
-CONFIG_XZ_DEC=y
-CONFIG_PRINTK_TIME=y
-CONFIG_DYNAMIC_DEBUG_CORE=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_INFO_DWARF5=y
-CONFIG_DEBUG_INFO_REDUCED=y
-CONFIG_DEBUG_INFO_COMPRESSED=y
-CONFIG_HEADERS_INSTALL=y
-# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_UBSAN=y
-CONFIG_UBSAN_TRAP=y
-CONFIG_UBSAN_LOCAL_BOUNDS=y
-# CONFIG_UBSAN_SHIFT is not set
-# CONFIG_UBSAN_BOOL is not set
-# CONFIG_UBSAN_ENUM is not set
-CONFIG_PAGE_OWNER=y
-CONFIG_DEBUG_STACK_USAGE=y
-CONFIG_KFENCE=y
-CONFIG_KFENCE_SAMPLE_INTERVAL=500
-CONFIG_KFENCE_NUM_OBJECTS=63
-CONFIG_PANIC_ON_OOPS=y
-CONFIG_PANIC_TIMEOUT=-1
-CONFIG_SOFTLOCKUP_DETECTOR=y
-CONFIG_WQ_WATCHDOG=y
-CONFIG_SCHEDSTATS=y
-CONFIG_BUG_ON_DATA_CORRUPTION=y
-CONFIG_HIST_TRIGGERS=y
-CONFIG_UNWINDER_FRAME_POINTER=y
diff --git a/modules.options b/modules.options
new file mode 100644
index 0000000..eeb5e08
--- /dev/null
+++ b/modules.options
@@ -0,0 +1,4 @@
+# Reduce slab size usage from virtio vsock to reduce slab fragmentation
+options vmw_vsock_virtio_transport_common virtio_transport_max_vsock_pkt_buf_size=16384
+# TODO(b/176860479): Remove once goldfish and cuttlefish share a wifi implementation
+options mac80211_hwsim radios=0
diff --git a/uapi/linux/virtio_video.h b/uapi/linux/virtio_video.h
new file mode 100644
index 0000000..70247e8
--- /dev/null
+++ b/uapi/linux/virtio_video.h
@@ -0,0 +1,495 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Virtio Video Device
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Copyright (C) 2020 OpenSynergy GmbH.
+ */
+
+#ifndef _UAPI_LINUX_VIRTIO_VIDEO_H
+#define _UAPI_LINUX_VIRTIO_VIDEO_H
+
+#include <linux/types.h>
+#include <linux/virtio_config.h>
+
+enum virtio_video_device_type {
+ VIRTIO_VIDEO_DEVICE_ENCODER = 0x0100,
+ VIRTIO_VIDEO_DEVICE_DECODER,
+ VIRTIO_VIDEO_DEVICE_CAMERA,
+};
+
+/*
+ * Feature bits
+ */
+enum {
+ /* Guest pages can be used for video buffers. */
+ VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES = 0,
+ /* The host can process buffers even if they are non-contiguous memory
+ such as scatter-gather lists. */
+ VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG = 1,
+ /* Support of vendor virtqueues */
+ VIRTIO_VIDEO_F_VENDOR = 2
+};
+
+
+#define VIRTIO_VIDEO_MAX_PLANES 8
+
+/*
+ * Image formats
+ */
+
+enum virtio_video_format {
+ /* Raw formats */
+ VIRTIO_VIDEO_FORMAT_RAW_MIN = 1,
+ VIRTIO_VIDEO_FORMAT_ARGB8888 = VIRTIO_VIDEO_FORMAT_RAW_MIN,
+ VIRTIO_VIDEO_FORMAT_BGRA8888,
+ VIRTIO_VIDEO_FORMAT_RGBA8888,
+ VIRTIO_VIDEO_FORMAT_NV12, /* 12 Y/CbCr 4:2:0 */
+ VIRTIO_VIDEO_FORMAT_YUV420, /* 12 YUV 4:2:0 */
+ VIRTIO_VIDEO_FORMAT_YVU420, /* 12 YVU 4:2:0 */
+ VIRTIO_VIDEO_FORMAT_YUV422, /* 16 YUV 4:2:2 */
+ VIRTIO_VIDEO_FORMAT_RAW_MAX = VIRTIO_VIDEO_FORMAT_YUV422,
+
+ /* Coded formats */
+ VIRTIO_VIDEO_FORMAT_CODED_MIN = 0x1000,
+ VIRTIO_VIDEO_FORMAT_MPEG2 =
+ VIRTIO_VIDEO_FORMAT_CODED_MIN, /* MPEG-2 Part 2 */
+ VIRTIO_VIDEO_FORMAT_MPEG4, /* MPEG-4 Part 2 */
+ VIRTIO_VIDEO_FORMAT_H264, /* H.264 */
+ VIRTIO_VIDEO_FORMAT_HEVC, /* HEVC aka H.265*/
+ VIRTIO_VIDEO_FORMAT_VP8, /* VP8 */
+ VIRTIO_VIDEO_FORMAT_VP9, /* VP9 */
+ VIRTIO_VIDEO_FORMAT_CODED_MAX = VIRTIO_VIDEO_FORMAT_VP9,
+};
+
+enum virtio_video_profile {
+ /* H.264 */
+ VIRTIO_VIDEO_PROFILE_H264_MIN = 0x100,
+ VIRTIO_VIDEO_PROFILE_H264_BASELINE = VIRTIO_VIDEO_PROFILE_H264_MIN,
+ VIRTIO_VIDEO_PROFILE_H264_MAIN,
+ VIRTIO_VIDEO_PROFILE_H264_EXTENDED,
+ VIRTIO_VIDEO_PROFILE_H264_HIGH,
+ VIRTIO_VIDEO_PROFILE_H264_HIGH10PROFILE,
+ VIRTIO_VIDEO_PROFILE_H264_HIGH422PROFILE,
+ VIRTIO_VIDEO_PROFILE_H264_HIGH444PREDICTIVEPROFILE,
+ VIRTIO_VIDEO_PROFILE_H264_SCALABLEBASELINE,
+ VIRTIO_VIDEO_PROFILE_H264_SCALABLEHIGH,
+ VIRTIO_VIDEO_PROFILE_H264_STEREOHIGH,
+ VIRTIO_VIDEO_PROFILE_H264_MULTIVIEWHIGH,
+ VIRTIO_VIDEO_PROFILE_H264_MAX = VIRTIO_VIDEO_PROFILE_H264_MULTIVIEWHIGH,
+
+ /* HEVC */
+ VIRTIO_VIDEO_PROFILE_HEVC_MIN = 0x200,
+ VIRTIO_VIDEO_PROFILE_HEVC_MAIN = VIRTIO_VIDEO_PROFILE_HEVC_MIN,
+ VIRTIO_VIDEO_PROFILE_HEVC_MAIN10,
+ VIRTIO_VIDEO_PROFILE_HEVC_MAIN_STILL_PICTURE,
+ VIRTIO_VIDEO_PROFILE_HEVC_MAX =
+ VIRTIO_VIDEO_PROFILE_HEVC_MAIN_STILL_PICTURE,
+
+ /* VP8 */
+ VIRTIO_VIDEO_PROFILE_VP8_MIN = 0x300,
+ VIRTIO_VIDEO_PROFILE_VP8_PROFILE0 = VIRTIO_VIDEO_PROFILE_VP8_MIN,
+ VIRTIO_VIDEO_PROFILE_VP8_PROFILE1,
+ VIRTIO_VIDEO_PROFILE_VP8_PROFILE2,
+ VIRTIO_VIDEO_PROFILE_VP8_PROFILE3,
+ VIRTIO_VIDEO_PROFILE_VP8_MAX = VIRTIO_VIDEO_PROFILE_VP8_PROFILE3,
+
+ /* VP9 */
+ VIRTIO_VIDEO_PROFILE_VP9_MIN = 0x400,
+ VIRTIO_VIDEO_PROFILE_VP9_PROFILE0 = VIRTIO_VIDEO_PROFILE_VP9_MIN,
+ VIRTIO_VIDEO_PROFILE_VP9_PROFILE1,
+ VIRTIO_VIDEO_PROFILE_VP9_PROFILE2,
+ VIRTIO_VIDEO_PROFILE_VP9_PROFILE3,
+ VIRTIO_VIDEO_PROFILE_VP9_MAX = VIRTIO_VIDEO_PROFILE_VP9_PROFILE3,
+};
+
+enum virtio_video_level {
+ /* H.264 */
+ VIRTIO_VIDEO_LEVEL_H264_MIN = 0x100,
+ VIRTIO_VIDEO_LEVEL_H264_1_0 = VIRTIO_VIDEO_LEVEL_H264_MIN,
+ VIRTIO_VIDEO_LEVEL_H264_1_1,
+ VIRTIO_VIDEO_LEVEL_H264_1_2,
+ VIRTIO_VIDEO_LEVEL_H264_1_3,
+ VIRTIO_VIDEO_LEVEL_H264_2_0,
+ VIRTIO_VIDEO_LEVEL_H264_2_1,
+ VIRTIO_VIDEO_LEVEL_H264_2_2,
+ VIRTIO_VIDEO_LEVEL_H264_3_0,
+ VIRTIO_VIDEO_LEVEL_H264_3_1,
+ VIRTIO_VIDEO_LEVEL_H264_3_2,
+ VIRTIO_VIDEO_LEVEL_H264_4_0,
+ VIRTIO_VIDEO_LEVEL_H264_4_1,
+ VIRTIO_VIDEO_LEVEL_H264_4_2,
+ VIRTIO_VIDEO_LEVEL_H264_5_0,
+ VIRTIO_VIDEO_LEVEL_H264_5_1,
+ VIRTIO_VIDEO_LEVEL_H264_MAX = VIRTIO_VIDEO_LEVEL_H264_5_1,
+};
+
+/*
+ * Config
+ */
+
+struct virtio_video_config {
+ __le32 version;
+ __le32 max_caps_length;
+ __le32 max_resp_length;
+};
+
+/*
+ * Commands
+ */
+
+enum virtio_video_cmd_type {
+ /* Command */
+ VIRTIO_VIDEO_CMD_QUERY_CAPABILITY = 0x0100,
+ VIRTIO_VIDEO_CMD_STREAM_CREATE,
+ VIRTIO_VIDEO_CMD_STREAM_DESTROY,
+ VIRTIO_VIDEO_CMD_STREAM_DRAIN,
+ VIRTIO_VIDEO_CMD_RESOURCE_ATTACH,
+ VIRTIO_VIDEO_CMD_RESOURCE_QUEUE,
+ VIRTIO_VIDEO_CMD_QUEUE_DETACH_RESOURCES,
+ VIRTIO_VIDEO_CMD_QUEUE_CLEAR,
+ VIRTIO_VIDEO_CMD_GET_PARAMS,
+ VIRTIO_VIDEO_CMD_SET_PARAMS,
+ VIRTIO_VIDEO_CMD_QUERY_CONTROL,
+ VIRTIO_VIDEO_CMD_GET_CONTROL,
+ VIRTIO_VIDEO_CMD_SET_CONTROL,
+
+ /* Response */
+ VIRTIO_VIDEO_RESP_OK_NODATA = 0x0200,
+ VIRTIO_VIDEO_RESP_OK_QUERY_CAPABILITY,
+ VIRTIO_VIDEO_RESP_OK_RESOURCE_QUEUE,
+ VIRTIO_VIDEO_RESP_OK_GET_PARAMS,
+ VIRTIO_VIDEO_RESP_OK_QUERY_CONTROL,
+ VIRTIO_VIDEO_RESP_OK_GET_CONTROL,
+
+ VIRTIO_VIDEO_RESP_ERR_INVALID_OPERATION = 0x0300,
+ VIRTIO_VIDEO_RESP_ERR_OUT_OF_MEMORY,
+ VIRTIO_VIDEO_RESP_ERR_INVALID_STREAM_ID,
+ VIRTIO_VIDEO_RESP_ERR_INVALID_RESOURCE_ID,
+ VIRTIO_VIDEO_RESP_ERR_INVALID_PARAMETER,
+ VIRTIO_VIDEO_RESP_ERR_UNSUPPORTED_CONTROL,
+};
+
+struct virtio_video_cmd_hdr {
+ __le32 type; /* One of enum virtio_video_cmd_type */
+ __le32 stream_id;
+};
+
+/* VIRTIO_VIDEO_CMD_QUERY_CAPABILITY */
+enum virtio_video_queue_type {
+ VIRTIO_VIDEO_QUEUE_TYPE_INPUT = 0x100,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT,
+};
+
+struct virtio_video_query_capability {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 queue_type; /* One of VIRTIO_VIDEO_QUEUE_TYPE_* types */
+ __u8 padding[4];
+};
+
+enum virtio_video_planes_layout_flag {
+ VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER = 1 << 0,
+ VIRTIO_VIDEO_PLANES_LAYOUT_PER_PLANE = 1 << 1,
+};
+
+struct virtio_video_format_range {
+ __le32 min;
+ __le32 max;
+ __le32 step;
+ __u8 padding[4];
+};
+
+struct virtio_video_format_frame {
+ struct virtio_video_format_range width;
+ struct virtio_video_format_range height;
+ __le32 num_rates;
+ __u8 padding[4];
+ /* Followed by struct virtio_video_format_range frame_rates[] */
+};
+
+struct virtio_video_format_desc {
+ __le64 mask;
+ __le32 format; /* One of VIRTIO_VIDEO_FORMAT_* types */
+ __le32 planes_layout; /* Bitmask with VIRTIO_VIDEO_PLANES_LAYOUT_* */
+ __le32 plane_align;
+ __le32 num_frames;
+ /* Followed by struct virtio_video_format_frame frames[] */
+};
+
+struct virtio_video_query_capability_resp {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 num_descs;
+ __u8 padding[4];
+ /* Followed by struct virtio_video_format_desc descs[] */
+};
+
+/* VIRTIO_VIDEO_CMD_STREAM_CREATE */
+enum virtio_video_mem_type {
+ VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES,
+};
+
+struct virtio_video_stream_create {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 in_mem_type; /* One of VIRTIO_VIDEO_MEM_TYPE_* types */
+ __le32 out_mem_type; /* One of VIRTIO_VIDEO_MEM_TYPE_* types */
+ __le32 coded_format; /* One of VIRTIO_VIDEO_FORMAT_* types */
+ __u8 padding[4];
+ __u8 tag[64];
+};
+
+/* VIRTIO_VIDEO_CMD_STREAM_DESTROY */
+struct virtio_video_stream_destroy {
+ struct virtio_video_cmd_hdr hdr;
+};
+
+/* VIRTIO_VIDEO_CMD_STREAM_DRAIN */
+struct virtio_video_stream_drain {
+ struct virtio_video_cmd_hdr hdr;
+};
+
+/* VIRTIO_VIDEO_CMD_RESOURCE_ATTACH */
+struct virtio_video_resource_object {
+ __u8 uuid [16];
+};
+
+struct virtio_video_resource_sg_entry {
+ __le64 addr;
+ __le32 length;
+ __u8 padding[4];
+};
+
+struct virtio_video_resource_sg_list {
+ __le32 num_entries;
+ __u8 padding[4];
+ struct virtio_video_resource_sg_entry entries[];
+};
+#define VIRTIO_VIDEO_RESOURCE_SG_SIZE(n) \
+ offsetof(struct virtio_video_resource_sg_list, entries[n])
+
+union virtio_video_resource {
+ struct virtio_video_resource_sg_list sg_list;
+ struct virtio_video_resource_object object;
+};
+
+struct virtio_video_resource_attach {
+ __le32 cmd_type;
+ __le32 stream_id;
+ __le32 queue_type; /* VIRTIO_VIDEO_QUEUE_TYPE_* */
+ __le32 resource_id;
+ /* Followed by struct virtio_video_resource resources[] */
+};
+
+/* VIRTIO_VIDEO_CMD_RESOURCE_QUEUE */
+struct virtio_video_resource_queue {
+ __le32 cmd_type;
+ __le32 stream_id;
+ __le32 queue_type; /* VIRTIO_VIDEO_QUEUE_TYPE_* */
+ __le32 resource_id;
+ __le32 flags; /* Bitmask with VIRTIO_VIDEO_ENQUEUE_FLAG_ * */
+ __u8 padding[4];
+ __le64 timestamp;
+ __le32 data_sizes[VIRTIO_VIDEO_MAX_PLANES];
+};
+
+enum virtio_video_dequeue_flag {
+ VIRTIO_VIDEO_DEQUEUE_FLAG_ERR = 0,
+ VIRTIO_VIDEO_DEQUEUE_FLAG_EOS,
+
+ /* Encoder only */
+ VIRTIO_VIDEO_DEQUEUE_FLAG_KEY_FRAME,
+ VIRTIO_VIDEO_DEQUEUE_FLAG_PFRAME,
+ VIRTIO_VIDEO_DEQUEUE_FLAG_BFRAME,
+};
+
+struct virtio_video_resource_queue_resp {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 flags;
+ __le64 timestamp;
+ __le32 data_sizes[VIRTIO_VIDEO_MAX_PLANES];
+};
+
+/* VIRTIO_VIDEO_CMD_QUEUE_DETACH_RESOURCES */
+struct virtio_video_queue_detach_resources {
+ __le32 cmd_type;
+ __le32 stream_id;
+ __le32 queue_type; /* One of VIRTIO_VIDEO_QUEUE_TYPE_* types */
+ __u8 padding[4];
+};
+
+/* VIRTIO_VIDEO_CMD_QUEUE_CLEAR */
+struct virtio_video_queue_clear {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 queue_type; /* One of VIRTIO_VIDEO_QUEUE_TYPE_* types */
+ __u8 padding[4];
+};
+
+/* VIRTIO_VIDEO_CMD_GET_PARAMS */
+struct virtio_video_plane_format {
+ __le32 plane_size;
+ __le32 stride;
+};
+
+struct virtio_video_crop {
+ __le32 left;
+ __le32 top;
+ __le32 width;
+ __le32 height;
+};
+
+struct virtio_video_params {
+ __le32 queue_type; /* One of VIRTIO_VIDEO_QUEUE_TYPE_* types */
+ __le32 format; /* One of VIRTIO_VIDEO_FORMAT_* types */
+ __le32 frame_width;
+ __le32 frame_height;
+ __le32 min_buffers;
+ __le32 max_buffers;
+ struct virtio_video_crop crop;
+ __le32 frame_rate;
+ __le32 num_planes;
+ struct virtio_video_plane_format plane_formats[VIRTIO_VIDEO_MAX_PLANES];
+};
+
+struct virtio_video_get_params {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 queue_type; /* One of VIRTIO_VIDEO_QUEUE_TYPE_* types */
+ __u8 padding[4];
+};
+
+struct virtio_video_get_params_resp {
+ struct virtio_video_cmd_hdr hdr;
+ struct virtio_video_params params;
+};
+
+/* VIRTIO_VIDEO_CMD_SET_PARAMS */
+struct virtio_video_set_params {
+ struct virtio_video_cmd_hdr hdr;
+ struct virtio_video_params params;
+};
+
+/* VIRTIO_VIDEO_CMD_QUERY_CONTROL */
+enum virtio_video_control_type {
+ VIRTIO_VIDEO_CONTROL_BITRATE = 1,
+ VIRTIO_VIDEO_CONTROL_PROFILE,
+ VIRTIO_VIDEO_CONTROL_LEVEL,
+};
+
+struct virtio_video_query_control_profile {
+ __le32 format; /* One of VIRTIO_VIDEO_FORMAT_* */
+ __u8 padding[4];
+};
+
+struct virtio_video_query_control_level {
+ __le32 format; /* One of VIRTIO_VIDEO_FORMAT_* */
+ __u8 padding[4];
+};
+
+struct virtio_video_query_control {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 control; /* One of VIRTIO_VIDEO_CONTROL_* types */
+ __u8 padding[4];
+ /*
+ * Followed by a value of struct virtio_video_query_control_*
+ * in accordance with the value of control.
+ */
+};
+
+struct virtio_video_query_control_resp_profile {
+ __le32 num;
+ __u8 padding[4];
+ /* Followed by an array le32 profiles[] */
+};
+
+struct virtio_video_query_control_resp_level {
+ __le32 num;
+ __u8 padding[4];
+ /* Followed by an array le32 level[] */
+};
+
+struct virtio_video_query_control_resp {
+ struct virtio_video_cmd_hdr hdr;
+ /* Followed by one of struct virtio_video_query_control_resp_* */
+};
+
+/* VIRTIO_VIDEO_CMD_GET_CONTROL */
+struct virtio_video_get_control {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 control; /* One of VIRTIO_VIDEO_CONTROL_* types */
+ __u8 padding[4];
+};
+
+struct virtio_video_control_val_bitrate {
+ __le32 bitrate;
+ __u8 padding[4];
+};
+
+struct virtio_video_control_val_profile {
+ __le32 profile;
+ __u8 padding[4];
+};
+
+struct virtio_video_control_val_level {
+ __le32 level;
+ __u8 padding[4];
+};
+
+struct virtio_video_get_control_resp {
+ struct virtio_video_cmd_hdr hdr;
+ /* Followed by one of struct virtio_video_control_val_* */
+};
+
+/* VIRTIO_VIDEO_CMD_SET_CONTROL */
+struct virtio_video_set_control {
+ struct virtio_video_cmd_hdr hdr;
+ __le32 control; /* One of VIRTIO_VIDEO_CONTROL_* types */
+ __u8 padding[4];
+ /* Followed by one of struct virtio_video_control_val_* */
+};
+
+struct virtio_video_set_control_resp {
+ struct virtio_video_cmd_hdr hdr;
+};
+
+/*
+ * Events
+ */
+
+enum virtio_video_event_type {
+ /* For all devices */
+ VIRTIO_VIDEO_EVENT_ERROR = 0x0100,
+
+ /* For decoder only */
+ VIRTIO_VIDEO_EVENT_DECODER_RESOLUTION_CHANGED = 0x0200,
+};
+
+struct virtio_video_event {
+ __le32 event_type; /* One of VIRTIO_VIDEO_EVENT_* types */
+ __le32 stream_id;
+};
+
+#endif /* _UAPI_LINUX_VIRTIO_VIDEO_H */
diff --git a/virtio_video/Kbuild b/virtio_video/Kbuild
new file mode 100644
index 0000000..04e41cd
--- /dev/null
+++ b/virtio_video/Kbuild
@@ -0,0 +1,22 @@
+#
+# Kbuild for the virtio-video driver
+#
+
+# --tags to take into account non-annotated tags
+# --dirty to mark version with uncommitted changes as dirty
+GIT_VERSION = $(shell git -C "$(MODULE_GIT_REPOSITORY_DIR)" describe --tags --dirty | sed 's/^v//')
+
+ccflags-y += -DDRIVER_VERSION=\"$(GIT_VERSION)\"
+
+virtio_video-y := \
+ virtio_video_driver.o \
+ virtio_video_vq.o \
+ virtio_video_device.o \
+ virtio_video_dec.o \
+ virtio_video_enc.o \
+ virtio_video_cam.o \
+ virtio_video_caps.o \
+ virtio_video_helpers.o
+
+obj-m += virtio_video.o
+LINUXINCLUDE := -I$(srctree)/../common-modules/virtual-device/include/uapi ${LINUXINCLUDE}
diff --git a/virtio_video/virtio_video.h b/virtio_video/virtio_video.h
new file mode 100644
index 0000000..9ece57d
--- /dev/null
+++ b/virtio_video/virtio_video.h
@@ -0,0 +1,446 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Common header for virtio video driver.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _VIRTIO_VIDEO_H
+#define _VIRTIO_VIDEO_H
+
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_video.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define DRIVER_NAME "virtio-video"
+
+#define VIRTIO_ID_VIDEO_DEC 31
+#define VIRTIO_ID_VIDEO_ENC 30
+#define VIRTIO_ID_VIDEO_CAM 100
+
+#define MIN_BUFS_MIN 0
+#define MIN_BUFS_MAX VIDEO_MAX_FRAME
+#define MIN_BUFS_STEP 1
+#define MIN_BUFS_DEF 1
+
+struct video_format_frame {
+ struct virtio_video_format_frame frame;
+ struct virtio_video_format_range *frame_rates;
+};
+
+struct video_format {
+ struct list_head formats_list_entry;
+ struct virtio_video_format_desc desc;
+ struct video_format_frame *frames;
+};
+
+struct video_control_fmt_data {
+ uint32_t min;
+ uint32_t max;
+ uint32_t num;
+ uint32_t skip_mask;
+ uint32_t *entries;
+};
+
+struct video_control_format {
+ struct list_head controls_list_entry;
+ uint32_t format;
+ struct video_control_fmt_data *profile;
+ struct video_control_fmt_data *level;
+};
+
+struct video_plane_format {
+ uint32_t plane_size;
+ uint32_t stride;
+};
+
+struct video_format_info {
+ uint32_t fourcc_format;
+ uint32_t frame_rate;
+ uint32_t frame_width;
+ uint32_t frame_height;
+ uint32_t min_buffers;
+ uint32_t max_buffers;
+ struct virtio_video_crop crop;
+ uint32_t num_planes;
+ struct video_plane_format plane_format[VIRTIO_VIDEO_MAX_PLANES];
+};
+
+struct video_control_info {
+ uint32_t profile;
+ uint32_t level;
+ uint32_t bitrate;
+};
+
+struct virtio_video_device;
+struct virtio_video_vbuffer;
+
+typedef void (*virtio_video_resp_cb)(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf);
+
+struct virtio_video_vbuffer {
+ char *buf;
+ int size;
+ uint32_t id;
+
+ void *data_buf;
+ uint32_t data_size;
+
+ char *resp_buf;
+ int resp_size;
+
+ void *priv;
+ virtio_video_resp_cb resp_cb;
+
+ bool is_sync;
+ struct completion reclaimed;
+
+ struct list_head pending_list_entry;
+};
+
+struct virtio_video_cmd_queue {
+ struct virtqueue *vq;
+ bool ready;
+ spinlock_t qlock;
+ wait_queue_head_t reclaim_queue;
+};
+
+struct virtio_video_event_queue {
+ struct virtqueue *vq;
+ bool ready;
+ struct work_struct work;
+};
+
+enum video_stream_state {
+ STREAM_STATE_IDLE = 0,
+ STREAM_STATE_INIT,
+ STREAM_STATE_DYNAMIC_RES_CHANGE, /* specific to decoder */
+ STREAM_STATE_RUNNING,
+ STREAM_STATE_DRAIN,
+ STREAM_STATE_STOPPED,
+ STREAM_STATE_RESET, /* specific to encoder */
+ STREAM_STATE_ERROR,
+};
+
+struct virtio_video_stream {
+ uint32_t stream_id;
+ atomic_t state;
+ struct video_device *video_dev;
+ struct v4l2_fh fh;
+ struct mutex vq_mutex;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct video_format_info in_info;
+ struct video_format_info out_info;
+ struct video_control_info control;
+ struct video_format_frame *current_frame;
+};
+
+struct virtio_video_device {
+ struct virtio_device *vdev;
+ struct virtio_video_cmd_queue commandq;
+ struct virtio_video_event_queue eventq;
+ wait_queue_head_t wq;
+
+ struct kmem_cache *vbufs;
+ struct virtio_video_event *evts;
+
+ struct idr resource_idr;
+ spinlock_t resource_idr_lock;
+ struct idr stream_idr;
+ spinlock_t stream_idr_lock;
+
+ uint32_t max_caps_len;
+ uint32_t max_resp_len;
+
+ bool has_iommu;
+ bool supp_non_contig;
+
+ int debug;
+ int use_dma_mem;
+
+ struct v4l2_device v4l2_dev;
+ struct video_device video_dev;
+ struct mutex video_dev_mutex;
+
+ bool is_m2m_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ /* non-m2m queue (camera) */
+ struct vb2_queue vb2_output_queue;
+ struct list_head pending_buf_list;
+ spinlock_t pending_buf_list_lock;
+
+ uint32_t vbufs_sent;
+ struct list_head pending_vbuf_list;
+
+ /* device_busy - to block multiple opens for non-m2m (camera) */
+ bool device_busy;
+
+ /* vid_dev_nr - try register starting at video device number */
+ int vid_dev_nr;
+
+ /* is_mplane_cam - camera has multiplanar capabilities (default true) */
+ bool is_mplane_cam;
+
+ /* VIRTIO_VIDEO_FUNC_ */
+ uint32_t type;
+
+ uint32_t num_input_fmts;
+ struct list_head input_fmt_list;
+
+ uint32_t num_output_fmts;
+ struct list_head output_fmt_list;
+
+ struct list_head controls_fmt_list;
+ struct virtio_video_device_ops *ops;
+};
+
+struct virtio_video_device_ops {
+ int (*init_ctrls)(struct virtio_video_stream *stream);
+ int (*init_queues)(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+ void* (*get_fmt_list)(struct virtio_video_device *vvd);
+};
+
+struct virtio_video_buffer {
+ struct v4l2_m2m_buffer v4l2_m2m_vb;
+ uint32_t resource_id;
+ bool queued;
+ struct list_head list;
+};
+
+static inline gfp_t
+virtio_video_gfp_flags(struct virtio_video_device *vvd)
+{
+ if (vvd->use_dma_mem)
+ return GFP_DMA;
+ else
+ return 0;
+}
+
+static inline const struct vb2_mem_ops *
+virtio_video_mem_ops(struct virtio_video_device *vvd)
+{
+ if (vvd->supp_non_contig)
+ return &vb2_dma_sg_memops;
+ else
+ return &vb2_dma_contig_memops;
+}
+
+static inline struct virtio_video_device *
+to_virtio_vd(struct video_device *video_dev)
+{
+ return container_of(video_dev, struct virtio_video_device,
+ video_dev);
+}
+
+static inline struct virtio_video_stream *file2stream(struct file *file)
+{
+ return container_of(file->private_data, struct virtio_video_stream, fh);
+}
+
+static inline struct virtio_video_stream *ctrl2stream(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct virtio_video_stream,
+ ctrl_handler);
+}
+
+static inline struct virtio_video_buffer *to_virtio_vb(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_vb = to_vb2_v4l2_buffer(vb);
+
+ return container_of(v4l2_vb, struct virtio_video_buffer,
+ v4l2_m2m_vb.vb);
+}
+
+static inline enum virtio_video_queue_type
+to_virtio_queue_type(enum v4l2_buf_type type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return VIRTIO_VIDEO_QUEUE_TYPE_INPUT;
+ else
+ return VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT;
+}
+
+static inline bool within_range(uint32_t min, uint32_t val, uint32_t max)
+{
+ return ((min <= val) && (val <= max));
+}
+
+static inline bool needs_alignment(uint32_t val, uint32_t a)
+{
+ if (a == 0 || IS_ALIGNED(val, a))
+ return false;
+
+ return true;
+}
+
+enum video_stream_state virtio_video_state(struct virtio_video_stream *stream);
+void virtio_video_state_reset(struct virtio_video_stream *stream);
+void virtio_video_state_update(struct virtio_video_stream *stream,
+ enum video_stream_state new_state);
+
+int virtio_video_alloc_vbufs(struct virtio_video_device *vvd);
+void virtio_video_free_vbufs(struct virtio_video_device *vvd);
+int virtio_video_alloc_events(struct virtio_video_device *vvd);
+
+int virtio_video_device_init(struct virtio_video_device *vvd);
+void virtio_video_device_deinit(struct virtio_video_device *vvd);
+
+int virtio_video_dec_init(struct virtio_video_device *vvd);
+int virtio_video_enc_init(struct virtio_video_device *vvd);
+int virtio_video_cam_init(struct virtio_video_device *vvd);
+
+void virtio_video_stream_id_get(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ uint32_t *id);
+void virtio_video_stream_id_put(struct virtio_video_device *vvd, uint32_t id);
+void virtio_video_resource_id_get(struct virtio_video_device *vvd,
+ uint32_t *id);
+void virtio_video_resource_id_put(struct virtio_video_device *vvd, uint32_t id);
+
+int virtio_video_cmd_stream_create(struct virtio_video_device *vvd,
+ uint32_t stream_id,
+ enum virtio_video_format format,
+ const char *tag);
+int virtio_video_cmd_stream_destroy(struct virtio_video_device *vvd,
+ uint32_t stream_id);
+int virtio_video_cmd_stream_drain(struct virtio_video_device *vvd,
+ uint32_t stream_id);
+int virtio_video_cmd_resource_attach(struct virtio_video_device *vvd,
+ uint32_t stream_id, uint32_t resource_id,
+ enum virtio_video_queue_type queue_type,
+ void *buf, size_t buf_size);
+int virtio_video_cmd_resource_queue(struct virtio_video_device *vvd,
+ uint32_t stream_id,
+ struct virtio_video_buffer *virtio_vb,
+ uint32_t data_size[], uint8_t num_data_size,
+ enum virtio_video_queue_type queue_type);
+int virtio_video_cmd_queue_detach_resources(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type);
+int virtio_video_cmd_queue_clear(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type);
+int virtio_video_cmd_query_capability(struct virtio_video_device *vvd,
+ void *resp_buf, size_t resp_size,
+ enum virtio_video_queue_type queue_type);
+int virtio_video_query_control_profile(struct virtio_video_device *vvd,
+ void *resp_buf, size_t resp_size,
+ enum virtio_video_format format);
+int virtio_video_query_control_level(struct virtio_video_device *vvd,
+ void *resp_buf, size_t resp_size,
+ enum virtio_video_format format);
+int virtio_video_cmd_set_params(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ struct video_format_info *format_info,
+ enum virtio_video_queue_type queue_type);
+int virtio_video_cmd_get_params(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type);
+int virtio_video_cmd_set_control(struct virtio_video_device *vvd,
+ uint32_t stream_id,
+ enum virtio_video_control_type control,
+ uint32_t value);
+int virtio_video_cmd_get_control(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_control_type control);
+
+void virtio_video_queue_res_chg_event(struct virtio_video_stream *stream);
+void virtio_video_queue_eos_event(struct virtio_video_stream *stream);
+void virtio_video_handle_error(struct virtio_video_stream *stream);
+int virtio_video_queue_release_buffers(struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type);
+
+void virtio_video_cmd_cb(struct virtqueue *vq);
+void virtio_video_event_cb(struct virtqueue *vq);
+void virtio_video_process_events(struct work_struct *work);
+
+void virtio_video_buf_done(struct virtio_video_buffer *virtio_vb,
+ uint32_t flags, uint64_t timestamp,
+ uint32_t data_sizes[]);
+int virtio_video_buf_plane_init(uint32_t idx,uint32_t resource_id,
+ struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ struct vb2_buffer *vb);
+int virtio_video_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[]);
+int virtio_video_buf_init(struct vb2_buffer *vb);
+void virtio_video_buf_cleanup(struct vb2_buffer *vb);
+void virtio_video_buf_queue(struct vb2_buffer *vb);
+int virtio_video_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf);
+int virtio_video_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf);
+int virtio_video_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap);
+int virtio_video_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *f);
+int virtio_video_enum_framemintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *f);
+int virtio_video_g_fmt(struct file *file, void *fh, struct v4l2_format *f);
+int virtio_video_s_fmt(struct file *file, void *fh, struct v4l2_format *f);
+int virtio_video_try_fmt(struct virtio_video_stream *stream,
+ struct v4l2_format *f);
+int virtio_video_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb);
+int virtio_video_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub);
+
+void virtio_video_free_caps_list(struct list_head *caps_list);
+int virtio_video_parse_virtio_capabilities(struct virtio_video_device *vvd,
+ void *input_buf, void *output_buf);
+void virtio_video_clean_capability(struct virtio_video_device *vvd);
+int virtio_video_parse_virtio_control(struct virtio_video_device *vvd);
+void virtio_video_clean_control(struct virtio_video_device *vvd);
+
+uint32_t virtio_video_format_to_v4l2(uint32_t format);
+uint32_t virtio_video_control_to_v4l2(uint32_t control);
+uint32_t virtio_video_profile_to_v4l2(uint32_t profile);
+uint32_t virtio_video_level_to_v4l2(uint32_t level);
+uint32_t virtio_video_v4l2_format_to_virtio(uint32_t v4l2_format);
+uint32_t virtio_video_v4l2_control_to_virtio(uint32_t v4l2_control);
+uint32_t virtio_video_v4l2_profile_to_virtio(uint32_t v4l2_profile);
+uint32_t virtio_video_v4l2_level_to_virtio(uint32_t v4l2_level);
+
+struct video_format *virtio_video_find_video_format(struct list_head *fmts_list,
+ uint32_t fourcc);
+void virtio_video_format_from_info(struct video_format_info *info,
+ struct v4l2_pix_format_mplane *pix_mp);
+void virtio_video_format_fill_default_info(struct video_format_info *dst_info,
+ struct video_format_info *src_info);
+void virtio_video_pix_fmt_sp2mp(const struct v4l2_pix_format *pix,
+ struct v4l2_pix_format_mplane *pix_mp);
+void virtio_video_pix_fmt_mp2sp(const struct v4l2_pix_format_mplane *pix_mp,
+ struct v4l2_pix_format *pix);
+
+int virtio_video_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel);
+
+int virtio_video_stream_get_params(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream);
+int virtio_video_stream_get_controls(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream);
+
+#endif /* _VIRTIO_VIDEO_H */
diff --git a/virtio_video/virtio_video_cam.c b/virtio_video/virtio_video_cam.c
new file mode 100644
index 0000000..d657ba8
--- /dev/null
+++ b/virtio_video/virtio_video_cam.c
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Capture for virtio video device.
+ *
+ * Copyright 2021 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/version.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+#include "virtio_video.h"
+
+static int virtio_video_cam_start_streaming(struct vb2_queue *vq,
+ unsigned int count)
+{
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (virtio_video_state(stream) >= STREAM_STATE_INIT)
+ virtio_video_state_update(stream, STREAM_STATE_RUNNING);
+
+ return 0;
+}
+
+static void virtio_video_cam_stop_streaming(struct vb2_queue *vq)
+{
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+
+ virtio_video_queue_release_buffers(stream,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+
+ vb2_wait_for_all_buffers(vq);
+}
+
+static const struct vb2_ops virtio_video_cam_qops = {
+ .queue_setup = virtio_video_queue_setup,
+ .buf_init = virtio_video_buf_init,
+ .buf_cleanup = virtio_video_buf_cleanup,
+ .buf_queue = virtio_video_buf_queue,
+ .start_streaming = virtio_video_cam_start_streaming,
+ .stop_streaming = virtio_video_cam_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int virtio_video_cam_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ struct virtio_video_stream *stream = ctrl2stream(ctrl);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ if (virtio_video_state(stream) >=
+ STREAM_STATE_DYNAMIC_RES_CHANGE)
+ ctrl->val = stream->out_info.min_buffers;
+ else
+ ctrl->val = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops virtio_video_cam_ctrl_ops = {
+ .g_volatile_ctrl = virtio_video_cam_g_ctrl,
+};
+
+int virtio_video_cam_init_ctrls(struct virtio_video_stream *stream)
+{
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_ctrl_handler_init(&stream->ctrl_handler, 2);
+
+ ctrl = v4l2_ctrl_new_std(&stream->ctrl_handler,
+ &virtio_video_cam_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+ MIN_BUFS_MIN, MIN_BUFS_MAX, MIN_BUFS_STEP,
+ MIN_BUFS_DEF);
+
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ if (stream->ctrl_handler.error)
+ return stream->ctrl_handler.error;
+
+ v4l2_ctrl_handler_setup(&stream->ctrl_handler);
+
+ return 0;
+}
+
+int virtio_video_cam_init_queues(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct virtio_video_stream *stream = priv;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct device *dev = vvd->v4l2_dev.dev;
+ int vq_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+ if (!vvd->is_mplane_cam)
+ vq_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ dst_vq->type = vq_type;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = stream;
+ dst_vq->buf_struct_size = sizeof(struct virtio_video_buffer);
+ dst_vq->ops = &virtio_video_cam_qops;
+ dst_vq->mem_ops = virtio_video_mem_ops(vvd);
+ dst_vq->min_buffers_needed = stream->out_info.min_buffers;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ dst_vq->lock = &stream->vq_mutex;
+ dst_vq->gfp_flags = virtio_video_gfp_flags(vvd);
+ dst_vq->dev = dev;
+
+ return vb2_queue_init(dst_vq);
+}
+
+int virtio_video_cam_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct v4l2_format *fmt_try = f;
+ struct v4l2_format fmt_mp = { 0 };
+ int ret;
+
+ if (!vvd->is_mplane_cam) {
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ fmt_mp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt_try = &fmt_mp;
+
+ virtio_video_pix_fmt_sp2mp(&f->fmt.pix, &fmt_try->fmt.pix_mp);
+ }
+
+ ret = virtio_video_try_fmt(stream, fmt_try);
+ if (ret)
+ return ret;
+
+ if (!vvd->is_mplane_cam) {
+ if (fmt_try->fmt.pix_mp.num_planes != 1)
+ return -EINVAL;
+
+ virtio_video_pix_fmt_mp2sp(&fmt_try->fmt.pix_mp, &f->fmt.pix);
+ }
+
+ return 0;
+}
+
+static int virtio_video_cam_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format *fmt;
+ int idx = 0;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ if (f->index >= vvd->num_output_fmts)
+ return -EINVAL;
+
+ list_for_each_entry(fmt, &vvd->output_fmt_list, formats_list_entry) {
+ if (f->index == idx) {
+ f->pixelformat = fmt->desc.format;
+ return 0;
+ }
+ idx++;
+ }
+ return -EINVAL;
+}
+
+static int virtio_video_cam_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct v4l2_format fmt_mp = { 0 };
+ struct v4l2_format *fmt_get = f;
+ int ret;
+
+ if (!vvd->is_mplane_cam) {
+ fmt_mp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt_get = &fmt_mp;
+ }
+
+ ret = virtio_video_g_fmt(file, fh, fmt_get);
+ if (ret)
+ return ret;
+
+ if (virtio_video_state(stream) == STREAM_STATE_IDLE)
+ virtio_video_state_update(stream, STREAM_STATE_INIT);
+
+ if (!vvd->is_mplane_cam) {
+ if (fmt_get->fmt.pix_mp.num_planes != 1)
+ return -EINVAL;
+
+ virtio_video_pix_fmt_mp2sp(&fmt_get->fmt.pix_mp, &f->fmt.pix);
+ }
+
+ return 0;
+}
+
+static int virtio_video_cam_s_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret;
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct v4l2_format fmt_mp = { 0 };
+ struct v4l2_format *fmt_set = f;
+
+ if (!vvd->is_mplane_cam) {
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ fmt_mp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt_set = &fmt_mp;
+
+ virtio_video_pix_fmt_sp2mp(&f->fmt.pix, &fmt_set->fmt.pix_mp);
+ }
+
+ ret = virtio_video_s_fmt(file, fh, fmt_set);
+ if (ret)
+ return ret;
+
+ if (virtio_video_state(stream) == STREAM_STATE_IDLE)
+ virtio_video_state_update(stream, STREAM_STATE_INIT);
+
+ if (!vvd->is_mplane_cam) {
+ if (fmt_set->fmt.pix_mp.num_planes != 1)
+ return -EINVAL;
+
+ virtio_video_pix_fmt_mp2sp(&fmt_set->fmt.pix_mp, &f->fmt.pix);
+ }
+
+ return 0;
+}
+
+static int virtio_video_cam_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(sel->type))
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ stream->out_info.crop.top = sel->r.top;
+ stream->out_info.crop.left = sel->r.left;
+ stream->out_info.crop.width = sel->r.width;
+ stream->out_info.crop.height = sel->r.height;
+ v4l2_info(&vvd->v4l2_dev,
+ "Set : top:%d, left:%d, w:%d, h:%d\n",
+ sel->r.top, sel->r.left, sel->r.width, sel->r.height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = virtio_video_cmd_set_params(vvd, stream, &stream->out_info,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+ if (ret)
+ return -EINVAL;
+
+ ret = virtio_video_cmd_get_params(vvd, stream,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops virtio_video_cam_ioctl_ops = {
+ .vidioc_querycap = virtio_video_querycap,
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0))
+ .vidioc_enum_fmt_vid_cap_mplane = virtio_video_cam_enum_fmt_vid_cap,
+#endif
+ .vidioc_try_fmt_vid_cap_mplane = virtio_video_cam_try_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = virtio_video_cam_g_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = virtio_video_cam_s_fmt,
+
+ .vidioc_enum_fmt_vid_cap = virtio_video_cam_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = virtio_video_cam_try_fmt,
+ .vidioc_g_fmt_vid_cap = virtio_video_cam_g_fmt,
+ .vidioc_s_fmt_vid_cap = virtio_video_cam_s_fmt,
+
+ .vidioc_g_selection = virtio_video_g_selection,
+ .vidioc_s_selection = virtio_video_cam_s_selection,
+
+ .vidioc_enum_frameintervals = virtio_video_enum_framemintervals,
+ .vidioc_enum_framesizes = virtio_video_enum_framesizes,
+
+ .vidioc_reqbufs = virtio_video_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = virtio_video_qbuf,
+ .vidioc_dqbuf = virtio_video_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_subscribe_event = virtio_video_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+void *virtio_video_cam_get_fmt_list(struct virtio_video_device *vvd)
+{
+ return &vvd->output_fmt_list;
+}
+
+static struct virtio_video_device_ops virtio_video_cam_ops = {
+ .init_ctrls = virtio_video_cam_init_ctrls,
+ .init_queues = virtio_video_cam_init_queues,
+ .get_fmt_list = virtio_video_cam_get_fmt_list,
+};
+
+int virtio_video_cam_init(struct virtio_video_device *vvd)
+{
+ ssize_t num;
+ struct video_device *vd = &vvd->video_dev;
+
+ vd->ioctl_ops = &virtio_video_cam_ioctl_ops;
+ vvd->ops = &virtio_video_cam_ops;
+
+ num = strscpy(vd->name, "camera", sizeof(vd->name));
+ if (num < 0)
+ return num;
+
+ return 0;
+}
diff --git a/virtio_video/virtio_video_caps.c b/virtio_video/virtio_video_caps.c
new file mode 100644
index 0000000..fc815b1
--- /dev/null
+++ b/virtio_video/virtio_video_caps.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Driver for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "virtio_video.h"
+
+static void virtio_video_free_frame_rates(struct video_format_frame *frame)
+{
+ kfree(frame->frame_rates);
+}
+
+static void virtio_video_free_frames(struct video_format *fmt)
+{
+ size_t idx = 0;
+
+ for (idx = 0; idx < fmt->desc.num_frames; idx++)
+ virtio_video_free_frame_rates(&fmt->frames[idx]);
+ kfree(fmt->frames);
+}
+
+static void virtio_video_free_fmt(struct list_head *fmts_list)
+{
+ struct video_format *fmt, *tmp;
+
+ list_for_each_entry_safe(fmt, tmp, fmts_list, formats_list_entry) {
+ list_del(&fmt->formats_list_entry);
+ virtio_video_free_frames(fmt);
+ kfree(fmt);
+ }
+}
+
+static void virtio_video_free_fmts(struct virtio_video_device *vvd)
+{
+ virtio_video_free_fmt(&vvd->input_fmt_list);
+ virtio_video_free_fmt(&vvd->output_fmt_list);
+}
+
+static void virtio_video_copy_fmt_range(struct virtio_video_format_range *d_rge,
+ struct virtio_video_format_range *s_rge)
+{
+ d_rge->min = le32_to_cpu(s_rge->min);
+ d_rge->max = le32_to_cpu(s_rge->max);
+ d_rge->step = le32_to_cpu(s_rge->step);
+}
+
+static size_t
+virtio_video_parse_virtio_frame_rate(struct virtio_video_device *vvd,
+ struct virtio_video_format_range *f_rate,
+ void *buf)
+{
+ struct virtio_video_format_range *virtio_frame_rate;
+
+ virtio_frame_rate = buf;
+ virtio_video_copy_fmt_range(f_rate, virtio_frame_rate);
+
+ return sizeof(struct virtio_video_format_range);
+}
+
+static size_t virtio_video_parse_virtio_frame(struct virtio_video_device *vvd,
+ struct video_format_frame *frm,
+ void *buf)
+{
+ struct virtio_video_format_frame *virtio_frame;
+ struct virtio_video_format_frame *frame = &frm->frame;
+ struct virtio_video_format_range *rate;
+ size_t idx, offset, extra_size;
+
+ virtio_frame = buf;
+
+ virtio_video_copy_fmt_range(&frame->width, &virtio_frame->width);
+ virtio_video_copy_fmt_range(&frame->height, &virtio_frame->height);
+
+ frame->num_rates = le32_to_cpu(virtio_frame->num_rates);
+ frm->frame_rates = kcalloc(frame->num_rates,
+ sizeof(struct virtio_video_format_range),
+ GFP_KERNEL);
+
+ offset = sizeof(struct virtio_video_format_frame);
+ for (idx = 0; idx < frame->num_rates; idx++) {
+ rate = &frm->frame_rates[idx];
+ extra_size =
+ virtio_video_parse_virtio_frame_rate(vvd, rate,
+ buf + offset);
+ if (extra_size == 0) {
+ kfree(frm->frame_rates);
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to parse frame rate\n");
+ return 0;
+ }
+ offset += extra_size;
+ }
+
+ return offset;
+}
+
+static size_t virtio_video_parse_virtio_fmt(struct virtio_video_device *vvd,
+ struct video_format *fmt, void *buf)
+{
+ struct virtio_video_format_desc *virtio_fmt_desc;
+ struct virtio_video_format_desc *fmt_desc;
+ struct video_format_frame *frame;
+ size_t idx, offset, extra_size;
+
+ virtio_fmt_desc = buf;
+ fmt_desc = &fmt->desc;
+
+ fmt_desc->format =
+ virtio_video_format_to_v4l2
+ (le32_to_cpu(virtio_fmt_desc->format));
+ fmt_desc->mask = le64_to_cpu(virtio_fmt_desc->mask);
+ fmt_desc->planes_layout = le32_to_cpu(virtio_fmt_desc->planes_layout);
+
+ fmt_desc->num_frames = le32_to_cpu(virtio_fmt_desc->num_frames);
+ fmt->frames = kcalloc(fmt_desc->num_frames,
+ sizeof(struct video_format_frame),
+ GFP_KERNEL);
+
+ offset = sizeof(struct virtio_video_format_desc);
+ for (idx = 0; idx < fmt_desc->num_frames; idx++) {
+ frame = &fmt->frames[idx];
+ extra_size =
+ virtio_video_parse_virtio_frame(vvd, frame,
+ buf + offset);
+ if (extra_size == 0) {
+ kfree(fmt->frames);
+ v4l2_err(&vvd->v4l2_dev, "failed to parse frame\n");
+ return 0;
+ }
+ offset += extra_size;
+ }
+
+ return offset;
+}
+
+int virtio_video_parse_virtio_capability(struct virtio_video_device *vvd,
+ void *resp_buf,
+ struct list_head *ret_fmt_list,
+ uint32_t *ret_num_fmts)
+{
+ struct virtio_video_query_capability_resp *resp = resp_buf;
+ struct video_format *fmt;
+ uint32_t fmt_count;
+ int fmt_idx;
+ size_t offset;
+ int ret;
+
+ if (!resp || ret_fmt_list == NULL || ret_num_fmts == NULL) {
+ v4l2_err(&vvd->v4l2_dev, "invalid arguments!\n");
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(resp->num_descs) <= 0) {
+ v4l2_err(&vvd->v4l2_dev, "invalid capability response\n");
+ return -EINVAL;
+ }
+
+ fmt_count = le32_to_cpu(resp->num_descs);
+ offset = sizeof(struct virtio_video_query_capability_resp);
+
+ for (fmt_idx = 0; fmt_idx < fmt_count; fmt_idx++) {
+ size_t fmt_size = 0;
+
+ fmt = kzalloc(sizeof(*fmt), GFP_KERNEL);
+ if (!fmt) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+
+ fmt_size = virtio_video_parse_virtio_fmt(vvd, fmt,
+ resp_buf + offset);
+ if (fmt_size == 0) {
+ v4l2_err(&vvd->v4l2_dev, "failed to parse fmt\n");
+ ret = -ENOENT;
+ goto parse_fmt_err;
+ }
+ offset += fmt_size;
+ list_add(&fmt->formats_list_entry, ret_fmt_list);
+ }
+
+ *ret_num_fmts = fmt_count;
+ return 0;
+
+parse_fmt_err:
+ kfree(fmt);
+alloc_err:
+ virtio_video_free_fmts(vvd);
+ return ret;
+}
+
+int virtio_video_parse_virtio_capabilities(struct virtio_video_device *vvd,
+ void *input_buf, void *output_buf)
+{
+ int ret;
+
+ if (input_buf) {
+ ret = virtio_video_parse_virtio_capability(vvd, input_buf,
+ &vvd->input_fmt_list,
+ &vvd->num_input_fmts);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "Failed to parse input capability: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (output_buf) {
+ ret = virtio_video_parse_virtio_capability(vvd, output_buf,
+ &vvd->output_fmt_list,
+ &vvd->num_output_fmts);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "Failed to parse output capability: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void virtio_video_clean_capability(struct virtio_video_device *vvd)
+{
+ virtio_video_free_fmts(vvd);
+}
+
+static void
+virtio_video_free_control_fmt_data(struct video_control_fmt_data *data)
+{
+ kfree(data->entries);
+ kfree(data);
+}
+
+static void virtio_video_free_control_formats(struct virtio_video_device *vvd)
+{
+ struct video_control_format *c_fmt, *tmp;
+
+ list_for_each_entry_safe(c_fmt, tmp, &vvd->controls_fmt_list,
+ controls_list_entry) {
+ list_del(&c_fmt->controls_list_entry);
+ virtio_video_free_control_fmt_data(c_fmt->profile);
+ virtio_video_free_control_fmt_data(c_fmt->level);
+ kfree(c_fmt);
+ }
+}
+
+static int virtio_video_parse_control_levels(struct virtio_video_device *vvd,
+ struct video_control_format *fmt)
+{
+ int idx, ret;
+ struct virtio_video_query_control_resp *resp_buf;
+ struct virtio_video_query_control_resp_level *l_resp_buf;
+ struct video_control_fmt_data *level;
+ enum virtio_video_format virtio_format;
+ uint32_t *virtio_levels;
+ uint32_t num_levels, mask = 0;
+ int max = 0, min = UINT_MAX;
+ size_t resp_size;
+
+ resp_size = vvd->max_resp_len;
+
+ virtio_format = virtio_video_v4l2_format_to_virtio(fmt->format);
+
+ resp_buf = kzalloc(resp_size, GFP_KERNEL);
+ if (IS_ERR(resp_buf)) {
+ ret = PTR_ERR(resp_buf);
+ goto lvl_err;
+ }
+
+ ret = virtio_video_query_control_level(vvd, resp_buf, resp_size,
+ virtio_format);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to query level\n");
+ goto lvl_err;
+ }
+
+ l_resp_buf = (void *)((char *)resp_buf + sizeof(*resp_buf));
+ num_levels = le32_to_cpu(l_resp_buf->num);
+ if (num_levels == 0)
+ goto lvl_err;
+
+ fmt->level = kzalloc(sizeof(*level), GFP_KERNEL);
+ if (!fmt->level) {
+ ret = -ENOMEM;
+ goto lvl_err;
+ }
+
+ level = fmt->level;
+ level->entries = kcalloc(num_levels, sizeof(uint32_t), GFP_KERNEL);
+ if (!level->entries) {
+ kfree(fmt->level);
+ ret = -ENOMEM;
+ goto lvl_err;
+ }
+
+ virtio_levels = (void *)((char *)l_resp_buf + sizeof(*l_resp_buf));
+
+ for (idx = 0; idx < num_levels; idx++) {
+ level->entries[idx] =
+ virtio_video_level_to_v4l2
+ (le32_to_cpu(virtio_levels[idx]));
+
+ mask = mask | (1 << level->entries[idx]);
+ if (level->entries[idx] > max)
+ max = level->entries[idx];
+ if (level->entries[idx] < min)
+ min = level->entries[idx];
+ }
+ level->min = min;
+ level->max = max;
+ level->num = num_levels;
+ level->skip_mask = ~mask;
+
+lvl_err:
+ kfree(resp_buf);
+
+ return ret;
+}
+
+static int virtio_video_parse_control_profiles(struct virtio_video_device *vvd,
+ struct video_control_format *fmt)
+{
+ int idx, ret;
+ struct virtio_video_query_control_resp *resp_buf;
+ struct virtio_video_query_control_resp_profile *p_resp_buf;
+ struct video_control_fmt_data *profile;
+ uint32_t virtio_format, num_profiles, mask = 0;
+ uint32_t *virtio_profiles;
+ int max = 0, min = UINT_MAX;
+ size_t resp_size;
+
+ resp_size = vvd->max_resp_len;
+ virtio_format = virtio_video_v4l2_format_to_virtio(fmt->format);
+ resp_buf = kzalloc(resp_size, GFP_KERNEL);
+ if (IS_ERR(resp_buf)) {
+ ret = PTR_ERR(resp_buf);
+ goto prf_err;
+ }
+
+ ret = virtio_video_query_control_profile(vvd, resp_buf, resp_size,
+ virtio_format);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to query profile\n");
+ goto prf_err;
+ }
+
+ p_resp_buf = (void *)((char *)resp_buf + sizeof(*resp_buf));
+ num_profiles = le32_to_cpu(p_resp_buf->num);
+ if (num_profiles == 0)
+ goto prf_err;
+
+ fmt->profile = kzalloc(sizeof(*profile), GFP_KERNEL);
+ if (!fmt->profile) {
+ ret = -ENOMEM;
+ goto prf_err;
+ }
+
+ profile = fmt->profile;
+ profile->entries = kcalloc(num_profiles, sizeof(uint32_t), GFP_KERNEL);
+ if (!profile->entries) {
+ kfree(fmt->profile);
+ ret = -ENOMEM;
+ goto prf_err;
+ }
+
+ virtio_profiles = (void *)((char *)p_resp_buf + sizeof(*p_resp_buf));
+
+ for (idx = 0; idx < num_profiles; idx++) {
+ profile->entries[idx] =
+ virtio_video_profile_to_v4l2
+ (le32_to_cpu(virtio_profiles[idx]));
+
+ mask = mask | (1 << profile->entries[idx]);
+ if (profile->entries[idx] > max)
+ max = profile->entries[idx];
+ if (profile->entries[idx] < min)
+ min = profile->entries[idx];
+ }
+ profile->min = min;
+ profile->max = max;
+ profile->num = num_profiles;
+ profile->skip_mask = ~mask;
+
+prf_err:
+ kfree(resp_buf);
+
+ return ret;
+}
+
+int virtio_video_parse_virtio_control(struct virtio_video_device *vvd)
+{
+ struct video_format *fmt;
+ struct video_control_format *c_fmt;
+ uint32_t virtio_format;
+ int ret;
+
+ list_for_each_entry(fmt, &vvd->output_fmt_list, formats_list_entry) {
+ virtio_format =
+ virtio_video_v4l2_format_to_virtio(fmt->desc.format);
+ if (virtio_format < VIRTIO_VIDEO_FORMAT_CODED_MIN ||
+ virtio_format > VIRTIO_VIDEO_FORMAT_CODED_MAX)
+ continue;
+
+ c_fmt = kzalloc(sizeof(*c_fmt), GFP_KERNEL);
+ if (!c_fmt) {
+ ret = -ENOMEM;
+ goto parse_ctrl_alloc_err;
+ }
+
+ c_fmt->format = fmt->desc.format;
+
+ ret = virtio_video_parse_control_profiles(vvd, c_fmt);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to parse control profile\n");
+ goto parse_ctrl_prf_err;
+ }
+
+ ret = virtio_video_parse_control_levels(vvd, c_fmt);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to parse control level\n");
+ goto parse_ctrl_lvl_err;
+ }
+ list_add(&c_fmt->controls_list_entry, &vvd->controls_fmt_list);
+ }
+ return 0;
+
+parse_ctrl_lvl_err:
+ virtio_video_free_control_fmt_data(c_fmt->profile);
+parse_ctrl_prf_err:
+ kfree(c_fmt);
+parse_ctrl_alloc_err:
+ virtio_video_free_control_formats(vvd);
+
+ return ret;
+}
+
+void virtio_video_clean_control(struct virtio_video_device *vvd)
+{
+ virtio_video_free_control_formats(vvd);
+}
diff --git a/virtio_video/virtio_video_dec.c b/virtio_video/virtio_video_dec.c
new file mode 100644
index 0000000..301a9a1
--- /dev/null
+++ b/virtio_video/virtio_video_dec.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Decoder for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/version.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+#include "virtio_video.h"
+
+static int virtio_video_dec_start_streaming(struct vb2_queue *vq,
+ unsigned int count)
+{
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (!V4L2_TYPE_IS_OUTPUT(vq->type) &&
+ virtio_video_state(stream) >= STREAM_STATE_INIT)
+ virtio_video_state_update(stream, STREAM_STATE_RUNNING);
+
+ return 0;
+}
+
+static void virtio_video_dec_stop_streaming(struct vb2_queue *vq)
+{
+ int queue_type;
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ queue_type = VIRTIO_VIDEO_QUEUE_TYPE_INPUT;
+ else
+ queue_type = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT;
+
+ virtio_video_queue_release_buffers(stream, queue_type);
+ vb2_wait_for_all_buffers(vq);
+}
+
+static const struct vb2_ops virtio_video_dec_qops = {
+ .queue_setup = virtio_video_queue_setup,
+ .buf_init = virtio_video_buf_init,
+ .buf_cleanup = virtio_video_buf_cleanup,
+ .buf_queue = virtio_video_buf_queue,
+ .start_streaming = virtio_video_dec_start_streaming,
+ .stop_streaming = virtio_video_dec_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int virtio_video_dec_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ struct virtio_video_stream *stream = ctrl2stream(ctrl);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ if (virtio_video_state(stream) >=
+ STREAM_STATE_DYNAMIC_RES_CHANGE)
+ ctrl->val = stream->out_info.min_buffers;
+ else
+ ctrl->val = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops virtio_video_dec_ctrl_ops = {
+ .g_volatile_ctrl = virtio_video_dec_g_ctrl,
+};
+
+int virtio_video_dec_init_ctrls(struct virtio_video_stream *stream)
+{
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_ctrl_handler_init(&stream->ctrl_handler, 2);
+
+ ctrl = v4l2_ctrl_new_std(&stream->ctrl_handler,
+ &virtio_video_dec_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+ MIN_BUFS_MIN, MIN_BUFS_MAX, MIN_BUFS_STEP,
+ MIN_BUFS_DEF);
+
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ if (stream->ctrl_handler.error)
+ return stream->ctrl_handler.error;
+
+ (void)v4l2_ctrl_new_std(&stream->ctrl_handler, NULL,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
+ MIN_BUFS_MIN, MIN_BUFS_MAX, MIN_BUFS_STEP,
+ stream->in_info.min_buffers);
+
+ if (stream->ctrl_handler.error)
+ return stream->ctrl_handler.error;
+
+ v4l2_ctrl_handler_setup(&stream->ctrl_handler);
+
+ return 0;
+}
+
+int virtio_video_dec_init_queues(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ int ret;
+ struct virtio_video_stream *stream = priv;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct device *dev = vvd->v4l2_dev.dev;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = stream;
+ src_vq->buf_struct_size = sizeof(struct virtio_video_buffer);
+ src_vq->ops = &virtio_video_dec_qops;
+ src_vq->mem_ops = virtio_video_mem_ops(vvd);
+ src_vq->min_buffers_needed = stream->in_info.min_buffers;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &stream->vq_mutex;
+ src_vq->gfp_flags = virtio_video_gfp_flags(vvd);
+ src_vq->dev = dev;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = stream;
+ dst_vq->buf_struct_size = sizeof(struct virtio_video_buffer);
+ dst_vq->ops = &virtio_video_dec_qops;
+ dst_vq->mem_ops = virtio_video_mem_ops(vvd);
+ dst_vq->min_buffers_needed = stream->out_info.min_buffers;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &stream->vq_mutex;
+ dst_vq->gfp_flags = virtio_video_gfp_flags(vvd);
+ dst_vq->dev = dev;
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int virtio_video_try_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *cmd)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (virtio_video_state(stream) == STREAM_STATE_DRAIN)
+ return -EBUSY;
+
+ switch (cmd->cmd) {
+ case V4L2_DEC_CMD_STOP:
+ case V4L2_DEC_CMD_START:
+ if (cmd->flags != 0) {
+ v4l2_err(&vvd->v4l2_dev, "flags=%u are not supported",
+ cmd->flags);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int virtio_video_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *cmd)
+{
+ int ret;
+ struct vb2_queue *src_vq, *dst_vq;
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ ret = virtio_video_try_decoder_cmd(file, fh, cmd);
+ if (ret < 0)
+ return ret;
+
+ dst_vq = v4l2_m2m_get_vq(stream->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ switch (cmd->cmd) {
+ case V4L2_DEC_CMD_START:
+ vb2_clear_last_buffer_dequeued(dst_vq);
+ break;
+ case V4L2_DEC_CMD_STOP:
+ src_vq = v4l2_m2m_get_vq(stream->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ if (!vb2_is_streaming(src_vq)) {
+ v4l2_dbg(1, vvd->debug,
+ &vvd->v4l2_dev, "output is not streaming\n");
+ return 0;
+ }
+
+ if (!vb2_is_streaming(dst_vq)) {
+ v4l2_dbg(1, vvd->debug,
+ &vvd->v4l2_dev, "capture is not streaming\n");
+ return 0;
+ }
+
+ ret = virtio_video_cmd_stream_drain(vvd, stream->stream_id);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to drain stream\n");
+ return ret;
+ }
+
+ virtio_video_state_update(stream, STREAM_STATE_DRAIN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int virtio_video_dec_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format_info *info;
+ struct video_format *fmt;
+ unsigned long input_mask = 0;
+ int idx = 0, bit_num = 0;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ if (f->index >= vvd->num_output_fmts)
+ return -EINVAL;
+
+ info = &stream->in_info;
+ list_for_each_entry(fmt, &vvd->input_fmt_list, formats_list_entry) {
+ if (info->fourcc_format == fmt->desc.format) {
+ input_mask = fmt->desc.mask;
+ break;
+ }
+ }
+
+ if (input_mask == 0)
+ return -EINVAL;
+
+ list_for_each_entry(fmt, &vvd->output_fmt_list, formats_list_entry) {
+ if (test_bit(bit_num, &input_mask)) {
+ if (f->index == idx) {
+ f->pixelformat = fmt->desc.format;
+ return 0;
+ }
+ idx++;
+ }
+ bit_num++;
+ }
+ return -EINVAL;
+}
+
+
+int virtio_video_dec_enum_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format *fmt;
+ int idx = 0;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ if (f->index >= vvd->num_input_fmts)
+ return -EINVAL;
+
+ list_for_each_entry(fmt, &vvd->input_fmt_list, formats_list_entry) {
+ if (f->index == idx) {
+ f->pixelformat = fmt->desc.format;
+ return 0;
+ }
+ idx++;
+ }
+ return -EINVAL;
+}
+
+static int virtio_video_dec_s_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret;
+ struct virtio_video_stream *stream = file2stream(file);
+
+ ret = virtio_video_s_fmt(file, fh, f);
+ if (ret)
+ return ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ if (virtio_video_state(stream) == STREAM_STATE_IDLE)
+ virtio_video_state_update(stream, STREAM_STATE_INIT);
+ }
+
+ return 0;
+}
+
+static int virtio_video_dec_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(sel->type))
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ stream->out_info.crop.top = sel->r.top;
+ stream->out_info.crop.left = sel->r.left;
+ stream->out_info.crop.width = sel->r.width;
+ stream->out_info.crop.height = sel->r.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = virtio_video_cmd_set_params(vvd, stream, &stream->out_info,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+ if (ret)
+ return -EINVAL;
+
+ return virtio_video_cmd_get_params(vvd, stream,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+}
+
+static const struct v4l2_ioctl_ops virtio_video_dec_ioctl_ops = {
+ .vidioc_querycap = virtio_video_querycap,
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0))
+ .vidioc_enum_fmt_vid_cap = virtio_video_dec_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = virtio_video_dec_enum_fmt_vid_out,
+#else
+ .vidioc_enum_fmt_vid_cap_mplane = virtio_video_dec_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out_mplane = virtio_video_dec_enum_fmt_vid_out,
+#endif
+ .vidioc_g_fmt_vid_cap_mplane = virtio_video_g_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = virtio_video_dec_s_fmt,
+
+ .vidioc_g_fmt_vid_out_mplane = virtio_video_g_fmt,
+ .vidioc_s_fmt_vid_out_mplane = virtio_video_dec_s_fmt,
+
+ .vidioc_g_selection = virtio_video_g_selection,
+ .vidioc_s_selection = virtio_video_dec_s_selection,
+
+ .vidioc_try_decoder_cmd = virtio_video_try_decoder_cmd,
+ .vidioc_decoder_cmd = virtio_video_decoder_cmd,
+ .vidioc_enum_frameintervals = virtio_video_enum_framemintervals,
+ .vidioc_enum_framesizes = virtio_video_enum_framesizes,
+
+ .vidioc_reqbufs = virtio_video_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = virtio_video_qbuf,
+ .vidioc_dqbuf = virtio_video_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 = virtio_video_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+void *virtio_video_dec_get_fmt_list(struct virtio_video_device *vvd)
+{
+ return &vvd->input_fmt_list;
+}
+
+static struct virtio_video_device_ops virtio_video_dec_ops = {
+ .init_ctrls = virtio_video_dec_init_ctrls,
+ .init_queues = virtio_video_dec_init_queues,
+ .get_fmt_list = virtio_video_dec_get_fmt_list,
+};
+
+int virtio_video_dec_init(struct virtio_video_device *vvd)
+{
+ ssize_t num;
+ struct video_device *vd = &vvd->video_dev;
+
+ vd->ioctl_ops = &virtio_video_dec_ioctl_ops;
+ vvd->ops = &virtio_video_dec_ops;
+
+ num = strscpy(vd->name, "stateful-decoder", sizeof(vd->name));
+ if (num < 0)
+ return num;
+
+ return 0;
+}
diff --git a/virtio_video/virtio_video_device.c b/virtio_video/virtio_video_device.c
new file mode 100644
index 0000000..2e1f90a
--- /dev/null
+++ b/virtio_video/virtio_video_device.c
@@ -0,0 +1,1282 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Driver for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/version.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "virtio_video.h"
+
+enum video_stream_state virtio_video_state(struct virtio_video_stream *stream)
+{
+ return atomic_read(&stream->state);
+}
+
+void virtio_video_state_reset(struct virtio_video_stream *stream)
+{
+ atomic_set(&stream->state, STREAM_STATE_IDLE);
+}
+
+void virtio_video_state_update(struct virtio_video_stream *stream,
+ enum video_stream_state new_state)
+{
+ enum video_stream_state prev_state;
+
+ do {
+ prev_state = atomic_read(&stream->state);
+ if (prev_state == STREAM_STATE_ERROR)
+ return;
+ } while (atomic_cmpxchg(&stream->state, prev_state, new_state) !=
+ prev_state);
+}
+
+int virtio_video_pending_buf_list_empty(struct virtio_video_device *vvd)
+{
+ int ret = 0;
+
+ if (vvd->is_m2m_dev) {
+ v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
+ return -EPERM;
+ }
+
+ spin_lock(&vvd->pending_buf_list_lock);
+ if (list_empty(&vvd->pending_buf_list))
+ ret = 1;
+ spin_unlock(&vvd->pending_buf_list_lock);
+
+ return ret;
+}
+
+int virtio_video_pending_buf_list_pop(struct virtio_video_device *vvd,
+ struct virtio_video_buffer **virtio_vb)
+{
+ struct virtio_video_buffer *retbuf;
+
+ if (vvd->is_m2m_dev) {
+ v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
+ return -EPERM;
+ }
+
+ spin_lock(&vvd->pending_buf_list_lock);
+ if (list_empty(&vvd->pending_buf_list)) {
+ spin_unlock(&vvd->pending_buf_list_lock);
+ return -EAGAIN;
+ }
+
+ retbuf = list_first_entry(&vvd->pending_buf_list,
+ struct virtio_video_buffer, list);
+ spin_unlock(&vvd->pending_buf_list_lock);
+
+ *virtio_vb = retbuf;
+ return 0;
+}
+
+int virtio_video_pending_buf_list_add(struct virtio_video_device *vvd,
+ struct virtio_video_buffer *virtio_vb)
+{
+ if (vvd->is_m2m_dev) {
+ v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
+ return -EPERM;
+ }
+
+ spin_lock(&vvd->pending_buf_list_lock);
+ list_add_tail(&virtio_vb->list, &vvd->pending_buf_list);
+ spin_unlock(&vvd->pending_buf_list_lock);
+
+ return 0;
+}
+
+int virtio_video_pending_buf_list_del(struct virtio_video_device *vvd,
+ struct virtio_video_buffer *virtio_vb)
+{
+ struct virtio_video_buffer *vb, *vb_tmp;
+ int ret = -EINVAL;
+
+ if (vvd->is_m2m_dev) {
+ v4l2_err(&vvd->v4l2_dev, "Unexpected call for m2m device!\n");
+ return -EPERM;
+ }
+
+ spin_lock(&vvd->pending_buf_list_lock);
+ if (list_empty(&vvd->pending_buf_list)) {
+ spin_unlock(&vvd->pending_buf_list_lock);
+ return -EAGAIN;
+ }
+
+ list_for_each_entry_safe(vb, vb_tmp, &vvd->pending_buf_list, list) {
+ if (vb->resource_id == virtio_vb->resource_id) {
+ list_del(&vb->list);
+ ret = 0;
+ break;
+ }
+ }
+ spin_unlock(&vvd->pending_buf_list_lock);
+
+ return ret;
+}
+
+int virtio_video_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ int i;
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+ struct video_format_info *p_info;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (*num_planes)
+ return 0;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ p_info = &stream->in_info;
+ else
+ p_info = &stream->out_info;
+
+ *num_planes = p_info->num_planes;
+
+ for (i = 0; i < p_info->num_planes; i++)
+ sizes[i] = p_info->plane_format[i].plane_size;
+
+ return 0;
+}
+
+static unsigned int
+build_virtio_video_sglist_contig(struct virtio_video_resource_sg_list *sgl,
+ struct vb2_buffer *vb, unsigned int plane)
+{
+ sgl->entries[0].addr = cpu_to_le64(vb2_dma_contig_plane_dma_addr(vb, plane));
+ sgl->entries[0].length = cpu_to_le32(vb->planes[plane].length);
+
+ sgl->num_entries = 1;
+
+ return VIRTIO_VIDEO_RESOURCE_SG_SIZE(1);
+}
+
+static unsigned int
+build_virtio_video_sglist(struct virtio_video_resource_sg_list *sgl,
+ struct vb2_buffer *vb, unsigned int plane,
+ bool has_iommu)
+{
+ int i;
+ struct scatterlist *sg;
+ struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, plane);
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ sgl->entries[i].addr = cpu_to_le64(has_iommu
+ ? sg_dma_address(sg)
+ : sg_phys(sg));
+ sgl->entries[i].length = cpu_to_le32(sg->length);
+ }
+
+ sgl->num_entries = sgt->nents;
+
+ return VIRTIO_VIDEO_RESOURCE_SG_SIZE(sgt->nents);
+}
+
+int virtio_video_buf_init(struct vb2_buffer *vb)
+{
+ int ret = 0;
+ void *buf;
+ size_t buf_size = 0;
+ struct virtio_video_resource_sg_list *sg_list;
+ unsigned int i, offset = 0, resource_id, nents = 0;
+ struct vb2_queue *vq = vb->vb2_queue;
+ enum v4l2_buf_type queue_type = vq->type;
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+ struct virtio_video_buffer *virtio_vb = to_virtio_vb(vb);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+
+ if (vvd->supp_non_contig) {
+ for (i = 0; i < vb->num_planes; i++) {
+ nents = vb2_dma_sg_plane_desc(vb, i)->nents;
+ buf_size += VIRTIO_VIDEO_RESOURCE_SG_SIZE(nents);
+ }
+
+ buf = kcalloc(1, buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < vb->num_planes; i++) {
+ sg_list = buf + offset;
+ offset += build_virtio_video_sglist(sg_list, vb, i,
+ vvd->has_iommu);
+ }
+ } else {
+ buf_size = vb->num_planes * VIRTIO_VIDEO_RESOURCE_SG_SIZE(nents);
+
+ buf = kcalloc(1, buf_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < vb->num_planes; i++) {
+ sg_list = buf + offset;
+ offset += build_virtio_video_sglist_contig(sg_list,
+ vb, i);
+ }
+ }
+
+ virtio_video_resource_id_get(vvd, &resource_id);
+
+ ret = virtio_video_cmd_resource_attach(vvd, stream->stream_id,
+ resource_id,
+ to_virtio_queue_type(queue_type),
+ buf, buf_size);
+ if (ret) {
+ virtio_video_resource_id_put(vvd, resource_id);
+ kfree(buf);
+ return ret;
+ }
+
+ virtio_vb->queued = false;
+ virtio_vb->resource_id = resource_id;
+
+ return 0;
+}
+
+void virtio_video_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+ struct virtio_video_buffer *virtio_vb = to_virtio_vb(vb);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+
+ virtio_video_resource_id_put(vvd, virtio_vb->resource_id);
+}
+
+void virtio_video_buf_queue(struct vb2_buffer *vb)
+{
+ int i, ret;
+ struct virtio_video_buffer *virtio_vb;
+ uint32_t data_size[VB2_MAX_PLANES] = {0};
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+
+ for (i = 0; i < vb->num_planes; ++i)
+ data_size[i] = vb->planes[i].bytesused;
+
+ virtio_vb = to_virtio_vb(vb);
+
+ if (!vvd->is_m2m_dev)
+ virtio_video_pending_buf_list_add(vvd, virtio_vb);
+
+ ret = virtio_video_cmd_resource_queue(vvd, stream->stream_id,
+ virtio_vb, data_size,
+ vb->num_planes,
+ to_virtio_queue_type(vb->type));
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to queue buffer\n");
+ return;
+ }
+
+ virtio_vb->queued = true;
+}
+
+int virtio_video_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (vvd->is_m2m_dev)
+ return v4l2_m2m_ioctl_qbuf(file, priv, buf);
+
+ return vb2_ioctl_qbuf(file, priv, buf);
+}
+
+int virtio_video_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (vvd->is_m2m_dev)
+ return v4l2_m2m_ioctl_dqbuf(file, priv, buf);
+
+ return vb2_ioctl_dqbuf(file, priv, buf);
+}
+
+int virtio_video_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct video_device *video_dev = video_devdata(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ if (strscpy(cap->driver, DRIVER_NAME, sizeof(cap->driver)) < 0)
+ v4l2_err(&vvd->v4l2_dev, "failed to copy driver name\n");
+ if (strscpy(cap->card, video_dev->name, sizeof(cap->card)) < 0)
+ v4l2_err(&vvd->v4l2_dev, "failed to copy card name\n");
+
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "virtio:%s",
+ video_dev->name);
+
+ cap->device_caps = video_dev->device_caps;
+ return 0;
+}
+
+int virtio_video_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format *fmt;
+ struct video_format_frame *frm;
+ struct virtio_video_format_frame *frame;
+ int idx = f->index;
+
+ fmt = virtio_video_find_video_format(&vvd->input_fmt_list,
+ f->pixel_format);
+ if (fmt == NULL)
+ fmt = virtio_video_find_video_format(&vvd->output_fmt_list,
+ f->pixel_format);
+ if (fmt == NULL)
+ return -EINVAL;
+
+ if (idx >= fmt->desc.num_frames)
+ return -EINVAL;
+
+ frm = &fmt->frames[idx];
+ frame = &frm->frame;
+
+ if (frame->width.min == frame->width.max &&
+ frame->height.min == frame->height.max) {
+ f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ f->discrete.width = frame->width.min;
+ f->discrete.height = frame->height.min;
+ return 0;
+ }
+
+ f->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ f->stepwise.min_width = frame->width.min;
+ f->stepwise.max_width = frame->width.max;
+ f->stepwise.min_height = frame->height.min;
+ f->stepwise.max_height = frame->height.max;
+ f->stepwise.step_width = frame->width.step;
+ f->stepwise.step_height = frame->height.step;
+ return 0;
+}
+
+static bool in_stepped_interval(struct virtio_video_format_range range,
+ uint32_t point)
+{
+ if (point < range.min || point > range.max)
+ return false;
+
+ if (range.step == 0 && range.min == range.max && range.min == point)
+ return true;
+
+ if (range.step != 0 && (point - range.min) % range.step == 0)
+ return true;
+
+ return false;
+}
+
+int virtio_video_enum_framemintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format *fmt;
+ struct video_format_frame *frm;
+ struct virtio_video_format_frame *frame;
+ struct virtio_video_format_range *frate;
+ int idx = f->index;
+ int f_idx;
+
+ fmt = virtio_video_find_video_format(&vvd->input_fmt_list,
+ f->pixel_format);
+ if (fmt == NULL)
+ fmt = virtio_video_find_video_format(&vvd->output_fmt_list,
+ f->pixel_format);
+ if (fmt == NULL)
+ return -EINVAL;
+
+ for (f_idx = 0; f_idx <= fmt->desc.num_frames; f_idx++) {
+ frm = &fmt->frames[f_idx];
+ frame = &frm->frame;
+ if (in_stepped_interval(frame->width, f->width) &&
+ in_stepped_interval(frame->height, f->height))
+ break;
+ }
+
+ if (frame == NULL || f->index >= frame->num_rates)
+ return -EINVAL;
+
+ frate = &frm->frame_rates[idx];
+ if (frate->max == frate->min) {
+ f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ f->discrete.numerator = 1;
+ f->discrete.denominator = frate->max;
+ } else {
+ f->stepwise.min.numerator = 1;
+ f->stepwise.min.denominator = frate->max;
+ f->stepwise.max.numerator = 1;
+ f->stepwise.max.denominator = frate->min;
+ f->stepwise.step.numerator = 1;
+ f->stepwise.step.denominator = frate->step;
+ if (frate->step == 1)
+ f->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
+ else
+ f->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+ }
+ return 0;
+}
+
+int virtio_video_stream_get_params(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream)
+{
+ int ret;
+
+ if (vvd->is_m2m_dev) {
+ ret = virtio_video_cmd_get_params(vvd, stream,
+ VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to get stream in params\n");
+ goto err_get_parms;
+ }
+ }
+
+ ret = virtio_video_cmd_get_params(vvd, stream,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+ if (ret)
+ v4l2_err(&vvd->v4l2_dev, "failed to get stream out params\n");
+
+err_get_parms:
+ return ret;
+}
+
+int virtio_video_stream_get_controls(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream)
+{
+ int ret;
+
+ ret = virtio_video_cmd_get_control(vvd, stream,
+ VIRTIO_VIDEO_CONTROL_PROFILE);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to get stream profile\n");
+ goto err_get_ctrl;
+ }
+
+ ret = virtio_video_cmd_get_control(vvd, stream,
+ VIRTIO_VIDEO_CONTROL_LEVEL);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to get stream level\n");
+ goto err_get_ctrl;
+ }
+
+ ret = virtio_video_cmd_get_control(vvd, stream,
+ VIRTIO_VIDEO_CONTROL_BITRATE);
+ if (ret)
+ v4l2_err(&vvd->v4l2_dev, "failed to get stream bitrate\n");
+
+err_get_ctrl:
+ return ret;
+}
+
+int virtio_video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct video_format_info *info;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct virtio_video_stream *stream = file2stream(file);
+
+ if (!V4L2_TYPE_IS_OUTPUT(f->type))
+ info = &stream->out_info;
+ else
+ info = &stream->in_info;
+
+ virtio_video_format_from_info(info, pix_mp);
+
+ return 0;
+}
+
+int virtio_video_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ int i, ret;
+ struct virtio_video_stream *stream = file2stream(file);
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format_info info;
+ struct video_format_info *p_info;
+ uint32_t queue;
+
+ ret = virtio_video_try_fmt(stream, f);
+ if (ret)
+ return ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ virtio_video_format_fill_default_info(&info, &stream->in_info);
+ queue = VIRTIO_VIDEO_QUEUE_TYPE_INPUT;
+ } else {
+ virtio_video_format_fill_default_info(&info, &stream->out_info);
+ queue = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT;
+ }
+
+ info.frame_width = pix_mp->width;
+ info.frame_height = pix_mp->height;
+ info.num_planes = pix_mp->num_planes;
+ info.fourcc_format = pix_mp->pixelformat;
+
+ for (i = 0; i < info.num_planes; i++) {
+ info.plane_format[i].stride =
+ pix_mp->plane_fmt[i].bytesperline;
+ info.plane_format[i].plane_size =
+ pix_mp->plane_fmt[i].sizeimage;
+ }
+
+ virtio_video_cmd_set_params(vvd, stream, &info, queue);
+ virtio_video_stream_get_params(vvd, stream);
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ p_info = &stream->in_info;
+ else
+ p_info = &stream->out_info;
+
+ virtio_video_format_from_info(p_info, pix_mp);
+
+ return 0;
+}
+
+int virtio_video_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct video_format_info *info;
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ switch (vvd->type) {
+ case VIRTIO_VIDEO_DEVICE_ENCODER:
+ if (!V4L2_TYPE_IS_OUTPUT(sel->type))
+ return -EINVAL;
+ info = &stream->in_info;
+ break;
+ case VIRTIO_VIDEO_DEVICE_DECODER:
+ case VIRTIO_VIDEO_DEVICE_CAMERA:
+ if (V4L2_TYPE_IS_OUTPUT(sel->type))
+ return -EINVAL;
+ info = &stream->out_info;
+ break;
+ default:
+ v4l2_err(&vvd->v4l2_dev, "unsupported device type\n");
+ return -EINVAL;
+ }
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ sel->r.width = info->frame_width;
+ sel->r.height = info->frame_height;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ sel->r.top = info->crop.top;
+ sel->r.left = info->crop.left;
+ sel->r.width = info->frame_width;
+ sel->r.height = info->frame_height;
+ break;
+ default:
+ v4l2_dbg(1, vvd->debug, &vvd->v4l2_dev,
+ "unsupported/invalid selection target: %d\n",
+ sel->target);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int virtio_video_try_fmt(struct virtio_video_stream *stream,
+ struct v4l2_format *f)
+{
+ int i, idx = 0;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format *fmt;
+ bool found = false;
+ struct video_format_frame *frm;
+ struct virtio_video_format_frame *frame;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ fmt = virtio_video_find_video_format(&vvd->input_fmt_list,
+ pix_mp->pixelformat);
+ else
+ fmt = virtio_video_find_video_format(&vvd->output_fmt_list,
+ pix_mp->pixelformat);
+
+ if (!fmt) {
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ virtio_video_format_from_info(&stream->out_info,
+ pix_mp);
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ virtio_video_format_from_info(&stream->in_info,
+ pix_mp);
+ else
+ return -EINVAL;
+ return 0;
+ }
+
+ for (i = 0; i < fmt->desc.num_frames && !found; i++) {
+ frm = &fmt->frames[i];
+ frame = &frm->frame;
+ if (!within_range(frame->width.min, pix_mp->width,
+ frame->width.max))
+ continue;
+
+ if (!within_range(frame->height.min, pix_mp->height,
+ frame->height.max))
+ continue;
+ idx = i;
+ /*
+ * Try to find a more suitable frame size. Go with the current
+ * one otherwise.
+ */
+ if (needs_alignment(pix_mp->width, frame->width.step))
+ continue;
+
+ if (needs_alignment(pix_mp->height, frame->height.step))
+ continue;
+
+ stream->current_frame = frm;
+ found = true;
+ }
+
+ if (!found) {
+ frm = &fmt->frames[idx];
+ if (!frm)
+ return -EINVAL;
+
+ frame = &frm->frame;
+ pix_mp->width = clamp(pix_mp->width, frame->width.min,
+ frame->width.max);
+ if (frame->width.step != 0)
+ pix_mp->width = ALIGN(pix_mp->width, frame->width.step);
+
+ pix_mp->height = clamp(pix_mp->height, frame->height.min,
+ frame->height.max);
+ if (frame->height.step != 0)
+ pix_mp->height = ALIGN(pix_mp->height,
+ frame->height.step);
+ stream->current_frame = frm;
+ }
+
+ return 0;
+}
+
+static int virtio_video_queue_free(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum v4l2_buf_type type)
+{
+ int ret;
+ enum virtio_video_queue_type queue_type = to_virtio_queue_type(type);
+
+ ret = virtio_video_cmd_queue_detach_resources(vvd, stream, queue_type);
+ if (ret) {
+ v4l2_warn(&vvd->v4l2_dev,
+ "failed to destroy resources\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int virtio_video_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ int ret;
+ struct virtio_video_stream *stream = file2stream(file);
+ struct v4l2_m2m_ctx *m2m_ctx = stream->fh.m2m_ctx;
+ struct virtio_video_device *vvd = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+ struct vb2_queue *vq;
+
+ if (vvd->is_m2m_dev)
+ vq = v4l2_m2m_get_vq(m2m_ctx, rb->type);
+ else
+ vq = vdev->queue;
+
+ if (rb->count == 0) {
+ ret = virtio_video_queue_free(vvd, stream, vq->type);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (vvd->is_m2m_dev)
+ return v4l2_m2m_reqbufs(file, m2m_ctx, rb);
+ else
+ return vb2_ioctl_reqbufs(file, priv, rb);
+}
+
+int virtio_video_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ default:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ }
+}
+
+void virtio_video_queue_eos_event(struct virtio_video_stream *stream)
+{
+ static const struct v4l2_event eos_event = {
+ .type = V4L2_EVENT_EOS
+ };
+
+ v4l2_event_queue_fh(&stream->fh, &eos_event);
+}
+
+void virtio_video_queue_res_chg_event(struct virtio_video_stream *stream)
+{
+ static const struct v4l2_event ev_src_ch = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes =
+ V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
+ v4l2_event_queue_fh(&stream->fh, &ev_src_ch);
+}
+
+void virtio_video_handle_error(struct virtio_video_stream *stream)
+{
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+
+ if (vvd->is_m2m_dev)
+ virtio_video_queue_release_buffers
+ (stream, VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
+
+ virtio_video_queue_release_buffers
+ (stream, VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+}
+
+int virtio_video_queue_release_buffers(struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type)
+{
+ int ret;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct vb2_v4l2_buffer *v4l2_vb;
+ struct virtio_video_buffer *vvb;
+
+ ret = virtio_video_cmd_queue_clear(vvd, stream, queue_type);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to clear queue\n");
+ return ret;
+ }
+
+ if (!vvd->is_m2m_dev) {
+ while (!virtio_video_pending_buf_list_pop(vvd, &vvb) && vvb) {
+ v4l2_vb = &vvb->v4l2_m2m_vb.vb;
+ v4l2_m2m_buf_done(v4l2_vb, VB2_BUF_STATE_ERROR);
+ }
+ return 0;
+ }
+
+ for (;;) {
+ if (queue_type == VIRTIO_VIDEO_QUEUE_TYPE_INPUT)
+ v4l2_vb = v4l2_m2m_src_buf_remove(stream->fh.m2m_ctx);
+ else
+ v4l2_vb = v4l2_m2m_dst_buf_remove(stream->fh.m2m_ctx);
+ if (!v4l2_vb)
+ break;
+ v4l2_m2m_buf_done(v4l2_vb, VB2_BUF_STATE_ERROR);
+ }
+
+ return 0;
+}
+
+void virtio_video_buf_done(struct virtio_video_buffer *virtio_vb,
+ uint32_t flags, uint64_t timestamp,
+ uint32_t data_sizes[])
+{
+ int i;
+ enum vb2_buffer_state done_state = VB2_BUF_STATE_DONE;
+ struct vb2_v4l2_buffer *v4l2_vb = &virtio_vb->v4l2_m2m_vb.vb;
+ struct vb2_buffer *vb = &v4l2_vb->vb2_buf;
+ struct vb2_queue *vb2_queue = vb->vb2_queue;
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vb2_queue);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format_info *p_info;
+
+ virtio_vb->queued = false;
+
+ if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_ERR)
+ done_state = VB2_BUF_STATE_ERROR;
+
+ if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_KEY_FRAME)
+ v4l2_vb->flags |= V4L2_BUF_FLAG_KEYFRAME;
+
+ if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_BFRAME)
+ v4l2_vb->flags |= V4L2_BUF_FLAG_BFRAME;
+
+ if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_PFRAME)
+ v4l2_vb->flags |= V4L2_BUF_FLAG_PFRAME;
+
+ if (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_EOS) {
+ v4l2_vb->flags |= V4L2_BUF_FLAG_LAST;
+ virtio_video_state_update(stream, STREAM_STATE_STOPPED);
+ virtio_video_queue_eos_event(stream);
+ }
+
+ if ((flags & VIRTIO_VIDEO_DEQUEUE_FLAG_ERR) ||
+ (flags & VIRTIO_VIDEO_DEQUEUE_FLAG_EOS)) {
+ vb->planes[0].bytesused = 0;
+
+ if (!vvd->is_m2m_dev)
+ virtio_video_pending_buf_list_del(vvd, virtio_vb);
+
+ v4l2_m2m_buf_done(v4l2_vb, done_state);
+ return;
+ }
+
+ if (!V4L2_TYPE_IS_OUTPUT(vb2_queue->type)) {
+ switch (vvd->type) {
+ case VIRTIO_VIDEO_DEVICE_ENCODER:
+ for (i = 0; i < vb->num_planes; i++)
+ vb->planes[i].bytesused =
+ le32_to_cpu(data_sizes[i]);
+ break;
+ case VIRTIO_VIDEO_DEVICE_CAMERA:
+ case VIRTIO_VIDEO_DEVICE_DECODER:
+ p_info = &stream->out_info;
+ for (i = 0; i < p_info->num_planes; i++)
+ vb->planes[i].bytesused =
+ p_info->plane_format[i].plane_size;
+ break;
+ }
+
+ vb->timestamp = timestamp;
+ }
+
+ if (!vvd->is_m2m_dev)
+ virtio_video_pending_buf_list_del(vvd, virtio_vb);
+
+ v4l2_m2m_buf_done(v4l2_vb, done_state);
+}
+
+static int virtio_video_set_device_busy(struct virtio_video_device *vvd)
+{
+ struct video_device *vd = &vvd->video_dev;
+ int ret = 0;
+
+ /* Multiple open is allowed for m2m device */
+ if (vvd->is_m2m_dev)
+ return 0;
+
+ mutex_lock(vd->lock);
+
+ if (vvd->device_busy)
+ ret = -EBUSY;
+ else
+ vvd->device_busy = true;
+
+ mutex_unlock(vd->lock);
+
+ return ret;
+}
+
+static void virtio_video_clear_device_busy(struct virtio_video_device *vvd,
+ struct mutex *lock)
+{
+ /* Nothing to do for m2m device */
+ if (vvd->is_m2m_dev)
+ return;
+
+ if (lock)
+ mutex_lock(lock);
+
+ vvd->device_busy = false;
+
+ if (lock)
+ mutex_unlock(lock);
+}
+
+static int virtio_video_device_open(struct file *file)
+{
+ int ret;
+ uint32_t stream_id;
+ char name[TASK_COMM_LEN];
+ struct virtio_video_stream *stream;
+ struct video_format *default_fmt;
+ enum virtio_video_format format;
+ struct video_device *video_dev = video_devdata(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ ret = virtio_video_set_device_busy(vvd);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "device already in use.\n");
+ return ret;
+ }
+
+ default_fmt = list_first_entry_or_null(vvd->ops->get_fmt_list(vvd),
+ struct video_format,
+ formats_list_entry);
+ if (!default_fmt) {
+ v4l2_err(&vvd->v4l2_dev, "device failed to start\n");
+ ret = -EIO;
+ goto err;
+ }
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ get_task_comm(name, current);
+ format = virtio_video_v4l2_format_to_virtio(default_fmt->desc.format);
+ virtio_video_stream_id_get(vvd, stream, &stream_id);
+ ret = virtio_video_cmd_stream_create(vvd, stream_id, format, name);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to create stream\n");
+ goto err_stream_create;
+ }
+
+ stream->video_dev = video_dev;
+ stream->stream_id = stream_id;
+
+ virtio_video_state_reset(stream);
+
+ ret = virtio_video_stream_get_params(vvd, stream);
+ if (ret)
+ goto err_stream_create;
+
+ if (format >= VIRTIO_VIDEO_FORMAT_CODED_MIN &&
+ format <= VIRTIO_VIDEO_FORMAT_CODED_MAX) {
+ ret = virtio_video_stream_get_controls(vvd, stream);
+ if (ret)
+ goto err_stream_create;
+ }
+
+ mutex_init(&stream->vq_mutex);
+ v4l2_fh_init(&stream->fh, video_dev);
+ stream->fh.ctrl_handler = &stream->ctrl_handler;
+
+ if (vvd->is_m2m_dev) {
+ stream->fh.m2m_ctx = v4l2_m2m_ctx_init(vvd->m2m_dev, stream,
+ vvd->ops->init_queues);
+ if (IS_ERR(stream->fh.m2m_ctx)) {
+ ret = PTR_ERR(stream->fh.m2m_ctx);
+ goto err_init_ctx;
+ }
+
+ v4l2_m2m_set_src_buffered(stream->fh.m2m_ctx, true);
+ v4l2_m2m_set_dst_buffered(stream->fh.m2m_ctx, true);
+ } else {
+ vvd->ops->init_queues(stream, NULL, &vvd->vb2_output_queue);
+ /* Video dev queue is required for vb2 ioctl wrappers */
+ video_dev->queue = &vvd->vb2_output_queue;
+ }
+
+ file->private_data = &stream->fh;
+ v4l2_fh_add(&stream->fh);
+
+ if (vvd->ops->init_ctrls) {
+ ret = vvd->ops->init_ctrls(stream);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to init controls\n");
+ goto err_init_ctrls;
+ }
+ }
+ return 0;
+
+err_init_ctrls:
+ v4l2_fh_del(&stream->fh);
+ mutex_lock(video_dev->lock);
+ if (vvd->is_m2m_dev)
+ v4l2_m2m_ctx_release(stream->fh.m2m_ctx);
+ mutex_unlock(video_dev->lock);
+err_init_ctx:
+ v4l2_fh_exit(&stream->fh);
+err_stream_create:
+ virtio_video_stream_id_put(vvd, stream_id);
+ kfree(stream);
+err:
+ virtio_video_clear_device_busy(vvd, video_dev->lock);
+ return ret;
+}
+
+static int virtio_video_device_release(struct file *file)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct video_device *video_dev = video_devdata(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ mutex_lock(video_dev->lock);
+
+ v4l2_fh_del(&stream->fh);
+ if (vvd->is_m2m_dev) {
+ v4l2_m2m_ctx_release(stream->fh.m2m_ctx);
+ } else if (file->private_data == video_dev->queue->owner) {
+ vb2_queue_release(&vvd->vb2_output_queue);
+ video_dev->queue->owner = NULL;
+ }
+
+ v4l2_fh_exit(&stream->fh);
+
+ virtio_video_cmd_stream_destroy(vvd, stream->stream_id);
+ virtio_video_stream_id_put(vvd, stream->stream_id);
+
+ kfree(stream);
+
+ /* Mutex already locked here, passing NULL */
+ virtio_video_clear_device_busy(vvd, NULL);
+
+ mutex_unlock(video_dev->lock);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations virtio_video_device_m2m_fops = {
+ .owner = THIS_MODULE,
+ .open = virtio_video_device_open,
+ .release = virtio_video_device_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct v4l2_file_operations virtio_video_device_fops = {
+ .owner = THIS_MODULE,
+ .open = virtio_video_device_open,
+ .release = virtio_video_device_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static void virtio_video_device_run(void *priv)
+{
+
+}
+
+static void virtio_video_device_job_abort(void *priv)
+{
+ struct virtio_video_stream *stream = priv;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+
+ v4l2_m2m_job_finish(vvd->m2m_dev, stream->fh.m2m_ctx);
+}
+
+static const struct v4l2_m2m_ops virtio_video_device_m2m_ops = {
+ .device_run = virtio_video_device_run,
+ .job_abort = virtio_video_device_job_abort,
+};
+
+static int virtio_video_device_register(struct virtio_video_device *vvd)
+{
+ int ret;
+ struct video_device *vd;
+
+ vd = &vvd->video_dev;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
+ ret = video_register_device(vd, VFL_TYPE_VIDEO, vvd->vid_dev_nr);
+#else
+ ret = video_register_device(vd, VFL_TYPE_GRABBER, vvd->vid_dev_nr);
+#endif
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to register video device\n");
+ return ret;
+ }
+
+ v4l2_info(&vvd->v4l2_dev, "Device '%s' registered as /dev/video%d\n",
+ vd->name, vd->num);
+
+ return 0;
+}
+
+static void virtio_video_device_unregister(struct virtio_video_device *vvd)
+{
+ video_unregister_device(&vvd->video_dev);
+}
+
+static int
+virtio_video_query_capability(struct virtio_video_device *vvd,
+ void *resp_buf,
+ enum virtio_video_queue_type queue_type)
+{
+ int ret;
+ int resp_size = vvd->max_caps_len;
+
+ ret = virtio_video_cmd_query_capability(vvd, resp_buf, resp_size,
+ queue_type);
+ if (ret)
+ v4l2_err(&vvd->v4l2_dev, "failed to query capability\n");
+
+ return ret;
+}
+
+int virtio_video_device_init(struct virtio_video_device *vvd)
+{
+ int ret;
+ int vfl_dir;
+ u32 dev_caps;
+ struct video_device *vd;
+ struct v4l2_m2m_dev *m2m_dev;
+ const struct v4l2_file_operations *fops;
+ void *input_resp_buf, *output_resp_buf;
+
+ output_resp_buf = kzalloc(vvd->max_caps_len, GFP_KERNEL);
+ if (!output_resp_buf)
+ return -ENOMEM;
+
+ ret = virtio_video_query_capability(vvd, output_resp_buf,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to get output caps\n");
+ goto err_output_cap;
+ }
+
+ if (vvd->is_m2m_dev) {
+ input_resp_buf = kzalloc(vvd->max_caps_len, GFP_KERNEL);
+ if (!input_resp_buf) {
+ ret = -ENOMEM;
+ goto err_input_buf;
+ }
+
+ ret = virtio_video_query_capability(vvd, input_resp_buf,
+ VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to get input caps\n");
+ goto err_input_cap;
+ }
+
+ m2m_dev = v4l2_m2m_init(&virtio_video_device_m2m_ops);
+ if (IS_ERR(m2m_dev)) {
+ v4l2_err(&vvd->v4l2_dev, "failed to init m2m device\n");
+ ret = PTR_ERR(m2m_dev);
+ goto err_m2m_dev;
+ }
+ vfl_dir = VFL_DIR_M2M;
+ fops = &virtio_video_device_m2m_fops;
+ dev_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
+ } else {
+ input_resp_buf = NULL;
+ m2m_dev = NULL;
+ vfl_dir = VFL_DIR_RX;
+ fops = &virtio_video_device_fops;
+ dev_caps = V4L2_CAP_STREAMING;
+ if (vvd->is_mplane_cam)
+ dev_caps |= V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ else
+ dev_caps |= V4L2_CAP_VIDEO_CAPTURE;
+ }
+
+ vvd->m2m_dev = m2m_dev;
+ mutex_init(&vvd->video_dev_mutex);
+ vd = &vvd->video_dev;
+ vd->lock = &vvd->video_dev_mutex;
+ vd->v4l2_dev = &vvd->v4l2_dev;
+ vd->vfl_dir = vfl_dir;
+ vd->ioctl_ops = NULL;
+ vd->fops = fops;
+ vd->device_caps = dev_caps;
+ vd->release = video_device_release_empty;
+
+ /* Use the selection API instead */
+ v4l2_disable_ioctl(vd, VIDIOC_CROPCAP);
+ v4l2_disable_ioctl(vd, VIDIOC_G_CROP);
+
+ video_set_drvdata(vd, vvd);
+
+ INIT_LIST_HEAD(&vvd->input_fmt_list);
+ INIT_LIST_HEAD(&vvd->output_fmt_list);
+ INIT_LIST_HEAD(&vvd->controls_fmt_list);
+ INIT_LIST_HEAD(&vvd->pending_buf_list);
+
+ vvd->num_output_fmts = 0;
+ vvd->num_input_fmts = 0;
+
+ switch (vvd->type) {
+ case VIRTIO_VIDEO_DEVICE_CAMERA:
+ virtio_video_cam_init(vvd);
+ break;
+ case VIRTIO_VIDEO_DEVICE_ENCODER:
+ virtio_video_enc_init(vvd);
+ break;
+ case VIRTIO_VIDEO_DEVICE_DECODER:
+ default:
+ virtio_video_dec_init(vvd);
+ break;
+ }
+
+ ret = virtio_video_parse_virtio_capabilities(vvd, input_resp_buf,
+ output_resp_buf);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to parse a function\n");
+ goto parse_cap_err;
+ }
+
+ ret = virtio_video_parse_virtio_control(vvd);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to query controls\n");
+ goto parse_ctrl_err;
+ }
+
+ ret = virtio_video_device_register(vvd);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to init virtio video device\n");
+ goto register_err;
+ }
+
+ goto out_cleanup;
+
+register_err:
+ virtio_video_clean_control(vvd);
+parse_ctrl_err:
+ virtio_video_clean_capability(vvd);
+parse_cap_err:
+ if (vvd->is_m2m_dev)
+ v4l2_m2m_release(vvd->m2m_dev);
+err_m2m_dev:
+err_input_cap:
+out_cleanup:
+ if (vvd->is_m2m_dev)
+ kfree(input_resp_buf);
+err_input_buf:
+err_output_cap:
+ kfree(output_resp_buf);
+
+ return ret;
+}
+
+void virtio_video_device_deinit(struct virtio_video_device *vvd)
+{
+ vvd->commandq.ready = false;
+ vvd->eventq.ready = false;
+
+ virtio_video_device_unregister(vvd);
+ if (vvd->is_m2m_dev)
+ v4l2_m2m_release(vvd->m2m_dev);
+ virtio_video_clean_control(vvd);
+ virtio_video_clean_capability(vvd);
+}
diff --git a/virtio_video/virtio_video_driver.c b/virtio_video/virtio_video_driver.c
new file mode 100644
index 0000000..734c5b7
--- /dev/null
+++ b/virtio_video/virtio_video_driver.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Driver for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0)
+#include <linux/dma-map-ops.h>
+#else
+#include <linux/dma-mapping.h>
+#endif
+
+#include "virtio_video.h"
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+
+static unsigned int use_dma_mem;
+module_param(use_dma_mem, uint, 0644);
+MODULE_PARM_DESC(use_dma_mem, "Try to allocate buffers from the DMA zone");
+
+static int vid_nr_dec = -1;
+module_param(vid_nr_dec, int, 0644);
+MODULE_PARM_DESC(vid_nr_dec, "videoN start number, -1 is autodetect");
+
+static int vid_nr_enc = -1;
+module_param(vid_nr_enc, int, 0644);
+MODULE_PARM_DESC(vid_nr_enc, "videoN start number, -1 is autodetect");
+
+static int vid_nr_cam = -1;
+module_param(vid_nr_cam, int, 0644);
+MODULE_PARM_DESC(vid_nr_cam, "videoN start number, -1 is autodetect");
+
+static bool mplane_cam = true;
+module_param(mplane_cam, bool, 0644);
+MODULE_PARM_DESC(mplane_cam,
+ "1 (default) - multiplanar camera, 0 - single planar camera");
+
+static int virtio_video_probe(struct virtio_device *vdev)
+{
+ int ret;
+ struct virtio_video_device *vvd;
+ struct virtqueue *vqs[2];
+ struct device *dev = &vdev->dev;
+ struct device *pdev = dev->parent;
+
+ static const char * const names[] = { "commandq", "eventq" };
+ static vq_callback_t *callbacks[] = {
+ virtio_video_cmd_cb,
+ virtio_video_event_cb
+ };
+
+ if (!virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES)) {
+ dev_err(dev, "device must support guest allocated buffers\n");
+ return -ENODEV;
+ }
+
+ vvd = devm_kzalloc(dev, sizeof(*vvd), GFP_KERNEL);
+ if (!vvd)
+ return -ENOMEM;
+
+ vvd->is_m2m_dev = true;
+
+ switch (vdev->id.device) {
+ case VIRTIO_ID_VIDEO_CAM:
+ vvd->is_m2m_dev = false;
+ vvd->vid_dev_nr = vid_nr_cam;
+ vvd->is_mplane_cam = mplane_cam;
+ vvd->type = VIRTIO_VIDEO_DEVICE_CAMERA;
+ break;
+ case VIRTIO_ID_VIDEO_ENC:
+ vvd->vid_dev_nr = vid_nr_enc;
+ vvd->type = VIRTIO_VIDEO_DEVICE_ENCODER;
+ break;
+ case VIRTIO_ID_VIDEO_DEC:
+ default:
+ vvd->vid_dev_nr = vid_nr_dec;
+ vvd->type = VIRTIO_VIDEO_DEVICE_DECODER;
+ break;
+ }
+
+ vvd->vdev = vdev;
+ vvd->debug = debug;
+ vvd->use_dma_mem = use_dma_mem;
+ vdev->priv = vvd;
+
+ spin_lock_init(&vvd->pending_buf_list_lock);
+ spin_lock_init(&vvd->resource_idr_lock);
+ idr_init(&vvd->resource_idr);
+ spin_lock_init(&vvd->stream_idr_lock);
+ idr_init(&vvd->stream_idr);
+
+ init_waitqueue_head(&vvd->wq);
+
+ if (virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG))
+ vvd->supp_non_contig = true;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
+ vvd->has_iommu = !virtio_has_dma_quirk(vdev);
+#else
+ vvd->has_iommu = !virtio_has_iommu_quirk(vdev);
+#endif
+
+ if (!dev->dma_ops)
+ set_dma_ops(dev, pdev->dma_ops);
+
+ /*
+ * Set it to coherent_dma_mask by default if the architecture
+ * code has not set it.
+ */
+ if (!dev->dma_mask)
+ dev->dma_mask = &dev->coherent_dma_mask;
+
+ dma_set_mask(dev, *pdev->dma_mask);
+
+ dev_set_name(dev, "%s.%i", DRIVER_NAME, vdev->index);
+ ret = v4l2_device_register(dev, &vvd->v4l2_dev);
+ if (ret)
+ goto err_v4l2_reg;
+
+ spin_lock_init(&vvd->commandq.qlock);
+ init_waitqueue_head(&vvd->commandq.reclaim_queue);
+
+ INIT_WORK(&vvd->eventq.work, virtio_video_process_events);
+
+ INIT_LIST_HEAD(&vvd->pending_vbuf_list);
+
+ ret = virtio_find_vqs(vdev, 2, vqs, callbacks, names, NULL);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to find virt queues\n");
+ goto err_vqs;
+ }
+
+ vvd->commandq.vq = vqs[0];
+ vvd->eventq.vq = vqs[1];
+
+ ret = virtio_video_alloc_vbufs(vvd);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to alloc vbufs\n");
+ goto err_vbufs;
+ }
+
+ virtio_cread(vdev, struct virtio_video_config, max_caps_length,
+ &vvd->max_caps_len);
+ if (!vvd->max_caps_len) {
+ v4l2_err(&vvd->v4l2_dev, "max_caps_len is zero\n");
+ ret = -EINVAL;
+ goto err_config;
+ }
+
+ virtio_cread(vdev, struct virtio_video_config, max_resp_length,
+ &vvd->max_resp_len);
+ if (!vvd->max_resp_len) {
+ v4l2_err(&vvd->v4l2_dev, "max_resp_len is zero\n");
+ ret = -EINVAL;
+ goto err_config;
+ }
+
+ ret = virtio_video_alloc_events(vvd);
+ if (ret)
+ goto err_events;
+
+ virtio_device_ready(vdev);
+ vvd->commandq.ready = true;
+ vvd->eventq.ready = true;
+
+ ret = virtio_video_device_init(vvd);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to init virtio video\n");
+ goto err_init;
+ }
+
+ return 0;
+
+err_init:
+err_events:
+err_config:
+ virtio_video_free_vbufs(vvd);
+err_vbufs:
+ vdev->config->del_vqs(vdev);
+err_vqs:
+ v4l2_device_unregister(&vvd->v4l2_dev);
+err_v4l2_reg:
+ devm_kfree(&vdev->dev, vvd);
+
+ return ret;
+}
+
+static void virtio_video_remove(struct virtio_device *vdev)
+{
+ struct virtio_video_device *vvd = vdev->priv;
+
+ virtio_video_device_deinit(vvd);
+ virtio_video_free_vbufs(vvd);
+ vdev->config->del_vqs(vdev);
+ v4l2_device_unregister(&vvd->v4l2_dev);
+ devm_kfree(&vdev->dev, vvd);
+}
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_VIDEO_DEC, VIRTIO_DEV_ANY_ID },
+ { VIRTIO_ID_VIDEO_ENC, VIRTIO_DEV_ANY_ID },
+ { VIRTIO_ID_VIDEO_CAM, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static unsigned int features[] = {
+ VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES,
+ VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG,
+};
+
+static struct virtio_driver virtio_video_driver = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .driver.name = DRIVER_NAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = virtio_video_probe,
+ .remove = virtio_video_remove,
+};
+
+module_virtio_driver(virtio_video_driver);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("virtio video driver");
+MODULE_AUTHOR("Dmitry Sepp <dmitry.sepp@opensynergy.com>");
+MODULE_AUTHOR("Kiran Pawar <kiran.pawar@opensynergy.com>");
+MODULE_AUTHOR("Nikolay Martyanov <nikolay.martyanov@opensynergy.com>");
+MODULE_AUTHOR("Samiullah Khawaja <samiullah.khawaja@opensynergy.com>");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/virtio_video/virtio_video_enc.c b/virtio_video/virtio_video_enc.c
new file mode 100644
index 0000000..66730bd
--- /dev/null
+++ b/virtio_video/virtio_video_enc.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Encoder for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/version.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+#include "virtio_video.h"
+
+static int virtio_video_enc_start_streaming(struct vb2_queue *vq,
+ unsigned int count)
+{
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+ bool input_queue = V4L2_TYPE_IS_OUTPUT(vq->type);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (virtio_video_state(stream) == STREAM_STATE_INIT ||
+ (!input_queue &&
+ virtio_video_state(stream) == STREAM_STATE_RESET) ||
+ (input_queue &&
+ virtio_video_state(stream) == STREAM_STATE_STOPPED))
+ virtio_video_state_update(stream, STREAM_STATE_RUNNING);
+
+ return 0;
+}
+
+static void virtio_video_enc_stop_streaming(struct vb2_queue *vq)
+{
+ int ret, queue_type;
+ struct virtio_video_stream *stream = vb2_get_drv_priv(vq);
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ queue_type = VIRTIO_VIDEO_QUEUE_TYPE_INPUT;
+ else
+ queue_type = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT;
+
+ ret = virtio_video_queue_release_buffers(stream, queue_type);
+ if (ret)
+ return;
+
+ vb2_wait_for_all_buffers(vq);
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ virtio_video_state_update(stream, STREAM_STATE_STOPPED);
+ else
+ virtio_video_state_update(stream, STREAM_STATE_RESET);
+}
+
+static const struct vb2_ops virtio_video_enc_qops = {
+ .queue_setup = virtio_video_queue_setup,
+ .buf_init = virtio_video_buf_init,
+ .buf_cleanup = virtio_video_buf_cleanup,
+ .buf_queue = virtio_video_buf_queue,
+ .start_streaming = virtio_video_enc_start_streaming,
+ .stop_streaming = virtio_video_enc_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int virtio_video_enc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ struct virtio_video_stream *stream = ctrl2stream(ctrl);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ uint32_t control, value;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ control = virtio_video_v4l2_control_to_virtio(ctrl->id);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ret = virtio_video_cmd_set_control(vvd, stream->stream_id,
+ control, ctrl->val);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ value = virtio_video_v4l2_level_to_virtio(ctrl->val);
+ ret = virtio_video_cmd_set_control(vvd, stream->stream_id,
+ control, value);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ value = virtio_video_v4l2_profile_to_virtio(ctrl->val);
+ ret = virtio_video_cmd_set_control(vvd, stream->stream_id,
+ control, value);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int virtio_video_enc_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ struct virtio_video_stream *stream = ctrl2stream(ctrl);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ if (virtio_video_state(stream) >= STREAM_STATE_INIT)
+ ctrl->val = stream->in_info.min_buffers;
+ else
+ ctrl->val = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops virtio_video_enc_ctrl_ops = {
+ .g_volatile_ctrl = virtio_video_enc_g_ctrl,
+ .s_ctrl = virtio_video_enc_s_ctrl,
+};
+
+int virtio_video_enc_init_ctrls(struct virtio_video_stream *stream)
+{
+ struct v4l2_ctrl *ctrl;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_control_format *c_fmt = NULL;
+
+ v4l2_ctrl_handler_init(&stream->ctrl_handler, 1);
+
+ ctrl = v4l2_ctrl_new_std(&stream->ctrl_handler,
+ &virtio_video_enc_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
+ MIN_BUFS_MIN, MIN_BUFS_MAX, MIN_BUFS_STEP,
+ MIN_BUFS_DEF);
+
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ list_for_each_entry(c_fmt, &vvd->controls_fmt_list,
+ controls_list_entry) {
+ switch (c_fmt->format) {
+ case V4L2_PIX_FMT_H264:
+ if (c_fmt->profile)
+ v4l2_ctrl_new_std_menu
+ (&stream->ctrl_handler,
+ &virtio_video_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ c_fmt->profile->max,
+ c_fmt->profile->skip_mask,
+ c_fmt->profile->min);
+
+ if (c_fmt->level)
+ v4l2_ctrl_new_std_menu
+ (&stream->ctrl_handler,
+ &virtio_video_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ c_fmt->level->max,
+ c_fmt->level->skip_mask,
+ c_fmt->level->min);
+ break;
+ default:
+ v4l2_dbg(1, vvd->debug,
+ &vvd->v4l2_dev, "unsupported format\n");
+ break;
+ }
+ }
+
+ if (stream->control.bitrate) {
+ v4l2_ctrl_new_std(&stream->ctrl_handler,
+ &virtio_video_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 1, S32_MAX,
+ 1, stream->control.bitrate);
+ }
+
+ if (stream->ctrl_handler.error)
+ return stream->ctrl_handler.error;
+
+ v4l2_ctrl_handler_setup(&stream->ctrl_handler);
+
+ return 0;
+}
+
+int virtio_video_enc_init_queues(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ int ret;
+ struct virtio_video_stream *stream = priv;
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct device *dev = vvd->v4l2_dev.dev;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = stream;
+ src_vq->buf_struct_size = sizeof(struct virtio_video_buffer);
+ src_vq->ops = &virtio_video_enc_qops;
+ src_vq->mem_ops = virtio_video_mem_ops(vvd);
+ src_vq->min_buffers_needed = stream->in_info.min_buffers;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &stream->vq_mutex;
+ src_vq->gfp_flags = virtio_video_gfp_flags(vvd);
+ src_vq->dev = dev;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = stream;
+ dst_vq->buf_struct_size = sizeof(struct virtio_video_buffer);
+ dst_vq->ops = &virtio_video_enc_qops;
+ dst_vq->mem_ops = virtio_video_mem_ops(vvd);
+ dst_vq->min_buffers_needed = stream->out_info.min_buffers;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &stream->vq_mutex;
+ dst_vq->gfp_flags = virtio_video_gfp_flags(vvd);
+ dst_vq->dev = dev;
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int virtio_video_try_encoder_cmd(struct file *file, void *fh,
+ struct v4l2_encoder_cmd *cmd)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (virtio_video_state(stream) == STREAM_STATE_DRAIN)
+ return -EBUSY;
+
+ switch (cmd->cmd) {
+ case V4L2_ENC_CMD_STOP:
+ case V4L2_ENC_CMD_START:
+ if (cmd->flags != 0) {
+ v4l2_err(&vvd->v4l2_dev, "flags=%u are not supported",
+ cmd->flags);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int virtio_video_encoder_cmd(struct file *file, void *fh,
+ struct v4l2_encoder_cmd *cmd)
+{
+ int ret;
+ struct vb2_queue *src_vq, *dst_vq;
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = video_drvdata(file);
+
+ ret = virtio_video_try_encoder_cmd(file, fh, cmd);
+ if (ret < 0)
+ return ret;
+
+ dst_vq = v4l2_m2m_get_vq(stream->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ switch (cmd->cmd) {
+ case V4L2_ENC_CMD_START:
+ vb2_clear_last_buffer_dequeued(dst_vq);
+ virtio_video_state_update(stream, STREAM_STATE_RUNNING);
+ break;
+ case V4L2_ENC_CMD_STOP:
+ src_vq = v4l2_m2m_get_vq(stream->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ if (!vb2_is_streaming(src_vq)) {
+ v4l2_dbg(1, vvd->debug,
+ &vvd->v4l2_dev, "output is not streaming\n");
+ return 0;
+ }
+
+ if (!vb2_is_streaming(dst_vq)) {
+ v4l2_dbg(1, vvd->debug,
+ &vvd->v4l2_dev, "capture is not streaming\n");
+ return 0;
+ }
+
+ ret = virtio_video_cmd_stream_drain(vvd, stream->stream_id);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to drain stream\n");
+ return ret;
+ }
+
+ virtio_video_state_update(stream, STREAM_STATE_DRAIN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int virtio_video_enc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format *fmt;
+ int idx = 0;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ if (f->index >= vvd->num_output_fmts)
+ return -EINVAL;
+
+ list_for_each_entry(fmt, &vvd->output_fmt_list, formats_list_entry) {
+ if (f->index == idx) {
+ f->pixelformat = fmt->desc.format;
+ return 0;
+ }
+ idx++;
+ }
+ return -EINVAL;
+}
+
+static int virtio_video_enc_enum_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct video_format_info *info = NULL;
+ struct video_format *fmt = NULL;
+ unsigned long output_mask = 0;
+ int idx = 0, bit_num = 0;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ if (f->index >= vvd->num_input_fmts)
+ return -EINVAL;
+
+ info = &stream->out_info;
+ list_for_each_entry(fmt, &vvd->output_fmt_list, formats_list_entry) {
+ if (info->fourcc_format == fmt->desc.format) {
+ output_mask = fmt->desc.mask;
+ break;
+ }
+ }
+
+ if (output_mask == 0)
+ return -EINVAL;
+
+ list_for_each_entry(fmt, &vvd->input_fmt_list, formats_list_entry) {
+ if (test_bit(bit_num, &output_mask)) {
+ if (f->index == idx) {
+ f->pixelformat = fmt->desc.format;
+ return 0;
+ }
+ idx++;
+ }
+ bit_num++;
+ }
+ return -EINVAL;
+}
+
+static int virtio_video_enc_s_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret;
+ struct virtio_video_stream *stream = file2stream(file);
+
+ ret = virtio_video_s_fmt(file, fh, f);
+ if (ret)
+ return ret;
+
+ if (!V4L2_TYPE_IS_OUTPUT(f->type)) {
+ if (virtio_video_state(stream) == STREAM_STATE_IDLE)
+ virtio_video_state_update(stream, STREAM_STATE_INIT);
+ }
+
+ return 0;
+}
+
+static int virtio_video_enc_try_framerate(struct virtio_video_stream *stream,
+ unsigned int fps)
+{
+ int rate_idx;
+ struct video_format_frame *frame = NULL;
+
+ if (stream->current_frame == NULL)
+ return -EINVAL;
+
+ frame = stream->current_frame;
+ for (rate_idx = 0; rate_idx < frame->frame.num_rates; rate_idx++) {
+ struct virtio_video_format_range *frame_rate =
+ &frame->frame_rates[rate_idx];
+
+ if (within_range(frame_rate->min, fps, frame_rate->max))
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void virtio_video_timeperframe_from_info(struct video_format_info *info,
+ struct v4l2_fract *timeperframe)
+{
+ timeperframe->numerator = 1;
+ timeperframe->denominator = info->frame_rate;
+}
+
+static int virtio_video_enc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct v4l2_outputparm *out = &a->parm.output;
+ struct v4l2_fract *timeperframe = &out->timeperframe;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (!V4L2_TYPE_IS_OUTPUT(a->type)) {
+ v4l2_err(&vvd->v4l2_dev,
+ "getting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+
+ out->capability = V4L2_CAP_TIMEPERFRAME;
+ virtio_video_timeperframe_from_info(&stream->in_info, timeperframe);
+
+ return 0;
+}
+
+static int virtio_video_enc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
+{
+ int ret;
+ u64 frame_interval, frame_rate;
+ struct video_format_info info;
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ struct v4l2_outputparm *out = &a->parm.output;
+ struct v4l2_fract *timeperframe = &out->timeperframe;
+
+ if (virtio_video_state(stream) == STREAM_STATE_ERROR)
+ return -EIO;
+
+ if (V4L2_TYPE_IS_OUTPUT(a->type)) {
+ frame_interval = timeperframe->numerator * (u64)USEC_PER_SEC;
+ do_div(frame_interval, timeperframe->denominator);
+ if (!frame_interval)
+ return -EINVAL;
+
+ frame_rate = (u64)USEC_PER_SEC;
+ do_div(frame_rate, frame_interval);
+ } else {
+ v4l2_err(&vvd->v4l2_dev,
+ "setting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+
+ ret = virtio_video_enc_try_framerate(stream, frame_rate);
+ if (ret)
+ return ret;
+
+ virtio_video_format_fill_default_info(&info, &stream->in_info);
+ info.frame_rate = frame_rate;
+
+ virtio_video_cmd_set_params(vvd, stream, &info,
+ VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
+ virtio_video_stream_get_params(vvd, stream);
+
+ out->capability = V4L2_CAP_TIMEPERFRAME;
+ virtio_video_timeperframe_from_info(&stream->in_info, timeperframe);
+
+ return 0;
+}
+
+static int virtio_video_enc_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct virtio_video_stream *stream = file2stream(file);
+ struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+ int ret;
+
+ if (!V4L2_TYPE_IS_OUTPUT(sel->type))
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ stream->in_info.crop.top = sel->r.top;
+ stream->in_info.crop.left = sel->r.left;
+ stream->in_info.crop.width = sel->r.width;
+ stream->in_info.crop.height = sel->r.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = virtio_video_cmd_set_params(vvd, stream, &stream->in_info,
+ VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
+ if (ret)
+ return -EINVAL;
+
+ return virtio_video_cmd_get_params(vvd, stream,
+ VIRTIO_VIDEO_QUEUE_TYPE_INPUT);
+}
+
+static const struct v4l2_ioctl_ops virtio_video_enc_ioctl_ops = {
+ .vidioc_querycap = virtio_video_querycap,
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0))
+ .vidioc_enum_fmt_vid_cap = virtio_video_enc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = virtio_video_enc_enum_fmt_vid_out,
+#else
+ .vidioc_enum_fmt_vid_cap_mplane = virtio_video_enc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out_mplane = virtio_video_enc_enum_fmt_vid_out,
+#endif
+ .vidioc_g_fmt_vid_cap_mplane = virtio_video_g_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = virtio_video_enc_s_fmt,
+
+ .vidioc_g_fmt_vid_out_mplane = virtio_video_g_fmt,
+ .vidioc_s_fmt_vid_out_mplane = virtio_video_enc_s_fmt,
+
+ .vidioc_try_encoder_cmd = virtio_video_try_encoder_cmd,
+ .vidioc_encoder_cmd = virtio_video_encoder_cmd,
+ .vidioc_enum_frameintervals = virtio_video_enum_framemintervals,
+ .vidioc_enum_framesizes = virtio_video_enum_framesizes,
+
+ .vidioc_g_selection = virtio_video_g_selection,
+ .vidioc_s_selection = virtio_video_enc_s_selection,
+
+ .vidioc_reqbufs = virtio_video_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = virtio_video_qbuf,
+ .vidioc_dqbuf = virtio_video_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_s_parm = virtio_video_enc_s_parm,
+ .vidioc_g_parm = virtio_video_enc_g_parm,
+
+ .vidioc_subscribe_event = virtio_video_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+void *virtio_video_enc_get_fmt_list(struct virtio_video_device *vvd)
+{
+ return &vvd->output_fmt_list;
+}
+
+static struct virtio_video_device_ops virtio_video_enc_ops = {
+ .init_ctrls = virtio_video_enc_init_ctrls,
+ .init_queues = virtio_video_enc_init_queues,
+ .get_fmt_list = virtio_video_enc_get_fmt_list,
+};
+
+int virtio_video_enc_init(struct virtio_video_device *vvd)
+{
+ ssize_t num;
+ struct video_device *vd = &vvd->video_dev;
+
+ vd->ioctl_ops = &virtio_video_enc_ioctl_ops;
+ vvd->ops = &virtio_video_enc_ops;
+
+ num = strscpy(vd->name, "stateful-encoder", sizeof(vd->name));
+ if (num < 0)
+ return num;
+
+ return 0;
+}
diff --git a/virtio_video/virtio_video_helpers.c b/virtio_video/virtio_video_helpers.c
new file mode 100644
index 0000000..ac48705
--- /dev/null
+++ b/virtio_video/virtio_video_helpers.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Driver for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "virtio_video.h"
+
+struct virtio_video_convert_table {
+ uint32_t virtio_value;
+ uint32_t v4l2_value;
+};
+
+static struct virtio_video_convert_table level_table[] = {
+ { VIRTIO_VIDEO_LEVEL_H264_1_0, V4L2_MPEG_VIDEO_H264_LEVEL_1_0 },
+ { VIRTIO_VIDEO_LEVEL_H264_1_1, V4L2_MPEG_VIDEO_H264_LEVEL_1_1 },
+ { VIRTIO_VIDEO_LEVEL_H264_1_2, V4L2_MPEG_VIDEO_H264_LEVEL_1_2 },
+ { VIRTIO_VIDEO_LEVEL_H264_1_3, V4L2_MPEG_VIDEO_H264_LEVEL_1_3 },
+ { VIRTIO_VIDEO_LEVEL_H264_2_0, V4L2_MPEG_VIDEO_H264_LEVEL_2_0 },
+ { VIRTIO_VIDEO_LEVEL_H264_2_1, V4L2_MPEG_VIDEO_H264_LEVEL_2_1 },
+ { VIRTIO_VIDEO_LEVEL_H264_2_2, V4L2_MPEG_VIDEO_H264_LEVEL_2_2 },
+ { VIRTIO_VIDEO_LEVEL_H264_3_0, V4L2_MPEG_VIDEO_H264_LEVEL_3_0 },
+ { VIRTIO_VIDEO_LEVEL_H264_3_1, V4L2_MPEG_VIDEO_H264_LEVEL_3_1 },
+ { VIRTIO_VIDEO_LEVEL_H264_3_2, V4L2_MPEG_VIDEO_H264_LEVEL_3_2 },
+ { VIRTIO_VIDEO_LEVEL_H264_4_0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0 },
+ { VIRTIO_VIDEO_LEVEL_H264_4_1, V4L2_MPEG_VIDEO_H264_LEVEL_4_1 },
+ { VIRTIO_VIDEO_LEVEL_H264_4_2, V4L2_MPEG_VIDEO_H264_LEVEL_4_2 },
+ { VIRTIO_VIDEO_LEVEL_H264_5_0, V4L2_MPEG_VIDEO_H264_LEVEL_5_0 },
+ { VIRTIO_VIDEO_LEVEL_H264_5_1, V4L2_MPEG_VIDEO_H264_LEVEL_5_1 },
+ { 0 },
+};
+
+uint32_t virtio_video_level_to_v4l2(uint32_t level)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(level_table); idx++) {
+ if (level_table[idx].virtio_value == level)
+ return level_table[idx].v4l2_value;
+ }
+
+ return 0;
+}
+
+uint32_t virtio_video_v4l2_level_to_virtio(uint32_t v4l2_level)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(level_table); idx++) {
+ if (level_table[idx].v4l2_value == v4l2_level)
+ return level_table[idx].virtio_value;
+ }
+
+ return 0;
+}
+
+static struct virtio_video_convert_table profile_table[] = {
+ { VIRTIO_VIDEO_PROFILE_H264_BASELINE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE },
+ { VIRTIO_VIDEO_PROFILE_H264_MAIN, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN },
+ { VIRTIO_VIDEO_PROFILE_H264_EXTENDED,
+ V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED },
+ { VIRTIO_VIDEO_PROFILE_H264_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH },
+ { VIRTIO_VIDEO_PROFILE_H264_HIGH10PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10 },
+ { VIRTIO_VIDEO_PROFILE_H264_HIGH422PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422},
+ { VIRTIO_VIDEO_PROFILE_H264_HIGH444PREDICTIVEPROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE },
+ { VIRTIO_VIDEO_PROFILE_H264_SCALABLEBASELINE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE },
+ { VIRTIO_VIDEO_PROFILE_H264_SCALABLEHIGH,
+ V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH },
+ { VIRTIO_VIDEO_PROFILE_H264_STEREOHIGH,
+ V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH },
+ { VIRTIO_VIDEO_PROFILE_H264_MULTIVIEWHIGH,
+ V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH },
+ { 0 },
+};
+
+uint32_t virtio_video_profile_to_v4l2(uint32_t profile)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(profile_table); idx++) {
+ if (profile_table[idx].virtio_value == profile)
+ return profile_table[idx].v4l2_value;
+ }
+
+ return 0;
+}
+
+uint32_t virtio_video_v4l2_profile_to_virtio(uint32_t v4l2_profile)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(profile_table); idx++) {
+ if (profile_table[idx].v4l2_value == v4l2_profile)
+ return profile_table[idx].virtio_value;
+ }
+
+ return 0;
+}
+
+static struct virtio_video_convert_table format_table[] = {
+ { VIRTIO_VIDEO_FORMAT_ARGB8888, V4L2_PIX_FMT_ARGB32 },
+ { VIRTIO_VIDEO_FORMAT_BGRA8888, V4L2_PIX_FMT_ABGR32 },
+ { VIRTIO_VIDEO_FORMAT_RGBA8888, V4L2_PIX_FMT_RGB32 },
+ { VIRTIO_VIDEO_FORMAT_NV12, V4L2_PIX_FMT_NV12 },
+ { VIRTIO_VIDEO_FORMAT_YUV420, V4L2_PIX_FMT_YUV420 },
+ { VIRTIO_VIDEO_FORMAT_YVU420, V4L2_PIX_FMT_YVU420 },
+ { VIRTIO_VIDEO_FORMAT_YUV422, V4L2_PIX_FMT_YUYV },
+ { VIRTIO_VIDEO_FORMAT_MPEG2, V4L2_PIX_FMT_MPEG2 },
+ { VIRTIO_VIDEO_FORMAT_MPEG4, V4L2_PIX_FMT_MPEG4 },
+ { VIRTIO_VIDEO_FORMAT_H264, V4L2_PIX_FMT_H264 },
+ { VIRTIO_VIDEO_FORMAT_HEVC, V4L2_PIX_FMT_HEVC },
+ { VIRTIO_VIDEO_FORMAT_VP8, V4L2_PIX_FMT_VP8 },
+ { VIRTIO_VIDEO_FORMAT_VP9, V4L2_PIX_FMT_VP9 },
+ { 0 },
+};
+
+uint32_t virtio_video_format_to_v4l2(uint32_t format)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(format_table); idx++) {
+ if (format_table[idx].virtio_value == format)
+ return format_table[idx].v4l2_value;
+ }
+
+ return 0;
+}
+
+uint32_t virtio_video_v4l2_format_to_virtio(uint32_t v4l2_format)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(format_table); idx++) {
+ if (format_table[idx].v4l2_value == v4l2_format)
+ return format_table[idx].virtio_value;
+ }
+
+ return 0;
+}
+
+static struct virtio_video_convert_table control_table[] = {
+ { VIRTIO_VIDEO_CONTROL_BITRATE, V4L2_CID_MPEG_VIDEO_BITRATE },
+ { VIRTIO_VIDEO_CONTROL_PROFILE, V4L2_CID_MPEG_VIDEO_H264_PROFILE },
+ { VIRTIO_VIDEO_CONTROL_LEVEL, V4L2_CID_MPEG_VIDEO_H264_LEVEL },
+ { 0 },
+};
+
+uint32_t virtio_video_control_to_v4l2(uint32_t control)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(control_table); idx++) {
+ if (control_table[idx].virtio_value == control)
+ return control_table[idx].v4l2_value;
+ }
+
+ return 0;
+}
+
+uint32_t virtio_video_v4l2_control_to_virtio(uint32_t v4l2_control)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(control_table); idx++) {
+ if (control_table[idx].v4l2_value == v4l2_control)
+ return control_table[idx].virtio_value;
+ }
+
+ return 0;
+}
+
+uint32_t virtio_video_get_format_from_virtio_profile(uint32_t virtio_profile)
+{
+ if (virtio_profile >= VIRTIO_VIDEO_PROFILE_H264_MIN &&
+ virtio_profile <= VIRTIO_VIDEO_PROFILE_H264_MAX)
+ return VIRTIO_VIDEO_FORMAT_H264;
+ else if (virtio_profile >= VIRTIO_VIDEO_PROFILE_HEVC_MIN &&
+ virtio_profile <= VIRTIO_VIDEO_PROFILE_HEVC_MAX)
+ return VIRTIO_VIDEO_FORMAT_HEVC;
+ else if (virtio_profile >= VIRTIO_VIDEO_PROFILE_VP8_MIN &&
+ virtio_profile <= VIRTIO_VIDEO_PROFILE_VP8_MAX)
+ return VIRTIO_VIDEO_FORMAT_VP8;
+ else if (virtio_profile >= VIRTIO_VIDEO_PROFILE_VP9_MIN &&
+ virtio_profile <= VIRTIO_VIDEO_PROFILE_VP9_MAX)
+ return VIRTIO_VIDEO_FORMAT_VP9;
+
+ return 0;
+}
+
+struct video_format *virtio_video_find_video_format(struct list_head *fmts_list,
+ uint32_t format)
+{
+ struct video_format *fmt = NULL;
+
+ list_for_each_entry(fmt, fmts_list, formats_list_entry) {
+ if (fmt->desc.format == format)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+void virtio_video_format_from_info(struct video_format_info *info,
+ struct v4l2_pix_format_mplane *pix_mp)
+{
+ int i;
+
+ pix_mp->width = info->frame_width;
+ pix_mp->height = info->frame_height;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->colorspace = V4L2_COLORSPACE_REC709;
+ pix_mp->xfer_func = 0;
+ pix_mp->ycbcr_enc = 0;
+ pix_mp->quantization = 0;
+ memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
+ memset(pix_mp->plane_fmt[0].reserved, 0,
+ sizeof(pix_mp->plane_fmt[0].reserved));
+
+ pix_mp->num_planes = info->num_planes;
+ pix_mp->pixelformat = info->fourcc_format;
+
+ for (i = 0; i < info->num_planes; i++) {
+ pix_mp->plane_fmt[i].bytesperline =
+ info->plane_format[i].stride;
+ pix_mp->plane_fmt[i].sizeimage =
+ info->plane_format[i].plane_size;
+ }
+}
+
+void virtio_video_format_fill_default_info(struct video_format_info *dst_info,
+ struct video_format_info *src_info)
+{
+ memcpy(dst_info, src_info, sizeof(*dst_info));
+}
+
+void virtio_video_pix_fmt_sp2mp(const struct v4l2_pix_format *pix,
+ struct v4l2_pix_format_mplane *pix_mp)
+{
+ memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
+ memset(&pix_mp->plane_fmt[0].reserved, 0,
+ sizeof(pix_mp->plane_fmt[0].reserved));
+ pix_mp->num_planes = 1;
+ pix_mp->width = pix->width;
+ pix_mp->height = pix->height;
+ pix_mp->pixelformat = pix->pixelformat;
+ pix_mp->field = pix->field;
+ pix_mp->plane_fmt[0].bytesperline = pix->bytesperline;
+ pix_mp->plane_fmt[0].sizeimage = pix->sizeimage;
+ pix_mp->colorspace = pix->colorspace;
+ pix_mp->flags = pix->flags;
+ pix_mp->ycbcr_enc = pix->ycbcr_enc;
+ pix_mp->quantization = pix->quantization;
+ pix_mp->xfer_func = pix->xfer_func;
+}
+
+void virtio_video_pix_fmt_mp2sp(const struct v4l2_pix_format_mplane *pix_mp,
+ struct v4l2_pix_format *pix)
+{
+ pix->width = pix_mp->width;
+ pix->height = pix_mp->height;
+ pix->pixelformat = pix_mp->pixelformat;
+ pix->field = pix_mp->field;
+ pix->bytesperline = pix_mp->plane_fmt[0].bytesperline;
+ pix->sizeimage = pix_mp->plane_fmt[0].sizeimage;
+ pix->colorspace = pix_mp->colorspace;
+ pix->priv = 0;
+ pix->flags = pix_mp->flags;
+ pix->ycbcr_enc = pix_mp->ycbcr_enc;
+ pix->quantization = pix_mp->quantization;
+ pix->xfer_func = pix_mp->xfer_func;
+}
diff --git a/virtio_video/virtio_video_vq.c b/virtio_video/virtio_video_vq.c
new file mode 100644
index 0000000..03c5a62
--- /dev/null
+++ b/virtio_video/virtio_video_vq.c
@@ -0,0 +1,980 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Driver for virtio video device.
+ *
+ * Copyright 2020 OpenSynergy GmbH.
+ *
+ * Based on drivers/gpu/drm/virtio/virtgpu_vq.c
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "virtio_video.h"
+
+#define MAX_INLINE_CMD_SIZE 298
+#define MAX_INLINE_RESP_SIZE 298
+#define VBUFFER_SIZE (sizeof(struct virtio_video_vbuffer) \
+ + MAX_INLINE_CMD_SIZE \
+ + MAX_INLINE_RESP_SIZE)
+
+static int virtio_video_queue_event_buffer(struct virtio_video_device *vvd,
+ struct virtio_video_event *evt);
+static void virtio_video_handle_event(struct virtio_video_device *vvd,
+ struct virtio_video_event *evt);
+
+void virtio_video_resource_id_get(struct virtio_video_device *vvd, uint32_t *id)
+{
+ int handle;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&vvd->resource_idr_lock);
+ handle = idr_alloc(&vvd->resource_idr, NULL, 1, 0, GFP_NOWAIT);
+ spin_unlock(&vvd->resource_idr_lock);
+ idr_preload_end();
+ *id = handle;
+}
+
+void virtio_video_resource_id_put(struct virtio_video_device *vvd, uint32_t id)
+{
+ spin_lock(&vvd->resource_idr_lock);
+ idr_remove(&vvd->resource_idr, id);
+ spin_unlock(&vvd->resource_idr_lock);
+}
+
+void virtio_video_stream_id_get(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ uint32_t *id)
+{
+ int handle;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock(&vvd->stream_idr_lock);
+ handle = idr_alloc(&vvd->stream_idr, stream, 1, 0, 0);
+ spin_unlock(&vvd->stream_idr_lock);
+ idr_preload_end();
+ *id = handle;
+}
+
+void virtio_video_stream_id_put(struct virtio_video_device *vvd, uint32_t id)
+{
+ spin_lock(&vvd->stream_idr_lock);
+ idr_remove(&vvd->stream_idr, id);
+ spin_unlock(&vvd->stream_idr_lock);
+}
+
+static bool vbuf_is_pending(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ struct virtio_video_vbuffer *entry;
+
+ list_for_each_entry(entry, &vvd->pending_vbuf_list, pending_list_entry)
+ {
+ if (entry == vbuf && entry->id == vbuf->id)
+ return true;
+ }
+
+ return false;
+}
+
+static void free_vbuf(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ list_del(&vbuf->pending_list_entry);
+ kfree(vbuf->data_buf);
+ kmem_cache_free(vvd->vbufs, vbuf);
+}
+
+void virtio_video_cmd_cb(struct virtqueue *vq)
+{
+ struct virtio_video_device *vvd = vq->vdev->priv;
+ struct virtio_video_vbuffer *vbuf;
+ unsigned long flags;
+ unsigned int len;
+
+ spin_lock_irqsave(&vvd->commandq.qlock, flags);
+ while (vvd->commandq.ready) {
+ virtqueue_disable_cb(vq);
+
+ while ((vbuf = virtqueue_get_buf(vq, &len))) {
+ if (!vbuf_is_pending(vvd, vbuf))
+ continue;
+
+ if (vbuf->resp_cb)
+ vbuf->resp_cb(vvd, vbuf);
+
+ if (vbuf->is_sync)
+ complete(&vbuf->reclaimed);
+ else
+ free_vbuf(vvd, vbuf);
+ }
+
+ if (unlikely(virtqueue_is_broken(vq)))
+ break;
+
+ if (virtqueue_enable_cb(vq))
+ break;
+ }
+ spin_unlock_irqrestore(&vvd->commandq.qlock, flags);
+
+ wake_up(&vvd->commandq.reclaim_queue);
+}
+
+void virtio_video_process_events(struct work_struct *work)
+{
+ struct virtio_video_device *vvd = container_of(work,
+ struct virtio_video_device, eventq.work);
+ struct virtqueue *vq = vvd->eventq.vq;
+ struct virtio_video_event *evt;
+ unsigned int len;
+
+ while (vvd->eventq.ready) {
+ virtqueue_disable_cb(vq);
+
+ while ((evt = virtqueue_get_buf(vq, &len))) {
+ virtio_video_handle_event(vvd, evt);
+ virtio_video_queue_event_buffer(vvd, evt);
+ }
+
+ if (unlikely(virtqueue_is_broken(vq)))
+ break;
+
+ if (virtqueue_enable_cb(vq))
+ break;
+ }
+}
+
+void virtio_video_event_cb(struct virtqueue *vq)
+{
+ struct virtio_video_device *vvd = vq->vdev->priv;
+
+ schedule_work(&vvd->eventq.work);
+}
+
+static struct virtio_video_vbuffer *
+virtio_video_get_vbuf(struct virtio_video_device *vvd, int size, int resp_size,
+ void *resp_buf, virtio_video_resp_cb resp_cb)
+{
+ struct virtio_video_vbuffer *vbuf;
+
+ vbuf = kmem_cache_alloc(vvd->vbufs, GFP_KERNEL);
+ if (!vbuf)
+ return ERR_PTR(-ENOMEM);
+ memset(vbuf, 0, VBUFFER_SIZE);
+
+ BUG_ON(size > MAX_INLINE_CMD_SIZE);
+ vbuf->buf = (void *)vbuf + sizeof(*vbuf);
+ vbuf->size = size;
+
+ vbuf->resp_cb = resp_cb;
+ vbuf->resp_size = resp_size;
+ if (resp_size <= MAX_INLINE_RESP_SIZE && !resp_buf)
+ vbuf->resp_buf = (void *)vbuf->buf + size;
+ else
+ vbuf->resp_buf = resp_buf;
+ BUG_ON(!vbuf->resp_buf);
+
+ return vbuf;
+}
+
+int virtio_video_alloc_vbufs(struct virtio_video_device *vvd)
+{
+ vvd->vbufs =
+ kmem_cache_create("virtio-video-vbufs", VBUFFER_SIZE,
+ __alignof__(struct virtio_video_vbuffer), 0,
+ NULL);
+ if (!vvd->vbufs)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void virtio_video_free_vbufs(struct virtio_video_device *vvd)
+{
+ struct virtio_video_vbuffer *vbuf;
+
+ /* Release command buffers. Operation on vbufs here is lock safe,
+ since before device was deinitialized and queues was stopped
+ (in not ready state) */
+ while ((vbuf = virtqueue_detach_unused_buf(vvd->commandq.vq))) {
+ if (vbuf_is_pending(vvd, vbuf))
+ free_vbuf(vvd, vbuf);
+ }
+
+ kmem_cache_destroy(vvd->vbufs);
+ vvd->vbufs = NULL;
+
+ /* Release event buffers */
+ while (virtqueue_detach_unused_buf(vvd->eventq.vq));
+
+ kfree(vvd->evts);
+ vvd->evts = NULL;
+}
+
+static void *virtio_video_alloc_req(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer **vbuffer_p,
+ int size)
+{
+ struct virtio_video_vbuffer *vbuf;
+
+ vbuf = virtio_video_get_vbuf(vvd, size,
+ sizeof(struct virtio_video_cmd_hdr),
+ NULL, NULL);
+ if (IS_ERR(vbuf)) {
+ *vbuffer_p = NULL;
+ return ERR_CAST(vbuf);
+ }
+ *vbuffer_p = vbuf;
+
+ return vbuf->buf;
+}
+
+static void *
+virtio_video_alloc_req_resp(struct virtio_video_device *vvd,
+ virtio_video_resp_cb cb,
+ struct virtio_video_vbuffer **vbuffer_p,
+ int req_size, int resp_size,
+ void *resp_buf)
+{
+ struct virtio_video_vbuffer *vbuf;
+
+ vbuf = virtio_video_get_vbuf(vvd, req_size, resp_size, resp_buf, cb);
+ if (IS_ERR(vbuf)) {
+ *vbuffer_p = NULL;
+ return ERR_CAST(vbuf);
+ }
+ *vbuffer_p = vbuf;
+
+ return vbuf->buf;
+}
+
+static int
+virtio_video_queue_cmd_buffer(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ unsigned long flags;
+ struct virtqueue *vq = vvd->commandq.vq;
+ struct scatterlist *sgs[3], vreq, vout, vresp;
+ int outcnt = 0, incnt = 0;
+ int ret;
+
+ if (!vvd->commandq.ready)
+ return -ENODEV;
+
+ spin_lock_irqsave(&vvd->commandq.qlock, flags);
+
+ vbuf->id = vvd->vbufs_sent++;
+ list_add_tail(&vbuf->pending_list_entry, &vvd->pending_vbuf_list);
+
+ sg_init_one(&vreq, vbuf->buf, vbuf->size);
+ sgs[outcnt + incnt] = &vreq;
+ outcnt++;
+
+ if (vbuf->data_size) {
+ sg_init_one(&vout, vbuf->data_buf, vbuf->data_size);
+ sgs[outcnt + incnt] = &vout;
+ outcnt++;
+ }
+
+ if (vbuf->resp_size) {
+ sg_init_one(&vresp, vbuf->resp_buf, vbuf->resp_size);
+ sgs[outcnt + incnt] = &vresp;
+ incnt++;
+ }
+
+retry:
+ ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC);
+ if (ret == -ENOSPC) {
+ spin_unlock_irqrestore(&vvd->commandq.qlock, flags);
+ wait_event(vvd->commandq.reclaim_queue, vq->num_free);
+ spin_lock_irqsave(&vvd->commandq.qlock, flags);
+ goto retry;
+ } else {
+ virtqueue_kick(vq);
+ }
+
+ spin_unlock_irqrestore(&vvd->commandq.qlock, flags);
+
+ return ret;
+}
+
+static int
+virtio_video_queue_cmd_buffer_sync(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ int ret;
+ unsigned long rem;
+ unsigned long flags;
+
+ vbuf->is_sync = true;
+ init_completion(&vbuf->reclaimed);
+
+ ret = virtio_video_queue_cmd_buffer(vvd, vbuf);
+ if (ret)
+ return ret;
+
+ rem = wait_for_completion_timeout(&vbuf->reclaimed, 5 * HZ);
+ if (rem == 0)
+ ret = -ETIMEDOUT;
+
+ spin_lock_irqsave(&vvd->commandq.qlock, flags);
+ if (vbuf_is_pending(vvd, vbuf))
+ free_vbuf(vvd, vbuf);
+ spin_unlock_irqrestore(&vvd->commandq.qlock, flags);
+
+ return ret;
+}
+
+static int virtio_video_queue_event_buffer(struct virtio_video_device *vvd,
+ struct virtio_video_event *evt)
+{
+ int ret;
+ struct scatterlist sg;
+ struct virtqueue *vq = vvd->eventq.vq;
+
+ memset(evt, 0, sizeof(struct virtio_video_event));
+ sg_init_one(&sg, evt, sizeof(struct virtio_video_event));
+
+ ret = virtqueue_add_inbuf(vq, &sg, 1, evt, GFP_KERNEL);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev, "failed to queue event buffer\n");
+ return ret;
+ }
+
+ virtqueue_kick(vq);
+
+ return 0;
+}
+
+static void virtio_video_handle_event(struct virtio_video_device *vvd,
+ struct virtio_video_event *evt)
+{
+ struct virtio_video_stream *stream;
+ uint32_t stream_id = evt->stream_id;
+ struct video_device *vd = &vvd->video_dev;
+
+ mutex_lock(vd->lock);
+
+ stream = idr_find(&vvd->stream_idr, stream_id);
+ if (!stream) {
+ v4l2_warn(&vvd->v4l2_dev, "stream_id=%u not found for event\n",
+ stream_id);
+ mutex_unlock(vd->lock);
+ return;
+ }
+
+ switch (le32_to_cpu(evt->event_type)) {
+ case VIRTIO_VIDEO_EVENT_DECODER_RESOLUTION_CHANGED:
+ v4l2_dbg(1, vvd->debug, &vvd->v4l2_dev,
+ "stream_id=%u: resolution change event\n", stream_id);
+ virtio_video_cmd_get_params(vvd, stream,
+ VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT);
+ virtio_video_queue_res_chg_event(stream);
+ if (virtio_video_state(stream) == STREAM_STATE_INIT) {
+ virtio_video_state_update(stream,
+ STREAM_STATE_DYNAMIC_RES_CHANGE);
+ wake_up(&vvd->wq);
+ }
+ break;
+ case VIRTIO_VIDEO_EVENT_ERROR:
+ v4l2_err(&vvd->v4l2_dev, "stream_id=%i: error event\n",
+ stream_id);
+ virtio_video_state_update(stream, STREAM_STATE_ERROR);
+ virtio_video_handle_error(stream);
+ break;
+ default:
+ v4l2_warn(&vvd->v4l2_dev, "stream_id=%i: unknown event\n",
+ stream_id);
+ break;
+ }
+
+ mutex_unlock(vd->lock);
+}
+
+int virtio_video_alloc_events(struct virtio_video_device *vvd)
+{
+ int ret;
+ size_t i;
+ struct virtio_video_event *evts;
+ size_t num = vvd->eventq.vq->num_free;
+
+ evts = kzalloc(num * sizeof(struct virtio_video_event), GFP_KERNEL);
+ if (!evts) {
+ v4l2_err(&vvd->v4l2_dev, "failed to alloc event buffers!!!\n");
+ return -ENOMEM;
+ }
+ vvd->evts = evts;
+
+ for (i = 0; i < num; i++) {
+ ret = virtio_video_queue_event_buffer(vvd, &evts[i]);
+ if (ret) {
+ v4l2_err(&vvd->v4l2_dev,
+ "failed to queue event buffer\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_stream_create(struct virtio_video_device *vvd,
+ uint32_t stream_id,
+ enum virtio_video_format format,
+ const char *tag)
+{
+ struct virtio_video_stream_create *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_STREAM_CREATE);
+ req_p->hdr.stream_id = cpu_to_le32(stream_id);
+ req_p->in_mem_type = cpu_to_le32(VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES);
+ req_p->out_mem_type = cpu_to_le32(VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES);
+ req_p->coded_format = cpu_to_le32(format);
+ if (strscpy(req_p->tag, tag, sizeof(req_p->tag) - 1) < 0)
+ v4l2_err(&vvd->v4l2_dev, "failed to copy stream tag\n");
+ req_p->tag[sizeof(req_p->tag) - 1] = 0;
+
+ return virtio_video_queue_cmd_buffer(vvd, vbuf);
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_stream_destroy(struct virtio_video_device *vvd,
+ uint32_t stream_id)
+{
+ struct virtio_video_stream_destroy *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_STREAM_DESTROY);
+ req_p->hdr.stream_id = cpu_to_le32(stream_id);
+
+ return virtio_video_queue_cmd_buffer(vvd, vbuf);
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_stream_drain(struct virtio_video_device *vvd,
+ uint32_t stream_id)
+{
+ struct virtio_video_stream_drain *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_STREAM_DRAIN);
+ req_p->hdr.stream_id = cpu_to_le32(stream_id);
+
+ return virtio_video_queue_cmd_buffer(vvd, vbuf);
+}
+
+int virtio_video_cmd_resource_attach(struct virtio_video_device *vvd,
+ uint32_t stream_id, uint32_t resource_id,
+ enum virtio_video_queue_type queue_type,
+ void *buf, size_t buf_size)
+{
+ struct virtio_video_resource_attach *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->cmd_type = cpu_to_le32(VIRTIO_VIDEO_CMD_RESOURCE_ATTACH);
+ req_p->stream_id = cpu_to_le32(stream_id);
+ req_p->queue_type = cpu_to_le32(queue_type);
+ req_p->resource_id = cpu_to_le32(resource_id);
+
+ vbuf->data_buf = buf;
+ vbuf->data_size = buf_size;
+
+ return virtio_video_queue_cmd_buffer(vvd, vbuf);
+}
+
+int virtio_video_cmd_queue_detach_resources(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type)
+{
+ int ret;
+ struct virtio_video_queue_detach_resources *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->cmd_type = cpu_to_le32(VIRTIO_VIDEO_CMD_QUEUE_DETACH_RESOURCES);
+ req_p->stream_id = cpu_to_le32(stream->stream_id);
+ req_p->queue_type = cpu_to_le32(queue_type);
+
+ ret = virtio_video_queue_cmd_buffer_sync(vvd, vbuf);
+ if (ret == -ETIMEDOUT)
+ v4l2_err(&vvd->v4l2_dev,
+ "timed out waiting for resource destruction for %s\n",
+ (queue_type == VIRTIO_VIDEO_QUEUE_TYPE_INPUT) ?
+ "OUTPUT" : "CAPTURE");
+ return ret;
+}
+
+static void
+virtio_video_cmd_resource_queue_cb(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ uint32_t flags;
+ uint64_t timestamp;
+ struct virtio_video_buffer *virtio_vb = vbuf->priv;
+ struct virtio_video_resource_queue_resp *resp =
+ (struct virtio_video_resource_queue_resp *)vbuf->resp_buf;
+
+ flags = le32_to_cpu(resp->flags);
+ timestamp = le64_to_cpu(resp->timestamp);
+
+ virtio_video_buf_done(virtio_vb, flags, timestamp, resp->data_sizes);
+}
+
+int virtio_video_cmd_resource_queue(struct virtio_video_device *vvd,
+ uint32_t stream_id,
+ struct virtio_video_buffer *virtio_vb,
+ uint32_t data_size[],
+ uint8_t num_data_size,
+ enum virtio_video_queue_type queue_type)
+{
+ uint8_t i;
+ struct virtio_video_resource_queue *req_p;
+ struct virtio_video_resource_queue_resp *resp_p;
+ struct virtio_video_vbuffer *vbuf;
+ size_t resp_size = sizeof(struct virtio_video_resource_queue_resp);
+
+ req_p = virtio_video_alloc_req_resp(vvd,
+ &virtio_video_cmd_resource_queue_cb,
+ &vbuf, sizeof(*req_p), resp_size,
+ NULL);
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->cmd_type = cpu_to_le32(VIRTIO_VIDEO_CMD_RESOURCE_QUEUE);
+ req_p->stream_id = cpu_to_le32(stream_id);
+ req_p->queue_type = cpu_to_le32(queue_type);
+ req_p->resource_id = cpu_to_le32(virtio_vb->resource_id);
+ req_p->flags = 0;
+ req_p->timestamp =
+ cpu_to_le64(virtio_vb->v4l2_m2m_vb.vb.vb2_buf.timestamp);
+
+ for (i = 0; i < num_data_size; ++i)
+ req_p->data_sizes[i] = cpu_to_le32(data_size[i]);
+
+ resp_p = (struct virtio_video_resource_queue_resp *)vbuf->resp_buf;
+ memset(resp_p, 0, sizeof(*resp_p));
+
+ vbuf->priv = virtio_vb;
+
+ return virtio_video_queue_cmd_buffer(vvd, vbuf);
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_queue_clear(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type)
+{
+ int ret;
+ struct virtio_video_queue_clear *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_QUEUE_CLEAR);
+ req_p->hdr.stream_id = cpu_to_le32(stream->stream_id);
+ req_p->queue_type = cpu_to_le32(queue_type);
+
+ ret = virtio_video_queue_cmd_buffer_sync(vvd, vbuf);
+ if (ret == -ETIMEDOUT)
+ v4l2_err(&vvd->v4l2_dev,
+ "timed out waiting for %s queue clear\n",
+ (queue_type == VIRTIO_VIDEO_QUEUE_TYPE_INPUT) ?
+ "OUTPUT" : "CAPTURE");
+ return ret;
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_query_capability(struct virtio_video_device *vvd,
+ void *resp_buf, size_t resp_size,
+ enum virtio_video_queue_type queue_type)
+{
+ int ret;
+ struct virtio_video_query_capability *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req_resp(vvd, NULL, &vbuf, sizeof(*req_p),
+ resp_size, resp_buf);
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_QUERY_CAPABILITY);
+ req_p->queue_type = cpu_to_le32(queue_type);
+
+ ret = virtio_video_queue_cmd_buffer_sync(vvd, vbuf);
+ if (ret == -ETIMEDOUT)
+ v4l2_err(&vvd->v4l2_dev,
+ "timed out waiting for capabilities for %s\n",
+ (queue_type == VIRTIO_VIDEO_QUEUE_TYPE_INPUT) ?
+ "OUTPUT" : "CAPTURE");
+ return ret;
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_query_control_level(struct virtio_video_device *vvd,
+ void *resp_buf, size_t resp_size,
+ enum virtio_video_format format)
+{
+ int ret;
+ struct virtio_video_query_control *req_p;
+ struct virtio_video_query_control_level *ctrl_l;
+ struct virtio_video_vbuffer *vbuf;
+ uint32_t req_size = 0;
+
+ req_size = sizeof(struct virtio_video_query_control) +
+ sizeof(struct virtio_video_query_control_level);
+
+ req_p = virtio_video_alloc_req_resp(vvd, NULL, &vbuf, req_size,
+ resp_size, resp_buf);
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_QUERY_CONTROL);
+ req_p->control = cpu_to_le32(VIRTIO_VIDEO_CONTROL_LEVEL);
+ ctrl_l = (void *)((char *)req_p +
+ sizeof(struct virtio_video_query_control));
+ ctrl_l->format = cpu_to_le32(format);
+
+ ret = virtio_video_queue_cmd_buffer_sync(vvd, vbuf);
+ if (ret == -ETIMEDOUT)
+ v4l2_err(&vvd->v4l2_dev,
+ "timed out waiting for level query\n");
+ return ret;
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_query_control_profile(struct virtio_video_device *vvd,
+ void *resp_buf, size_t resp_size,
+ enum virtio_video_format format)
+{
+ int ret;
+ struct virtio_video_query_control *req_p;
+ struct virtio_video_query_control_profile *ctrl_p;
+ struct virtio_video_vbuffer *vbuf;
+ uint32_t req_size = 0;
+
+ req_size = sizeof(struct virtio_video_query_control) +
+ sizeof(struct virtio_video_query_control_profile);
+
+ req_p = virtio_video_alloc_req_resp(vvd, NULL, &vbuf, req_size,
+ resp_size, resp_buf);
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_QUERY_CONTROL);
+ req_p->control = cpu_to_le32(VIRTIO_VIDEO_CONTROL_PROFILE);
+ ctrl_p = (void *)((char *)req_p +
+ sizeof(struct virtio_video_query_control));
+ ctrl_p->format = cpu_to_le32(format);
+
+ ret = virtio_video_queue_cmd_buffer_sync(vvd, vbuf);
+ if (ret == -ETIMEDOUT)
+ v4l2_err(&vvd->v4l2_dev,
+ "timed out waiting for profile query\n");
+ return ret;
+}
+
+static void
+virtio_video_cmd_get_params_cb(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ int i;
+ struct virtio_video_get_params_resp *resp =
+ (struct virtio_video_get_params_resp *)vbuf->resp_buf;
+ struct virtio_video_params *params = &resp->params;
+ struct virtio_video_stream *stream = vbuf->priv;
+ enum virtio_video_queue_type queue_type;
+ struct video_format_info *format_info;
+
+ queue_type = le32_to_cpu(params->queue_type);
+ if (queue_type == VIRTIO_VIDEO_QUEUE_TYPE_INPUT)
+ format_info = &stream->in_info;
+ else
+ format_info = &stream->out_info;
+
+ format_info->frame_rate = le32_to_cpu(params->frame_rate);
+ format_info->frame_width = le32_to_cpu(params->frame_width);
+ format_info->frame_height = le32_to_cpu(params->frame_height);
+ format_info->min_buffers = le32_to_cpu(params->min_buffers);
+ format_info->max_buffers = le32_to_cpu(params->max_buffers);
+ format_info->fourcc_format =
+ virtio_video_format_to_v4l2(le32_to_cpu(params->format));
+
+ format_info->crop.top = le32_to_cpu(params->crop.top);
+ format_info->crop.left = le32_to_cpu(params->crop.left);
+ format_info->crop.width = le32_to_cpu(params->crop.width);
+ format_info->crop.height = le32_to_cpu(params->crop.height);
+
+ format_info->num_planes = le32_to_cpu(params->num_planes);
+ for (i = 0; i < le32_to_cpu(params->num_planes); i++) {
+ struct virtio_video_plane_format *plane_formats =
+ &params->plane_formats[i];
+ struct video_plane_format *plane_format =
+ &format_info->plane_format[i];
+
+ plane_format->plane_size =
+ le32_to_cpu(plane_formats->plane_size);
+ plane_format->stride = le32_to_cpu(plane_formats->stride);
+ }
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_get_params(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_queue_type queue_type)
+{
+ int ret;
+ struct virtio_video_get_params *req_p;
+ struct virtio_video_vbuffer *vbuf;
+ struct virtio_video_get_params_resp *resp_p;
+ size_t resp_size = sizeof(struct virtio_video_get_params_resp);
+
+ req_p = virtio_video_alloc_req_resp(vvd,
+ &virtio_video_cmd_get_params_cb,
+ &vbuf, sizeof(*req_p), resp_size,
+ NULL);
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_GET_PARAMS);
+ req_p->hdr.stream_id = cpu_to_le32(stream->stream_id);
+ req_p->queue_type = cpu_to_le32(queue_type);
+
+ resp_p = (struct virtio_video_get_params_resp *)vbuf->resp_buf;
+
+ vbuf->priv = stream;
+
+ ret = virtio_video_queue_cmd_buffer_sync(vvd, vbuf);
+ if (ret == -ETIMEDOUT)
+ v4l2_err(&vvd->v4l2_dev,
+ "timed out waiting for get_params\n");
+ return ret;
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int
+virtio_video_cmd_set_params(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ struct video_format_info *format_info,
+ enum virtio_video_queue_type queue_type)
+{
+ int i;
+ struct virtio_video_set_params *req_p;
+ struct virtio_video_vbuffer *vbuf;
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_SET_PARAMS);
+ req_p->hdr.stream_id = cpu_to_le32(stream->stream_id);
+ req_p->params.queue_type = cpu_to_le32(queue_type);
+ req_p->params.frame_rate = cpu_to_le32(format_info->frame_rate);
+ req_p->params.frame_width = cpu_to_le32(format_info->frame_width);
+ req_p->params.frame_height = cpu_to_le32(format_info->frame_height);
+ req_p->params.format = virtio_video_v4l2_format_to_virtio(
+ cpu_to_le32(format_info->fourcc_format));
+ req_p->params.min_buffers = cpu_to_le32(format_info->min_buffers);
+ req_p->params.max_buffers = cpu_to_le32(format_info->max_buffers);
+ req_p->params.num_planes = cpu_to_le32(format_info->num_planes);
+
+ for (i = 0; i < format_info->num_planes; i++) {
+ struct virtio_video_plane_format *plane_formats =
+ &req_p->params.plane_formats[i];
+ struct video_plane_format *plane_format =
+ &format_info->plane_format[i];
+ plane_formats->plane_size =
+ cpu_to_le32(plane_format->plane_size);
+ plane_formats->stride = cpu_to_le32(plane_format->stride);
+ }
+
+ return virtio_video_queue_cmd_buffer(vvd, vbuf);
+}
+
+static void
+virtio_video_cmd_get_ctrl_profile_cb(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ struct virtio_video_get_control_resp *resp =
+ (struct virtio_video_get_control_resp *)vbuf->resp_buf;
+ struct virtio_video_control_val_profile *resp_p = NULL;
+ struct virtio_video_stream *stream = vbuf->priv;
+ struct video_control_info *control = &stream->control;
+
+ resp_p = (void *)((char *)resp +
+ sizeof(struct virtio_video_get_control_resp));
+
+ control->profile = le32_to_cpu(resp_p->profile);
+}
+
+static void
+virtio_video_cmd_get_ctrl_level_cb(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ struct virtio_video_get_control_resp *resp =
+ (struct virtio_video_get_control_resp *)vbuf->resp_buf;
+ struct virtio_video_control_val_level *resp_p;
+ struct virtio_video_stream *stream = vbuf->priv;
+ struct video_control_info *control = &stream->control;
+
+ resp_p = (void *)((char *)resp +
+ sizeof(struct virtio_video_get_control_resp));
+
+ control->level = le32_to_cpu(resp_p->level);
+}
+
+static void
+virtio_video_cmd_get_ctrl_bitrate_cb(struct virtio_video_device *vvd,
+ struct virtio_video_vbuffer *vbuf)
+{
+ struct virtio_video_get_control_resp *resp =
+ (struct virtio_video_get_control_resp *)vbuf->resp_buf;
+ struct virtio_video_control_val_bitrate *resp_p = NULL;
+ struct virtio_video_stream *stream = vbuf->priv;
+ struct video_control_info *control = &stream->control;
+
+ resp_p = (void *)((char *) resp +
+ sizeof(struct virtio_video_get_control_resp));
+
+ control->bitrate = le32_to_cpu(resp_p->bitrate);
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_get_control(struct virtio_video_device *vvd,
+ struct virtio_video_stream *stream,
+ enum virtio_video_control_type control)
+{
+ int ret;
+ struct virtio_video_get_control *req_p;
+ struct virtio_video_get_control_resp *resp_p;
+ struct virtio_video_vbuffer *vbuf;
+ size_t resp_size = sizeof(struct virtio_video_get_control_resp);
+ virtio_video_resp_cb cb;
+
+ switch (control) {
+ case VIRTIO_VIDEO_CONTROL_PROFILE:
+ resp_size += sizeof(struct virtio_video_control_val_profile);
+ cb = &virtio_video_cmd_get_ctrl_profile_cb;
+ break;
+ case VIRTIO_VIDEO_CONTROL_LEVEL:
+ resp_size += sizeof(struct virtio_video_control_val_level);
+ cb = &virtio_video_cmd_get_ctrl_level_cb;
+ break;
+ case VIRTIO_VIDEO_CONTROL_BITRATE:
+ resp_size += sizeof(struct virtio_video_control_val_bitrate);
+ cb = &virtio_video_cmd_get_ctrl_bitrate_cb;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ req_p = virtio_video_alloc_req_resp(vvd, cb, &vbuf,
+ sizeof(*req_p), resp_size, NULL);
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_GET_CONTROL);
+ req_p->hdr.stream_id = cpu_to_le32(stream->stream_id);
+ req_p->control = cpu_to_le32(control);
+
+ resp_p = (struct virtio_video_get_control_resp *)vbuf->resp_buf;
+
+ vbuf->priv = stream;
+
+ ret = virtio_video_queue_cmd_buffer_sync(vvd, vbuf);
+ if (ret == -ETIMEDOUT)
+ v4l2_err(&vvd->v4l2_dev,
+ "timed out waiting for get_control\n");
+ return ret;
+}
+
+// TODO: replace virtio_video_cmd_hdr accoring to specification v4
+int virtio_video_cmd_set_control(struct virtio_video_device *vvd,
+ uint32_t stream_id,
+ enum virtio_video_control_type control,
+ uint32_t value)
+{
+ struct virtio_video_set_control *req_p;
+ struct virtio_video_vbuffer *vbuf;
+ struct virtio_video_control_val_level *ctrl_l;
+ struct virtio_video_control_val_profile *ctrl_p;
+ struct virtio_video_control_val_bitrate *ctrl_b;
+ size_t size;
+
+ if (value == 0)
+ return -EINVAL;
+
+ switch (control) {
+ case VIRTIO_VIDEO_CONTROL_PROFILE:
+ size = sizeof(struct virtio_video_control_val_profile);
+ break;
+ case VIRTIO_VIDEO_CONTROL_LEVEL:
+ size = sizeof(struct virtio_video_control_val_level);
+ break;
+ case VIRTIO_VIDEO_CONTROL_BITRATE:
+ size = sizeof(struct virtio_video_control_val_bitrate);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ req_p = virtio_video_alloc_req(vvd, &vbuf, size + sizeof(*req_p));
+ if (IS_ERR(req_p))
+ return PTR_ERR(req_p);
+
+ req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_SET_CONTROL);
+ req_p->hdr.stream_id = cpu_to_le32(stream_id);
+ req_p->control = cpu_to_le32(control);
+
+ switch (control) {
+ case VIRTIO_VIDEO_CONTROL_PROFILE:
+ ctrl_p = (void *)((char *)req_p +
+ sizeof(struct virtio_video_set_control));
+ ctrl_p->profile = cpu_to_le32(value);
+ break;
+ case VIRTIO_VIDEO_CONTROL_LEVEL:
+ ctrl_l = (void *)((char *)req_p +
+ sizeof(struct virtio_video_set_control));
+ ctrl_l->level = cpu_to_le32(value);
+ break;
+ case VIRTIO_VIDEO_CONTROL_BITRATE:
+ ctrl_b = (void *)((char *)req_p +
+ sizeof(struct virtio_video_set_control));
+ ctrl_b->bitrate = cpu_to_le32(value);
+ break;
+ }
+
+ return virtio_video_queue_cmd_buffer(vvd, vbuf);
+}
+