2023-09-16 01:02:52

by Sebastian Fricke

[permalink] [raw]
Subject: [PATCH v12 0/7] Wave5 codec driver

The Wave5 codec driver is a stateful encoder/decoder.
It is found on the J721S2 SoC, JH7100 SoC, ssd202d SoC. Etc.
But current test report is based on J721S2 SoC and pre-silicon FPGA.

The driver currently supports V4L2_PIX_FMT_HEVC, V4L2_PIX_FMT_H264
for both encoding and decoding.

This driver has so far been tested on J721S2 EVM board and pre-silicon
FPGA.

Testing on J721S2 EVM board shows it working fine both decoder and
encoder.
The driver is successfully working with gstreamer v4l2 good-plugin
without any modification.

We have based the tree on top of the latest
https://git.linuxtv.org/media_tree.git repository.

M2M framework modification:
===========================

(Patch 1 & 2)
Added a set of changes to enable ignoring the streaming state of one or more
queues while checking whether a new job can be inserted into the ready queue.
The use-case we encountered for this is a stateful decoder/encoder chip with a
tight connection to a firmware. On that firmware the bitstream is first
analyzed and the firmware produces output indicating the requirements for the
output of the decode (e.g. the buffers of the CAPTURE queue). We want to be
able to perform this action within the M2M job workflow in order to rely on the
concurrency safety provided by the M2M jobs.

v4l2-compliance results:
========================

v4l2-compliance 1.25.0-5092, 64 bits, 64-bit time_t

Total for wave5-dec device /dev/video0: 45, Succeeded: 45, Failed: 0, Warnings: 0

Total for wave5-enc device /dev/video1: 45, Succeeded: 45, Failed: 0, Warnings: 0

Fluster test results:
=====================

Running test suite JCT-VC-HEVC_V1 with decoder GStreamer-H.265-V4L2-Gst1.0
Using 1 parallel job(s)
Ran 133/147 tests successfully in 95.898 secs

(1 test fails because of a missing frame and slight corruption, 2 tests fail
because of sizes which are incompatible with the IP, 11 tests fail because of
unsupported 10 bit format)

Running test suite JVT-AVC_V1 with decoder GStreamer-H.264-V4L2-Gst1.0
Using 1 parallel job(s)
Ran 78/135 tests successfully in 57.198 secs

(57 fail because the hardware is unable to decode
MBAFF / FMO / Field / Extended profile streams.)

Encoder testing:
================

Among other tests the driver is able to produce valid output with the following test:
`gst-launch-1.0 videotestsrc num-buffers=300 ! v4l2h264enc ! h264parse ! qtmux ! filesink location=test.mp4`

Changes since v11:
==================

* Major rework of the decoder
- Fix concurrency issues by moving the commands that invoke actions on the
firmware into the device_run function from M2M, effectively causing M2M to
take care of the concurrency via its job queue.
- In order to do that device_run needs to be able to run before both queues
are ready, as a sequence needs to be initialized on the firmware to get
the required buffer count & communicate the result back to userspace,
thus we added a routine to allow jobs to be queued in M2M with only one
of two queues being started. (See: "Add ignore_streaming flag to M2M"
series)
- Add support for more output formats (YUV422P, YUV422M, NV16, NV16M, NV61,
NV61M)
- Add proper state switch function to verify state switches
- Simplify the queue_setup function and move the decoder opening to STREAMON
as request in the review of V11
- Enable handling dynamic resolution change and seeking
- Remove thumbnail mode

* Similar reworks on the encoder
- Move encoding into device_run and encoder opening + sequence intialization
to STREAMON, this change simplifies the encoder as it previously was able
to run multiple encode jobs simultaneously

* Remove unused configurations and support for untested hardware
- Remove the ability to configure the endianess of memory writes as only a
single hardware can be tested so far
- Remove the ability to configure the bitstream_mode, as the driver is
currently hardcoded for the INTERRUPT mode
- Remove support for all CODECS, that were not tested (everything besides
H264/HEVC encoder + decoder)
- The encoder currently contains a lot of configurable parameter, which are
hard-coded in the `wave5_set_enc_openparam` function, remove all parameters
which aren't currently specified in that configuration
- Remove unused rotation and mirroring options from the decoder

* Add FLUSH_FIRMWARE firmware command
* Refactoring
* Add wrappers for frequently used routines or to make the code more
descriptive
- Wrapper for firmware command calling `send_firmware_command`
- Wrappers to intialize the sequence and to set up the framebuffers in the
decoder and encoder
- Using more general kernel functions and macros in various places
* Add Macros for constant values and bit shifts
* Fix typos and improve comments

* Fix bug with M2M instance stored in the driver instance, multiple
simultaneous instances would overwrite the M2M handler of each other. Use a
M2M handler per device instead to avoid overwriting.
* Adjust TRACE_PATH in the coda directory as highlighted in the review of V11
* Applied requested changes from review to the devicetree bindings file
(the bindings check didn't return any warnings or errors)

Changes since v10:
==================

* Remove structure member from the encoder and decoder output info
structs, that have assigned values from the registers but aren't used
in the driver, add comments to describe the register values in the
register definitions
* Fix issue with decoding videos with a dimension where the height is
not a multiple of 16 (270, 360, 540, 1024 etc.)
* Fix incorrect variable format identifiers in printks
* Use debug logs in loops to avoid flooding the message log
* Use the swap() function instead of manual swapping of two values
* Add extended controls for the encoder
* Fix control flow issue while handling bitstream buffers, where an
error while writing the source buffer into the hardware ring buffer
would result in skipping the problematic buffer, which in turn causes
a reordering of source buffers
* Use the rectangle format as described by the hardware, the hardware
uses for rectangles like the display rectangle 4 offsets (top, bottom,
left, right), which depict the offset from the respective edge. Use
this format instead of implicitly converting the bottom and right
attributes to width and height attributes.
* Return an error upon reading the sequence header while STREAMON
* Squash the VDI and the VPUAPI layer commits as they had circular
dependencies

changes since v9:
=================

* Move from staging to the media directory
* Move coda driver to sub-directory

* Fixes:
* Use platform_get_irq instead of platform_get_resource to fetch the IRQ

* General cleanups:
* Add missing error messages to error conditions
* Improve messages/variable names/comments, align parameter names across the driver
* Use macros instead of magic numbers in multiple occassions
* Reduce code duplication in multiple places
* Fix whitespace, newline and tab alignment issues
* Remove unused struct fields & commented out code
* Convert signed integers to unsigned if signed is not necessary
* Convert int/unsigned int to s32/u32, when the variable is assigned to the
return of a register read or provided as a parameter for a register write
(and vice versa)
* Fix incorrect bitwise operators where logical operators are appropriate
* Multiple smaller changes

* Generalization:
* Add new helper file providing generalized routines for vpu-dec & vpu-enc
* Generalize luma & chroma table size calculation and stride calculation

* Resource cleanup and error handling:
* Add error handling to all calls with ignored return codes
* Handle DMA resource cleanup properly
* Fix insufficient instance cleanup while opening dec/enc

Changes since v8:
=================

* add 'wave5' to DEV_NAME
* update to support Multi-stream
* update to support loop test/dynamic resolution change
* remove unnecessary memset, g_volatile, old version option

Changes since v7:
=================

* update v4l2-compliance test report
* fix build error on linux-kernel 5.18.0-rc4

Changes since v6:
=================

* update TODO file
* get sram info from device tree

Changes since v5:
=================

* support NV12/NV21 pixelformat for encoder and decoder
* handle adnormal exit and EOS

Changes since v4:
=================

* refactor functions in wave5-hw and fix bug reported by Daniel Palmer
* rename functions and variables to better names
* change variable types such as replacing s32 with u32 and int with bool
* as appropriate

Changes since v3:
=================

* Fixing all issues commented by Dan Carpenter
* Change file names to have wave5- prefix
* In wave5_vpu_probe, enable the clocks before reading registers, as
* commented from Daniel Palmer
* Add more to the TODO list,

Changes since v2:
=================

Main fixes includes:
* change the yaml and dirver code to support up to 4 clks (instead of
* one)
* fix Kconfig format
* remove unneeded cast,
* change var types
* change var names, func names
* checkpatch fixes

Changes since v1:
=================

Fix changes due to comments from Ezequiel and Dan Carpenter. Main fixes
inclueds:
* move all files to one dir 'wave5'
* replace private error codes with standard error codes
* fix extra spaces
* various checkpatch fixes
* replace private 'DPRINTK' macro with standard 'dev_err/dbg ..'
* fix error handling
* add more possible fixes to the TODO file

To: Mauro Carvalho Chehab <[email protected]>
To: Hans Verkuil <[email protected]>
To: Philipp Zabel <[email protected]>
To: Shawn Guo <[email protected]>
To: Sascha Hauer <[email protected]>
To: Pengutronix Kernel Team <[email protected]>
To: Fabio Estevam <[email protected]>
To: NXP Linux Team <[email protected]>
To: Jackson Lee <[email protected]>
To: Rob Herring <[email protected]>
To: Krzysztof Kozlowski <[email protected]>
To: Conor Dooley <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: Nicolas Dufresne <[email protected]>
Cc: Robert Beckett <[email protected]>
Cc: Nas Chung <[email protected]>
CC: [email protected]
Signed-off-by: Sebastian Fricke <[email protected]>

---
Nas Chung (2):
media: chips-media: wave5: Add vpuapi layer
media: chips-media: wave5: Add the v4l2 layer

Robert Beckett (2):
dt-bindings: media: wave5: add yaml devicetree bindings
media: chips-media: wave5: Add wave5 driver to maintainers file

Sebastian Fricke (3):
media: v4l2: Add ignore_streaming flag
media: v4l2: Allow M2M job queuing w/o streaming CAP queue
media: platform: chips-media: Move Coda to separate folder

.../devicetree/bindings/media/cnm,wave5.yaml | 66 +
MAINTAINERS | 10 +-
drivers/media/platform/chips-media/Kconfig | 18 +-
drivers/media/platform/chips-media/Makefile | 6 +-
drivers/media/platform/chips-media/coda/Kconfig | 18 +
drivers/media/platform/chips-media/coda/Makefile | 6 +
.../platform/chips-media/{ => coda}/coda-bit.c | 0
.../platform/chips-media/{ => coda}/coda-common.c | 0
.../platform/chips-media/{ => coda}/coda-gdi.c | 0
.../platform/chips-media/{ => coda}/coda-h264.c | 0
.../platform/chips-media/{ => coda}/coda-jpeg.c | 0
.../platform/chips-media/{ => coda}/coda-mpeg2.c | 0
.../platform/chips-media/{ => coda}/coda-mpeg4.c | 0
.../media/platform/chips-media/{ => coda}/coda.h | 0
.../platform/chips-media/{ => coda}/coda_regs.h | 0
.../platform/chips-media/{ => coda}/imx-vdoa.c | 0
.../platform/chips-media/{ => coda}/imx-vdoa.h | 0
.../media/platform/chips-media/{ => coda}/trace.h | 2 +-
drivers/media/platform/chips-media/wave5/Kconfig | 12 +
drivers/media/platform/chips-media/wave5/Makefile | 10 +
.../platform/chips-media/wave5/wave5-helper.c | 196 ++
.../platform/chips-media/wave5/wave5-helper.h | 30 +
.../media/platform/chips-media/wave5/wave5-hw.c | 2553 ++++++++++++++++++++
.../platform/chips-media/wave5/wave5-regdefine.h | 732 ++++++
.../media/platform/chips-media/wave5/wave5-vdi.c | 208 ++
.../media/platform/chips-media/wave5/wave5-vdi.h | 35 +
.../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 +++++++++++++++
.../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++
.../media/platform/chips-media/wave5/wave5-vpu.c | 331 +++
.../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
.../platform/chips-media/wave5/wave5-vpuapi.c | 958 ++++++++
.../platform/chips-media/wave5/wave5-vpuapi.h | 874 +++++++
.../platform/chips-media/wave5/wave5-vpuconfig.h | 77 +
.../platform/chips-media/wave5/wave5-vpuerror.h | 292 +++
drivers/media/platform/chips-media/wave5/wave5.h | 116 +
drivers/media/v4l2-core/v4l2-mem2mem.c | 9 +-
include/media/v4l2-mem2mem.h | 17 +
37 files changed, 10424 insertions(+), 25 deletions(-)
---
base-commit: 0bb80ecc33a8fb5a682236443c1e740d5c917d1d
change-id: 20230915-wave5_v12_on_media_master-ac30ff2c4c00

Best regards,
--
Sebastian Fricke <[email protected]>


2023-09-16 01:17:50

by Sebastian Fricke

[permalink] [raw]
Subject: [PATCH v12 4/7] media: chips-media: wave5: Add vpuapi layer

From: Nas Chung <[email protected]>

Add the vpuapi layer of the wave5 codec driver.
This layer is used to configure the hardware according
to the parameters.

Signed-off-by: Sebastian Fricke <[email protected]>
Signed-off-by: Nicolas Dufresne <[email protected]>
Signed-off-by: Dafna Hirschfeld <[email protected]>
Signed-off-by: Robert Beckett <[email protected]>
Signed-off-by: Nas Chung <[email protected]>
---
.../media/platform/chips-media/wave5/wave5-hw.c | 2553 ++++++++++++++++++++
.../platform/chips-media/wave5/wave5-regdefine.h | 732 ++++++
.../media/platform/chips-media/wave5/wave5-vdi.c | 208 ++
.../media/platform/chips-media/wave5/wave5-vdi.h | 35 +
.../platform/chips-media/wave5/wave5-vpuapi.c | 958 ++++++++
.../platform/chips-media/wave5/wave5-vpuapi.h | 874 +++++++
.../platform/chips-media/wave5/wave5-vpuconfig.h | 77 +
.../platform/chips-media/wave5/wave5-vpuerror.h | 292 +++
drivers/media/platform/chips-media/wave5/wave5.h | 116 +
9 files changed, 5845 insertions(+)

diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c
new file mode 100644
index 000000000000..5d533d01b820
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c
@@ -0,0 +1,2553 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - wave5 backend logic
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#include <linux/iopoll.h>
+#include <linux/bitfield.h>
+#include "wave5-vpu.h"
+#include "wave5.h"
+#include "wave5-regdefine.h"
+
+#define FIO_TIMEOUT 10000000
+#define FIO_CTRL_READY BIT(31)
+#define FIO_CTRL_WRITE BIT(16)
+#define VPU_BUSY_CHECK_TIMEOUT 10000000
+#define QUEUE_REPORT_MASK 0xffff
+
+/* Encoder support fields */
+#define FEATURE_HEVC10BIT_ENC BIT(3)
+#define FEATURE_AVC10BIT_ENC BIT(11)
+#define FEATURE_AVC_ENCODER BIT(1)
+#define FEATURE_HEVC_ENCODER BIT(0)
+
+/* Decoder support fields */
+#define FEATURE_AVC_DECODER BIT(3)
+#define FEATURE_HEVC_DECODER BIT(2)
+
+#define FEATURE_BACKBONE BIT(16)
+#define FEATURE_VCORE_BACKBONE BIT(22)
+#define FEATURE_VCPU_BACKBONE BIT(28)
+
+#define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff)
+#define REMAP_CTRL_REGISTER_VALUE(index) ( \
+ (BIT(31) | (index << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS) \
+)
+
+#define FASTIO_ADDRESS_MASK GENMASK(15, 0)
+#define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24)
+
+static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason,
+ const char *func);
+#define PRINT_REG_ERR(dev, reason) _wave5_print_reg_err((dev), (reason), __func__)
+
+static inline const char *cmd_to_str(int cmd, bool is_dec)
+{
+ switch (cmd) {
+ case W5_INIT_VPU:
+ return "W5_INIT_VPU";
+ case W5_WAKEUP_VPU:
+ return "W5_WAKEUP_VPU";
+ case W5_SLEEP_VPU:
+ return "W5_SLEEP_VPU";
+ case W5_CREATE_INSTANCE:
+ return "W5_CREATE_INSTANCE";
+ case W5_FLUSH_INSTANCE:
+ return "W5_FLUSH_INSTANCE";
+ case W5_DESTROY_INSTANCE:
+ return "W5_DESTROY_INSTANCE";
+ case W5_INIT_SEQ:
+ return "W5_INIT_SEQ";
+ case W5_SET_FB:
+ return "W5_SET_FB";
+ case W5_DEC_ENC_PIC:
+ if (is_dec)
+ return "W5_DEC_PIC";
+ return "W5_ENC_PIC";
+ case W5_ENC_SET_PARAM:
+ return "W5_ENC_SET_PARAM";
+ case W5_QUERY:
+ return "W5_QUERY";
+ case W5_UPDATE_BS:
+ return "W5_UPDATE_BS";
+ case W5_MAX_VPU_COMD:
+ return "W5_MAX_VPU_COMD";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason,
+ const char *func)
+{
+ struct device *dev = vpu_dev->dev;
+ u32 reg_val;
+
+ switch (reg_fail_reason) {
+ case WAVE5_SYSERR_QUEUEING_FAIL:
+ reg_val = vpu_read_reg(vpu_dev, W5_RET_QUEUE_FAIL_REASON);
+ dev_dbg(dev, "%s: queueing failure: 0x%x\n", func, reg_val);
+ break;
+ case WAVE5_SYSERR_RESULT_NOT_READY:
+ dev_err(dev, "%s: result not ready: 0x%x\n", func, reg_fail_reason);
+ break;
+ case WAVE5_SYSERR_ACCESS_VIOLATION_HW:
+ dev_err(dev, "%s: access violation: 0x%x\n", func, reg_fail_reason);
+ break;
+ case WAVE5_SYSERR_WATCHDOG_TIMEOUT:
+ dev_err(dev, "%s: watchdog timeout: 0x%x\n", func, reg_fail_reason);
+ break;
+ case WAVE5_SYSERR_BUS_ERROR:
+ dev_err(dev, "%s: bus error: 0x%x\n", func, reg_fail_reason);
+ break;
+ case WAVE5_SYSERR_DOUBLE_FAULT:
+ dev_err(dev, "%s: double fault: 0x%x\n", func, reg_fail_reason);
+ break;
+ case WAVE5_SYSERR_VPU_STILL_RUNNING:
+ dev_err(dev, "%s: still running: 0x%x\n", func, reg_fail_reason);
+ break;
+ case WAVE5_SYSERR_VLC_BUF_FULL:
+ dev_err(dev, "%s: vlc buf full: 0x%x\n", func, reg_fail_reason);
+ break;
+ default:
+ dev_err(dev, "%s: failure:: 0x%x\n", func, reg_fail_reason);
+ break;
+ }
+}
+
+static int wave5_wait_fio_readl(struct vpu_device *vpu_dev, u32 addr, u32 val)
+{
+ u32 ctrl;
+ int ret;
+
+ ctrl = addr & 0xffff;
+ wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl);
+ ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY,
+ 0, FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR);
+ if (ret)
+ return ret;
+
+ if (wave5_vdi_read_register(vpu_dev, W5_VPU_FIO_DATA) != val)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static void wave5_fio_writel(struct vpu_device *vpu_dev, unsigned int addr, unsigned int data)
+{
+ int ret;
+ unsigned int ctrl;
+
+ wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_DATA, data);
+ ctrl = FIELD_GET(FASTIO_ADDRESS_MASK, addr);
+ ctrl |= FIO_CTRL_WRITE;
+ wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl);
+ ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY, 0,
+ FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR);
+ if (ret)
+ dev_dbg_ratelimited(vpu_dev->dev, "FIO write timeout: addr=0x%x data=%x\n",
+ ctrl, data);
+}
+
+static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr)
+{
+ u32 gdi_status_check_value = 0x3f;
+
+ if (vpu_dev->product_code == WAVE521C_CODE ||
+ vpu_dev->product_code == WAVE521_CODE ||
+ vpu_dev->product_code == WAVE521E1_CODE)
+ gdi_status_check_value = 0x00ff1f3f;
+
+ return wave5_wait_fio_readl(vpu_dev, addr, gdi_status_check_value);
+}
+
+static int wave5_wait_vpu_busy(struct vpu_device *vpu_dev, unsigned int addr)
+{
+ u32 data;
+
+ return read_poll_timeout(wave5_vdi_read_register, data, data == 0,
+ 0, VPU_BUSY_CHECK_TIMEOUT, false, vpu_dev, addr);
+}
+
+static int wave5_wait_vcpu_bus_busy(struct vpu_device *vpu_dev, unsigned int addr)
+{
+ return wave5_wait_fio_readl(vpu_dev, addr, 0);
+}
+
+bool wave5_vpu_is_init(struct vpu_device *vpu_dev)
+{
+ return vpu_read_reg(vpu_dev, W5_VCPU_CUR_PC) != 0;
+}
+
+unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev)
+{
+ u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER);
+
+ switch (val) {
+ case WAVE521C_CODE:
+ return PRODUCT_ID_521;
+ case WAVE521_CODE:
+ case WAVE521C_DUAL_CODE:
+ case WAVE521E1_CODE:
+ case WAVE511_CODE:
+ case WAVE517_CODE:
+ case WAVE537_CODE:
+ dev_err(vpu_dev->dev, "Unsupported product id (%x)\n", val);
+ break;
+ default:
+ dev_err(vpu_dev->dev, "Invalid product id (%x)\n", val);
+ break;
+ }
+
+ return PRODUCT_ID_NONE;
+}
+
+void wave5_bit_issue_command(struct vpu_instance *inst, u32 cmd)
+{
+ u32 instance_index = inst->id;
+ u32 codec_mode = inst->std;
+
+ vpu_write_reg(inst->dev, W5_CMD_INSTANCE_INFO, (codec_mode << 16) |
+ (instance_index & 0xffff));
+ vpu_write_reg(inst->dev, W5_VPU_BUSY_STATUS, 1);
+ vpu_write_reg(inst->dev, W5_COMMAND, cmd);
+
+ dev_dbg(inst->dev->dev, "%s: cmd=0x%x (%s)\n", __func__, cmd,
+ cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC));
+
+ vpu_write_reg(inst->dev, W5_VPU_HOST_INT_REQ, 1);
+}
+
+static int wave5_vpu_firmware_command_queue_error_check(struct vpu_device *dev, u32 *fail_res)
+{
+ /* Check if we were able to add a command into the VCPU QUEUE */
+ if (!vpu_read_reg(dev, W5_RET_SUCCESS)) {
+ if (!fail_res)
+ return -EIO;
+
+ *fail_res = vpu_read_reg(dev, W5_RET_FAIL_REASON);
+ PRINT_REG_ERR(dev, *fail_res);
+ if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING)
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int send_firmware_command(struct vpu_instance *inst, u32 cmd, bool check_success,
+ u32 *queue_status, u32 *fail_result)
+{
+ int ret;
+
+ wave5_bit_issue_command(inst, cmd);
+ ret = wave5_wait_vpu_busy(inst->dev, W5_VPU_BUSY_STATUS);
+ if (ret) {
+ dev_warn(inst->dev->dev, "%s: command: '%s', timed out\n", __func__,
+ cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC));
+ return -ETIMEDOUT;
+ }
+
+ if (queue_status)
+ *queue_status = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
+
+ /* In some cases we want to send multiple commands before checking
+ * whether they are queued properly
+ */
+ if (!check_success)
+ return 0;
+
+ return wave5_vpu_firmware_command_queue_error_check(inst->dev, fail_result);
+}
+
+static int wave5_send_query(struct vpu_instance *inst, enum query_opt query_opt)
+{
+ int ret;
+
+ vpu_write_reg(inst->dev, W5_QUERY_OPTION, query_opt);
+ vpu_write_reg(inst->dev, W5_VPU_BUSY_STATUS, 1);
+ wave5_bit_issue_command(inst, W5_QUERY);
+
+ ret = wave5_wait_vpu_busy(inst->dev, W5_VPU_BUSY_STATUS);
+ if (ret) {
+ dev_warn(inst->dev->dev, "command: 'W5_QUERY', timed out opt=0x%x\n", query_opt);
+ return ret;
+ }
+
+ return wave5_vpu_firmware_command_queue_error_check(inst->dev, NULL);
+}
+
+static int setup_wave5_properties(struct device *dev)
+{
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+ struct vpu_attr *p_attr = &vpu_dev->attr;
+ u32 reg_val;
+ u8 *str;
+ int ret;
+ u32 hw_config_def0, hw_config_def1, hw_config_feature;
+
+ vpu_write_reg(vpu_dev, W5_QUERY_OPTION, GET_VPU_INFO);
+ vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
+ vpu_write_reg(vpu_dev, W5_COMMAND, W5_QUERY);
+ vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
+ if (ret)
+ return ret;
+
+ reg_val = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_NAME);
+ str = (u8 *)&reg_val;
+ p_attr->product_name[0] = str[3];
+ p_attr->product_name[1] = str[2];
+ p_attr->product_name[2] = str[1];
+ p_attr->product_name[3] = str[0];
+ p_attr->product_name[4] = 0;
+
+ p_attr->product_id = wave5_vpu_get_product_id(vpu_dev);
+ p_attr->product_version = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_VERSION);
+ p_attr->fw_version = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION);
+ p_attr->customer_id = vpu_read_reg(vpu_dev, W5_RET_CUSTOMER_ID);
+ hw_config_def0 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF0);
+ hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1);
+ hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE);
+
+ p_attr->support_hevc10bit_enc = FIELD_GET(FEATURE_HEVC10BIT_ENC, hw_config_feature);
+ p_attr->support_avc10bit_enc = FIELD_GET(FEATURE_AVC10BIT_ENC, hw_config_feature);
+
+ p_attr->support_decoders = FIELD_GET(FEATURE_AVC_DECODER, hw_config_def1) << STD_AVC;
+ p_attr->support_decoders |= FIELD_GET(FEATURE_HEVC_DECODER, hw_config_def1) << STD_HEVC;
+ p_attr->support_encoders = FIELD_GET(FEATURE_AVC_ENCODER, hw_config_def1) << STD_AVC;
+ p_attr->support_encoders |= FIELD_GET(FEATURE_HEVC_ENCODER, hw_config_def1) << STD_HEVC;
+
+ p_attr->support_backbone = FIELD_GET(FEATURE_BACKBONE, hw_config_def0);
+ p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0);
+ p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0);
+
+ return 0;
+}
+
+int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision)
+{
+ u32 reg_val;
+ int ret;
+
+ vpu_write_reg(vpu_dev, W5_QUERY_OPTION, GET_VPU_INFO);
+ vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
+ vpu_write_reg(vpu_dev, W5_COMMAND, W5_QUERY);
+ vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
+ if (ret) {
+ dev_err(vpu_dev->dev, "%s: timeout\n", __func__);
+ return ret;
+ }
+
+ ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
+ if (ret) {
+ dev_err(vpu_dev->dev, "%s: failed\n", __func__);
+ return ret;
+ }
+
+ reg_val = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION);
+ if (revision) {
+ *revision = reg_val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void remap_page(struct vpu_device *vpu_dev, dma_addr_t code_base, u32 index)
+{
+ vpu_write_reg(vpu_dev, W5_VPU_REMAP_CTRL, REMAP_CTRL_REGISTER_VALUE(index));
+ vpu_write_reg(vpu_dev, W5_VPU_REMAP_VADDR, index * W5_REMAP_MAX_SIZE);
+ vpu_write_reg(vpu_dev, W5_VPU_REMAP_PADDR, code_base + index * W5_REMAP_MAX_SIZE);
+}
+
+int wave5_vpu_init(struct device *dev, u8 *fw, size_t size)
+{
+ struct vpu_buf *common_vb;
+ dma_addr_t code_base, temp_base;
+ u32 code_size, temp_size;
+ u32 i, reg_val, reason_code;
+ int ret;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ common_vb = &vpu_dev->common_mem;
+
+ code_base = common_vb->daddr;
+ /* ALIGN TO 4KB */
+ code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
+ if (code_size < size * 2)
+ return -EINVAL;
+
+ temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET;
+ temp_size = WAVE5_TEMPBUF_SIZE;
+
+ ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size);
+ if (ret < 0) {
+ dev_err(vpu_dev->dev, "VPU init, Writing firmware to common buffer, fail: %d\n",
+ ret);
+ return ret;
+ }
+
+ vpu_write_reg(vpu_dev, W5_PO_CONF, 0);
+
+ /* clear registers */
+
+ for (i = W5_CMD_REG_BASE; i < W5_CMD_REG_END; i += 4)
+ vpu_write_reg(vpu_dev, i, 0x00);
+
+ remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
+ remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);
+
+ vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
+ vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
+ vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
+ vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base);
+ vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size);
+
+ /* These register must be reset explicitly */
+ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
+ vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
+
+ /* Encoder interrupt */
+ reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
+ reg_val |= BIT(INT_WAVE5_ENC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
+ /* Decoder interrupt */
+ reg_val |= BIT(INT_WAVE5_INIT_SEQ);
+ reg_val |= BIT(INT_WAVE5_DEC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
+ vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
+
+ reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
+ if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
+ reg_val = ((WAVE5_PROC_AXI_ID << 28) |
+ (WAVE5_PRP_AXI_ID << 24) |
+ (WAVE5_FBD_Y_AXI_ID << 20) |
+ (WAVE5_FBC_Y_AXI_ID << 16) |
+ (WAVE5_FBD_C_AXI_ID << 12) |
+ (WAVE5_FBC_C_AXI_ID << 8) |
+ (WAVE5_PRI_AXI_ID << 4) |
+ WAVE5_SEC_AXI_ID);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
+ }
+
+ vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
+ vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
+ vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
+ if (ret) {
+ dev_err(vpu_dev->dev, "VPU init(W5_VPU_REMAP_CORE_START) timeout\n");
+ return ret;
+ }
+
+ ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
+ if (ret)
+ return ret;
+
+ return setup_wave5_properties(dev);
+}
+
+int wave5_vpu_build_up_dec_param(struct vpu_instance *inst,
+ struct dec_open_param *param)
+{
+ int ret;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ p_dec_info->cycle_per_tick = 256;
+ if (vpu_dev->sram_buf.size) {
+ p_dec_info->sec_axi_info.use_bit_enable = 1;
+ p_dec_info->sec_axi_info.use_ip_enable = 1;
+ p_dec_info->sec_axi_info.use_lf_row_enable = 1;
+ }
+ switch (inst->std) {
+ case W_HEVC_DEC:
+ p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_HEVC;
+ break;
+ case W_AVC_DEC:
+ p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_AVC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE;
+ ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work);
+ if (ret)
+ return ret;
+
+ vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1);
+
+ wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work);
+
+ vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr);
+ vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size);
+
+ vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
+ vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
+
+ vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size);
+
+ /* NOTE: SDMA reads MSB first */
+ vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN);
+ /* This register must be reset explicitly */
+ vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
+ vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1));
+
+ ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
+ if (ret) {
+ wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
+ return ret;
+ }
+
+ p_dec_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER);
+
+ return 0;
+}
+
+int wave5_vpu_hw_flush_instance(struct vpu_instance *inst)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ u32 instance_queue_count, report_queue_count;
+ u32 reg_val = 0;
+ u32 fail_res = 0;
+ int ret;
+
+ ret = send_firmware_command(inst, W5_FLUSH_INSTANCE, true, &reg_val, &fail_res);
+ if (ret)
+ return ret;
+
+ instance_queue_count = (reg_val >> 16) & 0xff;
+ report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+ if (instance_queue_count != 0 || report_queue_count != 0) {
+ dev_warn(inst->dev->dev,
+ "FLUSH_INSTANCE cmd didn't reset the amount of queued commands & reports");
+ }
+
+ /* reset our local copy of the counts */
+ p_dec_info->instance_queue_count = 0;
+ p_dec_info->report_queue_count = 0;
+
+ return 0;
+}
+
+static u32 get_bitstream_options(struct dec_info *info)
+{
+ u32 bs_option = BSOPTION_ENABLE_EXPLICIT_END;
+
+ if (info->stream_endflag)
+ bs_option |= BSOPTION_HIGHLIGHT_STREAM_END;
+ return bs_option;
+}
+
+int wave5_vpu_dec_init_seq(struct vpu_instance *inst)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ u32 cmd_option = INIT_SEQ_NORMAL;
+ u32 reg_val, fail_res;
+ int ret;
+
+ if (!inst->codec_info)
+ return -EINVAL;
+
+ vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr);
+ vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);
+
+ vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
+
+ vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable);
+
+ ret = send_firmware_command(inst, W5_INIT_SEQ, true, &reg_val, &fail_res);
+ if (ret == -ETIMEDOUT)
+ return ret;
+
+ p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
+ p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+
+ dev_dbg(inst->dev->dev, "%s: init seq sent (queue %u : %u)\n", __func__,
+ p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initial_info *info)
+{
+ u32 reg_val;
+ u32 profile_compatibility_flag;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+
+ p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst);
+ info->rd_ptr = p_dec_info->stream_rd_ptr;
+
+ p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC);
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE);
+ info->pic_width = ((reg_val >> 16) & 0xffff);
+ info->pic_height = (reg_val & 0xffff);
+ info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_DEC_NUM_REQUIRED_FB);
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_LEFT_RIGHT);
+ info->pic_crop_rect.left = (reg_val >> 16) & 0xffff;
+ info->pic_crop_rect.right = reg_val & 0xffff;
+ reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_TOP_BOTTOM);
+ info->pic_crop_rect.top = (reg_val >> 16) & 0xffff;
+ info->pic_crop_rect.bottom = reg_val & 0xffff;
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_COLOR_SAMPLE_INFO);
+ info->luma_bitdepth = reg_val & 0xf;
+ info->chroma_bitdepth = (reg_val >> 4) & 0xf;
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_SEQ_PARAM);
+ profile_compatibility_flag = (reg_val >> 12) & 0xff;
+ info->profile = (reg_val >> 24) & 0x1f;
+
+ if (inst->std == W_HEVC_DEC) {
+ /* guessing profile */
+ if (!info->profile) {
+ if ((profile_compatibility_flag & 0x06) == 0x06)
+ info->profile = HEVC_PROFILE_MAIN; /* main profile */
+ else if ((profile_compatibility_flag & 0x04) == 0x04)
+ info->profile = HEVC_PROFILE_MAIN10; /* main10 profile */
+ else if ((profile_compatibility_flag & 0x08) == 0x08)
+ /* main still picture profile */
+ info->profile = HEVC_PROFILE_STILLPICTURE;
+ else
+ info->profile = HEVC_PROFILE_MAIN; /* for old version HM */
+ }
+ } else if (inst->std == W_AVC_DEC) {
+ info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val);
+ }
+
+ info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
+ info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
+ p_dec_info->vlc_buf_size = info->vlc_buf_size;
+ p_dec_info->param_buf_size = info->param_buf_size;
+}
+
+int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info)
+{
+ int ret;
+ u32 reg_val;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+
+ vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN);
+
+ /* send QUERY cmd */
+ ret = wave5_send_query(inst, GET_RESULT);
+ if (ret) {
+ if (ret == -EIO) {
+ reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
+ PRINT_REG_ERR(inst->dev, reg_val);
+ }
+ return ret;
+ }
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
+
+ p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
+ p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+
+ dev_dbg(inst->dev->dev, "%s: init seq complete (queue %u : %u)\n", __func__,
+ p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
+
+ /* this is not a fatal error, set ret to -EIO but don't return immediately */
+ if (vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_SUCCESS) != 1) {
+ info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_DEC_ERR_INFO);
+ ret = -EIO;
+ }
+
+ wave5_get_dec_seq_result(inst, info);
+
+ return ret;
+}
+
+int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_buffer *fb_arr,
+ enum tiled_map_type map_type, unsigned int count)
+{
+ int ret;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ struct dec_initial_info *init_info = &p_dec_info->initial_info;
+ size_t remain, idx, j, i, cnt_8_chunk, size;
+ u32 start_no, end_no;
+ u32 reg_val, cbcr_interleave, nv21, pic_size;
+ u32 addr_y, addr_cb, addr_cr;
+ u32 mv_col_size, frame_width, frame_height, fbc_y_tbl_size, fbc_c_tbl_size;
+ struct vpu_buf vb_buf;
+ bool justified = WTL_RIGHT_JUSTIFIED;
+ u32 format_no = WTL_PIXEL_8BIT;
+ u32 color_format = 0;
+ u32 pixel_order = 1;
+ u32 bwb_flag = (map_type == LINEAR_FRAME_MAP) ? 1 : 0;
+
+ cbcr_interleave = inst->cbcr_interleave;
+ nv21 = inst->nv21;
+ mv_col_size = 0;
+ fbc_y_tbl_size = 0;
+ fbc_c_tbl_size = 0;
+
+ if (map_type >= COMPRESSED_FRAME_MAP) {
+ cbcr_interleave = 0;
+ nv21 = 0;
+
+ switch (inst->std) {
+ case W_HEVC_DEC:
+ mv_col_size = WAVE5_DEC_HEVC_BUF_SIZE(init_info->pic_width,
+ init_info->pic_height);
+ break;
+ case W_AVC_DEC:
+ mv_col_size = WAVE5_DEC_AVC_BUF_SIZE(init_info->pic_width,
+ init_info->pic_height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) {
+ size = ALIGN(ALIGN(mv_col_size, 16), BUFFER_MARGIN) + BUFFER_MARGIN;
+ ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_mv, count, size);
+ if (ret)
+ goto free_mv_buffers;
+ }
+
+ frame_width = init_info->pic_width;
+ frame_height = init_info->pic_height;
+ fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(frame_width, frame_height), 16);
+ fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(frame_width, frame_height), 16);
+
+ size = ALIGN(fbc_y_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN;
+ ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_y_tbl, count, size);
+ if (ret)
+ goto free_fbc_y_tbl_buffers;
+
+ size = ALIGN(fbc_c_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN;
+ ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_c_tbl, count, size);
+ if (ret)
+ goto free_fbc_c_tbl_buffers;
+
+ pic_size = (init_info->pic_width << 16) | (init_info->pic_height);
+
+ vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) +
+ (p_dec_info->param_buf_size * COMMAND_QUEUE_DEPTH);
+ vb_buf.daddr = 0;
+
+ if (vb_buf.size != p_dec_info->vb_task.size) {
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
+ ret = wave5_vdi_allocate_dma_memory(inst->dev, &vb_buf);
+ if (ret)
+ goto free_fbc_c_tbl_buffers;
+
+ p_dec_info->vb_task = vb_buf;
+ }
+
+ vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
+ p_dec_info->vb_task.daddr);
+ vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_buf.size);
+ } else {
+ pic_size = (init_info->pic_width << 16) | (init_info->pic_height);
+
+ if (inst->output_format == FORMAT_422)
+ color_format = 1;
+ else
+ color_format = 0;
+ }
+ vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size);
+
+ reg_val = (bwb_flag << 28) |
+ (pixel_order << 23) |
+ (justified << 22) |
+ (format_no << 20) |
+ (color_format << 19) |
+ (nv21 << 17) |
+ (cbcr_interleave << 16) |
+ (fb_arr[0].stride);
+ vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, reg_val);
+
+ remain = count;
+ cnt_8_chunk = DIV_ROUND_UP(count, 8);
+ idx = 0;
+ for (j = 0; j < cnt_8_chunk; j++) {
+ reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3);
+ vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val);
+ start_no = j * 8;
+ end_no = start_no + ((remain >= 8) ? 8 : remain) - 1;
+
+ vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no);
+
+ for (i = 0; i < 8 && i < remain; i++) {
+ addr_y = fb_arr[i + start_no].buf_y;
+ addr_cb = fb_arr[i + start_no].buf_cb;
+ addr_cr = fb_arr[i + start_no].buf_cr;
+ vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), addr_y);
+ vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4), addr_cb);
+ if (map_type >= COMPRESSED_FRAME_MAP) {
+ /* luma FBC offset table */
+ vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4),
+ p_dec_info->vb_fbc_y_tbl[idx].daddr);
+ /* chroma FBC offset table */
+ vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4),
+ p_dec_info->vb_fbc_c_tbl[idx].daddr);
+ vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2),
+ p_dec_info->vb_mv[idx].daddr);
+ } else {
+ vpu_write_reg(inst->dev, W5_ADDR_CR_BASE0 + (i << 4), addr_cr);
+ vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), 0);
+ vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), 0);
+ }
+ idx++;
+ }
+ remain -= i;
+
+ ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL);
+ if (ret)
+ goto free_buffers;
+ }
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_SUCCESS);
+ if (!reg_val) {
+ ret = -EIO;
+ goto free_buffers;
+ }
+
+ return 0;
+
+free_buffers:
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
+free_fbc_c_tbl_buffers:
+ for (i = 0; i < count; i++)
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[i]);
+free_fbc_y_tbl_buffers:
+ for (i = 0; i < count; i++)
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[i]);
+free_mv_buffers:
+ for (i = 0; i < count; i++)
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[i]);
+ return ret;
+}
+
+int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res)
+{
+ u32 reg_val;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret;
+
+ vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr);
+ vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);
+
+ vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
+
+ /* secondary AXI */
+ reg_val = p_dec_info->sec_axi_info.use_bit_enable |
+ (p_dec_info->sec_axi_info.use_ip_enable << 9) |
+ (p_dec_info->sec_axi_info.use_lf_row_enable << 15);
+ vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val);
+
+ /* set attributes of user buffer */
+ vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable);
+
+ vpu_write_reg(inst->dev, W5_COMMAND_OPTION, DEC_PIC_NORMAL);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_TEMPORAL_ID_PLUS1,
+ (p_dec_info->target_spatial_id << 9) |
+ (p_dec_info->temp_id_select_mode << 8) | p_dec_info->target_temp_id);
+ vpu_write_reg(inst->dev, W5_CMD_SEQ_CHANGE_ENABLE_FLAG, p_dec_info->seq_change_mask);
+ /* When reordering is disabled we force the latency of the framebuffers */
+ vpu_write_reg(inst->dev, W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1, !p_dec_info->reorder_enable);
+
+ ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, &reg_val, fail_res);
+ if (ret == -ETIMEDOUT)
+ return ret;
+
+ p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
+ p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+
+ dev_dbg(inst->dev->dev, "%s: dec pic sent (queue %u : %u)\n", __func__,
+ p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result)
+{
+ int ret;
+ u32 index, nal_unit_type, reg_val, sub_layer_info;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN);
+
+ /* send QUERY cmd */
+ ret = wave5_send_query(inst, GET_RESULT);
+ if (ret) {
+ if (ret == -EIO) {
+ reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
+ PRINT_REG_ERR(inst->dev, reg_val);
+ }
+
+ return ret;
+ }
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
+
+ p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
+ p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+
+ dev_dbg(inst->dev->dev, "%s: dec pic complete (queue %u : %u)\n", __func__,
+ p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_TYPE);
+
+ nal_unit_type = (reg_val >> 4) & 0x3f;
+
+ if (inst->std == W_HEVC_DEC) {
+ if (reg_val & 0x04)
+ result->pic_type = PIC_TYPE_B;
+ else if (reg_val & 0x02)
+ result->pic_type = PIC_TYPE_P;
+ else if (reg_val & 0x01)
+ result->pic_type = PIC_TYPE_I;
+ else
+ result->pic_type = PIC_TYPE_MAX;
+ if ((nal_unit_type == 19 || nal_unit_type == 20) && result->pic_type == PIC_TYPE_I)
+ /* IDR_W_RADL, IDR_N_LP */
+ result->pic_type = PIC_TYPE_IDR;
+ } else if (inst->std == W_AVC_DEC) {
+ if (reg_val & 0x04)
+ result->pic_type = PIC_TYPE_B;
+ else if (reg_val & 0x02)
+ result->pic_type = PIC_TYPE_P;
+ else if (reg_val & 0x01)
+ result->pic_type = PIC_TYPE_I;
+ else
+ result->pic_type = PIC_TYPE_MAX;
+ if (nal_unit_type == 5 && result->pic_type == PIC_TYPE_I)
+ result->pic_type = PIC_TYPE_IDR;
+ }
+ index = vpu_read_reg(inst->dev, W5_RET_DEC_DISPLAY_INDEX);
+ result->index_frame_display = index;
+ index = vpu_read_reg(inst->dev, W5_RET_DEC_DECODED_INDEX);
+ result->index_frame_decoded = index;
+ result->index_frame_decoded_for_tiled = index;
+
+ sub_layer_info = vpu_read_reg(inst->dev, W5_RET_DEC_SUB_LAYER_INFO);
+ result->temporal_id = sub_layer_info & 0x7;
+
+ if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) {
+ result->decoded_poc = -1;
+ if (result->index_frame_decoded >= 0 ||
+ result->index_frame_decoded == DECODED_IDX_FLAG_SKIP)
+ result->decoded_poc = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_POC);
+ }
+
+ result->sequence_changed = vpu_read_reg(inst->dev, W5_RET_DEC_NOTIFICATION);
+ reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE);
+ result->dec_pic_width = reg_val >> 16;
+ result->dec_pic_height = reg_val & 0xffff;
+
+ if (result->sequence_changed) {
+ memcpy((void *)&p_dec_info->new_seq_info, (void *)&p_dec_info->initial_info,
+ sizeof(struct dec_initial_info));
+ wave5_get_dec_seq_result(inst, &p_dec_info->new_seq_info);
+ }
+
+ result->dec_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_DEC_HOST_CMD_TICK);
+ result->dec_decode_end_tick = vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_ENC_TICK);
+
+ if (!p_dec_info->first_cycle_check) {
+ result->frame_cycle =
+ (result->dec_decode_end_tick - result->dec_host_cmd_tick) *
+ p_dec_info->cycle_per_tick;
+ vpu_dev->last_performance_cycles = result->dec_decode_end_tick;
+ p_dec_info->first_cycle_check = true;
+ } else if (result->index_frame_decoded_for_tiled != -1) {
+ result->frame_cycle =
+ (result->dec_decode_end_tick - vpu_dev->last_performance_cycles) *
+ p_dec_info->cycle_per_tick;
+ vpu_dev->last_performance_cycles = result->dec_decode_end_tick;
+ if (vpu_dev->last_performance_cycles < result->dec_host_cmd_tick)
+ result->frame_cycle =
+ (result->dec_decode_end_tick - result->dec_host_cmd_tick) *
+ p_dec_info->cycle_per_tick;
+ }
+
+ /* no remaining command. reset frame cycle. */
+ if (p_dec_info->instance_queue_count == 0 && p_dec_info->report_queue_count == 0)
+ p_dec_info->first_cycle_check = false;
+
+ return 0;
+}
+
+int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size)
+{
+ struct vpu_buf *common_vb;
+ dma_addr_t code_base, temp_base;
+ dma_addr_t old_code_base, temp_size;
+ u32 code_size, reason_code;
+ u32 reg_val;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ common_vb = &vpu_dev->common_mem;
+
+ code_base = common_vb->daddr;
+ /* ALIGN TO 4KB */
+ code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
+ if (code_size < size * 2)
+ return -EINVAL;
+ temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET;
+ temp_size = WAVE5_TEMPBUF_SIZE;
+
+ old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR);
+
+ if (old_code_base != code_base + W5_REMAP_INDEX1 * W5_REMAP_MAX_SIZE) {
+ int ret;
+
+ ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size);
+ if (ret < 0) {
+ dev_err(vpu_dev->dev,
+ "VPU init, Writing firmware to common buffer, fail: %d\n", ret);
+ return ret;
+ }
+
+ vpu_write_reg(vpu_dev, W5_PO_CONF, 0);
+
+ ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT);
+ if (ret < 0) {
+ dev_err(vpu_dev->dev, "VPU init, Resetting the VPU, fail: %d\n", ret);
+ return ret;
+ }
+
+ remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
+ remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);
+
+ vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
+ vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
+ vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
+ vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base);
+ vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size);
+
+ /* These register must be reset explicitly */
+ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
+ vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
+
+ /* Encoder interrupt */
+ reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
+ reg_val |= BIT(INT_WAVE5_ENC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
+ /* Decoder interrupt */
+ reg_val |= BIT(INT_WAVE5_INIT_SEQ);
+ reg_val |= BIT(INT_WAVE5_DEC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
+ vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
+
+ reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
+ if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
+ reg_val = ((WAVE5_PROC_AXI_ID << 28) |
+ (WAVE5_PRP_AXI_ID << 24) |
+ (WAVE5_FBD_Y_AXI_ID << 20) |
+ (WAVE5_FBC_Y_AXI_ID << 16) |
+ (WAVE5_FBD_C_AXI_ID << 12) |
+ (WAVE5_FBC_C_AXI_ID << 8) |
+ (WAVE5_PRI_AXI_ID << 4) |
+ WAVE5_SEC_AXI_ID);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
+ }
+
+ vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
+ vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
+ vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
+
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
+ if (ret) {
+ dev_err(vpu_dev->dev, "VPU reinit(W5_VPU_REMAP_CORE_START) timeout\n");
+ return ret;
+ }
+
+ ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
+ if (ret)
+ return ret;
+ }
+
+ return setup_wave5_properties(dev);
+}
+
+static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code,
+ size_t size)
+{
+ u32 reg_val;
+ struct vpu_buf *common_vb;
+ dma_addr_t code_base;
+ u32 code_size, reason_code;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+ int ret;
+
+ if (i_sleep_wake) {
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
+ if (ret)
+ return ret;
+
+ /*
+ * Declare who has ownership for the host interface access
+ * 1 = VPU
+ * 0 = Host processor
+ */
+ vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
+ vpu_write_reg(vpu_dev, W5_COMMAND, W5_SLEEP_VPU);
+ /* Send an interrupt named HOST to the VPU */
+ vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);
+
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
+ if (ret)
+ return ret;
+ } else { /* restore */
+ common_vb = &vpu_dev->common_mem;
+
+ code_base = common_vb->daddr;
+ /* ALIGN TO 4KB */
+ code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
+ if (code_size < size * 2) {
+ dev_err(dev, "size too small\n");
+ return -EINVAL;
+ }
+
+ /* Power on without DEBUG mode */
+ vpu_write_reg(vpu_dev, W5_PO_CONF, 0);
+
+ remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
+ remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);
+
+ vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
+ vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
+ vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
+
+ /* These register must be reset explicitly */
+ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
+ vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
+
+ /* Encoder interrupt */
+ reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
+ reg_val |= BIT(INT_WAVE5_ENC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
+ /* Decoder interrupt */
+ reg_val |= BIT(INT_WAVE5_INIT_SEQ);
+ reg_val |= BIT(INT_WAVE5_DEC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
+ vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
+
+ reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
+ if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
+ reg_val = ((WAVE5_PROC_AXI_ID << 28) |
+ (WAVE5_PRP_AXI_ID << 24) |
+ (WAVE5_FBD_Y_AXI_ID << 20) |
+ (WAVE5_FBC_Y_AXI_ID << 16) |
+ (WAVE5_FBD_C_AXI_ID << 12) |
+ (WAVE5_FBC_C_AXI_ID << 8) |
+ (WAVE5_PRI_AXI_ID << 4) |
+ WAVE5_SEC_AXI_ID);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
+ }
+
+ vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
+ vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU);
+ /* Start VPU after settings */
+ vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
+
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
+ if (ret) {
+ dev_err(vpu_dev->dev, "VPU wakeup(W5_VPU_REMAP_CORE_START) timeout\n");
+ return ret;
+ }
+
+ return wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
+ }
+
+ return 0;
+}
+
+int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode)
+{
+ u32 val = 0;
+ int ret = 0;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+ struct vpu_attr *p_attr = &vpu_dev->attr;
+ /* VPU doesn't send response. force to set BUSY flag to 0. */
+ vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 0);
+
+ if (reset_mode == SW_RESET_SAFETY) {
+ ret = wave5_vpu_sleep_wake(dev, true, NULL, 0);
+ if (ret)
+ return ret;
+ }
+
+ val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
+ if ((val >> 16) & 0x1)
+ p_attr->support_backbone = true;
+ if ((val >> 22) & 0x1)
+ p_attr->support_vcore_backbone = true;
+ if ((val >> 28) & 0x1)
+ p_attr->support_vcpu_backbone = true;
+
+ /* waiting for completion of bus transaction */
+ if (p_attr->support_backbone) {
+ dev_dbg(dev, "%s: backbone supported\n", __func__);
+
+ if (p_attr->support_vcore_backbone) {
+ if (p_attr->support_vcpu_backbone) {
+ /* step1 : disable request */
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0xFF);
+
+ /* step2 : waiting for completion of bus transaction */
+ ret = wave5_wait_vcpu_bus_busy(vpu_dev,
+ W5_BACKBONE_BUS_STATUS_VCPU);
+ if (ret) {
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00);
+ return ret;
+ }
+ }
+ /* step1 : disable request */
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x7);
+
+ /* step2 : waiting for completion of bus transaction */
+ if (wave5_wait_bus_busy(vpu_dev, W5_BACKBONE_BUS_STATUS_VCORE0)) {
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00);
+ return -EBUSY;
+ }
+ } else {
+ /* step1 : disable request */
+ wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x7);
+
+ /* step2 : waiting for completion of bus transaction */
+ if (wave5_wait_bus_busy(vpu_dev, W5_COMBINED_BACKBONE_BUS_STATUS)) {
+ wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00);
+ return -EBUSY;
+ }
+ }
+ } else {
+ dev_dbg(dev, "%s: backbone NOT supported\n", __func__);
+ /* step1 : disable request */
+ wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x100);
+
+ /* step2 : waiting for completion of bus transaction */
+ ret = wave5_wait_bus_busy(vpu_dev, W5_GDI_BUS_STATUS);
+ if (ret) {
+ wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00);
+ return ret;
+ }
+ }
+
+ switch (reset_mode) {
+ case SW_RESET_ON_BOOT:
+ case SW_RESET_FORCE:
+ case SW_RESET_SAFETY:
+ val = W5_RST_BLOCK_ALL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val) {
+ vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, val);
+
+ ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_RESET_STATUS);
+ if (ret) {
+ vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0);
+ return ret;
+ }
+ vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0);
+ }
+ /* step3 : must clear GDI_BUS_CTRL after done SW_RESET */
+ if (p_attr->support_backbone) {
+ if (p_attr->support_vcore_backbone) {
+ if (p_attr->support_vcpu_backbone)
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00);
+ } else {
+ wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00);
+ }
+ } else {
+ wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00);
+ }
+ if (reset_mode == SW_RESET_SAFETY || reset_mode == SW_RESET_FORCE)
+ ret = wave5_vpu_sleep_wake(dev, false, NULL, 0);
+
+ return ret;
+}
+
+int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res)
+{
+ return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res);
+}
+
+int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+
+ p_dec_info->stream_endflag = eos ? 1 : 0;
+ vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
+ vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);
+
+ return send_firmware_command(inst, W5_UPDATE_BS, true, NULL, NULL);
+}
+
+int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret;
+
+ vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, BIT(index));
+ vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, 0);
+ ret = wave5_send_query(inst, UPDATE_DISP_FLAG);
+
+ if (ret) {
+ if (ret == -EIO) {
+ u32 reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
+
+ PRINT_REG_ERR(inst->dev, reg_val);
+ }
+ return ret;
+ }
+
+ p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC);
+
+ return 0;
+}
+
+int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index)
+{
+ int ret;
+
+ vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, 0);
+ vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, BIT(index));
+ ret = wave5_send_query(inst, UPDATE_DISP_FLAG);
+
+ if (ret) {
+ if (ret == -EIO) {
+ u32 reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
+
+ PRINT_REG_ERR(inst->dev, reg_val);
+ }
+ return ret;
+ }
+
+ return 0;
+}
+
+int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags)
+{
+ u32 interrupt_reason;
+
+ interrupt_reason = vpu_read_reg(inst->dev, W5_VPU_VINT_REASON_USR);
+ interrupt_reason &= ~flags;
+ vpu_write_reg(inst->dev, W5_VPU_VINT_REASON_USR, interrupt_reason);
+
+ return 0;
+}
+
+dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst)
+{
+ int ret;
+
+ ret = wave5_send_query(inst, GET_BS_RD_PTR);
+
+ if (ret)
+ return inst->codec_info->dec_info.stream_rd_ptr;
+
+ return vpu_read_reg(inst->dev, W5_RET_QUERY_DEC_BS_RD_PTR);
+}
+
+int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr)
+{
+ int ret;
+
+ vpu_write_reg(inst->dev, W5_RET_QUERY_DEC_SET_BS_RD_PTR, addr);
+
+ ret = wave5_send_query(inst, SET_BS_RD_PTR);
+
+ return ret;
+}
+
+/************************************************************************/
+/* ENCODER functions */
+/************************************************************************/
+
+int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst,
+ struct enc_open_param *open_param)
+{
+ int ret;
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ u32 reg_val;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+ dma_addr_t buffer_addr;
+ size_t buffer_size;
+
+ p_enc_info->cycle_per_tick = 256;
+ if (vpu_dev->sram_buf.size) {
+ p_enc_info->sec_axi_info.use_enc_rdo_enable = 1;
+ p_enc_info->sec_axi_info.use_enc_lf_enable = 1;
+ }
+
+ p_enc_info->vb_work.size = WAVE521ENC_WORKBUF_SIZE;
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &p_enc_info->vb_work);
+ if (ret) {
+ memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work));
+ return ret;
+ }
+
+ wave5_vdi_clear_memory(vpu_dev, &p_enc_info->vb_work);
+
+ vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_enc_info->vb_work.daddr);
+ vpu_write_reg(inst->dev, W5_WORK_SIZE, p_enc_info->vb_work.size);
+
+ vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
+ vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
+
+ reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN;
+ vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val);
+ vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
+ vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1));
+
+ /* This register must be reset explicitly */
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_VCORE_INFO, 1);
+
+ ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
+ if (ret)
+ goto free_vb_work;
+
+ buffer_addr = open_param->bitstream_buffer;
+ buffer_size = open_param->bitstream_buffer_size;
+ p_enc_info->stream_rd_ptr = buffer_addr;
+ p_enc_info->stream_wr_ptr = buffer_addr;
+ p_enc_info->line_buf_int_en = open_param->line_buf_int_en;
+ p_enc_info->stream_buf_start_addr = buffer_addr;
+ p_enc_info->stream_buf_size = buffer_size;
+ p_enc_info->stream_buf_end_addr = buffer_addr + buffer_size;
+ p_enc_info->stride = 0;
+ p_enc_info->initial_info_obtained = false;
+ p_enc_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER);
+
+ return 0;
+free_vb_work:
+ wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work);
+ return ret;
+}
+
+static void wave5_set_enc_crop_info(u32 codec, struct enc_wave_param *param, int rot_mode,
+ int src_width, int src_height)
+{
+ int aligned_width = (codec == W_HEVC_ENC) ? ALIGN(src_width, 32) : ALIGN(src_width, 16);
+ int aligned_height = (codec == W_HEVC_ENC) ? ALIGN(src_height, 32) : ALIGN(src_height, 16);
+ int pad_right, pad_bot;
+ int crop_right, crop_left, crop_top, crop_bot;
+ int prp_mode = rot_mode >> 1; /* remove prp_enable bit */
+
+ if (codec == W_HEVC_ENC &&
+ (!rot_mode || prp_mode == 14)) /* prp_mode 14 : hor_mir && ver_mir && rot_180 */
+ return;
+
+ pad_right = aligned_width - src_width;
+ pad_bot = aligned_height - src_height;
+
+ if (param->conf_win_right > 0)
+ crop_right = param->conf_win_right + pad_right;
+ else
+ crop_right = pad_right;
+
+ if (param->conf_win_bot > 0)
+ crop_bot = param->conf_win_bot + pad_bot;
+ else
+ crop_bot = pad_bot;
+
+ crop_top = param->conf_win_top;
+ crop_left = param->conf_win_left;
+
+ param->conf_win_top = crop_top;
+ param->conf_win_left = crop_left;
+ param->conf_win_bot = crop_bot;
+ param->conf_win_right = crop_right;
+
+ switch (prp_mode) {
+ case 1:
+ case 15:
+ param->conf_win_top = crop_right;
+ param->conf_win_left = crop_top;
+ param->conf_win_bot = crop_left;
+ param->conf_win_right = crop_bot;
+ break;
+ case 2:
+ case 12:
+ param->conf_win_top = crop_bot;
+ param->conf_win_left = crop_right;
+ param->conf_win_bot = crop_top;
+ param->conf_win_right = crop_left;
+ break;
+ case 3:
+ case 13:
+ param->conf_win_top = crop_left;
+ param->conf_win_left = crop_bot;
+ param->conf_win_bot = crop_right;
+ param->conf_win_right = crop_top;
+ break;
+ case 4:
+ case 10:
+ param->conf_win_top = crop_bot;
+ param->conf_win_bot = crop_top;
+ break;
+ case 8:
+ case 6:
+ param->conf_win_left = crop_right;
+ param->conf_win_right = crop_left;
+ break;
+ case 5:
+ case 11:
+ param->conf_win_top = crop_left;
+ param->conf_win_left = crop_top;
+ param->conf_win_bot = crop_right;
+ param->conf_win_right = crop_bot;
+ break;
+ case 7:
+ case 9:
+ param->conf_win_top = crop_right;
+ param->conf_win_left = crop_bot;
+ param->conf_win_bot = crop_left;
+ param->conf_win_right = crop_top;
+ break;
+ default:
+ WARN(1, "Invalid prp_mode: %d, must be in range of 1 - 15\n", prp_mode);
+ }
+}
+
+int wave5_vpu_enc_init_seq(struct vpu_instance *inst)
+{
+ u32 reg_val = 0, rot_mir_mode, fixed_cu_size_mode = 0x7;
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ struct enc_open_param *p_open_param = &p_enc_info->open_param;
+ struct enc_wave_param *p_param = &p_open_param->wave_param;
+
+ /*
+ * OPT_COMMON:
+ * the last SET_PARAM command should be called with OPT_COMMON
+ */
+ rot_mir_mode = 0;
+ if (p_enc_info->rotation_enable) {
+ switch (p_enc_info->rotation_angle) {
+ case 0:
+ rot_mir_mode |= NONE_ROTATE;
+ break;
+ case 90:
+ rot_mir_mode |= ROT_CLOCKWISE_90;
+ break;
+ case 180:
+ rot_mir_mode |= ROT_CLOCKWISE_180;
+ break;
+ case 270:
+ rot_mir_mode |= ROT_CLOCKWISE_270;
+ break;
+ }
+ }
+
+ if (p_enc_info->mirror_enable) {
+ switch (p_enc_info->mirror_direction) {
+ case MIRDIR_NONE:
+ rot_mir_mode |= NONE_ROTATE;
+ break;
+ case MIRDIR_VER:
+ rot_mir_mode |= MIR_VER_FLIP;
+ break;
+ case MIRDIR_HOR:
+ rot_mir_mode |= MIR_HOR_FLIP;
+ break;
+ case MIRDIR_HOR_VER:
+ rot_mir_mode |= MIR_HOR_VER_FLIP;
+ break;
+ }
+ }
+
+ wave5_set_enc_crop_info(inst->std, p_param, rot_mir_mode, p_open_param->pic_width,
+ p_open_param->pic_height);
+
+ /* SET_PARAM + COMMON */
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SET_PARAM_OPTION, OPT_COMMON);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SRC_SIZE, p_open_param->pic_height << 16
+ | p_open_param->pic_width);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN, VDI_LITTLE_ENDIAN);
+
+ reg_val = p_param->profile |
+ (p_param->level << 3) |
+ (p_param->internal_bit_depth << 14);
+ if (inst->std == W_HEVC_ENC)
+ reg_val |= (p_param->tier << 12) |
+ (p_param->tmvp_enable << 23) |
+ (p_param->sao_enable << 24) |
+ (p_param->skip_intra_trans << 25) |
+ (p_param->strong_intra_smooth_enable << 27) |
+ (p_param->en_still_picture << 30);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val);
+
+ reg_val = (p_param->lossless_enable) |
+ (p_param->const_intra_pred_flag << 1) |
+ (p_param->lf_cross_slice_boundary_enable << 2) |
+ (p_param->wpp_enable << 4) |
+ (p_param->disable_deblk << 5) |
+ ((p_param->beta_offset_div2 & 0xF) << 6) |
+ ((p_param->tc_offset_div2 & 0xF) << 10) |
+ ((p_param->chroma_cb_qp_offset & 0x1F) << 14) |
+ ((p_param->chroma_cr_qp_offset & 0x1F) << 19) |
+ (p_param->transform8x8_enable << 29) |
+ (p_param->entropy_coding_mode << 30);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_PPS_PARAM, reg_val);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_GOP_PARAM, p_param->gop_preset_idx);
+
+ if (inst->std == W_AVC_ENC)
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, p_param->intra_qp |
+ ((p_param->intra_period & 0x7ff) << 6) |
+ ((p_param->avc_idr_period & 0x7ff) << 17));
+ else if (inst->std == W_HEVC_ENC)
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM,
+ p_param->decoding_refresh_type | (p_param->intra_qp << 3) |
+ (p_param->intra_period << 16));
+
+ reg_val = (p_param->rdo_skip << 2) |
+ (p_param->lambda_scaling_enable << 3) |
+ (fixed_cu_size_mode << 5) |
+ (p_param->intra_nx_n_enable << 8) |
+ (p_param->max_num_merge << 18);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RDO_PARAM, reg_val);
+
+ if (inst->std == W_AVC_ENC)
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH,
+ p_param->intra_mb_refresh_arg << 16 | p_param->intra_mb_refresh_mode);
+ else if (inst->std == W_HEVC_ENC)
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH,
+ p_param->intra_refresh_arg << 16 | p_param->intra_refresh_mode);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_FRAME_RATE, p_open_param->frame_rate_info);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_TARGET_RATE, p_open_param->bit_rate);
+
+ reg_val = p_open_param->rc_enable |
+ (p_param->hvs_qp_enable << 2) |
+ (p_param->hvs_qp_scale << 4) |
+ ((p_param->initial_rc_qp & 0x3F) << 14) |
+ (p_open_param->vbv_buffer_size << 20);
+ if (inst->std == W_AVC_ENC)
+ reg_val |= (p_param->mb_level_rc_enable << 1);
+ else if (inst->std == W_HEVC_ENC)
+ reg_val |= (p_param->cu_level_rc_enable << 1);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_PARAM, 0);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM,
+ p_param->rc_weight_buf << 8 | p_param->rc_weight_param);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_MIN_MAX_QP, p_param->min_qp_i |
+ (p_param->max_qp_i << 6) | (p_param->hvs_max_delta_qp << 12));
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP, p_param->min_qp_p |
+ (p_param->max_qp_p << 6) | (p_param->min_qp_b << 12) |
+ (p_param->max_qp_b << 18));
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT,
+ p_param->conf_win_bot << 16 | p_param->conf_win_top);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT,
+ p_param->conf_win_right << 16 | p_param->conf_win_left);
+
+ if (inst->std == W_AVC_ENC)
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE,
+ p_param->avc_slice_arg << 16 | p_param->avc_slice_mode);
+ else if (inst->std == W_HEVC_ENC)
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE,
+ p_param->independ_slice_mode_arg << 16 |
+ p_param->independ_slice_mode);
+
+ if (inst->std == W_HEVC_ENC) {
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_DEPENDENT_SLICE,
+ p_param->depend_slice_mode_arg << 16 | p_param->depend_slice_mode);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_PARAM, 0);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_WEIGHT,
+ p_param->nr_intra_weight_y |
+ (p_param->nr_intra_weight_cb << 5) |
+ (p_param->nr_intra_weight_cr << 10) |
+ (p_param->nr_inter_weight_y << 15) |
+ (p_param->nr_inter_weight_cb << 20) |
+ (p_param->nr_inter_weight_cr << 25));
+ }
+
+ return send_firmware_command(inst, W5_ENC_SET_PARAM, true, NULL, NULL);
+}
+
+int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info)
+{
+ int ret;
+ u32 reg_val;
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+
+ /* send QUERY cmd */
+ ret = wave5_send_query(inst, GET_RESULT);
+ if (ret) {
+ if (ret == -EIO) {
+ reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
+ PRINT_REG_ERR(inst->dev, reg_val);
+ }
+ return ret;
+ }
+
+ dev_dbg(inst->dev->dev, "%s: init seq\n", __func__);
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
+
+ p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff;
+ p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+
+ if (vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS) != 1) {
+ info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO);
+ ret = -EIO;
+ } else {
+ info->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO);
+ }
+
+ info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_ENC_NUM_REQUIRED_FB);
+ info->min_src_frame_count = vpu_read_reg(inst->dev, W5_RET_ENC_MIN_SRC_BUF_NUM);
+ info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
+ info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
+ p_enc_info->vlc_buf_size = info->vlc_buf_size;
+ p_enc_info->param_buf_size = info->param_buf_size;
+
+ return ret;
+}
+
+static u32 calculate_luma_stride(u32 width, u32 bit_depth)
+{
+ return ALIGN(ALIGN(width, 16) * ((bit_depth > 8) ? 5 : 4), 32);
+}
+
+static u32 calculate_chroma_stride(u32 width, u32 bit_depth)
+{
+ return ALIGN(ALIGN(width / 2, 16) * ((bit_depth > 8) ? 5 : 4), 32);
+}
+
+int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst,
+ struct frame_buffer *fb_arr, enum tiled_map_type map_type,
+ unsigned int count)
+{
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+ int ret = 0;
+ u32 stride;
+ u32 start_no, end_no;
+ size_t remain, idx, j, i, cnt_8_chunk;
+ u32 reg_val = 0, pic_size = 0, mv_col_size, fbc_y_tbl_size, fbc_c_tbl_size;
+ u32 sub_sampled_size = 0;
+ u32 luma_stride, chroma_stride;
+ u32 buf_height = 0, buf_width = 0;
+ u32 bit_depth;
+ bool avc_encoding = (inst->std == W_AVC_ENC);
+ struct vpu_buf vb_mv = {0};
+ struct vpu_buf vb_fbc_y_tbl = {0};
+ struct vpu_buf vb_fbc_c_tbl = {0};
+ struct vpu_buf vb_sub_sam_buf = {0};
+ struct vpu_buf vb_task = {0};
+ struct enc_open_param *p_open_param;
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+
+ p_open_param = &p_enc_info->open_param;
+ mv_col_size = 0;
+ fbc_y_tbl_size = 0;
+ fbc_c_tbl_size = 0;
+ stride = p_enc_info->stride;
+ bit_depth = p_open_param->wave_param.internal_bit_depth;
+
+ if (avc_encoding) {
+ buf_width = ALIGN(p_open_param->pic_width, 16);
+ buf_height = ALIGN(p_open_param->pic_height, 16);
+
+ if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
+ !(p_enc_info->rotation_angle == 180 &&
+ p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
+ buf_width = ALIGN(p_open_param->pic_width, 16);
+ buf_height = ALIGN(p_open_param->pic_height, 16);
+ }
+
+ if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) {
+ buf_width = ALIGN(p_open_param->pic_height, 16);
+ buf_height = ALIGN(p_open_param->pic_width, 16);
+ }
+ } else {
+ buf_width = ALIGN(p_open_param->pic_width, 8);
+ buf_height = ALIGN(p_open_param->pic_height, 8);
+
+ if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
+ !(p_enc_info->rotation_angle == 180 &&
+ p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
+ buf_width = ALIGN(p_open_param->pic_width, 32);
+ buf_height = ALIGN(p_open_param->pic_height, 32);
+ }
+
+ if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) {
+ buf_width = ALIGN(p_open_param->pic_height, 32);
+ buf_height = ALIGN(p_open_param->pic_width, 32);
+ }
+ }
+
+ pic_size = (buf_width << 16) | buf_height;
+
+ if (avc_encoding) {
+ mv_col_size = WAVE5_ENC_AVC_BUF_SIZE(buf_width, buf_height);
+ vb_mv.daddr = 0;
+ vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
+ } else {
+ mv_col_size = WAVE5_ENC_HEVC_BUF_SIZE(buf_width, buf_height);
+ mv_col_size = ALIGN(mv_col_size, 16);
+ vb_mv.daddr = 0;
+ vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
+ }
+
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_mv);
+ if (ret)
+ return ret;
+
+ p_enc_info->vb_mv = vb_mv;
+
+ fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(buf_width, buf_height), 16);
+ fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(buf_width, buf_height), 16);
+
+ vb_fbc_y_tbl.daddr = 0;
+ vb_fbc_y_tbl.size = ALIGN(fbc_y_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_y_tbl);
+ if (ret)
+ goto free_vb_fbc_y_tbl;
+
+ p_enc_info->vb_fbc_y_tbl = vb_fbc_y_tbl;
+
+ vb_fbc_c_tbl.daddr = 0;
+ vb_fbc_c_tbl.size = ALIGN(fbc_c_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_c_tbl);
+ if (ret)
+ goto free_vb_fbc_c_tbl;
+
+ p_enc_info->vb_fbc_c_tbl = vb_fbc_c_tbl;
+
+ if (avc_encoding)
+ sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE_AVC(buf_width, buf_height);
+ else
+ sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE(buf_width, buf_height);
+ vb_sub_sam_buf.size = ALIGN(sub_sampled_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
+ vb_sub_sam_buf.daddr = 0;
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_sub_sam_buf);
+ if (ret)
+ goto free_vb_sam_buf;
+
+ p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf;
+
+ vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) +
+ (p_enc_info->param_buf_size * COMMAND_QUEUE_DEPTH);
+ vb_task.daddr = 0;
+ if (p_enc_info->vb_task.size == 0) {
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task);
+ if (ret)
+ goto free_vb_task;
+
+ p_enc_info->vb_task = vb_task;
+
+ vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
+ p_enc_info->vb_task.daddr);
+ vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_task.size);
+ }
+
+ /* set sub-sampled buffer base addr */
+ vpu_write_reg(inst->dev, W5_ADDR_SUB_SAMPLED_FB_BASE, vb_sub_sam_buf.daddr);
+ /* set sub-sampled buffer size for one frame */
+ vpu_write_reg(inst->dev, W5_SUB_SAMPLED_ONE_FB_SIZE, sub_sampled_size);
+
+ vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size);
+
+ /* set stride of luma/chroma for compressed buffer */
+ if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
+ !(p_enc_info->rotation_angle == 180 &&
+ p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
+ luma_stride = calculate_luma_stride(buf_width, bit_depth);
+ chroma_stride = calculate_chroma_stride(buf_width / 2, bit_depth);
+ } else {
+ luma_stride = calculate_luma_stride(p_open_param->pic_width, bit_depth);
+ chroma_stride = calculate_chroma_stride(p_open_param->pic_width / 2, bit_depth);
+ }
+
+ vpu_write_reg(inst->dev, W5_FBC_STRIDE, luma_stride << 16 | chroma_stride);
+ vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, stride);
+
+ remain = count;
+ cnt_8_chunk = DIV_ROUND_UP(count, 8);
+ idx = 0;
+ for (j = 0; j < cnt_8_chunk; j++) {
+ reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3);
+ vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val);
+ start_no = j * 8;
+ end_no = start_no + ((remain >= 8) ? 8 : remain) - 1;
+
+ vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no);
+
+ for (i = 0; i < 8 && i < remain; i++) {
+ vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), fb_arr[i +
+ start_no].buf_y);
+ vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4),
+ fb_arr[i + start_no].buf_cb);
+ /* luma FBC offset table */
+ vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4),
+ vb_fbc_y_tbl.daddr + idx * fbc_y_tbl_size);
+ /* chroma FBC offset table */
+ vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4),
+ vb_fbc_c_tbl.daddr + idx * fbc_c_tbl_size);
+
+ vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2),
+ vb_mv.daddr + idx * mv_col_size);
+ idx++;
+ }
+ remain -= i;
+
+ ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL);
+ if (ret)
+ goto free_vb_mem;
+ }
+
+ ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
+ if (ret)
+ goto free_vb_mem;
+
+ return ret;
+
+free_vb_mem:
+ wave5_vdi_free_dma_memory(vpu_dev, &vb_task);
+free_vb_task:
+ wave5_vdi_free_dma_memory(vpu_dev, &vb_sub_sam_buf);
+free_vb_sam_buf:
+ wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_c_tbl);
+free_vb_fbc_c_tbl:
+ wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_y_tbl);
+free_vb_fbc_y_tbl:
+ wave5_vdi_free_dma_memory(vpu_dev, &vb_mv);
+ return ret;
+}
+
+int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res)
+{
+ u32 src_frame_format;
+ u32 reg_val = 0;
+ u32 src_stride_c = 0;
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ struct frame_buffer *p_src_frame = option->source_frame;
+ struct enc_open_param *p_open_param = &p_enc_info->open_param;
+ bool justified = WTL_RIGHT_JUSTIFIED;
+ u32 format_no = WTL_PIXEL_8BIT;
+ int ret;
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_BS_START_ADDR, option->pic_stream_buffer_addr);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_BS_SIZE, option->pic_stream_buffer_size);
+ p_enc_info->stream_buf_start_addr = option->pic_stream_buffer_addr;
+ p_enc_info->stream_buf_size = option->pic_stream_buffer_size;
+ p_enc_info->stream_buf_end_addr =
+ option->pic_stream_buffer_addr + option->pic_stream_buffer_size;
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_AXI_SEL, DEFAULT_SRC_AXI);
+ /* secondary AXI */
+ reg_val = (p_enc_info->sec_axi_info.use_enc_rdo_enable << 11) |
+ (p_enc_info->sec_axi_info.use_enc_lf_enable << 15);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_USE_SEC_AXI, reg_val);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_REPORT_PARAM, 0);
+
+ /*
+ * CODEOPT_ENC_VCL is used to implicitly encode header/headers to generate bitstream.
+ * (use ENC_PUT_VIDEO_HEADER for give_command to encode only a header)
+ */
+ if (option->code_option.implicit_header_encode)
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION,
+ CODEOPT_ENC_HEADER_IMPLICIT | CODEOPT_ENC_VCL |
+ (option->code_option.encode_aud << 5) |
+ (option->code_option.encode_eos << 6) |
+ (option->code_option.encode_eob << 7));
+ else
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION,
+ option->code_option.implicit_header_encode |
+ (option->code_option.encode_vcl << 1) |
+ (option->code_option.encode_vps << 2) |
+ (option->code_option.encode_sps << 3) |
+ (option->code_option.encode_pps << 4) |
+ (option->code_option.encode_aud << 5) |
+ (option->code_option.encode_eos << 6) |
+ (option->code_option.encode_eob << 7));
+
+ if (option->src_end_flag)
+ /* no more source images. */
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, 0xFFFFFFFF);
+ else
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, option->src_idx);
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_Y, p_src_frame->buf_y);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_U, p_src_frame->buf_cb);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_V, p_src_frame->buf_cr);
+
+ switch (p_open_param->src_format) {
+ case FORMAT_420:
+ case FORMAT_422:
+ case FORMAT_YUYV:
+ case FORMAT_YVYU:
+ case FORMAT_UYVY:
+ case FORMAT_VYUY:
+ justified = WTL_LEFT_JUSTIFIED;
+ format_no = WTL_PIXEL_8BIT;
+ src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
+ (p_src_frame->stride / 2);
+ src_stride_c = (p_open_param->src_format == FORMAT_422) ? src_stride_c * 2 :
+ src_stride_c;
+ break;
+ case FORMAT_420_P10_16BIT_MSB:
+ case FORMAT_422_P10_16BIT_MSB:
+ case FORMAT_YUYV_P10_16BIT_MSB:
+ case FORMAT_YVYU_P10_16BIT_MSB:
+ case FORMAT_UYVY_P10_16BIT_MSB:
+ case FORMAT_VYUY_P10_16BIT_MSB:
+ justified = WTL_RIGHT_JUSTIFIED;
+ format_no = WTL_PIXEL_16BIT;
+ src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
+ (p_src_frame->stride / 2);
+ src_stride_c = (p_open_param->src_format ==
+ FORMAT_422_P10_16BIT_MSB) ? src_stride_c * 2 : src_stride_c;
+ break;
+ case FORMAT_420_P10_16BIT_LSB:
+ case FORMAT_422_P10_16BIT_LSB:
+ case FORMAT_YUYV_P10_16BIT_LSB:
+ case FORMAT_YVYU_P10_16BIT_LSB:
+ case FORMAT_UYVY_P10_16BIT_LSB:
+ case FORMAT_VYUY_P10_16BIT_LSB:
+ justified = WTL_LEFT_JUSTIFIED;
+ format_no = WTL_PIXEL_16BIT;
+ src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
+ (p_src_frame->stride / 2);
+ src_stride_c = (p_open_param->src_format ==
+ FORMAT_422_P10_16BIT_LSB) ? src_stride_c * 2 : src_stride_c;
+ break;
+ case FORMAT_420_P10_32BIT_MSB:
+ case FORMAT_422_P10_32BIT_MSB:
+ case FORMAT_YUYV_P10_32BIT_MSB:
+ case FORMAT_YVYU_P10_32BIT_MSB:
+ case FORMAT_UYVY_P10_32BIT_MSB:
+ case FORMAT_VYUY_P10_32BIT_MSB:
+ justified = WTL_RIGHT_JUSTIFIED;
+ format_no = WTL_PIXEL_32BIT;
+ src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
+ ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave);
+ src_stride_c = (p_open_param->src_format ==
+ FORMAT_422_P10_32BIT_MSB) ? src_stride_c * 2 : src_stride_c;
+ break;
+ case FORMAT_420_P10_32BIT_LSB:
+ case FORMAT_422_P10_32BIT_LSB:
+ case FORMAT_YUYV_P10_32BIT_LSB:
+ case FORMAT_YVYU_P10_32BIT_LSB:
+ case FORMAT_UYVY_P10_32BIT_LSB:
+ case FORMAT_VYUY_P10_32BIT_LSB:
+ justified = WTL_LEFT_JUSTIFIED;
+ format_no = WTL_PIXEL_32BIT;
+ src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
+ ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave);
+ src_stride_c = (p_open_param->src_format ==
+ FORMAT_422_P10_32BIT_LSB) ? src_stride_c * 2 : src_stride_c;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ src_frame_format = (inst->cbcr_interleave << 1) | (inst->nv21);
+ switch (p_open_param->packed_format) {
+ case PACKED_YUYV:
+ src_frame_format = 4;
+ break;
+ case PACKED_YVYU:
+ src_frame_format = 5;
+ break;
+ case PACKED_UYVY:
+ src_frame_format = 6;
+ break;
+ case PACKED_VYUY:
+ src_frame_format = 7;
+ break;
+ default:
+ break;
+ }
+
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_STRIDE,
+ (p_src_frame->stride << 16) | src_stride_c);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_FORMAT, src_frame_format |
+ (format_no << 3) | (justified << 5) | (PIC_SRC_ENDIANNESS_BIG_ENDIAN << 6));
+
+ ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, &reg_val, fail_res);
+ if (ret == -ETIMEDOUT)
+ return ret;
+
+ p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff;
+ p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result)
+{
+ int ret;
+ u32 encoding_success;
+ u32 reg_val;
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ ret = wave5_send_query(inst, GET_RESULT);
+ if (ret) {
+ if (ret == -EIO) {
+ reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
+ PRINT_REG_ERR(inst->dev, reg_val);
+ }
+ return ret;
+ }
+ dev_dbg(inst->dev->dev, "%s: enc pic complete\n", __func__);
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
+
+ p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff;
+ p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
+
+ encoding_success = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS);
+ if (!encoding_success) {
+ result->error_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO);
+ return -EIO;
+ }
+
+ result->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO);
+
+ reg_val = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_TYPE);
+ result->pic_type = reg_val & 0xFFFF;
+
+ result->enc_vcl_nut = vpu_read_reg(inst->dev, W5_RET_ENC_VCL_NUT);
+ /*
+ * To get the reconstructed frame use the following index on
+ * inst->frame_buf
+ */
+ result->recon_frame_index = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_IDX);
+ result->enc_pic_byte = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_BYTE);
+ result->enc_src_idx = vpu_read_reg(inst->dev, W5_RET_ENC_USED_SRC_IDX);
+ p_enc_info->stream_wr_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_WR_PTR);
+ p_enc_info->stream_rd_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR);
+
+ result->bitstream_buffer = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR);
+ result->rd_ptr = p_enc_info->stream_rd_ptr;
+ result->wr_ptr = p_enc_info->stream_wr_ptr;
+
+ /*result for header only(no vcl) encoding */
+ if (result->recon_frame_index == RECON_IDX_FLAG_HEADER_ONLY)
+ result->bitstream_size = result->enc_pic_byte;
+ else if (result->recon_frame_index < 0)
+ result->bitstream_size = 0;
+ else
+ result->bitstream_size = result->enc_pic_byte;
+
+ result->enc_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_ENC_HOST_CMD_TICK);
+ result->enc_encode_end_tick = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_END_TICK);
+
+ if (!p_enc_info->first_cycle_check) {
+ result->frame_cycle = (result->enc_encode_end_tick - result->enc_host_cmd_tick) *
+ p_enc_info->cycle_per_tick;
+ p_enc_info->first_cycle_check = true;
+ } else {
+ result->frame_cycle =
+ (result->enc_encode_end_tick - vpu_dev->last_performance_cycles) *
+ p_enc_info->cycle_per_tick;
+ if (vpu_dev->last_performance_cycles < result->enc_host_cmd_tick)
+ result->frame_cycle = (result->enc_encode_end_tick -
+ result->enc_host_cmd_tick) * p_enc_info->cycle_per_tick;
+ }
+ vpu_dev->last_performance_cycles = result->enc_encode_end_tick;
+
+ return 0;
+}
+
+int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res)
+{
+ return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res);
+}
+
+static bool wave5_vpu_enc_check_common_param_valid(struct vpu_instance *inst,
+ struct enc_open_param *open_param)
+{
+ bool low_delay = true;
+ struct enc_wave_param *param = &open_param->wave_param;
+ struct vpu_device *vpu_dev = inst->dev;
+ struct device *dev = vpu_dev->dev;
+ u32 num_ctu_row = (open_param->pic_height + 64 - 1) / 64;
+ u32 num_ctu_col = (open_param->pic_width + 64 - 1) / 64;
+ u32 ctu_sz = num_ctu_col * num_ctu_row;
+
+ if (inst->std == W_HEVC_ENC && low_delay &&
+ param->decoding_refresh_type == DEC_REFRESH_TYPE_CRA) {
+ dev_warn(dev,
+ "dec_refresh_type(CRA) shouldn't be used together with low delay GOP\n");
+ dev_warn(dev, "Suggested configuration parameter: decoding refresh type (IDR)\n");
+ param->decoding_refresh_type = 2;
+ }
+
+ if (param->wpp_enable && param->independ_slice_mode) {
+ unsigned int num_ctb_in_width = ALIGN(open_param->pic_width, 64) >> 6;
+
+ if (param->independ_slice_mode_arg % num_ctb_in_width) {
+ dev_err(dev, "independ_slice_mode_arg %u must be a multiple of %u\n",
+ param->independ_slice_mode_arg, num_ctb_in_width);
+ return false;
+ }
+ }
+
+ /* multi-slice & wpp */
+ if (param->wpp_enable && param->depend_slice_mode) {
+ dev_err(dev, "wpp_enable && depend_slice_mode cannot be used simultaneously\n");
+ return false;
+ }
+
+ if (!param->independ_slice_mode && param->depend_slice_mode) {
+ dev_err(dev, "depend_slice_mode requires independ_slice_mode\n");
+ return false;
+ } else if (param->independ_slice_mode &&
+ param->depend_slice_mode == DEPEND_SLICE_MODE_RECOMMENDED &&
+ param->independ_slice_mode_arg < param->depend_slice_mode_arg) {
+ dev_err(dev, "independ_slice_mode_arg: %u must be smaller than %u\n",
+ param->independ_slice_mode_arg, param->depend_slice_mode_arg);
+ return false;
+ }
+
+ if (param->independ_slice_mode && param->independ_slice_mode_arg > 65535) {
+ dev_err(dev, "independ_slice_mode_arg: %u must be smaller than 65535\n",
+ param->independ_slice_mode_arg);
+ return false;
+ }
+
+ if (param->depend_slice_mode && param->depend_slice_mode_arg > 65535) {
+ dev_err(dev, "depend_slice_mode_arg: %u must be smaller than 65535\n",
+ param->depend_slice_mode_arg);
+ return false;
+ }
+
+ if (param->conf_win_top % 2) {
+ dev_err(dev, "conf_win_top: %u, must be a multiple of 2\n", param->conf_win_top);
+ return false;
+ }
+
+ if (param->conf_win_bot % 2) {
+ dev_err(dev, "conf_win_bot: %u, must be a multiple of 2\n", param->conf_win_bot);
+ return false;
+ }
+
+ if (param->conf_win_left % 2) {
+ dev_err(dev, "conf_win_left: %u, must be a multiple of 2\n", param->conf_win_left);
+ return false;
+ }
+
+ if (param->conf_win_right % 2) {
+ dev_err(dev, "conf_win_right: %u, Must be a multiple of 2\n",
+ param->conf_win_right);
+ return false;
+ }
+
+ if (param->lossless_enable && open_param->rc_enable) {
+ dev_err(dev, "option rate_control cannot be used with lossless_coding\n");
+ return false;
+ }
+
+ if (param->lossless_enable && !param->skip_intra_trans) {
+ dev_err(dev, "option intra_trans_skip must be enabled with lossless_coding\n");
+ return false;
+ }
+
+ /* intra refresh */
+ if (param->intra_refresh_mode && param->intra_refresh_arg == 0) {
+ dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u must be > 0\n",
+ param->intra_refresh_mode, param->intra_refresh_arg);
+ return false;
+ }
+ switch (param->intra_refresh_mode) {
+ case REFRESH_MODE_CTU_ROWS:
+ if (param->intra_mb_refresh_arg > num_ctu_row)
+ goto invalid_refresh_argument;
+ break;
+ case REFRESH_MODE_CTU_COLUMNS:
+ if (param->intra_refresh_arg > num_ctu_col)
+ goto invalid_refresh_argument;
+ break;
+ case REFRESH_MODE_CTU_STEP_SIZE:
+ if (param->intra_refresh_arg > ctu_sz)
+ goto invalid_refresh_argument;
+ break;
+ case REFRESH_MODE_CTUS:
+ if (param->intra_refresh_arg > ctu_sz)
+ goto invalid_refresh_argument;
+ if (param->lossless_enable) {
+ dev_err(dev, "mode: %u cannot be used lossless_enable",
+ param->intra_refresh_mode);
+ return false;
+ }
+ };
+ return true;
+
+invalid_refresh_argument:
+ dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u > W(%u)xH(%u)\n",
+ param->intra_refresh_mode, param->intra_refresh_arg,
+ num_ctu_row, num_ctu_col);
+ return false;
+}
+
+static bool wave5_vpu_enc_check_param_valid(struct vpu_device *vpu_dev,
+ struct enc_open_param *open_param)
+{
+ struct enc_wave_param *param = &open_param->wave_param;
+
+ if (open_param->rc_enable) {
+ if (param->min_qp_i > param->max_qp_i || param->min_qp_p > param->max_qp_p ||
+ param->min_qp_b > param->max_qp_b) {
+ dev_err(vpu_dev->dev, "Configuration failed because min_qp is greater than max_qp\n");
+ dev_err(vpu_dev->dev, "Suggested configuration parameters: min_qp = max_qp\n");
+ return false;
+ }
+
+ if (open_param->bit_rate <= (int)open_param->frame_rate_info) {
+ dev_err(vpu_dev->dev,
+ "enc_bit_rate: %u must be greater than the frame_rate: %u\n",
+ open_param->bit_rate, (int)open_param->frame_rate_info);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param)
+{
+ u32 pic_width;
+ u32 pic_height;
+ s32 product_id = inst->dev->product;
+ struct vpu_attr *p_attr = &inst->dev->attr;
+ struct enc_wave_param *param;
+
+ if (!open_param)
+ return -EINVAL;
+
+ param = &open_param->wave_param;
+ pic_width = open_param->pic_width;
+ pic_height = open_param->pic_height;
+
+ if (inst->id >= MAX_NUM_INSTANCE) {
+ dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n",
+ inst->id, MAX_NUM_INSTANCE);
+ return -EOPNOTSUPP;
+ }
+
+ if (inst->std != W_HEVC_ENC &&
+ !(inst->std == W_AVC_ENC && product_id == PRODUCT_ID_521)) {
+ dev_err(inst->dev->dev, "Unsupported encoder-codec & product combination\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (param->internal_bit_depth == 10) {
+ if (inst->std == W_HEVC_ENC && !p_attr->support_hevc10bit_enc) {
+ dev_err(inst->dev->dev,
+ "Flag support_hevc10bit_enc must be set to encode 10bit HEVC\n");
+ return -EOPNOTSUPP;
+ } else if (inst->std == W_AVC_ENC && !p_attr->support_avc10bit_enc) {
+ dev_err(inst->dev->dev,
+ "Flag support_avc10bit_enc must be set to encode 10bit AVC\n");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if (!open_param->frame_rate_info) {
+ dev_err(inst->dev->dev, "No frame rate information.\n");
+ return -EINVAL;
+ }
+
+ if (open_param->bit_rate > MAX_BIT_RATE) {
+ dev_err(inst->dev->dev, "Invalid encoding bit-rate: %u (valid: 0-%u)\n",
+ open_param->bit_rate, MAX_BIT_RATE);
+ return -EINVAL;
+ }
+
+ if (pic_width < W5_MIN_ENC_PIC_WIDTH || pic_width > W5_MAX_ENC_PIC_WIDTH ||
+ pic_height < W5_MIN_ENC_PIC_HEIGHT || pic_height > W5_MAX_ENC_PIC_HEIGHT) {
+ dev_err(inst->dev->dev, "Invalid encoding dimension: %ux%u\n",
+ pic_width, pic_height);
+ return -EINVAL;
+ }
+
+ if (param->profile) {
+ if (inst->std == W_HEVC_ENC) {
+ if ((param->profile != HEVC_PROFILE_MAIN ||
+ (param->profile == HEVC_PROFILE_MAIN &&
+ param->internal_bit_depth > 8)) &&
+ (param->profile != HEVC_PROFILE_MAIN10 ||
+ (param->profile == HEVC_PROFILE_MAIN10 &&
+ param->internal_bit_depth < 10)) &&
+ param->profile != HEVC_PROFILE_STILLPICTURE) {
+ dev_err(inst->dev->dev,
+ "Invalid HEVC encoding profile: %u (bit-depth: %u)\n",
+ param->profile, param->internal_bit_depth);
+ return -EINVAL;
+ }
+ } else if (inst->std == W_AVC_ENC) {
+ if ((param->internal_bit_depth > 8 &&
+ param->profile != H264_PROFILE_HIGH10)) {
+ dev_err(inst->dev->dev,
+ "Invalid AVC encoding profile: %u (bit-depth: %u)\n",
+ param->profile, param->internal_bit_depth);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (param->decoding_refresh_type > DEC_REFRESH_TYPE_IDR) {
+ dev_err(inst->dev->dev, "Invalid decoding refresh type: %u (valid: 0-2)\n",
+ param->decoding_refresh_type);
+ return -EINVAL;
+ }
+
+ if (param->intra_refresh_mode > REFRESH_MODE_CTUS) {
+ dev_err(inst->dev->dev, "Invalid intra refresh mode: %d (valid: 0-4)\n",
+ param->intra_refresh_mode);
+ return -EINVAL;
+ }
+
+ if (inst->std == W_HEVC_ENC && param->independ_slice_mode &&
+ param->depend_slice_mode > DEPEND_SLICE_MODE_BOOST) {
+ dev_err(inst->dev->dev,
+ "Can't combine slice modes: independent and fast dependent for HEVC\n");
+ return -EINVAL;
+ }
+
+ if (!param->disable_deblk) {
+ if (param->beta_offset_div2 < -6 || param->beta_offset_div2 > 6) {
+ dev_err(inst->dev->dev, "Invalid beta offset: %d (valid: -6-6)\n",
+ param->beta_offset_div2);
+ return -EINVAL;
+ }
+
+ if (param->tc_offset_div2 < -6 || param->tc_offset_div2 > 6) {
+ dev_err(inst->dev->dev, "Invalid tc offset: %d (valid: -6-6)\n",
+ param->tc_offset_div2);
+ return -EINVAL;
+ }
+ }
+
+ if (param->intra_qp > MAX_INTRA_QP) {
+ dev_err(inst->dev->dev,
+ "Invalid intra quantization parameter: %u (valid: 0-%u)\n",
+ param->intra_qp, MAX_INTRA_QP);
+ return -EINVAL;
+ }
+
+ if (open_param->rc_enable) {
+ if (param->min_qp_i > MAX_INTRA_QP || param->max_qp_i > MAX_INTRA_QP ||
+ param->min_qp_p > MAX_INTRA_QP || param->max_qp_p > MAX_INTRA_QP ||
+ param->min_qp_b > MAX_INTRA_QP || param->max_qp_b > MAX_INTRA_QP) {
+ dev_err(inst->dev->dev,
+ "Invalid quantization parameter min/max values: "
+ "I: %u-%u, P: %u-%u, B: %u-%u (valid for each: 0-%u)\n",
+ param->min_qp_i, param->max_qp_i, param->min_qp_p, param->max_qp_p,
+ param->min_qp_b, param->max_qp_b, MAX_INTRA_QP);
+ return -EINVAL;
+ }
+
+ if (param->hvs_qp_enable && param->hvs_max_delta_qp > MAX_HVS_MAX_DELTA_QP) {
+ dev_err(inst->dev->dev,
+ "Invalid HVS max delta quantization parameter: %u (valid: 0-%u)\n",
+ param->hvs_max_delta_qp, MAX_HVS_MAX_DELTA_QP);
+ return -EINVAL;
+ }
+
+ if (open_param->vbv_buffer_size < MIN_VBV_BUFFER_SIZE ||
+ open_param->vbv_buffer_size > MAX_VBV_BUFFER_SIZE) {
+ dev_err(inst->dev->dev, "VBV buffer size: %u (valid: %u-%u)\n",
+ open_param->vbv_buffer_size, MIN_VBV_BUFFER_SIZE,
+ MAX_VBV_BUFFER_SIZE);
+ return -EINVAL;
+ }
+ }
+
+ if (!wave5_vpu_enc_check_common_param_valid(inst, open_param))
+ return -EINVAL;
+
+ if (!wave5_vpu_enc_check_param_valid(inst->dev, open_param))
+ return -EINVAL;
+
+ if (param->chroma_cb_qp_offset < -12 || param->chroma_cb_qp_offset > 12) {
+ dev_err(inst->dev->dev,
+ "Invalid chroma Cb quantization parameter offset: %d (valid: -12-12)\n",
+ param->chroma_cb_qp_offset);
+ return -EINVAL;
+ }
+
+ if (param->chroma_cr_qp_offset < -12 || param->chroma_cr_qp_offset > 12) {
+ dev_err(inst->dev->dev,
+ "Invalid chroma Cr quantization parameter offset: %d (valid: -12-12)\n",
+ param->chroma_cr_qp_offset);
+ return -EINVAL;
+ }
+
+ if (param->intra_refresh_mode == REFRESH_MODE_CTU_STEP_SIZE && !param->intra_refresh_arg) {
+ dev_err(inst->dev->dev,
+ "Intra refresh mode CTU step-size requires an argument\n");
+ return -EINVAL;
+ }
+
+ if (inst->std == W_HEVC_ENC) {
+ if (param->nr_intra_weight_y > MAX_INTRA_WEIGHT ||
+ param->nr_intra_weight_cb > MAX_INTRA_WEIGHT ||
+ param->nr_intra_weight_cr > MAX_INTRA_WEIGHT) {
+ dev_err(inst->dev->dev,
+ "Invalid intra weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n",
+ param->nr_intra_weight_y, param->nr_intra_weight_cb,
+ param->nr_intra_weight_cr, MAX_INTRA_WEIGHT);
+ return -EINVAL;
+ }
+
+ if (param->nr_inter_weight_y > MAX_INTER_WEIGHT ||
+ param->nr_inter_weight_cb > MAX_INTER_WEIGHT ||
+ param->nr_inter_weight_cr > MAX_INTER_WEIGHT) {
+ dev_err(inst->dev->dev,
+ "Invalid inter weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n",
+ param->nr_inter_weight_y, param->nr_inter_weight_cb,
+ param->nr_inter_weight_cr, MAX_INTER_WEIGHT);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h
new file mode 100644
index 000000000000..8bcc1e64094b
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h
@@ -0,0 +1,732 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - wave5 register definitions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef __WAVE5_REGISTER_DEFINE_H__
+#define __WAVE5_REGISTER_DEFINE_H__
+
+enum W5_VPU_COMMAND {
+ W5_INIT_VPU = 0x0001,
+ W5_WAKEUP_VPU = 0x0002,
+ W5_SLEEP_VPU = 0x0004,
+ W5_CREATE_INSTANCE = 0x0008, /* queuing command */
+ W5_FLUSH_INSTANCE = 0x0010,
+ W5_DESTROY_INSTANCE = 0x0020, /* queuing command */
+ W5_INIT_SEQ = 0x0040, /* queuing command */
+ W5_SET_FB = 0x0080,
+ W5_DEC_ENC_PIC = 0x0100, /* queuing command */
+ W5_ENC_SET_PARAM = 0x0200, /* queuing command */
+ W5_QUERY = 0x4000,
+ W5_UPDATE_BS = 0x8000,
+ W5_MAX_VPU_COMD = 0x10000,
+};
+
+enum query_opt {
+ GET_VPU_INFO = 0,
+ SET_WRITE_PROT = 1,
+ GET_RESULT = 2,
+ UPDATE_DISP_FLAG = 3,
+ GET_BW_REPORT = 4,
+ GET_BS_RD_PTR = 5, /* for decoder */
+ GET_BS_WR_PTR = 6, /* for encoder */
+ GET_SRC_BUF_FLAG = 7, /* for encoder */
+ SET_BS_RD_PTR = 8, /* for decoder */
+ GET_DEBUG_INFO = 0x61,
+};
+
+#define W5_REG_BASE 0x00000000
+#define W5_CMD_REG_BASE 0x00000100
+#define W5_CMD_REG_END 0x00000200
+
+/*
+ * COMMON
+ *
+ * ----
+ *
+ * Power on configuration
+ * PO_DEBUG_MODE [0] 1 - power on with debug mode
+ * USE_PO_CONF [3] 1 - use power-on-configuration
+ */
+#define W5_PO_CONF (W5_REG_BASE + 0x0000)
+#define W5_VCPU_CUR_PC (W5_REG_BASE + 0x0004)
+#define W5_VCPU_CUR_LR (W5_REG_BASE + 0x0008)
+#define W5_VPU_PDBG_STEP_MASK_V (W5_REG_BASE + 0x000C)
+#define W5_VPU_PDBG_CTRL (W5_REG_BASE + 0x0010) /* v_cpu debugger ctrl register */
+#define W5_VPU_PDBG_IDX_REG (W5_REG_BASE + 0x0014) /* v_cpu debugger index register */
+#define W5_VPU_PDBG_WDATA_REG (W5_REG_BASE + 0x0018) /* v_cpu debugger write data reg */
+#define W5_VPU_PDBG_RDATA_REG (W5_REG_BASE + 0x001C) /* v_cpu debugger read data reg */
+
+#define W5_VPU_FIO_CTRL_ADDR (W5_REG_BASE + 0x0020)
+#define W5_VPU_FIO_DATA (W5_REG_BASE + 0x0024)
+#define W5_VPU_VINT_REASON_USR (W5_REG_BASE + 0x0030)
+#define W5_VPU_VINT_REASON_CLR (W5_REG_BASE + 0x0034)
+#define W5_VPU_HOST_INT_REQ (W5_REG_BASE + 0x0038)
+#define W5_VPU_VINT_CLEAR (W5_REG_BASE + 0x003C)
+#define W5_VPU_HINT_CLEAR (W5_REG_BASE + 0x0040)
+#define W5_VPU_VPU_INT_STS (W5_REG_BASE + 0x0044)
+#define W5_VPU_VINT_ENABLE (W5_REG_BASE + 0x0048)
+#define W5_VPU_VINT_REASON (W5_REG_BASE + 0x004C)
+#define W5_VPU_RESET_REQ (W5_REG_BASE + 0x0050)
+#define W5_RST_BLOCK_CCLK(_core) BIT((_core))
+#define W5_RST_BLOCK_CCLK_ALL (0xff)
+#define W5_RST_BLOCK_BCLK(_core) (0x100 << (_core))
+#define W5_RST_BLOCK_BCLK_ALL (0xff00)
+#define W5_RST_BLOCK_ACLK(_core) (0x10000 << (_core))
+#define W5_RST_BLOCK_ACLK_ALL (0xff0000)
+#define W5_RST_BLOCK_VCPU_ALL (0x3f000000)
+#define W5_RST_BLOCK_ALL (0x3fffffff)
+#define W5_VPU_RESET_STATUS (W5_REG_BASE + 0x0054)
+
+#define W5_VCPU_RESTART (W5_REG_BASE + 0x0058)
+#define W5_VPU_CLK_MASK (W5_REG_BASE + 0x005C)
+
+/* REMAP_CTRL
+ * PAGE SIZE: [8:0] 0x001 - 4K
+ * 0x002 - 8K
+ * 0x004 - 16K
+ * ...
+ * 0x100 - 1M
+ * REGION ATTR1 [10] 0 - normal
+ * 1 - make bus error for the region
+ * REGION ATTR2 [11] 0 - normal
+ * 1 - bypass region
+ * REMAP INDEX [15:12] - 0 ~ 3
+ * ENDIAN [19:16] - NOTE: Currently not supported in this driver
+ * AXI-ID [23:20] - upper AXI-ID
+ * BUS_ERROR [29] 0 - bypass
+ * 1 - make BUS_ERROR for unmapped region
+ * BYPASS_ALL [30] 1 - bypass all
+ * ENABLE [31] 1 - update control register[30:16]
+ */
+#define W5_VPU_REMAP_CTRL (W5_REG_BASE + 0x0060)
+#define W5_VPU_REMAP_VADDR (W5_REG_BASE + 0x0064)
+#define W5_VPU_REMAP_PADDR (W5_REG_BASE + 0x0068)
+#define W5_VPU_REMAP_CORE_START (W5_REG_BASE + 0x006C)
+#define W5_VPU_BUSY_STATUS (W5_REG_BASE + 0x0070)
+#define W5_VPU_HALT_STATUS (W5_REG_BASE + 0x0074)
+#define W5_VPU_VCPU_STATUS (W5_REG_BASE + 0x0078)
+#define W5_VPU_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0094)
+/*
+ * assign vpu_config0 = {conf_map_converter_reg, // [31]
+ * conf_map_converter_sig, // [30]
+ * 8'd0, // [29:22]
+ * conf_std_switch_en, // [21]
+ * conf_bg_detect, // [20]
+ * conf_3dnr_en, // [19]
+ * conf_one_axi_en, // [18]
+ * conf_sec_axi_en, // [17]
+ * conf_bus_info, // [16]
+ * conf_afbc_en, // [15]
+ * conf_afbc_version_id, // [14:12]
+ * conf_fbc_en, // [11]
+ * conf_fbc_version_id, // [10:08]
+ * conf_scaler_en, // [07]
+ * conf_scaler_version_id, // [06:04]
+ * conf_bwb_en, // [03]
+ * 3'd0}; // [02:00]
+ */
+#define W5_VPU_RET_VPU_CONFIG0 (W5_REG_BASE + 0x0098)
+/*
+ * assign vpu_config1 = {4'd0, // [31:28]
+ * conf_perf_timer_en, // [27]
+ * conf_multi_core_en, // [26]
+ * conf_gcu_en, // [25]
+ * conf_cu_report, // [24]
+ * 4'd0, // [23:20]
+ * conf_vcore_id_3, // [19]
+ * conf_vcore_id_2, // [18]
+ * conf_vcore_id_1, // [17]
+ * conf_vcore_id_0, // [16]
+ * conf_bwb_opt, // [15]
+ * 7'd0, // [14:08]
+ * conf_cod_std_en_reserved_7, // [7]
+ * conf_cod_std_en_reserved_6, // [6]
+ * conf_cod_std_en_reserved_5, // [5]
+ * conf_cod_std_en_reserved_4, // [4]
+ * conf_cod_std_en_reserved_3, // [3]
+ * conf_cod_std_en_reserved_2, // [2]
+ * conf_cod_std_en_vp9, // [1]
+ * conf_cod_std_en_hevc}; // [0]
+ * }
+ */
+#define W5_VPU_RET_VPU_CONFIG1 (W5_REG_BASE + 0x009C)
+
+#define W5_VPU_DBG_REG0 (W5_REG_BASE + 0x00f0)
+#define W5_VPU_DBG_REG1 (W5_REG_BASE + 0x00f4)
+#define W5_VPU_DBG_REG2 (W5_REG_BASE + 0x00f8)
+#define W5_VPU_DBG_REG3 (W5_REG_BASE + 0x00fc)
+
+/************************************************************************/
+/* PRODUCT INFORMATION */
+/************************************************************************/
+#define W5_PRODUCT_NAME (W5_REG_BASE + 0x1040)
+#define W5_PRODUCT_NUMBER (W5_REG_BASE + 0x1044)
+
+/************************************************************************/
+/* DECODER/ENCODER COMMON */
+/************************************************************************/
+#define W5_COMMAND (W5_REG_BASE + 0x0100)
+#define W5_COMMAND_OPTION (W5_REG_BASE + 0x0104)
+#define W5_QUERY_OPTION (W5_REG_BASE + 0x0104)
+#define W5_RET_SUCCESS (W5_REG_BASE + 0x0108)
+#define W5_RET_FAIL_REASON (W5_REG_BASE + 0x010C)
+#define W5_RET_QUEUE_FAIL_REASON (W5_REG_BASE + 0x0110)
+#define W5_CMD_INSTANCE_INFO (W5_REG_BASE + 0x0110)
+
+#define W5_RET_QUEUE_STATUS (W5_REG_BASE + 0x01E0)
+#define W5_RET_BS_EMPTY_INST (W5_REG_BASE + 0x01E4)
+#define W5_RET_QUEUE_CMD_DONE_INST (W5_REG_BASE + 0x01E8)
+#define W5_RET_STAGE0_INSTANCE_INFO (W5_REG_BASE + 0x01EC)
+#define W5_RET_STAGE1_INSTANCE_INFO (W5_REG_BASE + 0x01F0)
+#define W5_RET_STAGE2_INSTANCE_INFO (W5_REG_BASE + 0x01F4)
+
+#define W5_RET_SEQ_DONE_INSTANCE_INFO (W5_REG_BASE + 0x01FC)
+
+#define W5_BS_OPTION (W5_REG_BASE + 0x0120)
+
+/* return info when QUERY (GET_RESULT) for en/decoder */
+#define W5_RET_VLC_BUF_SIZE (W5_REG_BASE + 0x01B0)
+/* return info when QUERY (GET_RESULT) for en/decoder */
+#define W5_RET_PARAM_BUF_SIZE (W5_REG_BASE + 0x01B4)
+
+/* set when SET_FB for en/decoder */
+#define W5_CMD_SET_FB_ADDR_TASK_BUF (W5_REG_BASE + 0x01D4)
+#define W5_CMD_SET_FB_TASK_BUF_SIZE (W5_REG_BASE + 0x01D8)
+/************************************************************************/
+/* INIT_VPU - COMMON */
+/************************************************************************/
+/* note: W5_ADDR_CODE_BASE should be aligned to 4KB */
+#define W5_ADDR_CODE_BASE (W5_REG_BASE + 0x0110)
+#define W5_CODE_SIZE (W5_REG_BASE + 0x0114)
+#define W5_CODE_PARAM (W5_REG_BASE + 0x0118)
+#define W5_ADDR_TEMP_BASE (W5_REG_BASE + 0x011C)
+#define W5_TEMP_SIZE (W5_REG_BASE + 0x0120)
+#define W5_HW_OPTION (W5_REG_BASE + 0x012C)
+#define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180)
+
+/************************************************************************/
+/* CREATE_INSTANCE - COMMON */
+/************************************************************************/
+#define W5_ADDR_WORK_BASE (W5_REG_BASE + 0x0114)
+#define W5_WORK_SIZE (W5_REG_BASE + 0x0118)
+#define W5_CMD_DEC_BS_START_ADDR (W5_REG_BASE + 0x011C)
+#define W5_CMD_DEC_BS_SIZE (W5_REG_BASE + 0x0120)
+#define W5_CMD_BS_PARAM (W5_REG_BASE + 0x0124)
+#define W5_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0130)
+#define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134)
+#define W5_CMD_EXT_ADDR (W5_REG_BASE + 0x0138)
+#define W5_CMD_NUM_CQ_DEPTH_M1 (W5_REG_BASE + 0x013C)
+#define W5_CMD_ERR_CONCEAL (W5_REG_BASE + 0x0140)
+
+/************************************************************************/
+/* DECODER - INIT_SEQ */
+/************************************************************************/
+#define W5_BS_RD_PTR (W5_REG_BASE + 0x0118)
+#define W5_BS_WR_PTR (W5_REG_BASE + 0x011C)
+/************************************************************************/
+/* SET_FRAME_BUF */
+/************************************************************************/
+/* SET_FB_OPTION 0x00 REGISTER FRAMEBUFFERS
+ * 0x01 UPDATE FRAMEBUFFER, just one framebuffer(linear, fbc and mvcol)
+ */
+#define W5_SFB_OPTION (W5_REG_BASE + 0x0104)
+#define W5_COMMON_PIC_INFO (W5_REG_BASE + 0x0118)
+#define W5_PIC_SIZE (W5_REG_BASE + 0x011C)
+#define W5_SET_FB_NUM (W5_REG_BASE + 0x0120)
+#define W5_EXTRA_PIC_INFO (W5_REG_BASE + 0x0124)
+
+#define W5_ADDR_LUMA_BASE0 (W5_REG_BASE + 0x0134)
+#define W5_ADDR_CB_BASE0 (W5_REG_BASE + 0x0138)
+#define W5_ADDR_CR_BASE0 (W5_REG_BASE + 0x013C)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET0 (W5_REG_BASE + 0x013C)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET0 (W5_REG_BASE + 0x0140)
+#define W5_ADDR_LUMA_BASE1 (W5_REG_BASE + 0x0144)
+#define W5_ADDR_CB_ADDR1 (W5_REG_BASE + 0x0148)
+#define W5_ADDR_CR_ADDR1 (W5_REG_BASE + 0x014C)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET1 (W5_REG_BASE + 0x014C)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET1 (W5_REG_BASE + 0x0150)
+#define W5_ADDR_LUMA_BASE2 (W5_REG_BASE + 0x0154)
+#define W5_ADDR_CB_ADDR2 (W5_REG_BASE + 0x0158)
+#define W5_ADDR_CR_ADDR2 (W5_REG_BASE + 0x015C)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET2 (W5_REG_BASE + 0x015C)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET2 (W5_REG_BASE + 0x0160)
+#define W5_ADDR_LUMA_BASE3 (W5_REG_BASE + 0x0164)
+#define W5_ADDR_CB_ADDR3 (W5_REG_BASE + 0x0168)
+#define W5_ADDR_CR_ADDR3 (W5_REG_BASE + 0x016C)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET3 (W5_REG_BASE + 0x016C)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET3 (W5_REG_BASE + 0x0170)
+#define W5_ADDR_LUMA_BASE4 (W5_REG_BASE + 0x0174)
+#define W5_ADDR_CB_ADDR4 (W5_REG_BASE + 0x0178)
+#define W5_ADDR_CR_ADDR4 (W5_REG_BASE + 0x017C)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET4 (W5_REG_BASE + 0x017C)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET4 (W5_REG_BASE + 0x0180)
+#define W5_ADDR_LUMA_BASE5 (W5_REG_BASE + 0x0184)
+#define W5_ADDR_CB_ADDR5 (W5_REG_BASE + 0x0188)
+#define W5_ADDR_CR_ADDR5 (W5_REG_BASE + 0x018C)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET5 (W5_REG_BASE + 0x018C)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET5 (W5_REG_BASE + 0x0190)
+#define W5_ADDR_LUMA_BASE6 (W5_REG_BASE + 0x0194)
+#define W5_ADDR_CB_ADDR6 (W5_REG_BASE + 0x0198)
+#define W5_ADDR_CR_ADDR6 (W5_REG_BASE + 0x019C)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET6 (W5_REG_BASE + 0x019C)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET6 (W5_REG_BASE + 0x01A0)
+#define W5_ADDR_LUMA_BASE7 (W5_REG_BASE + 0x01A4)
+#define W5_ADDR_CB_ADDR7 (W5_REG_BASE + 0x01A8)
+#define W5_ADDR_CR_ADDR7 (W5_REG_BASE + 0x01AC)
+/* compression offset table for luma */
+#define W5_ADDR_FBC_Y_OFFSET7 (W5_REG_BASE + 0x01AC)
+/* compression offset table for chroma */
+#define W5_ADDR_FBC_C_OFFSET7 (W5_REG_BASE + 0x01B0)
+#define W5_ADDR_MV_COL0 (W5_REG_BASE + 0x01B4)
+#define W5_ADDR_MV_COL1 (W5_REG_BASE + 0x01B8)
+#define W5_ADDR_MV_COL2 (W5_REG_BASE + 0x01BC)
+#define W5_ADDR_MV_COL3 (W5_REG_BASE + 0x01C0)
+#define W5_ADDR_MV_COL4 (W5_REG_BASE + 0x01C4)
+#define W5_ADDR_MV_COL5 (W5_REG_BASE + 0x01C8)
+#define W5_ADDR_MV_COL6 (W5_REG_BASE + 0x01CC)
+#define W5_ADDR_MV_COL7 (W5_REG_BASE + 0x01D0)
+
+/* UPDATE_FB */
+/* CMD_SET_FB_STRIDE [15:0] - FBC framebuffer stride
+ * [31:15] - linear framebuffer stride
+ */
+#define W5_CMD_SET_FB_STRIDE (W5_REG_BASE + 0x0118)
+#define W5_CMD_SET_FB_INDEX (W5_REG_BASE + 0x0120)
+#define W5_ADDR_LUMA_BASE (W5_REG_BASE + 0x0134)
+#define W5_ADDR_CB_BASE (W5_REG_BASE + 0x0138)
+#define W5_ADDR_CR_BASE (W5_REG_BASE + 0x013C)
+#define W5_ADDR_MV_COL (W5_REG_BASE + 0x0140)
+#define W5_ADDR_FBC_Y_BASE (W5_REG_BASE + 0x0144)
+#define W5_ADDR_FBC_C_BASE (W5_REG_BASE + 0x0148)
+#define W5_ADDR_FBC_Y_OFFSET (W5_REG_BASE + 0x014C)
+#define W5_ADDR_FBC_C_OFFSET (W5_REG_BASE + 0x0150)
+
+/************************************************************************/
+/* DECODER - DEC_PIC */
+/************************************************************************/
+#define W5_CMD_DEC_VCORE_INFO (W5_REG_BASE + 0x0194)
+/* sequence change enable mask register
+ * CMD_SEQ_CHANGE_ENABLE_FLAG [5] profile_idc
+ * [16] pic_width/height_in_luma_sample
+ * [19] sps_max_dec_pic_buffering, max_num_reorder, max_latency_increase
+ */
+#define W5_CMD_SEQ_CHANGE_ENABLE_FLAG (W5_REG_BASE + 0x0128)
+#define W5_CMD_DEC_USER_MASK (W5_REG_BASE + 0x012C)
+#define W5_CMD_DEC_TEMPORAL_ID_PLUS1 (W5_REG_BASE + 0x0130)
+#define W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1 (W5_REG_BASE + 0x0134)
+#define W5_USE_SEC_AXI (W5_REG_BASE + 0x0150)
+
+/************************************************************************/
+/* DECODER - QUERY : GET_VPU_INFO */
+/************************************************************************/
+#define W5_RET_FW_VERSION (W5_REG_BASE + 0x0118)
+#define W5_RET_PRODUCT_NAME (W5_REG_BASE + 0x011C)
+#define W5_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0120)
+#define W5_RET_STD_DEF0 (W5_REG_BASE + 0x0124)
+#define W5_RET_STD_DEF1 (W5_REG_BASE + 0x0128)
+#define W5_RET_CONF_FEATURE (W5_REG_BASE + 0x012C)
+#define W5_RET_CONF_DATE (W5_REG_BASE + 0x0130)
+#define W5_RET_CONF_REVISION (W5_REG_BASE + 0x0134)
+#define W5_RET_CONF_TYPE (W5_REG_BASE + 0x0138)
+#define W5_RET_PRODUCT_ID (W5_REG_BASE + 0x013C)
+#define W5_RET_CUSTOMER_ID (W5_REG_BASE + 0x0140)
+
+/************************************************************************/
+/* DECODER - QUERY : GET_RESULT */
+/************************************************************************/
+#define W5_CMD_DEC_ADDR_REPORT_BASE (W5_REG_BASE + 0x0114)
+#define W5_CMD_DEC_REPORT_SIZE (W5_REG_BASE + 0x0118)
+#define W5_CMD_DEC_REPORT_PARAM (W5_REG_BASE + 0x011C)
+
+#define W5_RET_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C)
+#define W5_RET_DEC_SEQ_PARAM (W5_REG_BASE + 0x0120)
+#define W5_RET_DEC_COLOR_SAMPLE_INFO (W5_REG_BASE + 0x0124)
+#define W5_RET_DEC_ASPECT_RATIO (W5_REG_BASE + 0x0128)
+#define W5_RET_DEC_BIT_RATE (W5_REG_BASE + 0x012C)
+#define W5_RET_DEC_FRAME_RATE_NR (W5_REG_BASE + 0x0130)
+#define W5_RET_DEC_FRAME_RATE_DR (W5_REG_BASE + 0x0134)
+#define W5_RET_DEC_NUM_REQUIRED_FB (W5_REG_BASE + 0x0138)
+#define W5_RET_DEC_NUM_REORDER_DELAY (W5_REG_BASE + 0x013C)
+#define W5_RET_DEC_SUB_LAYER_INFO (W5_REG_BASE + 0x0140)
+#define W5_RET_DEC_NOTIFICATION (W5_REG_BASE + 0x0144)
+/*
+ * USER_DATA_FLAGS for HEVC/H264 only.
+ * Bits:
+ * [1] - User data buffer full boolean
+ * [2] - VUI parameter flag
+ * [4] - Pic_timing SEI flag
+ * [5] - 1st user_data_registed_itu_t_t35 prefix SEI flag
+ * [6] - user_data_unregistered prefix SEI flag
+ * [7] - 1st user_data_registed_itu_t_t35 suffix SEI flag
+ * [8] - user_data_unregistered suffix SEI flag
+ * [10]- mastering_display_color_volume prefix SEI flag
+ * [11]- chroma_resampling_display_color_volume prefix SEI flag
+ * [12]- knee_function_info SEI flag
+ * [13]- tone_mapping_info prefix SEI flag
+ * [14]- film_grain_characteristics_info prefix SEI flag
+ * [15]- content_light_level_info prefix SEI flag
+ * [16]- color_remapping_info prefix SEI flag
+ * [28]- 2nd user_data_registed_itu_t_t35 prefix SEI flag
+ * [29]- 3rd user_data_registed_itu_t_t35 prefix SEI flag
+ * [30]- 2nd user_data_registed_itu_t_t35 suffix SEI flag
+ * [31]- 3rd user_data_registed_itu_t_t35 suffix SEI flag
+ */
+#define W5_RET_DEC_USERDATA_IDC (W5_REG_BASE + 0x0148)
+#define W5_RET_DEC_PIC_SIZE (W5_REG_BASE + 0x014C)
+#define W5_RET_DEC_CROP_TOP_BOTTOM (W5_REG_BASE + 0x0150)
+#define W5_RET_DEC_CROP_LEFT_RIGHT (W5_REG_BASE + 0x0154)
+/*
+ * #define W5_RET_DEC_AU_START_POS (W5_REG_BASE + 0x0158)
+ * => Access unit (AU) Bitstream start position
+ * #define W5_RET_DEC_AU_END_POS (W5_REG_BASE + 0x015C)
+ * => Access unit (AU) Bitstream end position
+ */
+
+/*
+ * Decoded picture type:
+ * reg_val & 0x7 => picture type
+ * (reg_val >> 4) & 0x3f => VCL NAL unit type
+ * (reg_val >> 31) & 0x1 => output_flag
+ * 16 << ((reg_val >> 10) & 0x3) => ctu_size
+ */
+#define W5_RET_DEC_PIC_TYPE (W5_REG_BASE + 0x0160)
+#define W5_RET_DEC_PIC_POC (W5_REG_BASE + 0x0164)
+/*
+ * #define W5_RET_DEC_RECOVERY_POINT (W5_REG_BASE + 0x0168)
+ * => HEVC recovery point
+ * reg_val & 0xff => number of signed recovery picture order counts
+ * (reg_val >> 16) & 0x1 => exact match flag
+ * (reg_val >> 17) & 0x1 => broken link flag
+ * (reg_val >> 18) & 0x1 => exist flag
+ */
+#define W5_RET_DEC_DEBUG_INDEX (W5_REG_BASE + 0x016C)
+#define W5_RET_DEC_DECODED_INDEX (W5_REG_BASE + 0x0170)
+#define W5_RET_DEC_DISPLAY_INDEX (W5_REG_BASE + 0x0174)
+/*
+ * #define W5_RET_DEC_REALLOC_INDEX (W5_REG_BASE + 0x0178)
+ * => display picture index in decoded picture buffer
+ * reg_val & 0xf => display picture index for FBC buffer (by reordering)
+ */
+#define W5_RET_DEC_DISP_IDC (W5_REG_BASE + 0x017C)
+/*
+ * #define W5_RET_DEC_ERR_CTB_NUM (W5_REG_BASE + 0x0180)
+ * => Number of error CTUs
+ * reg_val >> 16 => erroneous CTUs in bitstream
+ * reg_val & 0xffff => total CTUs in bitstream
+ *
+ * #define W5_RET_DEC_PIC_PARAM (W5_REG_BASE + 0x01A0)
+ * => Bitstream sequence/picture parameter information (AV1 only)
+ * reg_val & 0x1 => intrabc tool enable
+ * (reg_val >> 1) & 0x1 => screen content tools enable
+ */
+#define W5_RET_DEC_HOST_CMD_TICK (W5_REG_BASE + 0x01B8)
+/*
+ * #define W5_RET_DEC_SEEK_START_TICK (W5_REG_BASE + 0x01BC)
+ * #define W5_RET_DEC_SEEK_END_TICK (W5_REG_BASE + 0x01C0)
+ * => Start and end ticks for seeking slices of the picture
+ * #define W5_RET_DEC_PARSING_START_TICK (W5_REG_BASE + 0x01C4)
+ * #define W5_RET_DEC_PARSING_END_TICK (W5_REG_BASE + 0x01C8)
+ * => Start and end ticks for parsing slices of the picture
+ * #define W5_RET_DEC_DECODING_START_TICK (W5_REG_BASE + 0x01CC)
+ * => Start tick for decoding slices of the picture
+ */
+#define W5_RET_DEC_DECODING_ENC_TICK (W5_REG_BASE + 0x01D0)
+#define W5_RET_DEC_WARN_INFO (W5_REG_BASE + 0x01D4)
+#define W5_RET_DEC_ERR_INFO (W5_REG_BASE + 0x01D8)
+#define W5_RET_DEC_DECODING_SUCCESS (W5_REG_BASE + 0x01DC)
+
+/************************************************************************/
+/* DECODER - FLUSH_INSTANCE */
+/************************************************************************/
+#define W5_CMD_FLUSH_INST_OPT (W5_REG_BASE + 0x104)
+
+/************************************************************************/
+/* DECODER - QUERY : UPDATE_DISP_FLAG */
+/************************************************************************/
+#define W5_CMD_DEC_SET_DISP_IDC (W5_REG_BASE + 0x0118)
+#define W5_CMD_DEC_CLR_DISP_IDC (W5_REG_BASE + 0x011C)
+
+/************************************************************************/
+/* DECODER - QUERY : SET_BS_RD_PTR */
+/************************************************************************/
+#define W5_RET_QUERY_DEC_SET_BS_RD_PTR (W5_REG_BASE + 0x011C)
+
+/************************************************************************/
+/* DECODER - QUERY : GET_BS_RD_PTR */
+/************************************************************************/
+#define W5_RET_QUERY_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C)
+
+/************************************************************************/
+/* QUERY : GET_DEBUG_INFO */
+/************************************************************************/
+#define W5_RET_QUERY_DEBUG_PRI_REASON (W5_REG_BASE + 0x114)
+
+/************************************************************************/
+/* GDI register for debugging */
+/************************************************************************/
+#define W5_GDI_BASE 0x8800
+#define W5_GDI_BUS_CTRL (W5_GDI_BASE + 0x0F0)
+#define W5_GDI_BUS_STATUS (W5_GDI_BASE + 0x0F4)
+
+#define W5_BACKBONE_BASE_VCPU 0xFE00
+#define W5_BACKBONE_BUS_CTRL_VCPU (W5_BACKBONE_BASE_VCPU + 0x010)
+#define W5_BACKBONE_BUS_STATUS_VCPU (W5_BACKBONE_BASE_VCPU + 0x014)
+#define W5_BACKBONE_PROG_AXI_ID (W5_BACKBONE_BASE_VCPU + 0x00C)
+
+#define W5_BACKBONE_PROC_EXT_ADDR (W5_BACKBONE_BASE_VCPU + 0x0C0)
+#define W5_BACKBONE_AXI_PARAM (W5_BACKBONE_BASE_VCPU + 0x0E0)
+
+#define W5_BACKBONE_BASE_VCORE0 0x8E00
+#define W5_BACKBONE_BUS_CTRL_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x010)
+#define W5_BACKBONE_BUS_STATUS_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x014)
+
+#define W5_BACKBONE_BASE_VCORE1 0x9E00 /* for dual-core product */
+#define W5_BACKBONE_BUS_CTRL_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x010)
+#define W5_BACKBONE_BUS_STATUS_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x014)
+
+#define W5_COMBINED_BACKBONE_BASE 0xFE00
+#define W5_COMBINED_BACKBONE_BUS_CTRL (W5_COMBINED_BACKBONE_BASE + 0x010)
+#define W5_COMBINED_BACKBONE_BUS_STATUS (W5_COMBINED_BACKBONE_BASE + 0x014)
+
+/************************************************************************/
+/* */
+/* for ENCODER */
+/* */
+/************************************************************************/
+#define W5_RET_STAGE3_INSTANCE_INFO (W5_REG_BASE + 0x1F8)
+/************************************************************************/
+/* ENCODER - CREATE_INSTANCE */
+/************************************************************************/
+/* 0x114 ~ 0x124 : defined above (CREATE_INSTANCE COMMON) */
+#define W5_CMD_ENC_VCORE_INFO (W5_REG_BASE + 0x0194)
+#define W5_CMD_ENC_SRC_OPTIONS (W5_REG_BASE + 0x0128)
+
+/************************************************************************/
+/* ENCODER - SET_FB */
+/************************************************************************/
+#define W5_FBC_STRIDE (W5_REG_BASE + 0x128)
+#define W5_ADDR_SUB_SAMPLED_FB_BASE (W5_REG_BASE + 0x12C)
+#define W5_SUB_SAMPLED_ONE_FB_SIZE (W5_REG_BASE + 0x130)
+
+/************************************************************************/
+/* ENCODER - ENC_SET_PARAM (COMMON & CHANGE_PARAM) */
+/************************************************************************/
+#define W5_CMD_ENC_SEQ_SET_PARAM_OPTION (W5_REG_BASE + 0x104)
+#define W5_CMD_ENC_SEQ_SET_PARAM_ENABLE (W5_REG_BASE + 0x118)
+#define W5_CMD_ENC_SEQ_SRC_SIZE (W5_REG_BASE + 0x11C)
+#define W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN (W5_REG_BASE + 0x120)
+#define W5_CMD_ENC_SEQ_SPS_PARAM (W5_REG_BASE + 0x124)
+#define W5_CMD_ENC_SEQ_PPS_PARAM (W5_REG_BASE + 0x128)
+#define W5_CMD_ENC_SEQ_GOP_PARAM (W5_REG_BASE + 0x12C)
+#define W5_CMD_ENC_SEQ_INTRA_PARAM (W5_REG_BASE + 0x130)
+#define W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT (W5_REG_BASE + 0x134)
+#define W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT (W5_REG_BASE + 0x138)
+#define W5_CMD_ENC_SEQ_RDO_PARAM (W5_REG_BASE + 0x13C)
+#define W5_CMD_ENC_SEQ_INDEPENDENT_SLICE (W5_REG_BASE + 0x140)
+#define W5_CMD_ENC_SEQ_DEPENDENT_SLICE (W5_REG_BASE + 0x144)
+#define W5_CMD_ENC_SEQ_INTRA_REFRESH (W5_REG_BASE + 0x148)
+#define W5_CMD_ENC_SEQ_INPUT_SRC_PARAM (W5_REG_BASE + 0x14C)
+
+#define W5_CMD_ENC_SEQ_RC_FRAME_RATE (W5_REG_BASE + 0x150)
+#define W5_CMD_ENC_SEQ_RC_TARGET_RATE (W5_REG_BASE + 0x154)
+#define W5_CMD_ENC_SEQ_RC_PARAM (W5_REG_BASE + 0x158)
+#define W5_CMD_ENC_SEQ_RC_MIN_MAX_QP (W5_REG_BASE + 0x15C)
+#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_0_3 (W5_REG_BASE + 0x160)
+#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7 (W5_REG_BASE + 0x164)
+#define W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP (W5_REG_BASE + 0x168)
+#define W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM (W5_REG_BASE + 0x16C)
+
+#define W5_CMD_ENC_SEQ_ROT_PARAM (W5_REG_BASE + 0x170)
+#define W5_CMD_ENC_SEQ_NUM_UNITS_IN_TICK (W5_REG_BASE + 0x174)
+#define W5_CMD_ENC_SEQ_TIME_SCALE (W5_REG_BASE + 0x178)
+#define W5_CMD_ENC_SEQ_NUM_TICKS_POC_DIFF_ONE (W5_REG_BASE + 0x17C)
+
+#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU04 (W5_REG_BASE + 0x184)
+#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU08 (W5_REG_BASE + 0x188)
+#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU16 (W5_REG_BASE + 0x18C)
+#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU32 (W5_REG_BASE + 0x190)
+#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU08 (W5_REG_BASE + 0x194)
+#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU16 (W5_REG_BASE + 0x198)
+#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU32 (W5_REG_BASE + 0x19C)
+#define W5_CMD_ENC_SEQ_NR_PARAM (W5_REG_BASE + 0x1A0)
+#define W5_CMD_ENC_SEQ_NR_WEIGHT (W5_REG_BASE + 0x1A4)
+#define W5_CMD_ENC_SEQ_BG_PARAM (W5_REG_BASE + 0x1A8)
+#define W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR (W5_REG_BASE + 0x1AC)
+#define W5_CMD_ENC_SEQ_USER_SCALING_LIST_ADDR (W5_REG_BASE + 0x1B0)
+#define W5_CMD_ENC_SEQ_VUI_HRD_PARAM (W5_REG_BASE + 0x180)
+#define W5_CMD_ENC_SEQ_VUI_RBSP_ADDR (W5_REG_BASE + 0x1B8)
+#define W5_CMD_ENC_SEQ_HRD_RBSP_ADDR (W5_REG_BASE + 0x1BC)
+
+/************************************************************************/
+/* ENCODER - ENC_SET_PARAM (CUSTOM_GOP) */
+/************************************************************************/
+#define W5_CMD_ENC_CUSTOM_GOP_PARAM (W5_REG_BASE + 0x11C)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_0 (W5_REG_BASE + 0x120)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_1 (W5_REG_BASE + 0x124)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_2 (W5_REG_BASE + 0x128)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_3 (W5_REG_BASE + 0x12C)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_4 (W5_REG_BASE + 0x130)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_5 (W5_REG_BASE + 0x134)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_6 (W5_REG_BASE + 0x138)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_7 (W5_REG_BASE + 0x13C)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_8 (W5_REG_BASE + 0x140)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_9 (W5_REG_BASE + 0x144)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_10 (W5_REG_BASE + 0x148)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_11 (W5_REG_BASE + 0x14C)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_12 (W5_REG_BASE + 0x150)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_13 (W5_REG_BASE + 0x154)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_14 (W5_REG_BASE + 0x158)
+#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_15 (W5_REG_BASE + 0x15C)
+
+/************************************************************************/
+/* ENCODER - ENC_PIC */
+/************************************************************************/
+#define W5_CMD_ENC_BS_START_ADDR (W5_REG_BASE + 0x118)
+#define W5_CMD_ENC_BS_SIZE (W5_REG_BASE + 0x11C)
+#define W5_CMD_ENC_PIC_USE_SEC_AXI (W5_REG_BASE + 0x124)
+#define W5_CMD_ENC_PIC_REPORT_PARAM (W5_REG_BASE + 0x128)
+
+#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_PARAM (W5_REG_BASE + 0x138)
+#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_ADDR (W5_REG_BASE + 0x13C)
+#define W5_CMD_ENC_PIC_SRC_PIC_IDX (W5_REG_BASE + 0x144)
+#define W5_CMD_ENC_PIC_SRC_ADDR_Y (W5_REG_BASE + 0x148)
+#define W5_CMD_ENC_PIC_SRC_ADDR_U (W5_REG_BASE + 0x14C)
+#define W5_CMD_ENC_PIC_SRC_ADDR_V (W5_REG_BASE + 0x150)
+#define W5_CMD_ENC_PIC_SRC_STRIDE (W5_REG_BASE + 0x154)
+#define W5_CMD_ENC_PIC_SRC_FORMAT (W5_REG_BASE + 0x158)
+#define W5_CMD_ENC_PIC_SRC_AXI_SEL (W5_REG_BASE + 0x160)
+#define W5_CMD_ENC_PIC_CODE_OPTION (W5_REG_BASE + 0x164)
+#define W5_CMD_ENC_PIC_PIC_PARAM (W5_REG_BASE + 0x168)
+#define W5_CMD_ENC_PIC_LONGTERM_PIC (W5_REG_BASE + 0x16C)
+#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_Y (W5_REG_BASE + 0x170)
+#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_C (W5_REG_BASE + 0x174)
+#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_Y (W5_REG_BASE + 0x178)
+#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_C (W5_REG_BASE + 0x17C)
+#define W5_CMD_ENC_PIC_CF50_Y_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x190)
+#define W5_CMD_ENC_PIC_CF50_CB_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x194)
+#define W5_CMD_ENC_PIC_CF50_CR_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x198)
+#define W5_CMD_ENC_PIC_PREFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x180)
+#define W5_CMD_ENC_PIC_PREFIX_SEI_INFO (W5_REG_BASE + 0x184)
+#define W5_CMD_ENC_PIC_SUFFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x188)
+#define W5_CMD_ENC_PIC_SUFFIX_SEI_INFO (W5_REG_BASE + 0x18c)
+
+/************************************************************************/
+/* ENCODER - QUERY (GET_RESULT) */
+/************************************************************************/
+#define W5_RET_ENC_NUM_REQUIRED_FB (W5_REG_BASE + 0x11C)
+#define W5_RET_ENC_MIN_SRC_BUF_NUM (W5_REG_BASE + 0x120)
+#define W5_RET_ENC_PIC_TYPE (W5_REG_BASE + 0x124)
+/*
+ * #define W5_RET_ENC_PIC_POC (W5_REG_BASE + 0x128)
+ * => picture order count value of current encoded picture
+ */
+#define W5_RET_ENC_PIC_IDX (W5_REG_BASE + 0x12C)
+/*
+ * #define W5_RET_ENC_PIC_SLICE_NUM (W5_REG_BASE + 0x130)
+ * reg_val & 0xffff = total independent slice segment number (16 bits)
+ * (reg_val >> 16) & 0xffff = total dependent slice segment number (16 bits)
+ *
+ * #define W5_RET_ENC_PIC_SKIP (W5_REG_BASE + 0x134)
+ * reg_val & 0xfe = picture skip flag (7 bits)
+ *
+ * #define W5_RET_ENC_PIC_NUM_INTRA (W5_REG_BASE + 0x138)
+ * => number of intra blocks in 8x8 (32 bits)
+ *
+ * #define W5_RET_ENC_PIC_NUM_MERGE (W5_REG_BASE + 0x13C)
+ * => number of merge blocks in 8x8 (32 bits)
+ *
+ * #define W5_RET_ENC_PIC_NUM_SKIP (W5_REG_BASE + 0x144)
+ * => number of skip blocks in 8x8 (32 bits)
+ *
+ * #define W5_RET_ENC_PIC_AVG_CTU_QP (W5_REG_BASE + 0x148)
+ * => Average CTU QP value (32 bits)
+ */
+#define W5_RET_ENC_PIC_BYTE (W5_REG_BASE + 0x14C)
+/*
+ * #define W5_RET_ENC_GOP_PIC_IDX (W5_REG_BASE + 0x150)
+ * => picture index in group of pictures
+ */
+#define W5_RET_ENC_USED_SRC_IDX (W5_REG_BASE + 0x154)
+/*
+ * #define W5_RET_ENC_PIC_NUM (W5_REG_BASE + 0x158)
+ * => encoded picture number
+ */
+#define W5_RET_ENC_VCL_NUT (W5_REG_BASE + 0x15C)
+/*
+ * Only for H264:
+ * #define W5_RET_ENC_PIC_DIST_LOW (W5_REG_BASE + 0x164)
+ * => lower 32 bits of the sum of squared difference between source Y picture
+ * and reconstructed Y picture
+ * #define W5_RET_ENC_PIC_DIST_HIGH (W5_REG_BASE + 0x168)
+ * => upper 32 bits of the sum of squared difference between source Y picture
+ * and reconstructed Y picture
+ */
+#define W5_RET_ENC_PIC_MAX_LATENCY_PICS (W5_REG_BASE + 0x16C)
+
+#define W5_RET_ENC_HOST_CMD_TICK (W5_REG_BASE + 0x1B8)
+/*
+ * #define W5_RET_ENC_PREPARE_START_TICK (W5_REG_BASE + 0x1BC)
+ * #define W5_RET_ENC_PREPARE_END_TICK (W5_REG_BASE + 0x1C0)
+ * => Start and end ticks for preparing slices of the picture
+ * #define W5_RET_ENC_PROCESSING_START_TICK (W5_REG_BASE + 0x1C4)
+ * #define W5_RET_ENC_PROCESSING_END_TICK (W5_REG_BASE + 0x1C8)
+ * => Start and end ticks for processing slices of the picture
+ * #define W5_RET_ENC_ENCODING_START_TICK (W5_REG_BASE + 0x1CC)
+ * => Start tick for encoding slices of the picture
+ */
+#define W5_RET_ENC_ENCODING_END_TICK (W5_REG_BASE + 0x1D0)
+
+#define W5_RET_ENC_WARN_INFO (W5_REG_BASE + 0x1D4)
+#define W5_RET_ENC_ERR_INFO (W5_REG_BASE + 0x1D8)
+#define W5_RET_ENC_ENCODING_SUCCESS (W5_REG_BASE + 0x1DC)
+
+/************************************************************************/
+/* ENCODER - QUERY (GET_BS_WR_PTR) */
+/************************************************************************/
+#define W5_RET_ENC_RD_PTR (W5_REG_BASE + 0x114)
+#define W5_RET_ENC_WR_PTR (W5_REG_BASE + 0x118)
+#define W5_CMD_ENC_REASON_SEL (W5_REG_BASE + 0x11C)
+
+/************************************************************************/
+/* ENCODER - QUERY (GET_BW_REPORT) */
+/************************************************************************/
+#define RET_QUERY_BW_PRP_AXI_READ (W5_REG_BASE + 0x118)
+#define RET_QUERY_BW_PRP_AXI_WRITE (W5_REG_BASE + 0x11C)
+#define RET_QUERY_BW_FBD_Y_AXI_READ (W5_REG_BASE + 0x120)
+#define RET_QUERY_BW_FBC_Y_AXI_WRITE (W5_REG_BASE + 0x124)
+#define RET_QUERY_BW_FBD_C_AXI_READ (W5_REG_BASE + 0x128)
+#define RET_QUERY_BW_FBC_C_AXI_WRITE (W5_REG_BASE + 0x12C)
+#define RET_QUERY_BW_PRI_AXI_READ (W5_REG_BASE + 0x130)
+#define RET_QUERY_BW_PRI_AXI_WRITE (W5_REG_BASE + 0x134)
+#define RET_QUERY_BW_SEC_AXI_READ (W5_REG_BASE + 0x138)
+#define RET_QUERY_BW_SEC_AXI_WRITE (W5_REG_BASE + 0x13C)
+#define RET_QUERY_BW_PROC_AXI_READ (W5_REG_BASE + 0x140)
+#define RET_QUERY_BW_PROC_AXI_WRITE (W5_REG_BASE + 0x144)
+#define RET_QUERY_BW_BWB_AXI_WRITE (W5_REG_BASE + 0x148)
+#define W5_CMD_BW_OPTION (W5_REG_BASE + 0x14C)
+
+/************************************************************************/
+/* ENCODER - QUERY (GET_SRC_FLAG) */
+/************************************************************************/
+#define W5_RET_RELEASED_SRC_INSTANCE (W5_REG_BASE + 0x1EC)
+
+#define W5_ENC_PIC_SUB_FRAME_SYNC_IF (W5_REG_BASE + 0x0300)
+
+#endif /* __WAVE5_REGISTER_DEFINE_H__ */
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
new file mode 100644
index 000000000000..871090fa3407
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - low level access functions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#include <linux/bug.h>
+#include "wave5-vdi.h"
+#include "wave5-vpu.h"
+#include "wave5-regdefine.h"
+#include <linux/delay.h>
+
+static int wave5_vdi_allocate_common_memory(struct device *dev)
+{
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ if (!vpu_dev->common_mem.vaddr) {
+ int ret;
+
+ vpu_dev->common_mem.size = SIZE_COMMON;
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem);
+ if (ret) {
+ dev_err(dev, "unable to allocate common buffer\n");
+ return ret;
+ }
+ }
+
+ dev_dbg(dev, "[VDI] common_mem: daddr=%pad size=%zu vaddr=0x%p\n",
+ &vpu_dev->common_mem.daddr, vpu_dev->common_mem.size, vpu_dev->common_mem.vaddr);
+
+ return 0;
+}
+
+int wave5_vdi_init(struct device *dev)
+{
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = wave5_vdi_allocate_common_memory(dev);
+ if (ret < 0) {
+ dev_err(dev, "[VDI] failed to get vpu common buffer from driver\n");
+ return ret;
+ }
+
+ if (!PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
+ WARN_ONCE(1, "unsupported product code: 0x%x\n", vpu_dev->product_code);
+ return -EOPNOTSUPP;
+ }
+
+ /* if BIT processor is not running. */
+ if (wave5_vdi_read_register(vpu_dev, W5_VCPU_CUR_PC) == 0) {
+ int i;
+
+ for (i = 0; i < 64; i++)
+ wave5_vdi_write_register(vpu_dev, (i * 4) + 0x100, 0x0);
+ }
+
+ dev_dbg(dev, "[VDI] driver initialized successfully\n");
+
+ return 0;
+}
+
+int wave5_vdi_release(struct device *dev)
+{
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ vpu_dev->vdb_register = NULL;
+ wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem);
+
+ return 0;
+}
+
+void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data)
+{
+ writel(data, vpu_dev->vdb_register + addr);
+}
+
+unsigned int wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr)
+{
+ return readl(vpu_dev->vdb_register + addr);
+}
+
+int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
+{
+ if (!vb || !vb->vaddr) {
+ dev_err(vpu_dev->dev, "%s: unable to clear unmapped buffer\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(vb->vaddr, 0, vb->size);
+ return vb->size;
+}
+
+int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset,
+ u8 *data, size_t len)
+{
+ if (!vb || !vb->vaddr) {
+ dev_err(vpu_dev->dev, "%s: unable to write to unmapped buffer\n", __func__);
+ return -EINVAL;
+ }
+
+ if (offset > vb->size || len > vb->size || offset + len > vb->size) {
+ dev_err(vpu_dev->dev, "%s: buffer too small\n", __func__);
+ return -ENOSPC;
+ }
+
+ memcpy(vb->vaddr + offset, data, len);
+
+ return len;
+}
+
+int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
+{
+ void *vaddr;
+ dma_addr_t daddr;
+
+ if (!vb->size) {
+ dev_err(vpu_dev->dev, "%s: requested size==0\n", __func__);
+ return -EINVAL;
+ }
+
+ vaddr = dma_alloc_coherent(vpu_dev->dev, vb->size, &daddr, GFP_KERNEL);
+ if (!vaddr)
+ return -ENOMEM;
+ vb->vaddr = vaddr;
+ vb->daddr = daddr;
+
+ return 0;
+}
+
+int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
+{
+ if (vb->size == 0)
+ return -EINVAL;
+
+ if (!vb->vaddr)
+ dev_err(vpu_dev->dev, "%s: requested free of unmapped buffer\n", __func__);
+ else
+ dma_free_coherent(vpu_dev->dev, vb->size, vb->vaddr, vb->daddr);
+
+ memset(vb, 0, sizeof(*vb));
+
+ return 0;
+}
+
+int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count,
+ size_t size)
+{
+ struct vpu_buf vb_buf;
+ int i, ret = 0;
+
+ vb_buf.size = size;
+
+ for (i = 0; i < count; i++) {
+ if (array[i].size == size)
+ continue;
+
+ if (array[i].size != 0)
+ wave5_vdi_free_dma_memory(vpu_dev, &array[i]);
+
+ ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_buf);
+ if (ret)
+ return -ENOMEM;
+ array[i] = vb_buf;
+ }
+
+ for (i = count; i < MAX_REG_FRAME; i++) {
+ ret = wave5_vdi_free_dma_memory(vpu_dev, &array[i]);
+ if (ret)
+ break;
+ }
+
+ return 0;
+}
+
+void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev)
+{
+ struct vpu_buf *vb = &vpu_dev->sram_buf;
+
+ if (!vpu_dev->sram_pool || !vpu_dev->sram_size)
+ return;
+
+ if (!vb->vaddr) {
+ vb->size = vpu_dev->sram_size;
+ vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size,
+ &vb->daddr);
+ if (!vb->vaddr)
+ vb->size = 0;
+ }
+
+ dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n",
+ __func__, &vb->daddr, vb->size, vb->vaddr);
+}
+
+void wave5_vdi_free_sram(struct vpu_device *vpu_dev)
+{
+ struct vpu_buf *vb = &vpu_dev->sram_buf;
+
+ if (!vb->size || !vb->vaddr)
+ return;
+
+ if (vb->vaddr)
+ gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr,
+ vb->size);
+
+ memset(vb, 0, sizeof(*vb));
+}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.h b/drivers/media/platform/chips-media/wave5/wave5-vdi.h
new file mode 100644
index 000000000000..6a5a48cc6a35
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - low level access functions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef _VDI_H_
+#define _VDI_H_
+
+#include "wave5-vpuconfig.h"
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+/************************************************************************/
+/* COMMON REGISTERS */
+/************************************************************************/
+#define VPU_PRODUCT_CODE_REGISTER 0x1044
+
+/* system register write */
+#define vpu_write_reg(VPU_INST, ADDR, DATA) wave5_vdi_write_register(VPU_INST, ADDR, DATA)
+/* system register read */
+#define vpu_read_reg(CORE, ADDR) wave5_vdi_read_register(CORE, ADDR)
+
+struct vpu_buf {
+ size_t size;
+ dma_addr_t daddr;
+ void *vaddr;
+};
+
+int wave5_vdi_init(struct device *dev);
+int wave5_vdi_release(struct device *dev); //this function may be called only at system off.
+
+#endif //#ifndef _VDI_H_
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
new file mode 100644
index 000000000000..6b2552eeb3c9
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
@@ -0,0 +1,958 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - helper functions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#include <linux/bug.h>
+#include "wave5-vpuapi.h"
+#include "wave5-regdefine.h"
+#include "wave5.h"
+
+#define DECODE_ALL_TEMPORAL_LAYERS 0
+#define DECODE_ALL_SPATIAL_LAYERS 0
+
+static int wave5_initialize_vpu(struct device *dev, u8 *code, size_t size)
+{
+ int ret;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ if (wave5_vpu_is_init(vpu_dev)) {
+ wave5_vpu_re_init(dev, (void *)code, size);
+ ret = -EBUSY;
+ goto err_out;
+ }
+
+ ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT);
+ if (ret)
+ goto err_out;
+
+ ret = wave5_vpu_init(dev, (void *)code, size);
+
+err_out:
+ mutex_unlock(&vpu_dev->hw_lock);
+ return ret;
+}
+
+int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size)
+{
+ if (!bitcode || size == 0)
+ return -EINVAL;
+
+ return wave5_initialize_vpu(dev, bitcode, size);
+}
+
+int wave5_vpu_flush_instance(struct vpu_instance *inst)
+{
+ int ret = 0;
+ int retry = 0;
+
+ ret = mutex_lock_interruptible(&inst->dev->hw_lock);
+ if (ret)
+ return ret;
+ do {
+ /*
+ * Repeat the FLUSH command until the firmware reports that the
+ * VPU isn't running anymore
+ */
+ ret = wave5_vpu_hw_flush_instance(inst);
+ if (ret < 0 && ret != -EBUSY) {
+ dev_warn(inst->dev->dev, "Flush of %s instance with id: %d fail: %d\n",
+ inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id,
+ ret);
+ mutex_unlock(&inst->dev->hw_lock);
+ return ret;
+ }
+ if (ret == -EBUSY && retry++ >= MAX_FIRMWARE_CALL_RETRY) {
+ dev_warn(inst->dev->dev, "Flush of %s instance with id: %d timed out!\n",
+ inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id);
+ mutex_unlock(&inst->dev->hw_lock);
+ return -ETIMEDOUT;
+ }
+ } while (ret != 0);
+ mutex_unlock(&inst->dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id)
+{
+ int ret;
+ struct vpu_device *vpu_dev = dev_get_drvdata(dev);
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ if (!wave5_vpu_is_init(vpu_dev)) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ if (product_id)
+ *product_id = vpu_dev->product;
+ ret = wave5_vpu_get_version(vpu_dev, revision);
+
+err_out:
+ mutex_unlock(&vpu_dev->hw_lock);
+ return ret;
+}
+
+static int wave5_check_dec_open_param(struct vpu_instance *inst, struct dec_open_param *param)
+{
+ if (inst->id >= MAX_NUM_INSTANCE) {
+ dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n",
+ inst->id, MAX_NUM_INSTANCE);
+ return -EOPNOTSUPP;
+ }
+
+ if (param->bitstream_buffer % 8) {
+ dev_err(inst->dev->dev,
+ "Bitstream buffer must be aligned to a multiple of 8\n");
+ return -EINVAL;
+ }
+
+ if (param->bitstream_buffer_size % 1024 ||
+ param->bitstream_buffer_size < MIN_BITSTREAM_BUFFER_SIZE) {
+ dev_err(inst->dev->dev,
+ "Bitstream buffer size must be aligned to a multiple of 1024 and have a minimum size of %d\n",
+ MIN_BITSTREAM_BUFFER_SIZE);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param)
+{
+ struct dec_info *p_dec_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+ dma_addr_t buffer_addr;
+ size_t buffer_size;
+
+ ret = wave5_check_dec_open_param(inst, open_param);
+ if (ret)
+ return ret;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ if (!wave5_vpu_is_init(vpu_dev)) {
+ mutex_unlock(&vpu_dev->hw_lock);
+ return -ENODEV;
+ }
+
+ p_dec_info = &inst->codec_info->dec_info;
+ memcpy(&p_dec_info->open_param, open_param, sizeof(struct dec_open_param));
+
+ buffer_addr = open_param->bitstream_buffer;
+ buffer_size = open_param->bitstream_buffer_size;
+ p_dec_info->stream_wr_ptr = buffer_addr;
+ p_dec_info->stream_rd_ptr = buffer_addr;
+ p_dec_info->stream_buf_start_addr = buffer_addr;
+ p_dec_info->stream_buf_size = buffer_size;
+ p_dec_info->stream_buf_end_addr = buffer_addr + buffer_size;
+ p_dec_info->reorder_enable = TRUE;
+ p_dec_info->temp_id_select_mode = TEMPORAL_ID_MODE_ABSOLUTE;
+ p_dec_info->target_temp_id = DECODE_ALL_TEMPORAL_LAYERS;
+ p_dec_info->target_spatial_id = DECODE_ALL_SPATIAL_LAYERS;
+
+ ret = wave5_vpu_build_up_dec_param(inst, open_param);
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+static int reset_auxiliary_buffers(struct vpu_instance *inst, unsigned int index)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+
+ if (index >= MAX_REG_FRAME)
+ return -EINVAL;
+
+ if (p_dec_info->vb_mv[index].size == 0 && p_dec_info->vb_fbc_y_tbl[index].size == 0 &&
+ p_dec_info->vb_fbc_c_tbl[index].size == 0)
+ return -EINVAL;
+
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[index]);
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[index]);
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[index]);
+
+ return 0;
+}
+
+int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret;
+ int retry = 0;
+ struct vpu_device *vpu_dev = inst->dev;
+ int i;
+
+ *fail_res = 0;
+ if (!inst->codec_info)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ do {
+ ret = wave5_vpu_dec_finish_seq(inst, fail_res);
+ if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
+ dev_warn(inst->dev->dev, "dec_finish_seq timed out\n");
+ goto unlock_and_return;
+ }
+
+ if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
+ retry++ >= MAX_FIRMWARE_CALL_RETRY) {
+ ret = -ETIMEDOUT;
+ goto unlock_and_return;
+ }
+ } while (ret != 0);
+
+ dev_dbg(inst->dev->dev, "%s: dec_finish_seq complete\n", __func__);
+
+ wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
+
+ for (i = 0 ; i < MAX_REG_FRAME; i++) {
+ ret = reset_auxiliary_buffers(inst, i);
+ if (ret)
+ break;
+ }
+
+ wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task);
+
+unlock_and_return:
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst)
+{
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_dec_init_seq(inst);
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_dec_get_seq_info(inst, info);
+ if (!ret)
+ p_dec_info->initial_info_obtained = true;
+
+ info->rd_ptr = wave5_dec_get_rd_ptr(inst);
+ info->wr_ptr = p_dec_info->stream_wr_ptr;
+
+ p_dec_info->initial_info = *info;
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs,
+ int num_of_display_fbs, int stride, int height)
+{
+ struct dec_info *p_dec_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+ struct frame_buffer *fb;
+
+ if (num_of_decoding_fbs >= WAVE5_MAX_FBS || num_of_display_fbs >= WAVE5_MAX_FBS)
+ return -EINVAL;
+
+ p_dec_info = &inst->codec_info->dec_info;
+ p_dec_info->num_of_decoding_fbs = num_of_decoding_fbs;
+ p_dec_info->num_of_display_fbs = num_of_display_fbs;
+ p_dec_info->stride = stride;
+
+ if (!p_dec_info->initial_info_obtained)
+ return -EINVAL;
+
+ if (stride < p_dec_info->initial_info.pic_width || (stride % 8 != 0) ||
+ height < p_dec_info->initial_info.pic_height)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ fb = inst->frame_buf;
+ ret = wave5_vpu_dec_register_framebuffer(inst, &fb[p_dec_info->num_of_decoding_fbs],
+ LINEAR_FRAME_MAP, p_dec_info->num_of_display_fbs);
+ if (ret)
+ goto err_out;
+
+ ret = wave5_vpu_dec_register_framebuffer(inst, &fb[0], COMPRESSED_FRAME_MAP,
+ p_dec_info->num_of_decoding_fbs);
+
+err_out:
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr,
+ dma_addr_t *pwr_ptr, size_t *size)
+{
+ struct dec_info *p_dec_info;
+ dma_addr_t rd_ptr;
+ dma_addr_t wr_ptr;
+ int room;
+ struct vpu_device *vpu_dev = inst->dev;
+ int ret;
+
+ p_dec_info = &inst->codec_info->dec_info;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+ rd_ptr = wave5_dec_get_rd_ptr(inst);
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ wr_ptr = p_dec_info->stream_wr_ptr;
+
+ if (wr_ptr < rd_ptr)
+ room = rd_ptr - wr_ptr;
+ else
+ room = (p_dec_info->stream_buf_end_addr - wr_ptr) +
+ (rd_ptr - p_dec_info->stream_buf_start_addr);
+ room--;
+
+ if (prd_ptr)
+ *prd_ptr = rd_ptr;
+ if (pwr_ptr)
+ *pwr_ptr = wr_ptr;
+ if (size)
+ *size = room;
+
+ return 0;
+}
+
+int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size)
+{
+ struct dec_info *p_dec_info;
+ dma_addr_t wr_ptr;
+ dma_addr_t rd_ptr;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ if (!inst->codec_info)
+ return -EINVAL;
+
+ p_dec_info = &inst->codec_info->dec_info;
+ wr_ptr = p_dec_info->stream_wr_ptr;
+ rd_ptr = p_dec_info->stream_rd_ptr;
+
+ if (size > 0) {
+ if (wr_ptr < rd_ptr && rd_ptr <= wr_ptr + size)
+ return -EINVAL;
+
+ wr_ptr += size;
+
+ if (wr_ptr > p_dec_info->stream_buf_end_addr) {
+ u32 room = wr_ptr - p_dec_info->stream_buf_end_addr;
+
+ wr_ptr = p_dec_info->stream_buf_start_addr;
+ wr_ptr += room;
+ } else if (wr_ptr == p_dec_info->stream_buf_end_addr) {
+ wr_ptr = p_dec_info->stream_buf_start_addr;
+ }
+
+ p_dec_info->stream_wr_ptr = wr_ptr;
+ p_dec_info->stream_rd_ptr = rd_ptr;
+ }
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+ ret = wave5_vpu_dec_set_bitstream_flag(inst, (size == 0));
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ if (p_dec_info->stride == 0) /* this means frame buffers have not been registered. */
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_decode(inst, res_fail);
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ ret = wave5_dec_set_rd_ptr(inst, addr);
+
+ p_dec_info->stream_rd_ptr = addr;
+ if (update_wr_ptr)
+ p_dec_info->stream_wr_ptr = addr;
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst)
+{
+ int ret;
+ dma_addr_t rd_ptr;
+
+ ret = mutex_lock_interruptible(&inst->dev->hw_lock);
+ if (ret)
+ return ret;
+
+ rd_ptr = wave5_dec_get_rd_ptr(inst);
+
+ mutex_unlock(&inst->dev->hw_lock);
+
+ return rd_ptr;
+}
+
+int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info)
+{
+ struct dec_info *p_dec_info;
+ int ret;
+ struct vpu_rect rect_info;
+ u32 val;
+ u32 decoded_index;
+ u32 disp_idx;
+ u32 max_dec_index;
+ struct vpu_device *vpu_dev = inst->dev;
+ struct dec_output_info *disp_info;
+
+ if (!info)
+ return -EINVAL;
+
+ p_dec_info = &inst->codec_info->dec_info;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ memset(info, 0, sizeof(*info));
+
+ ret = wave5_vpu_dec_get_result(inst, info);
+ if (ret) {
+ info->rd_ptr = p_dec_info->stream_rd_ptr;
+ info->wr_ptr = p_dec_info->stream_wr_ptr;
+ goto err_out;
+ }
+
+ decoded_index = info->index_frame_decoded;
+
+ /* calculate display frame region */
+ val = 0;
+ rect_info.left = 0;
+ rect_info.right = 0;
+ rect_info.top = 0;
+ rect_info.bottom = 0;
+
+ if (decoded_index < WAVE5_MAX_FBS) {
+ if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC)
+ rect_info = p_dec_info->initial_info.pic_crop_rect;
+
+ if (inst->std == W_HEVC_DEC)
+ p_dec_info->dec_out_info[decoded_index].decoded_poc = info->decoded_poc;
+
+ p_dec_info->dec_out_info[decoded_index].rc_decoded = rect_info;
+ }
+ info->rc_decoded = rect_info;
+
+ disp_idx = info->index_frame_display;
+ disp_info = &p_dec_info->dec_out_info[disp_idx];
+ if (info->index_frame_display >= 0 && info->index_frame_display < WAVE5_MAX_FBS) {
+ if (info->index_frame_display != info->index_frame_decoded) {
+ /*
+ * when index_frame_decoded < 0, and index_frame_display >= 0
+ * info->dec_pic_width and info->dec_pic_height are still valid
+ * but those of p_dec_info->dec_out_info[disp_idx] are invalid in VP9
+ */
+ info->disp_pic_width = disp_info->dec_pic_width;
+ info->disp_pic_height = disp_info->dec_pic_height;
+ } else {
+ info->disp_pic_width = info->dec_pic_width;
+ info->disp_pic_height = info->dec_pic_height;
+ }
+
+ info->rc_display = disp_info->rc_decoded;
+
+ } else {
+ info->rc_display.left = 0;
+ info->rc_display.right = 0;
+ info->rc_display.top = 0;
+ info->rc_display.bottom = 0;
+ info->disp_pic_width = 0;
+ info->disp_pic_height = 0;
+ }
+
+ p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst);
+ p_dec_info->frame_display_flag = vpu_read_reg(vpu_dev, W5_RET_DEC_DISP_IDC);
+
+ val = p_dec_info->num_of_decoding_fbs; //fb_offset
+
+ max_dec_index = (p_dec_info->num_of_decoding_fbs > p_dec_info->num_of_display_fbs) ?
+ p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs;
+
+ if (info->index_frame_display >= 0 &&
+ info->index_frame_display < (int)max_dec_index)
+ info->disp_frame = inst->frame_buf[val + info->index_frame_display];
+
+ info->rd_ptr = p_dec_info->stream_rd_ptr;
+ info->wr_ptr = p_dec_info->stream_wr_ptr;
+ info->frame_display_flag = p_dec_info->frame_display_flag;
+
+ info->sequence_no = p_dec_info->initial_info.sequence_no;
+ if (decoded_index < WAVE5_MAX_FBS)
+ p_dec_info->dec_out_info[decoded_index] = *info;
+
+ if (disp_idx < WAVE5_MAX_FBS)
+ info->disp_frame.sequence_no = info->sequence_no;
+
+ if (info->sequence_changed) {
+ memcpy((void *)&p_dec_info->initial_info, (void *)&p_dec_info->new_seq_info,
+ sizeof(struct dec_initial_info));
+ p_dec_info->initial_info.sequence_no++;
+ }
+
+err_out:
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret = 0;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ if (index >= p_dec_info->num_of_display_fbs)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+ ret = wave5_dec_clr_disp_flag(inst, index);
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret = 0;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ if (index >= p_dec_info->num_of_display_fbs)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+ ret = wave5_dec_set_disp_flag(inst, index);
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index)
+{
+ if (index >= MAX_REG_FRAME)
+ return -EINVAL;
+
+ if (inst->frame_vbuf[index].size == 0)
+ return -EINVAL;
+
+ wave5_vdi_free_dma_memory(inst->dev, &inst->frame_vbuf[index]);
+
+ return 0;
+}
+
+int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter)
+{
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ int ret = 0;
+
+ switch (cmd) {
+ case DEC_GET_QUEUE_STATUS: {
+ struct queue_status_info *queue_info = parameter;
+
+ queue_info->instance_queue_count = p_dec_info->instance_queue_count;
+ queue_info->report_queue_count = p_dec_info->report_queue_count;
+ break;
+ }
+ case DEC_RESET_FRAMEBUF_INFO: {
+ int i;
+
+ for (i = 0; i < MAX_REG_FRAME; i++) {
+ ret = wave5_vpu_dec_reset_framebuffer(inst, i);
+ if (ret)
+ break;
+ }
+
+ for (i = 0; i < MAX_REG_FRAME; i++) {
+ ret = reset_auxiliary_buffers(inst, i);
+ if (ret)
+ break;
+ }
+
+ wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
+ break;
+ }
+ case DEC_GET_SEQ_INFO: {
+ struct dec_initial_info *seq_info = parameter;
+
+ *seq_info = p_dec_info->initial_info;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param)
+{
+ struct enc_info *p_enc_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ ret = wave5_vpu_enc_check_open_param(inst, open_param);
+ if (ret)
+ return ret;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ if (!wave5_vpu_is_init(vpu_dev)) {
+ mutex_unlock(&vpu_dev->hw_lock);
+ return -ENODEV;
+ }
+
+ p_enc_info = &inst->codec_info->enc_info;
+ p_enc_info->open_param = *open_param;
+
+ ret = wave5_vpu_build_up_enc_param(vpu_dev->dev, inst, open_param);
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ int ret;
+ int retry = 0;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ *fail_res = 0;
+ if (!inst->codec_info)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ do {
+ ret = wave5_vpu_enc_finish_seq(inst, fail_res);
+ if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
+ dev_warn(inst->dev->dev, "enc_finish_seq timed out\n");
+ mutex_unlock(&vpu_dev->hw_lock);
+ return ret;
+ }
+
+ if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
+ retry++ >= MAX_FIRMWARE_CALL_RETRY) {
+ mutex_unlock(&vpu_dev->hw_lock);
+ return -ETIMEDOUT;
+ }
+ } while (ret != 0);
+
+ dev_dbg(inst->dev->dev, "%s: enc_finish_seq complete\n", __func__);
+
+ wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work);
+
+ if (inst->std == W_HEVC_ENC || inst->std == W_AVC_ENC) {
+ wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_sub_sam_buf);
+ wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_mv);
+ wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_y_tbl);
+ wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_c_tbl);
+ }
+
+ wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task);
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return 0;
+}
+
+int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num,
+ unsigned int stride, int height,
+ enum tiled_map_type map_type)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+ unsigned int size_luma, size_chroma;
+ int i;
+
+ if (p_enc_info->stride)
+ return -EINVAL;
+
+ if (!p_enc_info->initial_info_obtained)
+ return -EINVAL;
+
+ if (num < p_enc_info->initial_info.min_frame_buffer_count)
+ return -EINVAL;
+
+ if (stride == 0 || stride % 8 != 0)
+ return -EINVAL;
+
+ if (height <= 0)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ p_enc_info->num_frame_buffers = num;
+ p_enc_info->stride = stride;
+
+ size_luma = stride * height;
+ size_chroma = ALIGN(stride / 2, 16) * height;
+
+ for (i = 0; i < num; i++) {
+ if (!inst->frame_buf[i].update_fb_info)
+ continue;
+
+ inst->frame_buf[i].update_fb_info = false;
+ inst->frame_buf[i].stride = stride;
+ inst->frame_buf[i].height = height;
+ inst->frame_buf[i].map_type = COMPRESSED_FRAME_MAP;
+ inst->frame_buf[i].buf_y_size = size_luma;
+ inst->frame_buf[i].buf_cb = inst->frame_buf[i].buf_y + size_luma;
+ inst->frame_buf[i].buf_cb_size = size_chroma;
+ inst->frame_buf[i].buf_cr_size = 0;
+ }
+
+ ret = wave5_vpu_enc_register_framebuffer(inst->dev->dev, inst, &inst->frame_buf[0],
+ COMPRESSED_FRAME_MAP,
+ p_enc_info->num_frame_buffers);
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+static int wave5_check_enc_param(struct vpu_instance *inst, struct enc_param *param)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+
+ if (!param)
+ return -EINVAL;
+
+ if (!param->source_frame)
+ return -EINVAL;
+
+ if (p_enc_info->open_param.bit_rate == 0 && inst->std == W_HEVC_ENC) {
+ if (param->pic_stream_buffer_addr % 16 || param->pic_stream_buffer_size == 0)
+ return -EINVAL;
+ }
+ if (param->pic_stream_buffer_addr % 8 || param->pic_stream_buffer_size == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param, u32 *fail_res)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ *fail_res = 0;
+
+ if (p_enc_info->stride == 0) /* this means frame buffers have not been registered. */
+ return -EINVAL;
+
+ ret = wave5_check_enc_param(inst, param);
+ if (ret)
+ return ret;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ p_enc_info->pts_map[param->src_idx] = param->pts;
+
+ ret = wave5_vpu_encode(inst, param, fail_res);
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_enc_get_result(inst, info);
+ if (ret) {
+ info->pts = 0;
+ goto unlock;
+ }
+
+ if (info->recon_frame_index >= 0)
+ info->pts = p_enc_info->pts_map[info->enc_src_idx];
+
+unlock:
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+
+ switch (cmd) {
+ case ENABLE_ROTATION:
+ p_enc_info->rotation_enable = true;
+ break;
+ case ENABLE_MIRRORING:
+ p_enc_info->mirror_enable = true;
+ break;
+ case SET_MIRROR_DIRECTION: {
+ enum mirror_direction mir_dir;
+
+ mir_dir = *(enum mirror_direction *)parameter;
+ if (mir_dir != MIRDIR_NONE && mir_dir != MIRDIR_HOR &&
+ mir_dir != MIRDIR_VER && mir_dir != MIRDIR_HOR_VER)
+ return -EINVAL;
+ p_enc_info->mirror_direction = mir_dir;
+ break;
+ }
+ case SET_ROTATION_ANGLE: {
+ int angle;
+
+ angle = *(int *)parameter;
+ if (angle && angle != 90 && angle != 180 && angle != 270)
+ return -EINVAL;
+ if (p_enc_info->initial_info_obtained && (angle == 90 || angle == 270))
+ return -EINVAL;
+ p_enc_info->rotation_angle = angle;
+ break;
+ }
+ case ENC_GET_QUEUE_STATUS: {
+ struct queue_status_info *queue_info = parameter;
+
+ queue_info->instance_queue_count = p_enc_info->instance_queue_count;
+ queue_info->report_queue_count = p_enc_info->report_queue_count;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst)
+{
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_enc_init_seq(inst);
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return ret;
+}
+
+int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ int ret;
+ struct vpu_device *vpu_dev = inst->dev;
+
+ if (!info)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
+ if (ret)
+ return ret;
+
+ ret = wave5_vpu_enc_get_seq_info(inst, info);
+ if (ret) {
+ p_enc_info->initial_info_obtained = false;
+ mutex_unlock(&vpu_dev->hw_lock);
+ return ret;
+ }
+
+ p_enc_info->initial_info_obtained = true;
+ p_enc_info->initial_info = *info;
+
+ mutex_unlock(&vpu_dev->hw_lock);
+
+ return 0;
+}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
new file mode 100644
index 000000000000..d33769ca9c2f
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
@@ -0,0 +1,874 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - helper definitions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef VPUAPI_H_INCLUDED
+#define VPUAPI_H_INCLUDED
+
+#include <linux/kfifo.h>
+#include <linux/idr.h>
+#include <linux/genalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ctrls.h>
+#include "wave5-vpuerror.h"
+#include "wave5-vpuconfig.h"
+#include "wave5-vdi.h"
+
+enum product_id {
+ PRODUCT_ID_521,
+ PRODUCT_ID_511,
+ PRODUCT_ID_517,
+ PRODUCT_ID_NONE,
+};
+
+struct vpu_attr;
+
+enum vpu_instance_type {
+ VPU_INST_TYPE_DEC = 0,
+ VPU_INST_TYPE_ENC = 1
+};
+
+enum vpu_instance_state {
+ VPU_INST_STATE_NONE = 0,
+ VPU_INST_STATE_OPEN = 1,
+ VPU_INST_STATE_INIT_SEQ = 2,
+ VPU_INST_STATE_PIC_RUN = 3,
+ VPU_INST_STATE_STOP = 4
+};
+
+#define WAVE5_MAX_FBS 32
+
+#define MAX_REG_FRAME (WAVE5_MAX_FBS * 2)
+
+#define WAVE5_DEC_HEVC_BUF_SIZE(_w, _h) (DIV_ROUND_UP(_w, 64) * DIV_ROUND_UP(_h, 64) * 256 + 64)
+#define WAVE5_DEC_AVC_BUF_SIZE(_w, _h) ((((ALIGN(_w, 256) / 16) * (ALIGN(_h, 16) / 16)) + 16) * 80)
+
+#define WAVE5_FBC_LUMA_TABLE_SIZE(_w, _h) (ALIGN(_h, 64) * ALIGN(_w, 256) / 32)
+#define WAVE5_FBC_CHROMA_TABLE_SIZE(_w, _h) (ALIGN((_h), 64) * ALIGN((_w) / 2, 256) / 32)
+#define WAVE5_ENC_AVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) * ALIGN(_h, 64) / 32)
+#define WAVE5_ENC_HEVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) / 64 * ALIGN(_h, 64) / 64 * 128)
+
+/*
+ * common struct and definition
+ */
+enum cod_std {
+ STD_AVC = 0,
+ STD_HEVC = 12,
+ STD_MAX
+};
+
+enum wave_std {
+ W_HEVC_DEC = 0x00,
+ W_HEVC_ENC = 0x01,
+ W_AVC_DEC = 0x02,
+ W_AVC_ENC = 0x03,
+ STD_UNKNOWN = 0xFF
+};
+
+enum set_param_option {
+ OPT_COMMON = 0, /* SET_PARAM command option for encoding sequence */
+ OPT_CUSTOM_GOP = 1, /* SET_PARAM command option for setting custom GOP */
+ OPT_CUSTOM_HEADER = 2, /* SET_PARAM command option for setting custom VPS/SPS/PPS */
+ OPT_VUI = 3, /* SET_PARAM command option for encoding VUI */
+ OPT_CHANGE_PARAM = 0x10,
+};
+
+/************************************************************************/
+/* PROFILE & LEVEL */
+/************************************************************************/
+/* HEVC */
+#define HEVC_PROFILE_MAIN 1
+#define HEVC_PROFILE_MAIN10 2
+#define HEVC_PROFILE_STILLPICTURE 3
+#define HEVC_PROFILE_MAIN10_STILLPICTURE 2
+
+/* H.264 profile for encoder*/
+#define H264_PROFILE_BP 1
+#define H264_PROFILE_MP 2
+#define H264_PROFILE_EXTENDED 3
+#define H264_PROFILE_HP 4
+#define H264_PROFILE_HIGH10 5
+#define H264_PROFILE_HIGH422 6
+#define H264_PROFILE_HIGH444 7
+
+/************************************************************************/
+/* error codes */
+/************************************************************************/
+
+/************************************************************************/
+/* utility macros */
+/************************************************************************/
+
+/* Initialize sequence firmware command mode */
+#define INIT_SEQ_NORMAL 1
+
+/* Decode firmware command mode */
+#define DEC_PIC_NORMAL 0
+
+/* bit_alloc_mode */
+#define BIT_ALLOC_MODE_FIXED_RATIO 2
+
+/* bit_rate */
+#define MAX_BIT_RATE 700000000
+
+/* decoding_refresh_type */
+#define DEC_REFRESH_TYPE_NON_IRAP 0
+#define DEC_REFRESH_TYPE_CRA 1
+#define DEC_REFRESH_TYPE_IDR 2
+
+/* depend_slice_mode */
+#define DEPEND_SLICE_MODE_RECOMMENDED 1
+#define DEPEND_SLICE_MODE_BOOST 2
+#define DEPEND_SLICE_MODE_FAST 3
+
+/* hvs_max_delta_qp */
+#define MAX_HVS_MAX_DELTA_QP 51
+
+/* intra_refresh_mode */
+#define REFRESH_MODE_CTU_ROWS 1
+#define REFRESH_MODE_CTU_COLUMNS 2
+#define REFRESH_MODE_CTU_STEP_SIZE 3
+#define REFRESH_MODE_CTUS 4
+
+/* intra_mb_refresh_mode */
+#define REFRESH_MB_MODE_NONE 0
+#define REFRESH_MB_MODE_CTU_ROWS 1
+#define REFRESH_MB_MODE_CTU_COLUMNS 2
+#define REFRESH_MB_MODE_CTU_STEP_SIZE 3
+
+/* intra_qp */
+#define MAX_INTRA_QP 63
+
+/* nr_inter_weight_* */
+#define MAX_INTER_WEIGHT 31
+
+/* nr_intra_weight_* */
+#define MAX_INTRA_WEIGHT 31
+
+/* nr_noise_sigma_* */
+#define MAX_NOISE_SIGMA 255
+
+/* bitstream_buffer_size */
+#define MIN_BITSTREAM_BUFFER_SIZE 1024
+#define MIN_BITSTREAM_BUFFER_SIZE_WAVE521 (1024 * 64)
+
+/* vbv_buffer_size */
+#define MIN_VBV_BUFFER_SIZE 10
+#define MAX_VBV_BUFFER_SIZE 3000
+
+#define BUFFER_MARGIN 4096
+
+#define MAX_FIRMWARE_CALL_RETRY 10
+
+#define VDI_LITTLE_ENDIAN 0x0
+
+/*
+ * Parameters of DEC_SET_SEQ_CHANGE_MASK
+ */
+#define SEQ_CHANGE_ENABLE_PROFILE BIT(5)
+#define SEQ_CHANGE_ENABLE_SIZE BIT(16)
+#define SEQ_CHANGE_ENABLE_BITDEPTH BIT(18)
+#define SEQ_CHANGE_ENABLE_DPB_COUNT BIT(19)
+#define SEQ_CHANGE_ENABLE_ASPECT_RATIO BIT(21)
+#define SEQ_CHANGE_ENABLE_VIDEO_SIGNAL BIT(23)
+#define SEQ_CHANGE_ENABLE_VUI_TIMING_INFO BIT(29)
+
+#define SEQ_CHANGE_ENABLE_ALL_HEVC (SEQ_CHANGE_ENABLE_PROFILE | \
+ SEQ_CHANGE_ENABLE_SIZE | \
+ SEQ_CHANGE_ENABLE_BITDEPTH | \
+ SEQ_CHANGE_ENABLE_DPB_COUNT)
+
+#define SEQ_CHANGE_ENABLE_ALL_AVC (SEQ_CHANGE_ENABLE_SIZE | \
+ SEQ_CHANGE_ENABLE_BITDEPTH | \
+ SEQ_CHANGE_ENABLE_DPB_COUNT | \
+ SEQ_CHANGE_ENABLE_ASPECT_RATIO | \
+ SEQ_CHANGE_ENABLE_VIDEO_SIGNAL | \
+ SEQ_CHANGE_ENABLE_VUI_TIMING_INFO)
+
+#define DISPLAY_IDX_FLAG_SEQ_END -1
+#define DISPLAY_IDX_FLAG_NO_FB -3
+#define DECODED_IDX_FLAG_NO_FB -1
+#define DECODED_IDX_FLAG_SKIP -2
+
+#define RECON_IDX_FLAG_ENC_END -1
+#define RECON_IDX_FLAG_ENC_DELAY -2
+#define RECON_IDX_FLAG_HEADER_ONLY -3
+#define RECON_IDX_FLAG_CHANGE_PARAM -4
+
+enum codec_command {
+ ENABLE_ROTATION,
+ ENABLE_MIRRORING,
+ SET_MIRROR_DIRECTION,
+ SET_ROTATION_ANGLE,
+ DEC_GET_QUEUE_STATUS,
+ ENC_GET_QUEUE_STATUS,
+ DEC_RESET_FRAMEBUF_INFO,
+ DEC_GET_SEQ_INFO,
+};
+
+enum mirror_direction {
+ MIRDIR_NONE, /* no mirroring */
+ MIRDIR_VER, /* vertical mirroring */
+ MIRDIR_HOR, /* horizontal mirroring */
+ MIRDIR_HOR_VER /* horizontal and vertical mirroring */
+};
+
+enum frame_buffer_format {
+ FORMAT_ERR = -1,
+ FORMAT_420 = 0, /* 8bit */
+ FORMAT_422, /* 8bit */
+ FORMAT_224, /* 8bit */
+ FORMAT_444, /* 8bit */
+ FORMAT_400, /* 8bit */
+
+ /* little endian perspective */
+ /* | addr 0 | addr 1 | */
+ FORMAT_420_P10_16BIT_MSB = 5, /* lsb |000000xx|xxxxxxxx | msb */
+ FORMAT_420_P10_16BIT_LSB, /* lsb |xxxxxxx |xx000000 | msb */
+ FORMAT_420_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */
+ FORMAT_420_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */
+
+ /* 4:2:2 packed format */
+ /* little endian perspective */
+ /* | addr 0 | addr 1 | */
+ FORMAT_422_P10_16BIT_MSB, /* lsb |000000xx |xxxxxxxx | msb */
+ FORMAT_422_P10_16BIT_LSB, /* lsb |xxxxxxxx |xx000000 | msb */
+ FORMAT_422_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */
+ FORMAT_422_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */
+
+ FORMAT_YUYV, /* 8bit packed format : Y0U0Y1V0 Y2U1Y3V1 ... */
+ FORMAT_YUYV_P10_16BIT_MSB,
+ FORMAT_YUYV_P10_16BIT_LSB,
+ FORMAT_YUYV_P10_32BIT_MSB,
+ FORMAT_YUYV_P10_32BIT_LSB,
+
+ FORMAT_YVYU, /* 8bit packed format : Y0V0Y1U0 Y2V1Y3U1 ... */
+ FORMAT_YVYU_P10_16BIT_MSB,
+ FORMAT_YVYU_P10_16BIT_LSB,
+ FORMAT_YVYU_P10_32BIT_MSB,
+ FORMAT_YVYU_P10_32BIT_LSB,
+
+ FORMAT_UYVY, /* 8bit packed format : U0Y0V0Y1 U1Y2V1Y3 ... */
+ FORMAT_UYVY_P10_16BIT_MSB,
+ FORMAT_UYVY_P10_16BIT_LSB,
+ FORMAT_UYVY_P10_32BIT_MSB,
+ FORMAT_UYVY_P10_32BIT_LSB,
+
+ FORMAT_VYUY, /* 8bit packed format : V0Y0U0Y1 V1Y2U1Y3 ... */
+ FORMAT_VYUY_P10_16BIT_MSB,
+ FORMAT_VYUY_P10_16BIT_LSB,
+ FORMAT_VYUY_P10_32BIT_MSB,
+ FORMAT_VYUY_P10_32BIT_LSB,
+
+ FORMAT_MAX,
+};
+
+enum packed_format_num {
+ NOT_PACKED = 0,
+ PACKED_YUYV,
+ PACKED_YVYU,
+ PACKED_UYVY,
+ PACKED_VYUY,
+};
+
+enum wave5_interrupt_bit {
+ INT_WAVE5_INIT_VPU = 0,
+ INT_WAVE5_WAKEUP_VPU = 1,
+ INT_WAVE5_SLEEP_VPU = 2,
+ INT_WAVE5_CREATE_INSTANCE = 3,
+ INT_WAVE5_FLUSH_INSTANCE = 4,
+ INT_WAVE5_DESTROY_INSTANCE = 5,
+ INT_WAVE5_INIT_SEQ = 6,
+ INT_WAVE5_SET_FRAMEBUF = 7,
+ INT_WAVE5_DEC_PIC = 8,
+ INT_WAVE5_ENC_PIC = 8,
+ INT_WAVE5_ENC_SET_PARAM = 9,
+ INT_WAVE5_DEC_QUERY = 14,
+ INT_WAVE5_BSBUF_EMPTY = 15,
+ INT_WAVE5_BSBUF_FULL = 15,
+};
+
+enum pic_type {
+ PIC_TYPE_I = 0,
+ PIC_TYPE_P = 1,
+ PIC_TYPE_B = 2,
+ PIC_TYPE_IDR = 5, /* H.264/H.265 IDR (Instantaneous Decoder Refresh) picture */
+ PIC_TYPE_MAX /* no meaning */
+};
+
+enum sw_reset_mode {
+ SW_RESET_SAFETY,
+ SW_RESET_FORCE,
+ SW_RESET_ON_BOOT
+};
+
+enum tiled_map_type {
+ LINEAR_FRAME_MAP = 0, /* linear frame map type */
+ COMPRESSED_FRAME_MAP = 17, /* compressed frame map type*/
+};
+
+enum temporal_id_mode {
+ TEMPORAL_ID_MODE_ABSOLUTE,
+ TEMPORAL_ID_MODE_RELATIVE,
+};
+
+struct vpu_attr {
+ u32 product_id;
+ char product_name[8]; /* product name in ascii code */
+ u32 product_version;
+ u32 fw_version;
+ u32 customer_id;
+ u32 support_decoders; /* bitmask: see <<vpuapi_h_cod_std>> */
+ u32 support_encoders; /* bitmask: see <<vpuapi_h_cod_std>> */
+ u32 support_backbone: 1;
+ u32 support_avc10bit_enc: 1;
+ u32 support_hevc10bit_enc: 1;
+ u32 support_vcore_backbone: 1;
+ u32 support_vcpu_backbone: 1;
+};
+
+struct frame_buffer {
+ dma_addr_t buf_y;
+ dma_addr_t buf_cb;
+ dma_addr_t buf_cr;
+ unsigned int buf_y_size;
+ unsigned int buf_cb_size;
+ unsigned int buf_cr_size;
+ enum tiled_map_type map_type;
+ unsigned int stride; /* horizontal stride for the given frame buffer */
+ unsigned int width; /* width of the given frame buffer */
+ unsigned int height; /* height of the given frame buffer */
+ size_t size; /* size of the given frame buffer */
+ unsigned int sequence_no;
+ bool update_fb_info;
+};
+
+struct vpu_rect {
+ unsigned int left; /* horizontal pixel offset from left edge */
+ unsigned int top; /* vertical pixel offset from top edge */
+ unsigned int right; /* horizontal pixel offset from right edge */
+ unsigned int bottom; /* vertical pixel offset from bottom edge */
+};
+
+/*
+ * decode struct and definition
+ */
+
+struct dec_open_param {
+ dma_addr_t bitstream_buffer;
+ size_t bitstream_buffer_size;
+};
+
+struct dec_initial_info {
+ u32 pic_width;
+ u32 pic_height;
+ struct vpu_rect pic_crop_rect;
+ u32 min_frame_buffer_count; /* between 1 to 16 */
+
+ u32 profile;
+ u32 luma_bitdepth; /* bit-depth of the luma sample */
+ u32 chroma_bitdepth; /* bit-depth of the chroma sample */
+ u32 seq_init_err_reason;
+ dma_addr_t rd_ptr; /* read pointer of bitstream buffer */
+ dma_addr_t wr_ptr; /* write pointer of bitstream buffer */
+ u32 sequence_no;
+ u32 vlc_buf_size;
+ u32 param_buf_size;
+};
+
+struct dec_output_info {
+ /**
+ * This is a frame buffer index for the picture to be displayed at the moment
+ * among frame buffers which are registered using vpu_dec_register_frame_buffer().
+ * Frame data that will be displayed is stored in the frame buffer with this index
+ * When there is no display delay, this index is always the equal to
+ * index_frame_decoded, however, if displaying is delayed (for display
+ * reordering in AVC or B-frames in VC1), this index might be different to
+ * index_frame_decoded. By checking this index, HOST applications can easily figure
+ * out whether sequence decoding has been finished or not.
+ *
+ * -3(0xFFFD) or -2(0xFFFE) : when a display output cannot be given due to picture
+ * reordering or skip option
+ * -1(0xFFFF) : when there is no more output for display at the end of sequence
+ * decoding
+ */
+ s32 index_frame_display;
+ /**
+ * This is the frame buffer index of the decoded picture among the frame buffers which were
+ * registered using vpu_dec_register_frame_buffer(). The currently decoded frame is stored
+ * into the frame buffer specified by this index.
+ *
+ * -2 : indicates that no decoded output is generated because decoder meets EOS
+ * (end of sequence) or skip
+ * -1 : indicates that the decoder fails to decode a picture because there is no available
+ * frame buffer
+ */
+ s32 index_frame_decoded;
+ s32 index_frame_decoded_for_tiled;
+ u32 nal_type;
+ unsigned int pic_type;
+ struct vpu_rect rc_display;
+ unsigned int disp_pic_width;
+ unsigned int disp_pic_height;
+ struct vpu_rect rc_decoded;
+ u32 dec_pic_width;
+ u32 dec_pic_height;
+ s32 decoded_poc;
+ int temporal_id; /* temporal ID of the picture */
+ dma_addr_t rd_ptr; /* stream buffer read pointer for the current decoder instance */
+ dma_addr_t wr_ptr; /* stream buffer write pointer for the current decoder instance */
+ struct frame_buffer disp_frame;
+ u32 frame_display_flag; /* it reports a frame buffer flag to be displayed */
+ /**
+ * this variable reports that sequence has been changed while H.264/AVC stream decoding.
+ * if it is 1, HOST application can get the new sequence information by calling
+ * vpu_dec_get_initial_info() or wave5_vpu_dec_issue_seq_init().
+ *
+ * for H.265/HEVC decoder, each bit has a different meaning as follows.
+ *
+ * sequence_changed[5] : it indicates that the profile_idc has been changed
+ * sequence_changed[16] : it indicates that the resolution has been changed
+ * sequence_changed[19] : it indicates that the required number of frame buffer has
+ * been changed.
+ */
+ unsigned int frame_cycle; /* reports the number of cycles for processing a frame */
+ u32 sequence_no;
+
+ u32 dec_host_cmd_tick; /* tick of DEC_PIC command for the picture */
+ u32 dec_decode_end_tick; /* end tick of decoding slices of the picture */
+
+ u32 sequence_changed;
+};
+
+struct queue_status_info {
+ u32 instance_queue_count;
+ u32 report_queue_count;
+};
+
+/*
+ * encode struct and definition
+ */
+
+#define MAX_NUM_TEMPORAL_LAYER 7
+#define MAX_NUM_SPATIAL_LAYER 3
+#define MAX_GOP_NUM 8
+
+struct custom_gop_pic_param {
+ u32 pic_type; /* picture type of nth picture in the custom GOP */
+ u32 poc_offset; /* POC of nth picture in the custom GOP */
+ u32 pic_qp; /* quantization parameter of nth picture in the custom GOP */
+ u32 use_multi_ref_p; /* use multiref pic for P picture. valid only if PIC_TYPE is P */
+ u32 ref_poc_l0; /* POC of reference L0 of nth picture in the custom GOP */
+ u32 ref_poc_l1; /* POC of reference L1 of nth picture in the custom GOP */
+ s32 temporal_id; /* temporal ID of nth picture in the custom GOP */
+};
+
+struct enc_wave_param {
+ /*
+ * profile indicator (HEVC only)
+ *
+ * 0 : the firmware determines a profile according to the internal_bit_depth
+ * 1 : main profile
+ * 2 : main10 profile
+ * 3 : main still picture profile
+ * In the AVC encoder, a profile cannot be set by the host application.
+ * The firmware decides it based on internal_bit_depth.
+ * profile = HIGH (bitdepth 8) profile = HIGH10 (bitdepth 10)
+ */
+ u32 profile;
+ u32 level; /* level indicator (level * 10) */
+ u32 internal_bit_depth: 4; /* 8/10 */
+ u32 gop_preset_idx: 4; /* 0 - 9 */
+ u32 decoding_refresh_type: 2; /* 0=non-IRAP, 1=CRA, 2=IDR */
+ u32 intra_qp; /* quantization parameter of intra picture */
+ u32 intra_period; /* period of intra picture in GOP size */
+ u32 conf_win_top; /* top offset of conformance window */
+ u32 conf_win_bot; /* bottom offset of conformance window */
+ u32 conf_win_left; /* left offset of conformance window */
+ u32 conf_win_right; /* right offset of conformance window */
+ u32 intra_refresh_mode: 3;
+ /*
+ * Argument for intra_ctu_refresh_mode.
+ *
+ * Depending on intra_refresh_mode, it can mean one of the following:
+ * - intra_ctu_refresh_mode (1) -> number of consecutive CTU rows
+ * - intra_ctu_refresh_mode (2) -> the number of consecutive CTU columns
+ * - intra_ctu_refresh_mode (3) -> step size in CTU
+ * - intra_ctu_refresh_mode (4) -> number of intra ct_us to be encoded in a picture
+ */
+ u32 intra_refresh_arg;
+ /*
+ * 0 : custom setting
+ * 1 : recommended encoder parameters (slow encoding speed, highest picture quality)
+ * 2 : boost mode (normal encoding speed, moderate picture quality)
+ * 3 : fast mode (fast encoding speed, low picture quality)
+ */
+ u32 depend_slice_mode : 2;
+ u32 depend_slice_mode_arg;
+ u32 independ_slice_mode : 1; /* 0=no-multi-slice, 1=slice-in-ctu-number*/
+ u32 independ_slice_mode_arg;
+ u32 max_num_merge: 2;
+ s32 beta_offset_div2: 4; /* sets beta_offset_div2 for deblocking filter */
+ s32 tc_offset_div2: 4; /* sets tc_offset_div3 for deblocking filter */
+ u32 hvs_qp_scale: 4; /* QP scaling factor for CU QP adjust if hvs_qp_scale_enable is 1 */
+ u32 hvs_max_delta_qp; /* maximum delta QP for HVS */
+ s32 chroma_cb_qp_offset; /* the value of chroma(cb) QP offset */
+ s32 chroma_cr_qp_offset; /* the value of chroma(cr) QP offset */
+ s32 initial_rc_qp;
+ u32 nr_intra_weight_y;
+ u32 nr_intra_weight_cb; /* weight to cb noise level for intra picture (0 ~ 31) */
+ u32 nr_intra_weight_cr; /* weight to cr noise level for intra picture (0 ~ 31) */
+ u32 nr_inter_weight_y;
+ u32 nr_inter_weight_cb; /* weight to cb noise level for inter picture (0 ~ 31) */
+ u32 nr_inter_weight_cr; /* weight to cr noise level for inter picture (0 ~ 31) */
+ u32 min_qp_i; /* minimum QP of I picture for rate control */
+ u32 max_qp_i; /* maximum QP of I picture for rate control */
+ u32 min_qp_p; /* minimum QP of P picture for rate control */
+ u32 max_qp_p; /* maximum QP of P picture for rate control */
+ u32 min_qp_b; /* minimum QP of B picture for rate control */
+ u32 max_qp_b; /* maximum QP of B picture for rate control */
+ u32 avc_idr_period; /* period of IDR picture (0 ~ 1024). 0 - implies an infinite period */
+ u32 avc_slice_arg; /* the number of MB for a slice when avc_slice_mode is set with 1 */
+ u32 intra_mb_refresh_mode: 2; /* 0=none, 1=row, 2=column, 3=step-size-in-mb */
+ /**
+ * Argument for intra_mb_refresh_mode.
+ *
+ * intra_mb_refresh_mode (1) -> number of consecutive MB rows
+ * intra_mb_refresh_mode (2) ->the number of consecutive MB columns
+ * intra_mb_refresh_mode (3) -> step size in MB
+ */
+ u32 intra_mb_refresh_arg;
+ u32 rc_weight_param;
+ u32 rc_weight_buf;
+
+ /* flags */
+ u32 en_still_picture: 1; /* still picture profile */
+ u32 tier: 1; /* 0=main, 1=high */
+ u32 avc_slice_mode: 1; /* 0=none, 1=slice-in-mb-number */
+ u32 entropy_coding_mode: 1; /* 0=CAVLC, 1=CABAC */
+ u32 lossless_enable: 1; /* enable lossless encoding */
+ u32 const_intra_pred_flag: 1; /* enable constrained intra prediction */
+ u32 tmvp_enable: 1; /* enable temporal motion vector prediction */
+ u32 wpp_enable: 1;
+ u32 disable_deblk: 1; /* disable in-loop deblocking filtering */
+ u32 lf_cross_slice_boundary_enable: 1;
+ u32 skip_intra_trans: 1;
+ u32 sao_enable: 1; /* enable SAO (sample adaptive offset) */
+ u32 intra_nx_n_enable: 1; /* enables intra nx_n p_us */
+ u32 cu_level_rc_enable: 1; /* enable CU level rate control */
+ u32 hvs_qp_enable: 1; /* enable CU QP adjustment for subjective quality enhancement */
+ u32 strong_intra_smooth_enable: 1; /* enable strong intra smoothing */
+ u32 rdo_skip: 1; /* skip RDO (rate distortion optimization) */
+ u32 lambda_scaling_enable: 1; /* enable lambda scaling using custom GOP */
+ u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */
+ u32 mb_level_rc_enable: 1; /* enable MB-level rate control */
+};
+
+struct enc_open_param {
+ dma_addr_t bitstream_buffer;
+ unsigned int bitstream_buffer_size;
+ u32 pic_width; /* width of a picture to be encoded in unit of sample */
+ u32 pic_height; /* height of a picture to be encoded in unit of sample */
+ u32 frame_rate_info;/* desired fps */
+ u32 vbv_buffer_size;
+ u32 bit_rate; /* target bitrate in bps */
+ struct enc_wave_param wave_param;
+ enum packed_format_num packed_format; /* <<vpuapi_h_packed_format_num>> */
+ enum frame_buffer_format src_format;
+ bool line_buf_int_en;
+ u32 rc_enable : 1; /* rate control */
+};
+
+struct enc_initial_info {
+ u32 min_frame_buffer_count; /* minimum number of frame buffers */
+ u32 min_src_frame_count; /* minimum number of source buffers */
+ u32 seq_init_err_reason;
+ u32 warn_info;
+ u32 vlc_buf_size; /* size of task buffer */
+ u32 param_buf_size; /* size of task buffer */
+};
+
+/*
+ * Flags to encode NAL units explicitly
+ */
+struct enc_code_opt {
+ u32 implicit_header_encode: 1;
+ u32 encode_vcl: 1;
+ u32 encode_vps: 1;
+ u32 encode_sps: 1;
+ u32 encode_pps: 1;
+ u32 encode_aud: 1;
+ u32 encode_eos: 1;
+ u32 encode_eob: 1;
+ u32 encode_vui: 1;
+};
+
+struct enc_param {
+ struct frame_buffer *source_frame;
+ u32 pic_stream_buffer_addr;
+ u64 pic_stream_buffer_size;
+ u32 src_idx; /* source frame buffer index */
+ struct enc_code_opt code_option;
+ u64 pts; /* presentation timestamp (PTS) of the input source */
+ bool src_end_flag;
+};
+
+struct enc_output_info {
+ u32 bitstream_buffer;
+ u32 bitstream_size; /* byte size of encoded bitstream */
+ u32 pic_type: 2; /* <<vpuapi_h_pic_type>> */
+ s32 recon_frame_index;
+ dma_addr_t rd_ptr;
+ dma_addr_t wr_ptr;
+ u32 enc_pic_byte; /* number of encoded picture bytes */
+ s32 enc_src_idx; /* source buffer index of the currently encoded picture */
+ u32 enc_vcl_nut;
+ u32 error_reason; /* error reason of the currently encoded picture */
+ u32 warn_info; /* warning information on the currently encoded picture */
+ unsigned int frame_cycle; /* param for reporting the cycle number of encoding one frame*/
+ u64 pts;
+ u32 enc_host_cmd_tick; /* tick of ENC_PIC command for the picture */
+ u32 enc_encode_end_tick; /* end tick of encoding slices of the picture */
+};
+
+enum enc_pic_code_option {
+ CODEOPT_ENC_HEADER_IMPLICIT = BIT(0),
+ CODEOPT_ENC_VCL = BIT(1), /* flag to encode VCL nal unit explicitly */
+};
+
+enum gop_preset_idx {
+ PRESET_IDX_CUSTOM_GOP = 0, /* user defined GOP structure */
+ PRESET_IDX_ALL_I = 1, /* all intra, gopsize = 1 */
+ PRESET_IDX_IPP = 2, /* consecutive P, cyclic gopsize = 1 */
+ PRESET_IDX_IBBB = 3, /* consecutive B, cyclic gopsize = 1 */
+ PRESET_IDX_IBPBP = 4, /* gopsize = 2 */
+ PRESET_IDX_IBBBP = 5, /* gopsize = 4 */
+ PRESET_IDX_IPPPP = 6, /* consecutive P, cyclic gopsize = 4 */
+ PRESET_IDX_IBBBB = 7, /* consecutive B, cyclic gopsize = 4 */
+ PRESET_IDX_RA_IB = 8, /* random access, cyclic gopsize = 8 */
+ PRESET_IDX_IPP_SINGLE = 9, /* consecutive P, cyclic gopsize = 1, with single ref */
+};
+
+struct sec_axi_info {
+ u32 use_ip_enable;
+ u32 use_bit_enable;
+ u32 use_lf_row_enable: 1;
+ u32 use_enc_rdo_enable: 1;
+ u32 use_enc_lf_enable: 1;
+};
+
+struct dec_info {
+ struct dec_open_param open_param;
+ struct dec_initial_info initial_info;
+ struct dec_initial_info new_seq_info; /* temporal new sequence information */
+ u32 stream_wr_ptr;
+ u32 stream_rd_ptr;
+ u32 frame_display_flag;
+ dma_addr_t stream_buf_start_addr;
+ dma_addr_t stream_buf_end_addr;
+ u32 stream_buf_size;
+ struct vpu_buf vb_mv[MAX_REG_FRAME];
+ struct vpu_buf vb_fbc_y_tbl[MAX_REG_FRAME];
+ struct vpu_buf vb_fbc_c_tbl[MAX_REG_FRAME];
+ unsigned int num_of_decoding_fbs: 7;
+ unsigned int num_of_display_fbs: 7;
+ unsigned int stride;
+ struct sec_axi_info sec_axi_info;
+ dma_addr_t user_data_buf_addr;
+ u32 user_data_enable;
+ u32 user_data_buf_size;
+ struct vpu_buf vb_work;
+ struct vpu_buf vb_task;
+ struct dec_output_info dec_out_info[WAVE5_MAX_FBS];
+ u32 seq_change_mask;
+ enum temporal_id_mode temp_id_select_mode;
+ u32 target_temp_id;
+ u32 target_spatial_id;
+ u32 instance_queue_count;
+ u32 report_queue_count;
+ u32 cycle_per_tick;
+ u32 product_code;
+ u32 vlc_buf_size;
+ u32 param_buf_size;
+ bool initial_info_obtained;
+ bool reorder_enable;
+ bool first_cycle_check;
+ u32 stream_endflag: 1;
+};
+
+struct enc_info {
+ struct enc_open_param open_param;
+ struct enc_initial_info initial_info;
+ u32 stream_rd_ptr;
+ u32 stream_wr_ptr;
+ dma_addr_t stream_buf_start_addr;
+ dma_addr_t stream_buf_end_addr;
+ u32 stream_buf_size;
+ unsigned int num_frame_buffers;
+ unsigned int stride;
+ bool rotation_enable;
+ bool mirror_enable;
+ enum mirror_direction mirror_direction;
+ unsigned int rotation_angle;
+ bool initial_info_obtained;
+ struct sec_axi_info sec_axi_info;
+ bool line_buf_int_en;
+ struct vpu_buf vb_work;
+ struct vpu_buf vb_mv; /* col_mv buffer */
+ struct vpu_buf vb_fbc_y_tbl; /* FBC luma table buffer */
+ struct vpu_buf vb_fbc_c_tbl; /* FBC chroma table buffer */
+ struct vpu_buf vb_sub_sam_buf; /* sub-sampled buffer for ME */
+ struct vpu_buf vb_task;
+ u64 cur_pts; /* current timestamp in 90_k_hz */
+ u64 pts_map[32]; /* PTS mapped with source frame index */
+ u32 instance_queue_count;
+ u32 report_queue_count;
+ bool first_cycle_check;
+ u32 cycle_per_tick;
+ u32 product_code;
+ u32 vlc_buf_size;
+ u32 param_buf_size;
+};
+
+struct vpu_device {
+ struct device *dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *v4l2_m2m_dec_dev;
+ struct v4l2_m2m_dev *v4l2_m2m_enc_dev;
+ struct list_head instances;
+ struct video_device *video_dev_dec;
+ struct video_device *video_dev_enc;
+ struct mutex dev_lock; /* lock for the src, dst v4l2 queues */
+ struct mutex hw_lock; /* lock hw configurations */
+ int irq;
+ enum product_id product;
+ struct vpu_attr attr;
+ struct vpu_buf common_mem;
+ u32 last_performance_cycles;
+ u32 sram_size;
+ struct gen_pool *sram_pool;
+ struct vpu_buf sram_buf;
+ void __iomem *vdb_register;
+ u32 product_code;
+ struct ida inst_ida;
+ struct clk_bulk_data *clks;
+ int num_clks;
+};
+
+struct vpu_instance;
+
+struct vpu_instance_ops {
+ void (*finish_process)(struct vpu_instance *inst);
+};
+
+struct vpu_instance {
+ struct list_head list;
+ struct v4l2_fh v4l2_fh;
+ struct v4l2_m2m_dev *v4l2_m2m_dev;
+ struct v4l2_ctrl_handler v4l2_ctrl_hdl;
+ struct vpu_device *dev;
+ struct kfifo irq_status;
+ struct completion irq_done;
+
+ struct v4l2_pix_format_mplane src_fmt;
+ struct v4l2_pix_format_mplane dst_fmt;
+ enum v4l2_colorspace colorspace;
+ enum v4l2_xfer_func xfer_func;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_quantization quantization;
+ enum v4l2_hsv_encoding hsv_enc;
+
+ enum vpu_instance_state state;
+ enum vpu_instance_type type;
+ const struct vpu_instance_ops *ops;
+ spinlock_t state_spinlock; /* This protects the instance state */
+
+ enum wave_std std;
+ s32 id;
+ union {
+ struct enc_info enc_info;
+ struct dec_info dec_info;
+ } *codec_info;
+ struct frame_buffer frame_buf[MAX_REG_FRAME];
+ struct vpu_buf frame_vbuf[MAX_REG_FRAME];
+ u32 fbc_buf_count;
+ u32 dst_buf_count;
+ u32 queued_src_buf_num;
+ u32 queued_dst_buf_num;
+ struct list_head avail_src_bufs;
+ struct list_head avail_dst_bufs;
+ struct v4l2_rect conf_win;
+ u64 timestamp;
+ enum frame_buffer_format output_format;
+ bool cbcr_interleave;
+ bool nv21;
+ bool eos;
+ struct vpu_buf bitstream_vbuf;
+ dma_addr_t last_rd_ptr;
+ size_t remaining_consumed_bytes;
+ bool needs_reallocation;
+
+ unsigned int min_src_buf_count;
+ unsigned int src_buf_count;
+ unsigned int rot_angle;
+ unsigned int mirror_direction;
+ unsigned int bit_depth;
+ unsigned int frame_rate;
+ unsigned int vbv_buf_size;
+ unsigned int rc_mode;
+ unsigned int rc_enable;
+ unsigned int bit_rate;
+ unsigned int encode_aud;
+ struct enc_wave_param enc_param;
+};
+
+void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data);
+u32 wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr);
+int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb);
+int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb);
+int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count,
+ size_t size);
+int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset,
+ u8 *data, size_t len);
+int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb);
+void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev);
+void wave5_vdi_free_sram(struct vpu_device *vpu_dev);
+
+int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size);
+int wave5_vpu_flush_instance(struct vpu_instance *inst);
+int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id);
+int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param);
+int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res);
+int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst);
+int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info);
+int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs,
+ int num_of_display_fbs, int stride, int height);
+int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail);
+int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info);
+int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr);
+dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst);
+int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index);
+int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter);
+int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr,
+ dma_addr_t *pwr_ptr, size_t *size);
+int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size);
+int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index);
+int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index);
+
+int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param);
+int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res);
+int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst);
+int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info);
+int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num,
+ unsigned int stride, int height,
+ enum tiled_map_type map_type);
+int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param,
+ u32 *fail_res);
+int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info);
+int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter);
+
+#endif
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
new file mode 100644
index 000000000000..6a78d602cf08
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - product config definitions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef _VPU_CONFIG_H_
+#define _VPU_CONFIG_H_
+
+#define WAVE517_CODE 0x5170
+#define WAVE537_CODE 0x5370
+#define WAVE511_CODE 0x5110
+#define WAVE521_CODE 0x5210
+#define WAVE521C_CODE 0x521c
+#define WAVE521C_DUAL_CODE 0x521d // wave521 dual core
+#define WAVE521E1_CODE 0x5211
+
+#define PRODUCT_CODE_W_SERIES(x) ({ \
+ int c = x; \
+ ((c) == WAVE517_CODE || (c) == WAVE537_CODE || \
+ (c) == WAVE511_CODE || (c) == WAVE521_CODE || \
+ (c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \
+ (c) == WAVE521C_DUAL_CODE); \
+})
+
+#define WAVE517_WORKBUF_SIZE (2 * 1024 * 1024)
+#define WAVE521ENC_WORKBUF_SIZE (128 * 1024) //HEVC 128K, AVC 40K
+#define WAVE521DEC_WORKBUF_SIZE (1784 * 1024)
+
+#define MAX_NUM_INSTANCE 32
+
+#define W5_MIN_ENC_PIC_WIDTH 256
+#define W5_MIN_ENC_PIC_HEIGHT 128
+#define W5_MAX_ENC_PIC_WIDTH 8192
+#define W5_MAX_ENC_PIC_HEIGHT 8192
+
+// application specific configuration
+#define VPU_ENC_TIMEOUT 60000
+#define VPU_DEC_TIMEOUT 60000
+
+// for WAVE encoder
+#define USE_SRC_PRP_AXI 0
+#define USE_SRC_PRI_AXI 1
+#define DEFAULT_SRC_AXI USE_SRC_PRP_AXI
+
+/************************************************************************/
+/* VPU COMMON MEMORY */
+/************************************************************************/
+#define VLC_BUF_NUM (2)
+
+#define COMMAND_QUEUE_DEPTH (2)
+
+#define W5_REMAP_INDEX0 0
+#define W5_REMAP_INDEX1 1
+#define W5_REMAP_MAX_SIZE (1024 * 1024)
+
+#define WAVE5_MAX_CODE_BUF_SIZE (2 * 1024 * 1024)
+#define WAVE5_TEMPBUF_OFFSET WAVE5_MAX_CODE_BUF_SIZE
+#define WAVE5_TEMPBUF_SIZE (1024 * 1024)
+
+#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE)
+
+//=====4. VPU REPORT MEMORY ======================//
+
+#define WAVE5_UPPER_PROC_AXI_ID 0x0
+
+#define WAVE5_PROC_AXI_ID 0x0
+#define WAVE5_PRP_AXI_ID 0x0
+#define WAVE5_FBD_Y_AXI_ID 0x0
+#define WAVE5_FBC_Y_AXI_ID 0x0
+#define WAVE5_FBD_C_AXI_ID 0x0
+#define WAVE5_FBC_C_AXI_ID 0x0
+#define WAVE5_SEC_AXI_ID 0x0
+#define WAVE5_PRI_AXI_ID 0x0
+
+#endif /* _VPU_CONFIG_H_ */
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h
new file mode 100644
index 000000000000..6d7aac0c593b
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - error values
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef ERROR_CODE_H_INCLUDED
+#define ERROR_CODE_H_INCLUDED
+
+/*
+ * WAVE5
+ */
+
+/************************************************************************/
+/* WAVE5 COMMON SYSTEM ERROR (FAIL_REASON) */
+/************************************************************************/
+#define WAVE5_SYSERR_QUEUEING_FAIL 0x00000001
+#define WAVE5_SYSERR_ACCESS_VIOLATION_HW 0x00000040
+#define WAVE5_SYSERR_BUS_ERROR 0x00000200
+#define WAVE5_SYSERR_DOUBLE_FAULT 0x00000400
+#define WAVE5_SYSERR_RESULT_NOT_READY 0x00000800
+#define WAVE5_SYSERR_VPU_STILL_RUNNING 0x00001000
+#define WAVE5_SYSERR_UNKNOWN_CMD 0x00002000
+#define WAVE5_SYSERR_UNKNOWN_CODEC_STD 0x00004000
+#define WAVE5_SYSERR_UNKNOWN_QUERY_OPTION 0x00008000
+#define WAVE5_SYSERR_VLC_BUF_FULL 0x00010000
+#define WAVE5_SYSERR_WATCHDOG_TIMEOUT 0x00020000
+#define WAVE5_SYSERR_VCPU_TIMEOUT 0x00080000
+#define WAVE5_SYSERR_TEMP_SEC_BUF_OVERFLOW 0x00200000
+#define WAVE5_SYSERR_NEED_MORE_TASK_BUF 0x00400000
+#define WAVE5_SYSERR_PRESCAN_ERR 0x00800000
+#define WAVE5_SYSERR_ENC_GBIN_OVERCONSUME 0x01000000
+#define WAVE5_SYSERR_ENC_MAX_ZERO_DETECT 0x02000000
+#define WAVE5_SYSERR_ENC_LVL_FIRST_ERROR 0x04000000
+#define WAVE5_SYSERR_ENC_EG_RANGE_OVER 0x08000000
+#define WAVE5_SYSERR_ENC_IRB_FRAME_DROP 0x10000000
+#define WAVE5_SYSERR_INPLACE_V 0x20000000
+#define WAVE5_SYSERR_FATAL_VPU_HANGUP 0xf0000000
+
+/************************************************************************/
+/* WAVE5 COMMAND QUEUE ERROR (FAIL_REASON) */
+/************************************************************************/
+#define WAVE5_CMDQ_ERR_NOT_QUEABLE_CMD 0x00000001
+#define WAVE5_CMDQ_ERR_SKIP_MODE_ENABLE 0x00000002
+#define WAVE5_CMDQ_ERR_INST_FLUSHING 0x00000003
+#define WAVE5_CMDQ_ERR_INST_INACTIVE 0x00000004
+#define WAVE5_CMDQ_ERR_QUEUE_FAIL 0x00000005
+#define WAVE5_CMDQ_ERR_CMD_BUF_FULL 0x00000006
+
+/************************************************************************/
+/* WAVE5 ERROR ON DECODER (ERR_INFO) */
+/************************************************************************/
+// HEVC
+#define HEVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000
+#define HEVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001
+#define HEVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002
+#define HEVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003
+#define HEVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004
+#define HEVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005
+#define HEVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006
+#define HEVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007
+#define HEVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008
+#define HEVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009
+#define HEVC_SPSERR_LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 0x0000100A
+#define HEVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B
+#define HEVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C
+#define HEVC_SPSERR_SPS_MAX_LATENCY_INCREASE 0x0000100D
+#define HEVC_SPSERR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3 0x0000100E
+#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE 0x0000100F
+#define HEVC_SPSERR_LOG2_MIN_TRANSFORM_BLOCK_SIZE_MINUS2 0x00001010
+#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_TRANSFORM_BLOCK_SIZE 0x00001011
+#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTER 0x00001012
+#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA 0x00001013
+#define HEVC_SPSERR_SCALING_LIST 0x00001014
+#define HEVC_SPSERR_LOG2_DIFF_MIN_PCM_LUMA_CODING_BLOCK_SIZE_MINUS3 0x00001015
+#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE 0x00001016
+#define HEVC_SPSERR_NUM_SHORT_TERM_REF_PIC_SETS 0x00001017
+#define HEVC_SPSERR_NUM_LONG_TERM_REF_PICS_SPS 0x00001018
+#define HEVC_SPSERR_GBU_PARSING_ERROR 0x00001019
+#define HEVC_SPSERR_EXTENSION_FLAG 0x0000101A
+#define HEVC_SPSERR_VUI_ERROR 0x0000101B
+#define HEVC_SPSERR_ACTIVATE_SPS 0x0000101C
+#define HEVC_SPSERR_PROFILE_SPACE 0x0000101D
+#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000
+#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001
+#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002
+#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003
+#define HEVC_PPSERR_INIT_QP_MINUS26 0x00002004
+#define HEVC_PPSERR_DIFF_CU_QP_DELTA_DEPTH 0x00002005
+#define HEVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006
+#define HEVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007
+#define HEVC_PPSERR_NUM_TILE_COLUMNS_MINUS1 0x00002008
+#define HEVC_PPSERR_NUM_TILE_ROWS_MINUS1 0x00002009
+#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1 0x0000200A
+#define HEVC_PPSERR_ROW_HEIGHT_MINUS1 0x0000200B
+#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2 0x0000200C
+#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2 0x0000200D
+#define HEVC_PPSERR_SCALING_LIST 0x0000200E
+#define HEVC_PPSERR_LOG2_PARALLEL_MERGE_LEVEL_MINUS2 0x0000200F
+#define HEVC_PPSERR_NUM_TILE_COLUMNS_RANGE_OUT 0x00002010
+#define HEVC_PPSERR_NUM_TILE_ROWS_RANGE_OUT 0x00002011
+#define HEVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012
+#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013
+#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014
+#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015
+#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016
+#define HEVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017
+#define HEVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018
+#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1_RANGE_OUT 0x00002019
+#define HEVC_PPSERR_ROW_HEIGHT_MINUS1_RANGE_OUT 0x00002020
+#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2_RANGE_OUT 0x00002021
+#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2_RANGE_OUT 0x00002022
+#define HEVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000
+#define HEVC_SHERR_ACTIVATE_PPS 0x00003001
+#define HEVC_SHERR_ACTIVATE_SPS 0x00003002
+#define HEVC_SHERR_SLICE_TYPE 0x00003003
+#define HEVC_SHERR_FIRST_SLICE_IS_DEPENDENT_SLICE 0x00003004
+#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_SPS_FLAG 0x00003005
+#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET 0x00003006
+#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_IDX 0x00003007
+#define HEVC_SHERR_NUM_LONG_TERM_SPS 0x00003008
+#define HEVC_SHERR_NUM_LONG_TERM_PICS 0x00003009
+#define HEVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A
+#define HEVC_SHERR_DELTA_POC_MSB_CYCLE_LT 0x0000300B
+#define HEVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C
+#define HEVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D
+#define HEVC_SHERR_COLLOCATED_REF_IDX 0x0000300E
+#define HEVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F
+#define HEVC_SHERR_FIVE_MINUS_MAX_NUM_MERGE_CAND 0x00003010
+#define HEVC_SHERR_SLICE_QP_DELTA 0x00003011
+#define HEVC_SHERR_SLICE_QP_DELTA_IS_OUT_OF_RANGE 0x00003012
+#define HEVC_SHERR_SLICE_CB_QP_OFFSET 0x00003013
+#define HEVC_SHERR_SLICE_CR_QP_OFFSET 0x00003014
+#define HEVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015
+#define HEVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016
+#define HEVC_SHERR_NUM_ENTRY_POINT_OFFSETS 0x00003017
+#define HEVC_SHERR_OFFSET_LEN_MINUS1 0x00003018
+#define HEVC_SHERR_SLICE_SEGMENT_HEADER_EXTENSION_LENGTH 0x00003019
+#define HEVC_SHERR_WRONG_POC_IN_STILL_PICTURE_PROFILE 0x0000301A
+#define HEVC_SHERR_SLICE_TYPE_ERROR_IN_STILL_PICTURE_PROFILE 0x0000301B
+#define HEVC_SHERR_PPS_ID_NOT_EQUAL_PREV_VALUE 0x0000301C
+#define HEVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000
+#define HEVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001
+#define HEVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002
+#define HEVC_SPECERR_OVER_BIT_DEPTH 0x00004003
+#define HEVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004
+#define HEVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005
+#define HEVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000
+#define HEVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001
+#define HEVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002
+#define HEVC_ETCERR_INPLACE_V 0x0000500F
+
+// AVC
+#define AVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000
+#define AVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001
+#define AVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002
+#define AVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003
+#define AVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004
+#define AVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005
+#define AVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006
+#define AVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007
+#define AVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008
+#define AVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009
+#define AVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B
+#define AVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C
+#define AVC_SPSERR_SCALING_LIST 0x00001014
+#define AVC_SPSERR_GBU_PARSING_ERROR 0x00001019
+#define AVC_SPSERR_VUI_ERROR 0x0000101B
+#define AVC_SPSERR_ACTIVATE_SPS 0x0000101C
+#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000
+#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001
+#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002
+#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003
+#define AVC_PPSERR_INIT_QP_MINUS26 0x00002004
+#define AVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006
+#define AVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007
+#define AVC_PPSERR_SCALING_LIST 0x0000200E
+#define AVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012
+#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013
+#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014
+#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015
+#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016
+#define AVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017
+#define AVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018
+#define AVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000
+#define AVC_SHERR_ACTIVATE_PPS 0x00003001
+#define AVC_SHERR_ACTIVATE_SPS 0x00003002
+#define AVC_SHERR_SLICE_TYPE 0x00003003
+#define AVC_SHERR_FIRST_MB_IN_SLICE 0x00003004
+#define AVC_SHERR_RPLM 0x00003006
+#define AVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A
+#define AVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C
+#define AVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D
+#define AVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F
+#define AVC_SHERR_SLICE_QP_DELTA 0x00003011
+#define AVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015
+#define AVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016
+#define AVC_SHERR_DISABLE_DEBLOCK_FILTER_IDC 0x00003017
+#define AVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000
+#define AVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001
+#define AVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002
+#define AVC_SPECERR_OVER_BIT_DEPTH 0x00004003
+#define AVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004
+#define AVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005
+#define AVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000
+#define AVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001
+#define AVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002
+#define AVC_ETCERR_ASO 0x00005004
+#define AVC_ETCERR_FMO 0x00005005
+#define AVC_ETCERR_INPLACE_V 0x0000500F
+
+/************************************************************************/
+/* WAVE5 WARNING ON DECODER (WARN_INFO) */
+/************************************************************************/
+// HEVC
+#define HEVC_SPSWARN_MAX_SUB_LAYERS_MINUS1 0x00000001
+#define HEVC_SPSWARN_GENERAL_RESERVED_ZERO_44BITS 0x00000002
+#define HEVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004
+#define HEVC_SPSWARN_SUB_LAYER_RESERVED_ZERO_44BITS 0x00000008
+#define HEVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010
+#define HEVC_SPSWARN_SPS_MAX_DEC_PIC_BUFFERING_VALUE_OVER 0x00000020
+#define HEVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040
+#define HEVC_SPSWARN_ST_RPS_UE_ERROR 0x00000080
+#define HEVC_SPSWARN_EXTENSION_FLAG 0x01000000
+#define HEVC_SPSWARN_REPLACED_WITH_PREV_SPS 0x02000000
+#define HEVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100
+#define HEVC_PPSWARN_REPLACED_WITH_PREV_PPS 0x00000200
+#define HEVC_SHWARN_FIRST_SLICE_SEGMENT_IN_PIC_FLAG 0x00001000
+#define HEVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000
+#define HEVC_SHWARN_PIC_OUTPUT_FLAG 0x00004000
+#define HEVC_SHWARN_DUPLICATED_SLICE_SEGMENT 0x00008000
+#define HEVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000
+#define HEVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000
+#define HEVC_ETCWARN_WRONG_TEMPORAL_ID 0x00040000
+#define HEVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000
+#define HEVC_SPECWARN_OVER_PROFILE 0x00100000
+#define HEVC_SPECWARN_OVER_LEVEL 0x00200000
+#define HEVC_PRESWARN_PARSING_ERR 0x04000000
+#define HEVC_PRESWARN_MVD_OUT_OF_RANGE 0x08000000
+#define HEVC_PRESWARN_CU_QP_DELTA_VAL_OUT_OF_RANGE 0x09000000
+#define HEVC_PRESWARN_COEFF_LEVEL_REMAINING_OUT_OF_RANGE 0x0A000000
+#define HEVC_PRESWARN_PCM_ERR 0x0B000000
+#define HEVC_PRESWARN_OVERCONSUME 0x0C000000
+#define HEVC_PRESWARN_END_OF_SUBSET_ONE_BIT_ERR 0x10000000
+#define HEVC_PRESWARN_END_OF_SLICE_SEGMENT_FLAG 0x20000000
+
+// AVC
+#define AVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004
+#define AVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010
+#define AVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040
+#define AVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100
+#define AVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000
+#define AVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000
+#define AVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000
+#define AVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000
+#define AVC_SPECWARN_OVER_PROFILE 0x00100000
+#define AVC_SPECWARN_OVER_LEVEL 0x00200000
+#define AVC_PRESWARN_MVD_RANGE_OUT 0x00400000
+#define AVC_PRESWARN_MB_QPD_RANGE_OUT 0x00500000
+#define AVC_PRESWARN_COEFF_RANGE_OUT 0x00600000
+#define AVC_PRESWARN_MV_RANGE_OUT 0x00700000
+#define AVC_PRESWARN_MB_SKIP_RUN_RANGE_OUT 0x00800000
+#define AVC_PRESWARN_MB_TYPE_RANGE_OUT 0x00900000
+#define AVC_PRESWARN_SUB_MB_TYPE_RANGE_OUT 0x00A00000
+#define AVC_PRESWARN_CBP_RANGE_OUT 0x00B00000
+#define AVC_PRESWARN_INTRA_CHROMA_PRED_MODE_RANGE_OUT 0x00C00000
+#define AVC_PRESWARN_REF_IDX_RANGE_OUT 0x00D00000
+#define AVC_PRESWARN_COEFF_TOKEN_RANGE_OUT 0x00E00000
+#define AVC_PRESWARN_TOTAL_ZERO_RANGE_OUT 0x00F00000
+#define AVC_PRESWARN_RUN_BEFORE_RANGE_OUT 0x01000000
+#define AVC_PRESWARN_OVERCONSUME 0x01100000
+#define AVC_PRESWARN_MISSING_SLICE 0x01200000
+
+/************************************************************************/
+/* WAVE5 ERROR ON ENCODER (ERR_INFO) */
+/************************************************************************/
+
+/************************************************************************/
+/* WAVE5 WARNING ON ENCODER (WARN_INFO) */
+/************************************************************************/
+#define WAVE5_ETCWARN_FORCED_SPLIT_BY_CU8X8 0x000000001
+
+/************************************************************************/
+/* WAVE5 debug info (PRI_REASON) */
+/************************************************************************/
+#define WAVE5_DEC_VCORE_VCE_HANGUP 0x0001
+#define WAVE5_DEC_VCORE_UNDETECTED_SYNTAX_ERR 0x0002
+#define WAVE5_DEC_VCORE_MIB_BUSY 0x0003
+#define WAVE5_DEC_VCORE_VLC_BUSY 0x0004
+
+#endif /* ERROR_CODE_H_INCLUDED */
diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h
new file mode 100644
index 000000000000..7144a090dc77
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - wave5 backend definitions
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef __WAVE5_FUNCTION_H__
+#define __WAVE5_FUNCTION_H__
+
+#define WAVE5_SUBSAMPLED_ONE_SIZE(_w, _h) (ALIGN((_w) / 4, 16) * ALIGN((_h) / 4, 8))
+#define WAVE5_SUBSAMPLED_ONE_SIZE_AVC(_w, _h) (ALIGN((_w) / 4, 32) * ALIGN((_h) / 4, 4))
+
+/*
+ * Bitstream buffer option: Explicit End
+ * When set to 1 the VPU assumes that the bitstream has at least one frame and
+ * will read until the end of the bitstream buffer.
+ * When set to 0 the VPU will not read the last few bytes.
+ * This option can be set anytime but cannot be cleared during processing.
+ * It can be set to force finish decoding even though there is not enough
+ * bitstream data for a full frame.
+ */
+#define BSOPTION_ENABLE_EXPLICIT_END BIT(0)
+#define BSOPTION_HIGHLIGHT_STREAM_END BIT(1)
+
+/*
+ * Currently the driver only supports hardware with little endian but for source
+ * picture format, the bitstream and the report parameter the hardware works
+ * with the opposite endianness, thus hard-code big endian for the register
+ * writes
+ */
+#define PIC_SRC_ENDIANNESS_BIG_ENDIAN 0xf
+#define BITSTREAM_ENDIANNESS_BIG_ENDIAN 0xf
+#define REPORT_PARAM_ENDIANNESS_BIG_ENDIAN 0xf
+
+#define WTL_RIGHT_JUSTIFIED 0
+#define WTL_LEFT_JUSTIFIED 1
+#define WTL_PIXEL_8BIT 0
+#define WTL_PIXEL_16BIT 1
+#define WTL_PIXEL_32BIT 2
+
+/* Mirror & rotation modes of the PRP (pre-processing) module */
+#define NONE_ROTATE 0x0
+#define ROT_CLOCKWISE_90 0x3
+#define ROT_CLOCKWISE_180 0x5
+#define ROT_CLOCKWISE_270 0x7
+#define MIR_HOR_FLIP 0x11
+#define MIR_VER_FLIP 0x9
+#define MIR_HOR_VER_FLIP (MIR_HOR_FLIP | MIR_VER_FLIP)
+
+bool wave5_vpu_is_init(struct vpu_device *vpu_dev);
+
+unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev);
+
+void wave5_bit_issue_command(struct vpu_instance *inst, u32 cmd);
+
+int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision);
+
+int wave5_vpu_init(struct device *dev, u8 *fw, size_t size);
+
+int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode);
+
+int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, struct dec_open_param *param);
+
+int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos);
+
+int wave5_vpu_hw_flush_instance(struct vpu_instance *inst);
+
+int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst,
+ struct frame_buffer *fb_arr, enum tiled_map_type map_type,
+ unsigned int count);
+
+int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size);
+
+int wave5_vpu_dec_init_seq(struct vpu_instance *inst);
+
+int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info);
+
+int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res);
+
+int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result);
+
+int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res);
+
+int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index);
+
+int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index);
+
+int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags);
+
+dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst);
+
+int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr);
+
+/***< WAVE5 encoder >******/
+
+int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst,
+ struct enc_open_param *open_param);
+
+int wave5_vpu_enc_init_seq(struct vpu_instance *inst);
+
+int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info);
+
+int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst,
+ struct frame_buffer *fb_arr, enum tiled_map_type map_type,
+ unsigned int count);
+
+int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res);
+
+int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result);
+
+int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res);
+
+int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param);
+
+#endif /* __WAVE5_FUNCTION_H__ */

--
2.25.1

2023-09-16 05:54:45

by Sebastian Fricke

[permalink] [raw]
Subject: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

From: Nas Chung <[email protected]>

Add the decoder and encoder implementing the v4l2
API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config

Signed-off-by: Sebastian Fricke <[email protected]>
Signed-off-by: Nicolas Dufresne <[email protected]>
Signed-off-by: Robert Beckett <[email protected]>
Signed-off-by: Dafna Hirschfeld <[email protected]>
Signed-off-by: Nas Chung <[email protected]>
---
drivers/media/platform/chips-media/Kconfig | 1 +
drivers/media/platform/chips-media/Makefile | 1 +
drivers/media/platform/chips-media/wave5/Kconfig | 12 +
drivers/media/platform/chips-media/wave5/Makefile | 10 +
.../platform/chips-media/wave5/wave5-helper.c | 196 ++
.../platform/chips-media/wave5/wave5-helper.h | 30 +
.../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
.../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
.../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
.../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
10 files changed, 4454 insertions(+)

diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig
index f87a0d693df7..ad350eb6b1fc 100644
--- a/drivers/media/platform/chips-media/Kconfig
+++ b/drivers/media/platform/chips-media/Kconfig
@@ -3,3 +3,4 @@
comment "Chips&Media media platform drivers"

source "drivers/media/platform/chips-media/coda/Kconfig"
+source "drivers/media/platform/chips-media/wave5/Kconfig"
diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile
index 5ee693f651c1..6b5d99de8b54 100644
--- a/drivers/media/platform/chips-media/Makefile
+++ b/drivers/media/platform/chips-media/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only

obj-y += coda/
+obj-y += wave5/
diff --git a/drivers/media/platform/chips-media/wave5/Kconfig b/drivers/media/platform/chips-media/wave5/Kconfig
new file mode 100644
index 000000000000..a3b949356cd5
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+config VIDEO_WAVE_VPU
+ tristate "Chips&Media Wave Codec Driver"
+ depends on VIDEO_DEV
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_MEM2MEM_DEV
+ help
+ Chips&Media stateful encoder and decoder driver.
+ The driver supports HEVC and H264 formats.
+ To compile this driver as modules, choose M here: the
+ modules will be called wave5.
diff --git a/drivers/media/platform/chips-media/wave5/Makefile b/drivers/media/platform/chips-media/wave5/Makefile
new file mode 100644
index 000000000000..3d738a03bd8e
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_VIDEO_WAVE_VPU) += wave5.o
+wave5-objs += wave5-hw.o \
+ wave5-vpuapi.o \
+ wave5-vdi.o \
+ wave5-vpu-dec.o \
+ wave5-vpu.o \
+ wave5-vpu-enc.o \
+ wave5-helper.o
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c
new file mode 100644
index 000000000000..07b6910bbfee
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - decoder interface
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#include "wave5-helper.h"
+
+const char *state_to_str(enum vpu_instance_state state)
+{
+ switch (state) {
+ case VPU_INST_STATE_NONE:
+ return "NONE";
+ case VPU_INST_STATE_OPEN:
+ return "OPEN";
+ case VPU_INST_STATE_INIT_SEQ:
+ return "INIT_SEQ";
+ case VPU_INST_STATE_PIC_RUN:
+ return "PIC_RUN";
+ case VPU_INST_STATE_STOP:
+ return "STOP";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+void wave5_cleanup_instance(struct vpu_instance *inst)
+{
+ int i;
+
+ if (list_is_singular(&inst->list))
+ wave5_vdi_free_sram(inst->dev);
+
+ for (i = 0; i < inst->fbc_buf_count; i++)
+ wave5_vpu_dec_reset_framebuffer(inst, i);
+
+ wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
+ v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl);
+ if (inst->v4l2_fh.vdev) {
+ v4l2_fh_del(&inst->v4l2_fh);
+ v4l2_fh_exit(&inst->v4l2_fh);
+ }
+ list_del_init(&inst->list);
+ kfifo_free(&inst->irq_status);
+ ida_free(&inst->dev->inst_ida, inst->id);
+ kfree(inst->codec_info);
+ kfree(inst);
+}
+
+int wave5_vpu_release_device(struct file *filp,
+ int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
+ char *name)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data);
+
+ v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
+ if (inst->state != VPU_INST_STATE_NONE) {
+ u32 fail_res;
+ int ret;
+
+ ret = close_func(inst, &fail_res);
+ if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) {
+ dev_err(inst->dev->dev, "%s close failed, device is still running\n",
+ name);
+ return -EBUSY;
+ }
+ if (ret && ret != -EIO) {
+ dev_err(inst->dev->dev, "%s close, fail: %d\n", name, ret);
+ return ret;
+ }
+ }
+
+ wave5_cleanup_instance(inst);
+
+ return 0;
+}
+
+int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
+ const struct vb2_ops *ops)
+{
+ struct vpu_instance *inst = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->ops = ops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
+ src_vq->drv_priv = inst;
+ src_vq->lock = &inst->dev->dev_lock;
+ src_vq->dev = inst->dev->v4l2_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->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->ops = ops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
+ dst_vq->drv_priv = inst;
+ dst_vq->lock = &inst->dev->dev_lock;
+ dst_vq->dev = inst->dev->v4l2_dev.dev;
+ ret = vb2_queue_init(dst_vq);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ bool is_decoder = inst->type == VPU_INST_TYPE_DEC;
+
+ dev_dbg(inst->dev->dev, "%s: [%s] type: %u id: %u | flags: %u\n", __func__,
+ is_decoder ? "decoder" : "encoder", sub->type, sub->id, sub->flags);
+
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ if (is_decoder)
+ return v4l2_src_change_event_subscribe(fh, sub);
+ return -EINVAL;
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ int i;
+
+ f->fmt.pix_mp.width = inst->src_fmt.width;
+ f->fmt.pix_mp.height = inst->src_fmt.height;
+ f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
+ f->fmt.pix_mp.field = inst->src_fmt.field;
+ f->fmt.pix_mp.flags = inst->src_fmt.flags;
+ f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
+ for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+ f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline;
+ f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage;
+ }
+
+ f->fmt.pix_mp.colorspace = inst->colorspace;
+ f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
+ f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
+ f->fmt.pix_mp.quantization = inst->quantization;
+ f->fmt.pix_mp.xfer_func = inst->xfer_func;
+
+ return 0;
+}
+
+const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
+ const struct vpu_format fmt_list[MAX_FMTS])
+{
+ unsigned int index;
+
+ for (index = 0; index < MAX_FMTS; index++) {
+ if (fmt_list[index].v4l2_pix_fmt == v4l2_pix_fmt)
+ return &fmt_list[index];
+ }
+
+ return NULL;
+}
+
+const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
+ const struct vpu_format fmt_list[MAX_FMTS])
+{
+ if (idx >= MAX_FMTS)
+ return NULL;
+
+ if (!fmt_list[idx].v4l2_pix_fmt)
+ return NULL;
+
+ return &fmt_list[idx];
+}
+
+enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type)
+{
+ switch (v4l2_pix_fmt) {
+ case V4L2_PIX_FMT_H264:
+ return type == VPU_INST_TYPE_DEC ? W_AVC_DEC : W_AVC_ENC;
+ case V4L2_PIX_FMT_HEVC:
+ return type == VPU_INST_TYPE_DEC ? W_HEVC_DEC : W_HEVC_ENC;
+ default:
+ return STD_UNKNOWN;
+ }
+}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h
new file mode 100644
index 000000000000..a55d5503f5b2
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - basic types
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#ifndef __WAVE_HELPER_H__
+#define __WAVE_HELPER_H__
+
+#include "wave5-vpu.h"
+
+#define FMT_TYPES 2
+#define MAX_FMTS 12
+
+const char *state_to_str(enum vpu_instance_state state);
+void wave5_cleanup_instance(struct vpu_instance *inst);
+int wave5_vpu_release_device(struct file *filp,
+ int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
+ char *name);
+int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
+ const struct vb2_ops *ops);
+int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);
+int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f);
+const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
+ const struct vpu_format fmt_list[MAX_FMTS]);
+const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
+ const struct vpu_format fmt_list[MAX_FMTS]);
+enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type);
+#endif
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
new file mode 100644
index 000000000000..41257a26504b
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -0,0 +1,1965 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - decoder interface
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#include "wave5-helper.h"
+
+#define VPU_DEC_DEV_NAME "C&M Wave5 VPU decoder"
+#define VPU_DEC_DRV_NAME "wave5-dec"
+
+#define DEFAULT_SRC_SIZE(width, height) ({ \
+ (width) * (height) / 8 * 3; \
+})
+
+static const struct vpu_format dec_fmt_list[FMT_TYPES][MAX_FMTS] = {
+ [VPU_FMT_TYPE_CODEC] = {
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_H264,
+ .max_width = 8192,
+ .min_width = 32,
+ .max_height = 4320,
+ .min_height = 32,
+ },
+ },
+ [VPU_FMT_TYPE_RAW] = {
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV16,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV61,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M,
+ .max_width = 8192,
+ .min_width = 8,
+ .max_height = 4320,
+ .min_height = 8,
+ },
+ }
+};
+
+/*
+ * Make sure that the state switch is allowed and add logging for debugging
+ * purposes
+ */
+static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state)
+{
+ switch (state) {
+ case VPU_INST_STATE_NONE:
+ break;
+ case VPU_INST_STATE_OPEN:
+ if (inst->state != VPU_INST_STATE_NONE)
+ goto invalid_state_switch;
+ goto valid_state_switch;
+ case VPU_INST_STATE_INIT_SEQ:
+ if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP)
+ goto invalid_state_switch;
+ goto valid_state_switch;
+ case VPU_INST_STATE_PIC_RUN:
+ if (inst->state != VPU_INST_STATE_INIT_SEQ)
+ goto invalid_state_switch;
+ goto valid_state_switch;
+ case VPU_INST_STATE_STOP:
+ goto valid_state_switch;
+ };
+invalid_state_switch:
+ WARN(1, "Invalid state switch from %s to %s.\n",
+ state_to_str(inst->state), state_to_str(state));
+ return -EINVAL;
+valid_state_switch:
+ dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n",
+ state_to_str(inst->state), state_to_str(state));
+ inst->state = state;
+ return 0;
+}
+
+static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
+{
+ int ret;
+
+ ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
+ if (ret) {
+ dev_err(inst->dev->dev,
+ "Setting EOS for the bitstream, fail: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static bool wave5_last_src_buffer_consumed(struct v4l2_m2m_ctx *m2m_ctx)
+{
+ struct vpu_src_buffer *vpu_buf;
+
+ if (!m2m_ctx->last_src_buf)
+ return false;
+
+ vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf);
+ return vpu_buf->consumed;
+}
+
+static void wave5_handle_src_buffer(struct vpu_instance *inst, dma_addr_t rd_ptr)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct v4l2_m2m_buffer *buf, *n;
+ size_t consumed_bytes = 0;
+
+ if (rd_ptr >= inst->last_rd_ptr) {
+ consumed_bytes = rd_ptr - inst->last_rd_ptr;
+ } else {
+ size_t rd_offs = rd_ptr - inst->bitstream_vbuf.daddr;
+ size_t last_rd_offs = inst->last_rd_ptr - inst->bitstream_vbuf.daddr;
+
+ consumed_bytes = rd_offs + (inst->bitstream_vbuf.size - last_rd_offs);
+ }
+
+ inst->last_rd_ptr = rd_ptr;
+ consumed_bytes += inst->remaining_consumed_bytes;
+
+ dev_dbg(inst->dev->dev, "%s: %zu bytes of bitstream was consumed", __func__,
+ consumed_bytes);
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) {
+ struct vb2_v4l2_buffer *src_buf = &buf->vb;
+ size_t src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+
+ if (src_size > consumed_bytes)
+ break;
+
+ dev_dbg(inst->dev->dev, "%s: removing src buffer %i",
+ __func__, src_buf->vb2_buf.index);
+ src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
+ inst->timestamp = src_buf->vb2_buf.timestamp;
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ consumed_bytes -= src_size;
+
+ /* Handle the case the last bitstream buffer has been picked */
+ if (src_buf == m2m_ctx->last_src_buf) {
+ int ret;
+
+ m2m_ctx->last_src_buf = NULL;
+ ret = wave5_vpu_dec_set_eos_on_firmware(inst);
+ if (ret)
+ dev_warn(inst->dev->dev,
+ "Setting EOS for the bitstream, fail: %d\n", ret);
+ break;
+ }
+ }
+
+ inst->remaining_consumed_bytes = consumed_bytes;
+}
+
+static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
+ unsigned int height)
+{
+ switch (pix_mp->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ pix_mp->width = round_up(width, 32);
+ pix_mp->height = round_up(height, 16);
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = width * height * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ pix_mp->width = round_up(width, 32);
+ pix_mp->height = round_up(height, 16);
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = width * height * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420M:
+ pix_mp->width = round_up(width, 32);
+ pix_mp->height = round_up(height, 16);
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = width * height;
+ pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
+ pix_mp->plane_fmt[1].sizeimage = width * height / 4;
+ pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
+ pix_mp->plane_fmt[2].sizeimage = width * height / 4;
+ break;
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_NV21M:
+ pix_mp->width = round_up(width, 32);
+ pix_mp->height = round_up(height, 16);
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = width * height;
+ pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[1].sizeimage = width * height / 2;
+ break;
+ case V4L2_PIX_FMT_YUV422M:
+ pix_mp->width = round_up(width, 32);
+ pix_mp->height = round_up(height, 16);
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = width * height;
+ pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
+ pix_mp->plane_fmt[1].sizeimage = width * height / 2;
+ pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
+ pix_mp->plane_fmt[2].sizeimage = width * height / 2;
+ break;
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_NV61M:
+ pix_mp->width = round_up(width, 32);
+ pix_mp->height = round_up(height, 16);
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = width * height;
+ pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[1].sizeimage = width * height;
+ break;
+ default:
+ pix_mp->width = width;
+ pix_mp->height = height;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+ pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_SRC_SIZE(width, height),
+ pix_mp->plane_fmt[0].sizeimage);
+ break;
+ }
+}
+
+static int start_decode(struct vpu_instance *inst, u32 *fail_res)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ int ret = 0;
+
+ ret = wave5_vpu_dec_start_one_frame(inst, fail_res);
+ if (ret) {
+ struct vb2_v4l2_buffer *src_buf;
+
+ /* FIXME this will have no effect on the firmware at all, and we
+ * are moving to STOP state, so the remaming src buffer won't be
+ * consumed. Maybe best here is just to make sure we can avoid
+ * deadlocking the app ?
+ */
+ src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
+ if (src_buf)
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ switch_state(inst, VPU_INST_STATE_STOP);
+
+ dev_dbg(inst->dev->dev, "%s: pic run failed / finish job", __func__);
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ }
+
+ return ret;
+}
+
+/* Must be called with state_spinlock */
+static void flag_last_buffer_done(struct vpu_instance *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_v4l2_buffer *vb;
+ int i;
+
+ vb = v4l2_m2m_dst_buf_remove(m2m_ctx);
+ if (!vb) {
+ m2m_ctx->is_draining = true;
+ m2m_ctx->next_buf_last = true;
+ return;
+ }
+
+ for (i = 0; i < vb->vb2_buf.num_planes; i++)
+ vb2_set_plane_payload(&vb->vb2_buf, i, 0);
+ vb->field = V4L2_FIELD_NONE;
+
+ v4l2_m2m_last_buffer_done(m2m_ctx, vb);
+}
+
+/* Must be called with state_spinlock */
+static void send_eos_event(struct vpu_instance *inst)
+{
+ static const struct v4l2_event vpu_event_eos = {
+ .type = V4L2_EVENT_EOS
+ };
+
+ v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos);
+ inst->eos = false;
+}
+
+/* Must be called with state_spinlock */
+static int handle_dynamic_resolution_change(struct vpu_instance *inst)
+{
+ struct v4l2_fh *fh = &inst->v4l2_fh;
+ static const struct v4l2_event vpu_event_src_ch = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ struct dec_initial_info *initial_info = &inst->codec_info->dec_info.initial_info;
+
+ dev_dbg(inst->dev->dev, "%s: rd_ptr %pad", __func__, &initial_info->rd_ptr);
+
+ dev_dbg(inst->dev->dev, "%s: width: %u height: %u profile: %u | minbuffer: %u\n",
+ __func__, initial_info->pic_width, initial_info->pic_height,
+ initial_info->profile, initial_info->min_frame_buffer_count);
+
+ inst->needs_reallocation = true;
+ inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1;
+ if (inst->dst_buf_count != inst->fbc_buf_count) {
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count);
+
+ inst->dst_buf_count = inst->fbc_buf_count;
+ }
+
+ if (p_dec_info->initial_info_obtained) {
+ inst->conf_win.left = initial_info->pic_crop_rect.left;
+ inst->conf_win.top = initial_info->pic_crop_rect.top;
+ inst->conf_win.width = initial_info->pic_width -
+ initial_info->pic_crop_rect.left - initial_info->pic_crop_rect.right;
+ inst->conf_win.height = initial_info->pic_height -
+ initial_info->pic_crop_rect.top - initial_info->pic_crop_rect.bottom;
+
+ wave5_update_pix_fmt(&inst->src_fmt, initial_info->pic_width,
+ initial_info->pic_height);
+ wave5_update_pix_fmt(&inst->dst_fmt, initial_info->pic_width,
+ initial_info->pic_height);
+ }
+
+ v4l2_event_queue_fh(fh, &vpu_event_src_ch);
+
+ return 0;
+}
+
+static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct dec_output_info dec_info;
+ int ret;
+ struct vb2_v4l2_buffer *dec_buf = NULL;
+ struct vb2_v4l2_buffer *disp_buf = NULL;
+ struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
+ struct queue_status_info q_status;
+
+ dev_dbg(inst->dev->dev, "%s: Fetch output info from firmware.", __func__);
+
+ ret = wave5_vpu_dec_get_output_info(inst, &dec_info);
+ if (ret) {
+ dev_warn(inst->dev->dev, "%s: could not get output info.", __func__);
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ return;
+ }
+
+ dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &dec_info.rd_ptr,
+ &dec_info.wr_ptr);
+ wave5_handle_src_buffer(inst, dec_info.rd_ptr);
+
+ dev_dbg(inst->dev->dev, "%s: dec_info dec_idx %i disp_idx %i", __func__,
+ dec_info.index_frame_decoded, dec_info.index_frame_display);
+
+ if (!vb2_is_streaming(dst_vq)) {
+ dev_dbg(inst->dev->dev, "%s: capture is not streaming..", __func__);
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ return;
+ }
+
+ /* Remove decoded buffer from the ready queue now that it has been
+ * decoded.
+ */
+ if (dec_info.index_frame_decoded >= 0) {
+ struct vb2_buffer *vb = vb2_get_buffer(dst_vq,
+ dec_info.index_frame_decoded);
+ if (vb)
+ dec_buf = to_vb2_v4l2_buffer(vb);
+ else
+ dev_warn(inst->dev->dev, "%s: invalid decoded frame index %i",
+ __func__, dec_info.index_frame_decoded);
+ }
+
+ if (dec_buf)
+ dec_buf->vb2_buf.timestamp = inst->timestamp;
+
+ if (dec_info.index_frame_display >= 0) {
+ disp_buf = v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, dec_info.index_frame_display);
+ if (!disp_buf)
+ dev_warn(inst->dev->dev, "%s: invalid display frame index %i",
+ __func__, dec_info.index_frame_display);
+ }
+
+ /* If there is anything to display, do that now */
+ if (disp_buf) {
+ struct vpu_dst_buffer *dst_vpu_buf = wave5_to_vpu_dst_buf(disp_buf);
+ int stride = dec_info.disp_frame.stride;
+ int height = dec_info.disp_pic_height;
+
+ /* FIXME we should calculate that once, and use v4l2-common if
+ * possible
+ */
+ if (inst->dst_fmt.num_planes == 1) {
+ vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
+ (stride * height * 3 / 2));
+ } else if (inst->dst_fmt.num_planes == 2) {
+ vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
+ (stride * height));
+ vb2_set_plane_payload(&disp_buf->vb2_buf, 1,
+ ((stride / 2) * height));
+ } else if (inst->dst_fmt.num_planes == 3) {
+ vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
+ (stride * height));
+ vb2_set_plane_payload(&disp_buf->vb2_buf, 1,
+ ((stride / 2) * (height / 2)));
+ vb2_set_plane_payload(&disp_buf->vb2_buf, 2,
+ ((stride / 2) * (height / 2)));
+ }
+
+ /* TODO implement interlace support */
+ disp_buf->field = V4L2_FIELD_NONE;
+ dst_vpu_buf->display = true;
+ v4l2_m2m_buf_done(disp_buf, VB2_BUF_STATE_DONE);
+
+ dev_dbg(inst->dev->dev, "%s: frame_cycle %8u (payload %lu stride %d height %d)\n",
+ __func__, dec_info.frame_cycle,
+ vb2_get_plane_payload(&disp_buf->vb2_buf, 0), stride, height);
+ }
+
+ if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END ||
+ dec_info.sequence_changed)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&inst->state_spinlock, flags);
+ if (!v4l2_m2m_has_stopped(m2m_ctx)) {
+ switch_state(inst, VPU_INST_STATE_STOP);
+
+ if (dec_info.sequence_changed)
+ handle_dynamic_resolution_change(inst);
+ else
+ send_eos_event(inst);
+
+ flag_last_buffer_done(inst);
+ }
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+ }
+
+ /*
+ * During a resolution change and while draining, the firmware may flush
+ * the reorder queue regardless of having a matching decoding operation
+ * pending. Only terminate the job if there are no more IRQ coming.
+ */
+ wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
+ if (q_status.report_queue_count == 0 &&
+ (q_status.instance_queue_count == 0 || dec_info.sequence_changed)) {
+ dev_dbg(inst->dev->dev, "%s: finishing job.\n", __func__);
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ }
+}
+
+static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, VPU_DEC_DRV_NAME, sizeof(cap->driver));
+ strscpy(cap->card, VPU_DEC_DRV_NAME, sizeof(cap->card));
+ strscpy(cap->bus_info, "platform:" VPU_DEC_DRV_NAME, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int wave5_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize)
+{
+ const struct vpu_format *vpu_fmt;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt) {
+ vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt)
+ return -EINVAL;
+ }
+
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = vpu_fmt->min_width;
+ fsize->stepwise.max_width = vpu_fmt->max_width;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = vpu_fmt->min_height;
+ fsize->stepwise.max_height = vpu_fmt->max_height;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int wave5_vpu_dec_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ const struct vpu_format *vpu_fmt;
+
+ vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ f->pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->flags = 0;
+
+ return 0;
+}
+
+static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ const struct vpu_format *vpu_fmt;
+ int width, height;
+
+ dev_dbg(inst->dev->dev,
+ "%s: fourcc: %u width: %u height: %u nm planes: %u colorspace: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt) {
+ width = inst->dst_fmt.width;
+ height = inst->dst_fmt.height;
+ f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
+ f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
+ } else {
+ const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
+
+ width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
+ height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
+ f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->fmt.pix_mp.num_planes = info->mem_planes;
+ }
+
+ if (p_dec_info->initial_info_obtained) {
+ width = inst->dst_fmt.width;
+ height = inst->dst_fmt.height;
+ }
+
+ wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
+ f->fmt.pix_mp.flags = 0;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = inst->colorspace;
+ f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
+ f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
+ f->fmt.pix_mp.quantization = inst->quantization;
+ f->fmt.pix_mp.xfer_func = inst->xfer_func;
+ memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
+
+ return 0;
+}
+
+static int wave5_vpu_dec_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ int i, ret;
+
+ dev_dbg(inst->dev->dev,
+ "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
+
+ ret = wave5_vpu_dec_try_fmt_cap(file, fh, f);
+ if (ret)
+ return ret;
+
+ inst->dst_fmt.width = f->fmt.pix_mp.width;
+ inst->dst_fmt.height = f->fmt.pix_mp.height;
+ inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
+ inst->dst_fmt.field = f->fmt.pix_mp.field;
+ inst->dst_fmt.flags = f->fmt.pix_mp.flags;
+ inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes;
+ for (i = 0; i < inst->dst_fmt.num_planes; i++) {
+ inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
+ inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
+
+ if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12 ||
+ inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12M) {
+ inst->cbcr_interleave = true;
+ inst->nv21 = false;
+ inst->output_format = FORMAT_420;
+ } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21 ||
+ inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21M) {
+ inst->cbcr_interleave = true;
+ inst->nv21 = true;
+ inst->output_format = FORMAT_420;
+ } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16 ||
+ inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16M) {
+ inst->cbcr_interleave = true;
+ inst->nv21 = false;
+ inst->output_format = FORMAT_422;
+ } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61 ||
+ inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61M) {
+ inst->cbcr_interleave = true;
+ inst->nv21 = true;
+ inst->output_format = FORMAT_422;
+ } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422P ||
+ inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422M) {
+ inst->cbcr_interleave = false;
+ inst->nv21 = false;
+ inst->output_format = FORMAT_422;
+ } else {
+ inst->cbcr_interleave = false;
+ inst->nv21 = false;
+ inst->output_format = FORMAT_420;
+ }
+
+ return 0;
+}
+
+static int wave5_vpu_dec_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ int i;
+
+ f->fmt.pix_mp.width = inst->dst_fmt.width;
+ f->fmt.pix_mp.height = inst->dst_fmt.height;
+ f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
+ f->fmt.pix_mp.field = inst->dst_fmt.field;
+ f->fmt.pix_mp.flags = inst->dst_fmt.flags;
+ f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
+ for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+ f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline;
+ f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage;
+ }
+
+ f->fmt.pix_mp.colorspace = inst->colorspace;
+ f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
+ f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
+ f->fmt.pix_mp.quantization = inst->quantization;
+ f->fmt.pix_mp.xfer_func = inst->xfer_func;
+
+ return 0;
+}
+
+static int wave5_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
+
+ dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
+
+ vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ f->pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED;
+
+ return 0;
+}
+
+static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
+
+ dev_dbg(inst->dev->dev,
+ "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt) {
+ f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
+ f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
+ } else {
+ int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
+ int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
+
+ f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->fmt.pix_mp.num_planes = 1;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
+ }
+
+ f->fmt.pix_mp.flags = 0;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
+
+ return 0;
+}
+
+static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ int i, ret;
+
+ dev_dbg(inst->dev->dev,
+ "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
+
+ ret = wave5_vpu_dec_try_fmt_out(file, fh, f);
+ if (ret)
+ return ret;
+
+ inst->std = wave5_to_vpu_std(f->fmt.pix_mp.pixelformat, inst->type);
+ if (inst->std == STD_UNKNOWN) {
+ dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
+ (char *)&f->fmt.pix_mp.pixelformat);
+ return -EINVAL;
+ }
+
+ inst->src_fmt.width = f->fmt.pix_mp.width;
+ inst->src_fmt.height = f->fmt.pix_mp.height;
+ inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
+ inst->src_fmt.field = f->fmt.pix_mp.field;
+ inst->src_fmt.flags = f->fmt.pix_mp.flags;
+ inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes;
+ for (i = 0; i < inst->src_fmt.num_planes; i++) {
+ inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
+ inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
+
+ inst->colorspace = f->fmt.pix_mp.colorspace;
+ inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ inst->hsv_enc = f->fmt.pix_mp.hsv_enc;
+ inst->quantization = f->fmt.pix_mp.quantization;
+ inst->xfer_func = f->fmt.pix_mp.xfer_func;
+
+ wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+
+ return 0;
+}
+
+static int wave5_vpu_dec_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+
+ dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->dst_fmt.width;
+ s->r.height = inst->dst_fmt.height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ s->r.left = 0;
+ s->r.top = 0;
+ if (inst->state > VPU_INST_STATE_OPEN) {
+ s->r = inst->conf_win;
+ } else {
+ s->r.width = inst->src_fmt.width;
+ s->r.height = inst->src_fmt.height;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wave5_vpu_dec_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (s->target != V4L2_SEL_TGT_COMPOSE)
+ return -EINVAL;
+
+ dev_dbg(inst->dev->dev, "V4L2_SEL_TGT_COMPOSE w: %u h: %u\n",
+ s->r.width, s->r.height);
+
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->dst_fmt.width;
+ s->r.height = inst->dst_fmt.height;
+
+ return 0;
+}
+
+static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct v4l2_format *f = &create->format;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -ENOTTY;
+
+ return v4l2_m2m_ioctl_create_bufs(file, priv, create);
+}
+
+/* Must be called with state_spinlock */
+static int wave5_vpu_dec_stop_unlocked(struct vpu_instance *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+
+ if (m2m_ctx->is_draining)
+ return -EBUSY;
+
+ if (inst->state != VPU_INST_STATE_NONE) {
+ int ret;
+
+ ret = wave5_vpu_dec_set_eos_on_firmware(inst);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Used to remember the EOS state after the streamoff/on transition on
+ * the capture queue.
+ */
+ inst->eos = true;
+
+ if (m2m_ctx->has_stopped)
+ return 0;
+
+ m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
+ m2m_ctx->is_draining = true;
+
+ /*
+ * Deferred to device run in case it wasn't in the ring buffer
+ * yet. In other case, we have to send the EOS signal to the
+ * firmware so that any pending PIC_RUN ends without new
+ * bitstream buffer.
+ */
+ if (m2m_ctx->last_src_buf)
+ return 0;
+
+ if (inst->state == VPU_INST_STATE_NONE) {
+ send_eos_event(inst);
+ flag_last_buffer_done(inst);
+ }
+
+ return 0;
+}
+
+/* Must be called with state_spinlock */
+static int wave5_vpu_dec_start_unlocked(struct vpu_instance *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
+
+ if (m2m_ctx->is_draining)
+ return -EBUSY;
+
+ if (m2m_ctx->has_stopped)
+ m2m_ctx->has_stopped = false;
+
+ vb2_clear_last_buffer_dequeued(dst_vq);
+ inst->eos = false;
+
+ return 0;
+}
+
+static int wave5_vpu_dec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ unsigned long flags;
+ int ret;
+
+ dev_dbg(inst->dev->dev, "decoder command: %u\n", dc->cmd);
+
+ ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc);
+ if (ret)
+ return ret;
+
+ switch (dc->cmd) {
+ case V4L2_DEC_CMD_STOP:
+ spin_lock_irqsave(&inst->state_spinlock, flags);
+ ret = wave5_vpu_dec_stop_unlocked(inst);
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+
+ /* Just in case we don't have anything to decode anymore */
+ v4l2_m2m_try_schedule(m2m_ctx);
+ break;
+ case V4L2_DEC_CMD_START:
+ spin_lock_irqsave(&inst->state_spinlock, flags);
+ ret = wave5_vpu_dec_start_unlocked(inst);
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops wave5_vpu_dec_ioctl_ops = {
+ .vidioc_querycap = wave5_vpu_dec_querycap,
+ .vidioc_enum_framesizes = wave5_vpu_dec_enum_framesizes,
+
+ .vidioc_enum_fmt_vid_cap = wave5_vpu_dec_enum_fmt_cap,
+ .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_dec_s_fmt_cap,
+ .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_dec_g_fmt_cap,
+ .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_dec_try_fmt_cap,
+
+ .vidioc_enum_fmt_vid_out = wave5_vpu_dec_enum_fmt_out,
+ .vidioc_s_fmt_vid_out_mplane = wave5_vpu_dec_s_fmt_out,
+ .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out,
+ .vidioc_try_fmt_vid_out_mplane = wave5_vpu_dec_try_fmt_out,
+
+ .vidioc_g_selection = wave5_vpu_dec_g_selection,
+ .vidioc_s_selection = wave5_vpu_dec_s_selection,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_create_bufs = wave5_vpu_dec_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
+ .vidioc_decoder_cmd = wave5_vpu_dec_decoder_cmd,
+
+ .vidioc_subscribe_event = wave5_vpu_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ struct v4l2_pix_format_mplane inst_format =
+ (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
+ unsigned int i;
+
+ dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
+ *num_buffers, *num_planes, q->type);
+
+ /* the CREATE_BUFS case */
+ if (*num_planes) {
+ if (inst_format.num_planes != *num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < *num_planes; i++) {
+ if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
+ return -EINVAL;
+ }
+ /* the REQBUFS case */
+ } else {
+ *num_planes = inst_format.num_planes;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ sizes[0] = inst_format.plane_fmt[0].sizeimage;
+ dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
+ } else if (*num_planes == 1) {
+ if (inst->output_format == FORMAT_422)
+ sizes[0] = inst_format.width * inst_format.height * 2;
+ else
+ sizes[0] = inst_format.width * inst_format.height * 3 / 2;
+ dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
+ } else if (*num_planes == 2) {
+ sizes[0] = inst_format.width * inst_format.height;
+ if (inst->output_format == FORMAT_422)
+ sizes[1] = inst_format.width * inst_format.height;
+ else
+ sizes[1] = inst_format.width * inst_format.height / 2;
+ dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n",
+ __func__, sizes[0], sizes[1]);
+ } else if (*num_planes == 3) {
+ sizes[0] = inst_format.width * inst_format.height;
+ if (inst->output_format == FORMAT_422) {
+ sizes[1] = inst_format.width * inst_format.height / 2;
+ sizes[2] = inst_format.width * inst_format.height / 2;
+ } else {
+ sizes[1] = inst_format.width * inst_format.height / 4;
+ sizes[2] = inst_format.width * inst_format.height / 4;
+ }
+ dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n",
+ __func__, sizes[0], sizes[1], sizes[2]);
+ }
+ }
+
+ if (inst->state == VPU_INST_STATE_INIT_SEQ &&
+ q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (*num_buffers > inst->dst_buf_count &&
+ *num_buffers < WAVE5_MAX_FBS)
+ inst->dst_buf_count = *num_buffers;
+
+ *num_buffers = inst->dst_buf_count;
+ }
+
+ return 0;
+}
+
+static int wave5_prepare_fb(struct vpu_instance *inst)
+{
+ int linear_num;
+ int non_linear_num;
+ int fb_stride = 0, fb_height = 0;
+ int luma_size, chroma_size;
+ int ret, i;
+ struct v4l2_m2m_buffer *buf, *n;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+
+ linear_num = inst->dst_buf_count;
+ non_linear_num = inst->fbc_buf_count;
+
+ for (i = 0; i < non_linear_num; i++) {
+ struct frame_buffer *frame = &inst->frame_buf[i];
+ struct vpu_buf *vframe = &inst->frame_vbuf[i];
+
+ fb_stride = inst->dst_fmt.width;
+ fb_height = ALIGN(inst->dst_fmt.height, 32);
+ luma_size = fb_stride * fb_height;
+
+ /* FIXME shall we do /4 in case of 422 ?*/
+ chroma_size = ALIGN(fb_stride / 2, 16) * fb_height;
+
+ if (vframe->size) {
+ if (vframe->size != (luma_size + chroma_size))
+ wave5_vpu_dec_reset_framebuffer(inst, i);
+ else
+ continue;
+ }
+
+ vframe->size = luma_size + chroma_size;
+ ret = wave5_vdi_allocate_dma_memory(inst->dev, vframe);
+ if (ret) {
+ dev_dbg(inst->dev->dev,
+ "%s: Allocating FBC buf of size %zu, fail: %d\n",
+ __func__, vframe->size, ret);
+ return ret;
+ }
+
+ frame->buf_y = vframe->daddr;
+ frame->buf_cb = vframe->daddr + luma_size;
+ frame->buf_cr = (dma_addr_t)-1;
+ frame->size = vframe->size;
+ frame->width = inst->src_fmt.width;
+ frame->stride = fb_stride;
+ frame->map_type = COMPRESSED_FRAME_MAP;
+ frame->update_fb_info = true;
+ }
+ /* In case the count has reduced, clean up leftover framebuffer memory */
+ for (i = non_linear_num; i < MAX_REG_FRAME; i++) {
+ ret = wave5_vpu_dec_reset_framebuffer(inst, i);
+ if (ret)
+ break;
+ }
+
+ for (i = 0; i < linear_num; i++) {
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
+ struct vb2_buffer *vb = vb2_get_buffer(dst_vq, i);
+ struct frame_buffer *frame = &inst->frame_buf[non_linear_num + i];
+ dma_addr_t buf_addr_y = 0, buf_addr_cb = 0, buf_addr_cr = 0;
+ u32 buf_size = 0;
+ u32 fb_stride = inst->dst_fmt.width;
+ u32 luma_size = fb_stride * inst->dst_fmt.height;
+ u32 chroma_size;
+
+ if (inst->output_format == FORMAT_422)
+ chroma_size = fb_stride * inst->dst_fmt.height / 2;
+ else
+ chroma_size = fb_stride * inst->dst_fmt.height / 4;
+
+ if (inst->dst_fmt.num_planes == 1) {
+ buf_size = vb2_plane_size(vb, 0);
+ buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf_addr_cb = buf_addr_y + luma_size;
+ buf_addr_cr = buf_addr_cb + chroma_size;
+ } else if (inst->dst_fmt.num_planes == 2) {
+ buf_size = vb2_plane_size(vb, 0) +
+ vb2_plane_size(vb, 1);
+ buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+ buf_addr_cr = buf_addr_cb + chroma_size;
+ } else if (inst->dst_fmt.num_planes == 3) {
+ buf_size = vb2_plane_size(vb, 0) +
+ vb2_plane_size(vb, 1) +
+ vb2_plane_size(vb, 2);
+ buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+ buf_addr_cr = vb2_dma_contig_plane_dma_addr(vb, 2);
+ }
+
+ frame->buf_y = buf_addr_y;
+ frame->buf_cb = buf_addr_cb;
+ frame->buf_cr = buf_addr_cr;
+ frame->size = buf_size;
+ frame->width = inst->src_fmt.width;
+ frame->stride = fb_stride;
+ frame->map_type = LINEAR_FRAME_MAP;
+ frame->update_fb_info = true;
+ }
+
+ ret = wave5_vpu_dec_register_frame_buffer_ex(inst, non_linear_num, linear_num,
+ fb_stride, inst->dst_fmt.height);
+ if (ret) {
+ dev_dbg(inst->dev->dev, "%s: vpu_dec_register_frame_buffer_ex fail: %d",
+ __func__, ret);
+ return ret;
+ }
+
+ /*
+ * Mark all frame buffers as out of display, to avoid using them before
+ * the application have them queued.
+ */
+ for (i = 0; i < inst->dst_buf_count; i++) {
+ ret = wave5_vpu_dec_set_disp_flag(inst, i);
+ if (ret) {
+ dev_dbg(inst->dev->dev,
+ "%s: Setting display flag of buf index: %u, fail: %d\n",
+ __func__, i, ret);
+ }
+ }
+
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) {
+ struct vb2_v4l2_buffer *vbuf = &buf->vb;
+
+ ret = wave5_vpu_dec_clr_disp_flag(inst, vbuf->vb2_buf.index);
+ if (ret) {
+ dev_dbg(inst->dev->dev,
+ "%s: Clearing display flag of buf index: %u, fail: %d\n",
+ __func__, i, ret);
+ }
+ }
+
+ return 0;
+}
+
+static int write_to_ringbuffer(struct vpu_instance *inst, void *buffer, size_t buffer_size,
+ struct vpu_buf *ring_buffer, dma_addr_t wr_ptr)
+{
+ size_t size;
+ size_t offset = wr_ptr - ring_buffer->daddr;
+ int ret;
+
+ if (wr_ptr + buffer_size > ring_buffer->daddr + ring_buffer->size) {
+ size = ring_buffer->daddr + ring_buffer->size - wr_ptr;
+ ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer, size);
+ if (ret < 0)
+ return ret;
+
+ ret = wave5_vdi_write_memory(inst->dev, ring_buffer, 0, (u8 *)buffer + size,
+ buffer_size - size);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer,
+ buffer_size);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fill_ringbuffer(struct vpu_instance *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct v4l2_m2m_buffer *buf, *n;
+ int ret;
+
+ if (m2m_ctx->last_src_buf) {
+ struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf);
+
+ if (vpu_buf->consumed) {
+ dev_dbg(inst->dev->dev, "last src buffer already written\n");
+ return 0;
+ }
+ }
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) {
+ struct vb2_v4l2_buffer *vbuf = &buf->vb;
+ struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf);
+ struct vpu_buf *ring_buffer = &inst->bitstream_vbuf;
+ size_t src_size = vb2_get_plane_payload(&vbuf->vb2_buf, 0);
+ void *src_buf = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+ dma_addr_t rd_ptr = 0;
+ dma_addr_t wr_ptr = 0;
+ size_t remain_size = 0;
+
+ if (vpu_buf->consumed) {
+ dev_dbg(inst->dev->dev, "already copied src buf (%u) to the ring buffer\n",
+ vbuf->vb2_buf.index);
+ continue;
+ }
+
+ if (!src_buf) {
+ dev_dbg(inst->dev->dev,
+ "%s: Acquiring kernel pointer to src buf (%u), fail\n",
+ __func__, vbuf->vb2_buf.index);
+ break;
+ }
+
+ ret = wave5_vpu_dec_get_bitstream_buffer(inst, &rd_ptr, &wr_ptr, &remain_size);
+ if (ret) {
+ /* Unable to acquire the mutex */
+ dev_err(inst->dev->dev, "Getting the bitstream buffer, fail: %d\n",
+ ret);
+ return ret;
+ }
+
+ dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &rd_ptr, &wr_ptr);
+
+ if (remain_size < src_size) {
+ dev_dbg(inst->dev->dev,
+ "%s: remaining size: %zu < source size: %zu for src buf (%u)\n",
+ __func__, remain_size, src_size, vbuf->vb2_buf.index);
+ break;
+ }
+
+ ret = write_to_ringbuffer(inst, src_buf, src_size, ring_buffer, wr_ptr);
+ if (ret) {
+ dev_err(inst->dev->dev, "Write src buf (%u) to ring buffer, fail: %d\n",
+ vbuf->vb2_buf.index, ret);
+ return ret;
+ }
+
+ ret = wave5_vpu_dec_update_bitstream_buffer(inst, src_size);
+ if (ret) {
+ dev_dbg(inst->dev->dev,
+ "update_bitstream_buffer fail: %d for src buf (%u)\n",
+ ret, vbuf->vb2_buf.index);
+ break;
+ }
+
+ vpu_buf->consumed = true;
+
+ /* Don't write buffers passed the last one while draining. */
+ if (v4l2_m2m_is_last_draining_src_buf(m2m_ctx, vbuf)) {
+ dev_dbg(inst->dev->dev, "last src buffer written to the ring bufferur\n");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb)
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf);
+
+ vpu_buf->consumed = false;
+ vbuf->sequence = inst->queued_src_buf_num++;
+
+ v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+}
+
+static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+
+ vbuf->sequence = inst->queued_dst_buf_num++;
+
+ if (inst->state == VPU_INST_STATE_PIC_RUN) {
+ struct vpu_dst_buffer *vpu_buf = wave5_to_vpu_dst_buf(vbuf);
+ int ret;
+
+ /*
+ * The buffer is already register, just clear the display flag
+ * to let the firmware know it can be used.
+ */
+ vpu_buf->display = false;
+ ret = wave5_vpu_dec_clr_disp_flag(inst, vb->index);
+ if (ret) {
+ dev_dbg(inst->dev->dev,
+ "%s: Clearing the display flag of buffer index: %u, fail: %d\n",
+ __func__, vb->index, ret);
+ }
+ }
+
+ if (vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(m2m_ctx)) {
+ unsigned int i;
+
+ for (i = 0; i < vb->num_planes; i++)
+ vb2_set_plane_payload(vb, i, 0);
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ send_eos_event(inst);
+ v4l2_m2m_last_buffer_done(m2m_ctx, vbuf);
+ } else {
+ v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+ }
+}
+
+static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
+
+ dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n",
+ __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0),
+ vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2));
+
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ wave5_vpu_dec_buf_queue_src(vb);
+ else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ wave5_vpu_dec_buf_queue_dst(vb);
+}
+
+static int wave5_vpu_dec_allocate_ring_buffer(struct vpu_instance *inst)
+{
+ int ret;
+ struct vpu_buf *ring_buffer = &inst->bitstream_vbuf;
+
+ ring_buffer->size = ALIGN(inst->src_fmt.plane_fmt[0].sizeimage, 1024) * 4;
+ ret = wave5_vdi_allocate_dma_memory(inst->dev, ring_buffer);
+ if (ret) {
+ dev_dbg(inst->dev->dev, "%s: allocate ring buffer of size %zu fail: %d\n",
+ __func__, ring_buffer->size, ret);
+ return ret;
+ }
+
+ inst->last_rd_ptr = ring_buffer->daddr;
+
+ return 0;
+}
+
+static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_v4l2_buffer *buf;
+ int ret = 0;
+
+ dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
+
+ v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && inst->state == VPU_INST_STATE_NONE) {
+ struct dec_open_param open_param;
+
+ memset(&open_param, 0, sizeof(struct dec_open_param));
+
+ ret = wave5_vpu_dec_allocate_ring_buffer(inst);
+ if (ret)
+ return ret;
+
+ inst->std = wave5_to_vpu_std(inst->src_fmt.pixelformat, inst->type);
+ if (inst->std == STD_UNKNOWN) {
+ dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
+ (char *)&inst->src_fmt.pixelformat);
+ goto free_bitstream_vbuf;
+ }
+ open_param.bitstream_buffer = inst->bitstream_vbuf.daddr;
+ open_param.bitstream_buffer_size = inst->bitstream_vbuf.size;
+
+ ret = wave5_vpu_dec_open(inst, &open_param);
+ if (ret) {
+ dev_dbg(inst->dev->dev, "%s: decoder opening, fail: %d\n",
+ __func__, ret);
+ goto free_bitstream_vbuf;
+ }
+
+ ret = switch_state(inst, VPU_INST_STATE_OPEN);
+ if (ret)
+ goto free_bitstream_vbuf;
+ } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ struct dec_initial_info *initial_info =
+ &inst->codec_info->dec_info.initial_info;
+
+ if (inst->state == VPU_INST_STATE_STOP)
+ ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
+
+ if (inst->state == VPU_INST_STATE_INIT_SEQ) {
+ if (initial_info->luma_bitdepth != 8) {
+ dev_info(inst->dev->dev, "%s: no support for %d bit depth",
+ __func__, initial_info->luma_bitdepth);
+ goto cleanup_dst_buffers;
+ }
+ }
+ }
+
+ return ret;
+
+free_bitstream_vbuf:
+ wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
+cleanup_dst_buffers:
+ while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx)))
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
+ return -EINVAL;
+}
+
+static int streamoff_output(struct vb2_queue *q)
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_v4l2_buffer *buf;
+ int ret;
+ dma_addr_t new_rd_ptr;
+
+ while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
+ dev_dbg(inst->dev->dev, "%s: (Multiplanar) buf type %4u | index %4u\n",
+ __func__, buf->vb2_buf.type, buf->vb2_buf.index);
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+ }
+
+ ret = wave5_vpu_flush_instance(inst);
+ if (ret)
+ return ret;
+
+ /* Reset the ring buffer information */
+ new_rd_ptr = wave5_vpu_dec_get_rd_ptr(inst);
+ inst->last_rd_ptr = new_rd_ptr;
+ inst->codec_info->dec_info.stream_rd_ptr = new_rd_ptr;
+ inst->codec_info->dec_info.stream_wr_ptr = new_rd_ptr;
+
+ if (v4l2_m2m_has_stopped(m2m_ctx))
+ send_eos_event(inst);
+
+ /* streamoff on output cancels any draining operation */
+ inst->eos = false;
+
+ return ret;
+}
+
+static int streamoff_capture(struct vb2_queue *q)
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_v4l2_buffer *buf;
+ unsigned int i;
+ int ret = 0;
+
+ /* TOOD unregister the framebuffers to restrict access from the firmware to
+ * buffers that are freed up
+ */
+ while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) {
+ u32 plane;
+
+ dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
+ __func__, buf->vb2_buf.type, buf->vb2_buf.index);
+
+ for (plane = 0; plane < inst->dst_fmt.num_planes; plane++)
+ vb2_set_plane_payload(&buf->vb2_buf, plane, 0);
+
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+ }
+
+ for (i = 0; i < inst->dst_buf_count; i++) {
+ ret = wave5_vpu_dec_set_disp_flag(inst, i);
+ if (ret) {
+ dev_dbg(inst->dev->dev,
+ "%s: Setting display flag of buf index: %u, fail: %d\n",
+ __func__, i, ret);
+ }
+ }
+
+ if (inst->needs_reallocation) {
+ wave5_vpu_dec_give_command(inst, DEC_RESET_FRAMEBUF_INFO, NULL);
+ inst->needs_reallocation = false;
+ }
+
+ if (v4l2_m2m_has_stopped(m2m_ctx)) {
+ ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ bool check_cmd = TRUE;
+
+ dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
+
+ while (check_cmd) {
+ struct queue_status_info q_status;
+ struct dec_output_info dec_output_info;
+
+ wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
+
+ if (q_status.report_queue_count == 0)
+ break;
+
+ if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0)
+ break;
+
+ if (wave5_vpu_dec_get_output_info(inst, &dec_output_info))
+ dev_dbg(inst->dev->dev, "Getting decoding results from fw, fail\n");
+ }
+
+ v4l2_m2m_update_stop_streaming_state(m2m_ctx, q);
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ streamoff_output(q);
+ else
+ streamoff_capture(q);
+}
+
+static const struct vb2_ops wave5_vpu_dec_vb2_ops = {
+ .queue_setup = wave5_vpu_dec_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_queue = wave5_vpu_dec_buf_queue,
+ .start_streaming = wave5_vpu_dec_start_streaming,
+ .stop_streaming = wave5_vpu_dec_stop_streaming,
+};
+
+static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
+ struct v4l2_pix_format_mplane *dst_fmt)
+{
+ unsigned int dst_pix_fmt = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
+ const struct v4l2_format_info *dst_fmt_info = v4l2_format_info(dst_pix_fmt);
+
+ src_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->flags = 0;
+ src_fmt->num_planes = 1;
+ wave5_update_pix_fmt(src_fmt, 720, 480);
+
+ dst_fmt->pixelformat = dst_pix_fmt;
+ dst_fmt->field = V4L2_FIELD_NONE;
+ dst_fmt->flags = 0;
+ dst_fmt->num_planes = dst_fmt_info->mem_planes;
+ wave5_update_pix_fmt(dst_fmt, 736, 480);
+}
+
+static int wave5_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_dec_vb2_ops);
+}
+
+static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = {
+ .finish_process = wave5_vpu_dec_finish_decode,
+};
+
+static int initialize_sequence(struct vpu_instance *inst)
+{
+ struct dec_initial_info initial_info;
+ int ret = 0;
+
+ memset(&initial_info, 0, sizeof(struct dec_initial_info));
+
+ ret = wave5_vpu_dec_issue_seq_init(inst);
+ if (ret) {
+ dev_dbg(inst->dev->dev, "%s: wave5_vpu_dec_issue_seq_init, fail: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0)
+ dev_dbg(inst->dev->dev, "%s: failed to call vpu_wait_interrupt()\n", __func__);
+
+ ret = wave5_vpu_dec_complete_seq_init(inst, &initial_info);
+ if (ret) {
+ dev_dbg(inst->dev->dev, "%s: vpu_dec_complete_seq_init, fail: %d, reason: %u\n",
+ __func__, ret, initial_info.seq_init_err_reason);
+ wave5_handle_src_buffer(inst, initial_info.rd_ptr);
+ } else {
+ handle_dynamic_resolution_change(inst);
+ }
+
+ return ret;
+}
+
+/* Must be called with state_spinlock */
+static bool wave5_is_draining_or_eos(struct vpu_instance *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+
+ return m2m_ctx->is_draining || inst->eos;
+}
+
+static void wave5_vpu_dec_device_run(void *priv)
+{
+ struct vpu_instance *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct queue_status_info q_status;
+ u32 fail_res = 0;
+ int ret = 0;
+
+ dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__);
+
+ ret = fill_ringbuffer(inst);
+ if (ret) {
+ dev_warn(inst->dev->dev, "Filling ring buffer failed\n");
+ goto finish_job_and_return;
+ }
+
+ switch (inst->state) {
+ case VPU_INST_STATE_OPEN:
+ ret = initialize_sequence(inst);
+ if (ret) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&inst->state_spinlock, flags);
+ if (wave5_is_draining_or_eos(inst) &&
+ wave5_last_src_buffer_consumed(m2m_ctx)) {
+ struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
+
+ switch_state(inst, VPU_INST_STATE_STOP);
+
+ if (vb2_is_streaming(dst_vq))
+ send_eos_event(inst);
+ else
+ handle_dynamic_resolution_change(inst);
+
+ flag_last_buffer_done(inst);
+ }
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+ } else {
+ switch_state(inst, VPU_INST_STATE_INIT_SEQ);
+ }
+
+ break;
+
+ case VPU_INST_STATE_INIT_SEQ:
+ /*
+ * Do this early, preparing the fb can trigger an IRQ before
+ * we had a chance to switch, which leads to an invalid state
+ * change.
+ */
+ switch_state(inst, VPU_INST_STATE_PIC_RUN);
+
+ /*
+ * During DRC, the picture decoding remains pending, so just leave the job
+ * active until this decode operation completes.
+ */
+ wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
+
+ /*
+ * The sequence must be analyzed first to calculate the proper
+ * size of the auxiliary buffers.
+ */
+ ret = wave5_prepare_fb(inst);
+ if (ret) {
+ dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret);
+ switch_state(inst, VPU_INST_STATE_STOP);
+ break;
+ }
+
+ if (q_status.instance_queue_count) {
+ dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
+ return;
+ }
+
+ fallthrough;
+ case VPU_INST_STATE_PIC_RUN:
+ ret = start_decode(inst, &fail_res);
+ if (ret) {
+ dev_err(inst->dev->dev,
+ "Frame decoding on m2m context (%p), fail: %d (result: %d)\n",
+ m2m_ctx, ret, fail_res);
+ break;
+ }
+ /* Return so that we leave this job active */
+ dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
+ return;
+ default:
+ WARN(1, "Execution of a job in state %s illegal.\n", state_to_str(inst->state));
+ break;
+ }
+
+finish_job_and_return:
+ dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+}
+
+static void wave5_vpu_dec_job_abort(void *priv)
+{
+ struct vpu_instance *inst = priv;
+ int ret;
+
+ ret = switch_state(inst, VPU_INST_STATE_STOP);
+ if (ret)
+ return;
+
+ ret = wave5_vpu_dec_set_eos_on_firmware(inst);
+ if (ret)
+ dev_warn(inst->dev->dev,
+ "Setting EOS for the bitstream, fail: %d\n", ret);
+}
+
+static int wave5_vpu_dec_job_ready(void *priv)
+{
+ struct vpu_instance *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ unsigned long flags;
+ bool ret = false;
+
+ spin_lock_irqsave(&inst->state_spinlock, flags);
+
+ switch (inst->state) {
+ case VPU_INST_STATE_NONE:
+ dev_dbg(inst->dev->dev, "Decoder must be open to start queueing M2M jobs!\n");
+ break;
+ case VPU_INST_STATE_OPEN:
+ if (wave5_is_draining_or_eos(inst) || !v4l2_m2m_has_stopped(m2m_ctx) ||
+ v4l2_m2m_num_src_bufs_ready(m2m_ctx) > 0) {
+ ret = true;
+ break;
+ }
+
+ dev_dbg(inst->dev->dev,
+ "Decoder must be draining or >= 1 OUTPUT queue buffer must be queued!\n");
+ break;
+ case VPU_INST_STATE_INIT_SEQ:
+ case VPU_INST_STATE_PIC_RUN:
+ if (!m2m_ctx->cap_q_ctx.q.streaming) {
+ dev_dbg(inst->dev->dev, "CAPTURE queue must be streaming to queue jobs!\n");
+ break;
+ } else if (v4l2_m2m_num_dst_bufs_ready(m2m_ctx) < (inst->fbc_buf_count - 1)) {
+ dev_dbg(inst->dev->dev,
+ "No capture buffer ready to decode!\n");
+ break;
+ } else if (!wave5_is_draining_or_eos(inst) &&
+ !v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
+ dev_dbg(inst->dev->dev,
+ "No bitstream data to decode!\n");
+ break;
+ }
+ ret = true;
+ break;
+ case VPU_INST_STATE_STOP:
+ dev_dbg(inst->dev->dev, "Decoder is stopped, not running.\n");
+ break;
+ }
+
+ spin_unlock_irqrestore(&inst->state_spinlock, flags);
+
+ return ret;
+}
+
+static const struct v4l2_m2m_ops wave5_vpu_dec_m2m_ops = {
+ .device_run = wave5_vpu_dec_device_run,
+ .job_abort = wave5_vpu_dec_job_abort,
+ .job_ready = wave5_vpu_dec_job_ready,
+};
+
+static int wave5_vpu_open_dec(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct vpu_device *dev = video_drvdata(filp);
+ struct vpu_instance *inst = NULL;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ int ret = 0;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ inst->dev = dev;
+ inst->type = VPU_INST_TYPE_DEC;
+ inst->ops = &wave5_vpu_dec_inst_ops;
+
+ spin_lock_init(&inst->state_spinlock);
+
+ inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
+ if (!inst->codec_info)
+ return -ENOMEM;
+
+ v4l2_fh_init(&inst->v4l2_fh, vdev);
+ filp->private_data = &inst->v4l2_fh;
+ v4l2_fh_add(&inst->v4l2_fh);
+
+ INIT_LIST_HEAD(&inst->list);
+ list_add_tail(&inst->list, &dev->instances);
+
+ inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_dec_dev;
+ inst->v4l2_fh.m2m_ctx =
+ v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_dec_queue_init);
+ if (IS_ERR(inst->v4l2_fh.m2m_ctx)) {
+ ret = PTR_ERR(inst->v4l2_fh.m2m_ctx);
+ goto cleanup_inst;
+ }
+ m2m_ctx = inst->v4l2_fh.m2m_ctx;
+
+ v4l2_m2m_set_src_buffered(m2m_ctx, true);
+ v4l2_m2m_set_dst_buffered(m2m_ctx, true);
+ /*
+ * We use the M2M job queue to ensure synchronization of steps where
+ * needed, as IOCTLs can occur at anytime and we need to run commands on
+ * the firmware in a specified order.
+ * In order to initialize the sequence on the firmware within an M2M
+ * job, the M2M framework needs to be able to queue jobs before
+ * the CAPTURE queue has been started, because we need the results of the
+ * initialization to properly prepare the CAPTURE queue with the correct
+ * amount of buffers
+ */
+ v4l2_m2m_set_dst_ignore_streaming(m2m_ctx, true);
+
+ v4l2_ctrl_handler_init(&inst->v4l2_ctrl_hdl, 10);
+ v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, NULL,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1);
+
+ if (inst->v4l2_ctrl_hdl.error) {
+ ret = -ENODEV;
+ goto cleanup_inst;
+ }
+
+ inst->v4l2_fh.ctrl_handler = &inst->v4l2_ctrl_hdl;
+ v4l2_ctrl_handler_setup(&inst->v4l2_ctrl_hdl);
+
+ wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
+ inst->colorspace = V4L2_COLORSPACE_REC709;
+ inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ inst->hsv_enc = 0;
+ inst->quantization = V4L2_QUANTIZATION_DEFAULT;
+ inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+ init_completion(&inst->irq_done);
+ ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
+ if (ret) {
+ dev_err(inst->dev->dev, "failed to allocate fifo\n");
+ goto cleanup_inst;
+ }
+
+ inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
+ if (inst->id < 0) {
+ dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id);
+ ret = inst->id;
+ goto cleanup_inst;
+ }
+
+ wave5_vdi_allocate_sram(inst->dev);
+
+ return 0;
+
+cleanup_inst:
+ wave5_cleanup_instance(inst);
+ return ret;
+}
+
+static int wave5_vpu_dec_release(struct file *filp)
+{
+ return wave5_vpu_release_device(filp, wave5_vpu_dec_close, "decoder");
+}
+
+static const struct v4l2_file_operations wave5_vpu_dec_fops = {
+ .owner = THIS_MODULE,
+ .open = wave5_vpu_open_dec,
+ .release = wave5_vpu_dec_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+int wave5_vpu_dec_register_device(struct vpu_device *dev)
+{
+ struct video_device *vdev_dec;
+ int ret;
+
+ vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL);
+ if (!vdev_dec)
+ return -ENOMEM;
+
+ dev->v4l2_m2m_dec_dev = v4l2_m2m_init(&wave5_vpu_dec_m2m_ops);
+ if (IS_ERR(dev->v4l2_m2m_dec_dev)) {
+ ret = PTR_ERR(dev->v4l2_m2m_dec_dev);
+ dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret);
+ return -EINVAL;
+ }
+
+ dev->video_dev_dec = vdev_dec;
+
+ strscpy(vdev_dec->name, VPU_DEC_DEV_NAME, sizeof(vdev_dec->name));
+ vdev_dec->fops = &wave5_vpu_dec_fops;
+ vdev_dec->ioctl_ops = &wave5_vpu_dec_ioctl_ops;
+ vdev_dec->release = video_device_release_empty;
+ vdev_dec->v4l2_dev = &dev->v4l2_dev;
+ vdev_dec->vfl_dir = VFL_DIR_M2M;
+ vdev_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ vdev_dec->lock = &dev->dev_lock;
+
+ ret = video_register_device(vdev_dec, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ return ret;
+
+ video_set_drvdata(vdev_dec, dev);
+
+ return 0;
+}
+
+void wave5_vpu_dec_unregister_device(struct vpu_device *dev)
+{
+ video_unregister_device(dev->video_dev_dec);
+ if (dev->v4l2_m2m_dec_dev)
+ v4l2_m2m_release(dev->v4l2_m2m_dec_dev);
+}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
new file mode 100644
index 000000000000..e8d8e31b0cbe
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
@@ -0,0 +1,1825 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - encoder interface
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+
+#include "wave5-helper.h"
+
+#define VPU_ENC_DEV_NAME "C&M Wave5 VPU encoder"
+#define VPU_ENC_DRV_NAME "wave5-enc"
+
+static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = {
+ [VPU_FMT_TYPE_CODEC] = {
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_H264,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ },
+ [VPU_FMT_TYPE_RAW] = {
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ },
+ }
+};
+
+static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state)
+{
+ switch (state) {
+ case VPU_INST_STATE_NONE:
+ goto invalid_state_switch;
+ case VPU_INST_STATE_OPEN:
+ if (inst->state != VPU_INST_STATE_NONE)
+ goto invalid_state_switch;
+ break;
+ case VPU_INST_STATE_INIT_SEQ:
+ if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP)
+ goto invalid_state_switch;
+ break;
+ case VPU_INST_STATE_PIC_RUN:
+ if (inst->state != VPU_INST_STATE_INIT_SEQ)
+ goto invalid_state_switch;
+ break;
+ case VPU_INST_STATE_STOP:
+ break;
+ };
+
+ dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n",
+ state_to_str(inst->state), state_to_str(state));
+ inst->state = state;
+ return 0;
+
+invalid_state_switch:
+ WARN(1, "Invalid state switch from %s to %s.\n",
+ state_to_str(inst->state), state_to_str(state));
+ return -EINVAL;
+}
+
+static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
+ unsigned int height)
+{
+ switch (pix_mp->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ pix_mp->width = width;
+ pix_mp->height = height;
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_YUV420M:
+ pix_mp->width = width;
+ pix_mp->height = height;
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
+ pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
+ pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 4;
+ pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
+ pix_mp->plane_fmt[2].sizeimage = round_up(width, 32) * height / 4;
+ break;
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_NV21M:
+ pix_mp->width = width;
+ pix_mp->height = height;
+ pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
+ pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
+ pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 2;
+ break;
+ default:
+ pix_mp->width = width;
+ pix_mp->height = height;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+ pix_mp->plane_fmt[0].sizeimage = width * height / 8 * 3;
+ break;
+ }
+}
+
+static int start_encode(struct vpu_instance *inst, u32 *fail_res)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ int ret;
+ struct vb2_v4l2_buffer *src_buf;
+ struct vb2_v4l2_buffer *dst_buf;
+ struct frame_buffer frame_buf;
+ struct enc_param pic_param;
+ u32 stride = ALIGN(inst->dst_fmt.width, 32);
+ u32 luma_size = (stride * inst->dst_fmt.height);
+ u32 chroma_size = ((stride / 2) * (inst->dst_fmt.height / 2));
+
+ memset(&pic_param, 0, sizeof(struct enc_param));
+ memset(&frame_buf, 0, sizeof(struct frame_buffer));
+
+ dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
+ if (!dst_buf) {
+ dev_dbg(inst->dev->dev, "%s: No destination buffer found\n", __func__);
+ return -EAGAIN;
+ }
+
+ pic_param.pic_stream_buffer_addr =
+ vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ pic_param.pic_stream_buffer_size =
+ vb2_plane_size(&dst_buf->vb2_buf, 0);
+
+ src_buf = v4l2_m2m_next_src_buf(m2m_ctx);
+ if (!src_buf) {
+ dev_dbg(inst->dev->dev, "%s: No source buffer found\n", __func__);
+ if (m2m_ctx->is_draining)
+ pic_param.src_end_flag = 1;
+ else
+ return -EAGAIN;
+ } else {
+ if (inst->src_fmt.num_planes == 1) {
+ frame_buf.buf_y =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ frame_buf.buf_cb = frame_buf.buf_y + luma_size;
+ frame_buf.buf_cr = frame_buf.buf_cb + chroma_size;
+ } else if (inst->src_fmt.num_planes == 2) {
+ frame_buf.buf_y =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ frame_buf.buf_cb =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
+ frame_buf.buf_cr = frame_buf.buf_cb + chroma_size;
+ } else if (inst->src_fmt.num_planes == 3) {
+ frame_buf.buf_y =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ frame_buf.buf_cb =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
+ frame_buf.buf_cr =
+ vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 2);
+ }
+ frame_buf.stride = stride;
+ pic_param.src_idx = src_buf->vb2_buf.index;
+ }
+
+ pic_param.source_frame = &frame_buf;
+ pic_param.code_option.implicit_header_encode = 1;
+ pic_param.code_option.encode_aud = inst->encode_aud;
+ ret = wave5_vpu_enc_start_one_frame(inst, &pic_param, fail_res);
+ if (ret) {
+ if (*fail_res == WAVE5_SYSERR_QUEUEING_FAIL)
+ return -EINVAL;
+
+ dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame fail: %d\n",
+ __func__, ret);
+ src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
+ if (!src_buf) {
+ dev_dbg(inst->dev->dev,
+ "%s: Removing src buf failed, the queue is empty\n",
+ __func__);
+ return -EINVAL;
+ }
+ dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
+ if (!dst_buf) {
+ dev_dbg(inst->dev->dev,
+ "%s: Removing dst buf failed, the queue is empty\n",
+ __func__);
+ return -EINVAL;
+ }
+ switch_state(inst, VPU_INST_STATE_STOP);
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+ } else {
+ dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n",
+ __func__);
+ /*
+ * Remove the source buffer from the ready-queue now and finish
+ * it in the videobuf2 framework once the index is returned by the
+ * firmware in finish_encode
+ */
+ if (src_buf)
+ v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index);
+ }
+
+ return 0;
+}
+
+static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ int ret;
+ struct enc_output_info enc_output_info;
+ struct vb2_v4l2_buffer *src_buf = NULL;
+ struct vb2_v4l2_buffer *dst_buf = NULL;
+
+ ret = wave5_vpu_enc_get_output_info(inst, &enc_output_info);
+ if (ret) {
+ dev_dbg(inst->dev->dev,
+ "%s: vpu_enc_get_output_info fail: %d reason: %u | info: %u\n",
+ __func__, ret, enc_output_info.error_reason, enc_output_info.warn_info);
+ return;
+ }
+
+ dev_dbg(inst->dev->dev,
+ "%s: pic_type %i recon_idx %i src_idx %i pic_byte %u pts %llu\n",
+ __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index,
+ enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts);
+
+ /*
+ * The source buffer will not be found in the ready-queue as it has been
+ * dropped after sending of the encode firmware command, locate it in
+ * the videobuf2 queue directly
+ */
+ if (enc_output_info.enc_src_idx >= 0) {
+ struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx),
+ enc_output_info.enc_src_idx);
+ if (vb->state != VB2_BUF_STATE_ACTIVE)
+ dev_warn(inst->dev->dev,
+ "%s: encoded buffer (%d) was not in ready queue %i.",
+ __func__, enc_output_info.enc_src_idx, vb->state);
+ else
+ src_buf = to_vb2_v4l2_buffer(vb);
+
+ if (src_buf) {
+ inst->timestamp = src_buf->vb2_buf.timestamp;
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ } else {
+ dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n",
+ __func__, enc_output_info.enc_src_idx);
+ }
+ }
+
+ dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
+ if (enc_output_info.recon_frame_index == RECON_IDX_FLAG_ENC_END) {
+ static const struct v4l2_event vpu_event_eos = {
+ .type = V4L2_EVENT_EOS
+ };
+
+ if (dst_buf) {
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
+ dst_buf->field = V4L2_FIELD_NONE;
+ v4l2_m2m_last_buffer_done(m2m_ctx, dst_buf);
+ } else {
+ WARN(1, "A job have been run without destination buffer.\n");
+ }
+
+ v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos);
+
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ } else {
+ if (!dst_buf) {
+ dev_warn(inst->dev->dev, "No bitstream buffer.");
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ return;
+ }
+
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_output_info.bitstream_size);
+
+ dst_buf->vb2_buf.timestamp = inst->timestamp;
+ dst_buf->field = V4L2_FIELD_NONE;
+ if (enc_output_info.pic_type == PIC_TYPE_I) {
+ if (enc_output_info.enc_vcl_nut == 19 ||
+ enc_output_info.enc_vcl_nut == 20)
+ dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ else
+ dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
+ } else if (enc_output_info.pic_type == PIC_TYPE_P) {
+ dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
+ } else if (enc_output_info.pic_type == PIC_TYPE_B) {
+ dst_buf->flags |= V4L2_BUF_FLAG_BFRAME;
+ }
+
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+
+ dev_dbg(inst->dev->dev, "%s: frame_cycle %8u\n",
+ __func__, enc_output_info.frame_cycle);
+
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+ }
+}
+
+static int wave5_vpu_enc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, VPU_ENC_DRV_NAME, sizeof(cap->driver));
+ strscpy(cap->card, VPU_ENC_DRV_NAME, sizeof(cap->card));
+ strscpy(cap->bus_info, "platform:" VPU_ENC_DRV_NAME, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int wave5_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize)
+{
+ const struct vpu_format *vpu_fmt;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt) {
+ vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt)
+ return -EINVAL;
+ }
+
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = vpu_fmt->min_width;
+ fsize->stepwise.max_width = vpu_fmt->max_width;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = vpu_fmt->min_height;
+ fsize->stepwise.max_height = vpu_fmt->max_height;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int wave5_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
+
+ dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
+
+ vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ f->pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->flags = 0;
+
+ return 0;
+}
+
+static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
+
+ dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt) {
+ f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
+ f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, inst->dst_fmt.width, inst->dst_fmt.height);
+ } else {
+ int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
+ int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
+
+ f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->fmt.pix_mp.num_planes = 1;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
+ }
+
+ f->fmt.pix_mp.flags = 0;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = inst->colorspace;
+ f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
+ f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
+ f->fmt.pix_mp.quantization = inst->quantization;
+ f->fmt.pix_mp.xfer_func = inst->xfer_func;
+ memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
+
+ return 0;
+}
+
+static int wave5_vpu_enc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ int i, ret;
+
+ dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
+
+ ret = wave5_vpu_enc_try_fmt_cap(file, fh, f);
+ if (ret)
+ return ret;
+
+ inst->dst_fmt.width = f->fmt.pix_mp.width;
+ inst->dst_fmt.height = f->fmt.pix_mp.height;
+ inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
+ inst->dst_fmt.field = f->fmt.pix_mp.field;
+ inst->dst_fmt.flags = f->fmt.pix_mp.flags;
+ inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes;
+ for (i = 0; i < inst->dst_fmt.num_planes; i++) {
+ inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
+ inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
+
+ return 0;
+}
+
+static int wave5_vpu_enc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ int i;
+
+ f->fmt.pix_mp.width = inst->dst_fmt.width;
+ f->fmt.pix_mp.height = inst->dst_fmt.height;
+ f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
+ f->fmt.pix_mp.field = inst->dst_fmt.field;
+ f->fmt.pix_mp.flags = inst->dst_fmt.flags;
+ f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
+ for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+ f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline;
+ f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage;
+ }
+
+ f->fmt.pix_mp.colorspace = inst->colorspace;
+ f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
+ f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
+ f->fmt.pix_mp.quantization = inst->quantization;
+ f->fmt.pix_mp.xfer_func = inst->xfer_func;
+
+ return 0;
+}
+
+static int wave5_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
+
+ dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
+
+ vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ f->pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->flags = 0;
+
+ return 0;
+}
+
+static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
+
+ dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt) {
+ f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
+ f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
+ } else {
+ int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
+ int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
+ const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
+
+ f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
+ f->fmt.pix_mp.num_planes = info->mem_planes;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
+ }
+
+ f->fmt.pix_mp.flags = 0;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
+
+ return 0;
+}
+
+static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ int i, ret;
+
+ dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
+ __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
+
+ ret = wave5_vpu_enc_try_fmt_out(file, fh, f);
+ if (ret)
+ return ret;
+
+ inst->src_fmt.width = f->fmt.pix_mp.width;
+ inst->src_fmt.height = f->fmt.pix_mp.height;
+ inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
+ inst->src_fmt.field = f->fmt.pix_mp.field;
+ inst->src_fmt.flags = f->fmt.pix_mp.flags;
+ inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes;
+ for (i = 0; i < inst->src_fmt.num_planes; i++) {
+ inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
+ inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
+
+ if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 ||
+ inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M) {
+ inst->cbcr_interleave = true;
+ inst->nv21 = false;
+ } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 ||
+ inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) {
+ inst->cbcr_interleave = true;
+ inst->nv21 = true;
+ } else {
+ inst->cbcr_interleave = false;
+ inst->nv21 = false;
+ }
+
+ inst->colorspace = f->fmt.pix_mp.colorspace;
+ inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ inst->hsv_enc = f->fmt.pix_mp.hsv_enc;
+ inst->quantization = f->fmt.pix_mp.quantization;
+ inst->xfer_func = f->fmt.pix_mp.xfer_func;
+
+ wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+
+ return 0;
+}
+
+static int wave5_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+
+ dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target);
+
+ if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->dst_fmt.width;
+ s->r.height = inst->dst_fmt.height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->dst_fmt.width;
+ s->r.height = inst->dst_fmt.height;
+ dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n",
+ __func__, s->r.width, s->r.height);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wave5_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ if (s->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n",
+ __func__, s->r.width, s->r.height);
+
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->src_fmt.width;
+ s->r.height = inst->src_fmt.height;
+
+ return 0;
+}
+
+static int wave5_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ int ret;
+
+ ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec);
+ if (ret)
+ return ret;
+
+ if (!wave5_vpu_both_queues_are_streaming(inst))
+ return 0;
+
+ switch (ec->cmd) {
+ case V4L2_ENC_CMD_STOP:
+ if (m2m_ctx->is_draining)
+ return -EBUSY;
+
+ if (m2m_ctx->has_stopped)
+ return 0;
+
+ m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
+ m2m_ctx->is_draining = true;
+ break;
+ case V4L2_ENC_CMD_START:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wave5_vpu_enc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+
+ dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.output.timeperframe.numerator = 1;
+ a->parm.output.timeperframe.denominator = inst->frame_rate;
+
+ dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n",
+ __func__, a->parm.output.timeperframe.numerator,
+ a->parm.output.timeperframe.denominator);
+
+ return 0;
+}
+
+static int wave5_vpu_enc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+
+ dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ if (a->parm.output.timeperframe.denominator && a->parm.output.timeperframe.numerator) {
+ inst->frame_rate = a->parm.output.timeperframe.denominator /
+ a->parm.output.timeperframe.numerator;
+ } else {
+ a->parm.output.timeperframe.numerator = 1;
+ a->parm.output.timeperframe.denominator = inst->frame_rate;
+ }
+
+ dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n",
+ __func__, a->parm.output.timeperframe.numerator,
+ a->parm.output.timeperframe.denominator);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops wave5_vpu_enc_ioctl_ops = {
+ .vidioc_querycap = wave5_vpu_enc_querycap,
+ .vidioc_enum_framesizes = wave5_vpu_enc_enum_framesizes,
+
+ .vidioc_enum_fmt_vid_cap = wave5_vpu_enc_enum_fmt_cap,
+ .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_enc_s_fmt_cap,
+ .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_enc_g_fmt_cap,
+ .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_enc_try_fmt_cap,
+
+ .vidioc_enum_fmt_vid_out = wave5_vpu_enc_enum_fmt_out,
+ .vidioc_s_fmt_vid_out_mplane = wave5_vpu_enc_s_fmt_out,
+ .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out,
+ .vidioc_try_fmt_vid_out_mplane = wave5_vpu_enc_try_fmt_out,
+
+ .vidioc_g_selection = wave5_vpu_enc_g_selection,
+ .vidioc_s_selection = wave5_vpu_enc_s_selection,
+
+ .vidioc_g_parm = wave5_vpu_enc_g_parm,
+ .vidioc_s_parm = wave5_vpu_enc_s_parm,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+ .vidioc_encoder_cmd = wave5_vpu_enc_encoder_cmd,
+
+ .vidioc_subscribe_event = wave5_vpu_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpu_instance *inst = wave5_ctrl_to_vpu_inst(ctrl);
+
+ dev_dbg(inst->dev->dev, "%s: name: %s | value: %d\n", __func__, ctrl->name, ctrl->val);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_AU_DELIMITER:
+ inst->encode_aud = ctrl->val;
+ break;
+ case V4L2_CID_HFLIP:
+ inst->mirror_direction |= (ctrl->val << 1);
+ break;
+ case V4L2_CID_VFLIP:
+ inst->mirror_direction |= ctrl->val;
+ break;
+ case V4L2_CID_ROTATE:
+ inst->rot_angle = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
+ inst->vbv_buf_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
+ inst->rc_mode = 0;
+ break;
+ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
+ inst->rc_mode = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ inst->bit_rate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ inst->enc_param.avc_idr_period = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ inst->enc_param.independ_slice_mode = ctrl->val;
+ inst->enc_param.avc_slice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ inst->enc_param.independ_slice_mode_arg = ctrl->val;
+ inst->enc_param.avc_slice_arg = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ inst->rc_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ inst->enc_param.mb_level_rc_enable = ctrl->val;
+ inst->enc_param.cu_level_rc_enable = ctrl->val;
+ inst->enc_param.hvs_qp_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
+ inst->enc_param.profile = HEVC_PROFILE_MAIN;
+ inst->bit_depth = 8;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
+ inst->enc_param.profile = HEVC_PROFILE_STILLPICTURE;
+ inst->enc_param.en_still_picture = 1;
+ inst->bit_depth = 8;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
+ inst->enc_param.profile = HEVC_PROFILE_MAIN10;
+ inst->bit_depth = 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
+ inst->enc_param.level = 10 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
+ inst->enc_param.level = 20 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
+ inst->enc_param.level = 21 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
+ inst->enc_param.level = 30 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
+ inst->enc_param.level = 31 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
+ inst->enc_param.level = 40 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
+ inst->enc_param.level = 41 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
+ inst->enc_param.level = 50 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
+ inst->enc_param.level = 51 * 3;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2:
+ inst->enc_param.level = 52 * 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
+ inst->enc_param.min_qp_i = ctrl->val;
+ inst->enc_param.min_qp_p = ctrl->val;
+ inst->enc_param.min_qp_b = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
+ inst->enc_param.max_qp_i = ctrl->val;
+ inst->enc_param.max_qp_p = ctrl->val;
+ inst->enc_param.max_qp_b = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
+ inst->enc_param.intra_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED:
+ inst->enc_param.disable_deblk = 1;
+ inst->enc_param.sao_enable = 0;
+ inst->enc_param.lf_cross_slice_boundary_enable = 0;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED:
+ inst->enc_param.disable_deblk = 0;
+ inst->enc_param.sao_enable = 1;
+ inst->enc_param.lf_cross_slice_boundary_enable = 1;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
+ inst->enc_param.disable_deblk = 0;
+ inst->enc_param.sao_enable = 1;
+ inst->enc_param.lf_cross_slice_boundary_enable = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2:
+ inst->enc_param.beta_offset_div2 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2:
+ inst->enc_param.tc_offset_div2 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE:
+ inst->enc_param.decoding_refresh_type = 0;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA:
+ inst->enc_param.decoding_refresh_type = 1;
+ break;
+ case V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR:
+ inst->enc_param.decoding_refresh_type = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD:
+ inst->enc_param.intra_period = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU:
+ inst->enc_param.lossless_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED:
+ inst->enc_param.const_intra_pred_flag = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT:
+ inst->enc_param.wpp_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING:
+ inst->enc_param.strong_intra_smooth_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1:
+ inst->enc_param.max_num_merge = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION:
+ inst->enc_param.tmvp_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+ inst->enc_param.profile = H264_PROFILE_BP;
+ inst->bit_depth = 8;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+ inst->enc_param.profile = H264_PROFILE_MP;
+ inst->bit_depth = 8;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
+ inst->enc_param.profile = H264_PROFILE_EXTENDED;
+ inst->bit_depth = 8;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+ inst->enc_param.profile = H264_PROFILE_HP;
+ inst->bit_depth = 8;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
+ inst->enc_param.profile = H264_PROFILE_HIGH10;
+ inst->bit_depth = 10;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
+ inst->enc_param.profile = H264_PROFILE_HIGH422;
+ inst->bit_depth = 10;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
+ inst->enc_param.profile = H264_PROFILE_HIGH444;
+ inst->bit_depth = 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+ inst->enc_param.level = 10;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+ inst->enc_param.level = 9;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+ inst->enc_param.level = 11;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+ inst->enc_param.level = 12;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+ inst->enc_param.level = 13;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+ inst->enc_param.level = 20;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+ inst->enc_param.level = 21;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+ inst->enc_param.level = 22;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+ inst->enc_param.level = 30;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+ inst->enc_param.level = 31;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+ inst->enc_param.level = 32;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+ inst->enc_param.level = 40;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+ inst->enc_param.level = 41;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+ inst->enc_param.level = 42;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+ inst->enc_param.level = 50;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+ inst->enc_param.level = 51;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ inst->enc_param.min_qp_i = ctrl->val;
+ inst->enc_param.min_qp_p = ctrl->val;
+ inst->enc_param.min_qp_b = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ inst->enc_param.max_qp_i = ctrl->val;
+ inst->enc_param.max_qp_p = ctrl->val;
+ inst->enc_param.max_qp_b = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ inst->enc_param.intra_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED:
+ inst->enc_param.disable_deblk = 1;
+ inst->enc_param.lf_cross_slice_boundary_enable = 1;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED:
+ inst->enc_param.disable_deblk = 0;
+ inst->enc_param.lf_cross_slice_boundary_enable = 1;
+ break;
+ case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
+ inst->enc_param.disable_deblk = 0;
+ inst->enc_param.lf_cross_slice_boundary_enable = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+ inst->enc_param.beta_offset_div2 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+ inst->enc_param.tc_offset_div2 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ inst->enc_param.transform8x8_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION:
+ inst->enc_param.const_intra_pred_flag = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET:
+ inst->enc_param.chroma_cb_qp_offset = ctrl->val;
+ inst->enc_param.chroma_cr_qp_offset = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+ inst->enc_param.intra_period = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ inst->enc_param.entropy_coding_mode = ctrl->val;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops wave5_vpu_enc_ctrl_ops = {
+ .s_ctrl = wave5_vpu_enc_s_ctrl,
+};
+
+static int wave5_vpu_enc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ struct v4l2_pix_format_mplane inst_format =
+ (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
+ unsigned int i;
+
+ dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
+ *num_buffers, *num_planes, q->type);
+
+ if (*num_planes) {
+ if (inst_format.num_planes != *num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < *num_planes; i++) {
+ if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
+ return -EINVAL;
+ }
+ } else {
+ *num_planes = inst_format.num_planes;
+ for (i = 0; i < *num_planes; i++) {
+ sizes[i] = inst_format.plane_fmt[i].sizeimage;
+ dev_dbg(inst->dev->dev, "%s: size[%u]: %u\n", __func__, i, sizes[i]);
+ }
+ }
+
+ dev_dbg(inst->dev->dev, "%s: size: %u\n", __func__, sizes[0]);
+
+ if (inst->state == VPU_INST_STATE_INIT_SEQ &&
+ q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (*num_buffers > inst->src_buf_count &&
+ *num_buffers < WAVE5_MAX_FBS)
+ inst->src_buf_count = *num_buffers;
+
+ *num_buffers = inst->src_buf_count;
+ dev_dbg(inst->dev->dev, "%s: src buf num: %u", __func__, *num_buffers);
+ }
+
+ return 0;
+}
+
+static void wave5_vpu_enc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+
+ dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n",
+ __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0),
+ vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2));
+
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ vbuf->sequence = inst->queued_src_buf_num++;
+ else
+ vbuf->sequence = inst->queued_dst_buf_num++;
+
+ v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+}
+
+static void wave5_set_enc_openparam(struct enc_open_param *open_param,
+ struct vpu_instance *inst)
+{
+ struct enc_wave_param input = inst->enc_param;
+ u32 num_ctu_row = ALIGN(inst->dst_fmt.height, 64) / 64;
+ u32 num_mb_row = ALIGN(inst->dst_fmt.height, 16) / 16;
+
+ open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE;
+ open_param->wave_param.hvs_qp_scale = 2;
+ open_param->wave_param.hvs_max_delta_qp = 10;
+ open_param->wave_param.skip_intra_trans = 1;
+ open_param->wave_param.intra_nx_n_enable = 1;
+ open_param->wave_param.nr_intra_weight_y = 7;
+ open_param->wave_param.nr_intra_weight_cb = 7;
+ open_param->wave_param.nr_intra_weight_cr = 7;
+ open_param->wave_param.nr_inter_weight_y = 4;
+ open_param->wave_param.nr_inter_weight_cb = 4;
+ open_param->wave_param.nr_inter_weight_cr = 4;
+ open_param->wave_param.rdo_skip = 1;
+ open_param->wave_param.lambda_scaling_enable = 1;
+
+ open_param->line_buf_int_en = true;
+ open_param->pic_width = inst->dst_fmt.width;
+ open_param->pic_height = inst->dst_fmt.height;
+ open_param->frame_rate_info = inst->frame_rate;
+ open_param->rc_enable = inst->rc_enable;
+ if (inst->rc_enable) {
+ open_param->wave_param.initial_rc_qp = -1;
+ open_param->wave_param.rc_weight_param = 16;
+ open_param->wave_param.rc_weight_buf = 128;
+ }
+ open_param->wave_param.mb_level_rc_enable = input.mb_level_rc_enable;
+ open_param->wave_param.cu_level_rc_enable = input.cu_level_rc_enable;
+ open_param->wave_param.hvs_qp_enable = input.hvs_qp_enable;
+ open_param->bit_rate = inst->bit_rate;
+ open_param->vbv_buffer_size = inst->vbv_buf_size;
+ if (inst->rc_mode == 0)
+ open_param->vbv_buffer_size = 3000;
+ open_param->wave_param.profile = input.profile;
+ open_param->wave_param.en_still_picture = input.en_still_picture;
+ open_param->wave_param.level = input.level;
+ open_param->wave_param.internal_bit_depth = inst->bit_depth;
+ open_param->wave_param.intra_qp = input.intra_qp;
+ open_param->wave_param.min_qp_i = input.min_qp_i;
+ open_param->wave_param.max_qp_i = input.max_qp_i;
+ open_param->wave_param.min_qp_p = input.min_qp_p;
+ open_param->wave_param.max_qp_p = input.max_qp_p;
+ open_param->wave_param.min_qp_b = input.min_qp_b;
+ open_param->wave_param.max_qp_b = input.max_qp_b;
+ open_param->wave_param.disable_deblk = input.disable_deblk;
+ open_param->wave_param.lf_cross_slice_boundary_enable =
+ input.lf_cross_slice_boundary_enable;
+ open_param->wave_param.tc_offset_div2 = input.tc_offset_div2;
+ open_param->wave_param.beta_offset_div2 = input.beta_offset_div2;
+ open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type;
+ open_param->wave_param.intra_period = input.intra_period;
+ if (inst->std == W_HEVC_ENC) {
+ if (input.intra_period == 0) {
+ open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR;
+ open_param->wave_param.intra_period = input.avc_idr_period;
+ }
+ } else {
+ open_param->wave_param.avc_idr_period = input.avc_idr_period;
+ }
+ open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode;
+ open_param->wave_param.lossless_enable = input.lossless_enable;
+ open_param->wave_param.const_intra_pred_flag = input.const_intra_pred_flag;
+ open_param->wave_param.wpp_enable = input.wpp_enable;
+ open_param->wave_param.strong_intra_smooth_enable = input.strong_intra_smooth_enable;
+ open_param->wave_param.max_num_merge = input.max_num_merge;
+ open_param->wave_param.tmvp_enable = input.tmvp_enable;
+ open_param->wave_param.transform8x8_enable = input.transform8x8_enable;
+ open_param->wave_param.chroma_cb_qp_offset = input.chroma_cb_qp_offset;
+ open_param->wave_param.chroma_cr_qp_offset = input.chroma_cr_qp_offset;
+ open_param->wave_param.independ_slice_mode = input.independ_slice_mode;
+ open_param->wave_param.independ_slice_mode_arg = input.independ_slice_mode_arg;
+ open_param->wave_param.avc_slice_mode = input.avc_slice_mode;
+ open_param->wave_param.avc_slice_arg = input.avc_slice_arg;
+ open_param->wave_param.intra_mb_refresh_mode = input.intra_mb_refresh_mode;
+ if (input.intra_mb_refresh_mode != REFRESH_MB_MODE_NONE) {
+ if (num_mb_row >= input.intra_mb_refresh_arg)
+ open_param->wave_param.intra_mb_refresh_arg =
+ num_mb_row / input.intra_mb_refresh_arg;
+ else
+ open_param->wave_param.intra_mb_refresh_arg = num_mb_row;
+ }
+ open_param->wave_param.intra_refresh_mode = input.intra_refresh_mode;
+ if (input.intra_refresh_mode != 0) {
+ if (num_ctu_row >= input.intra_refresh_arg)
+ open_param->wave_param.intra_refresh_arg =
+ num_ctu_row / input.intra_refresh_arg;
+ else
+ open_param->wave_param.intra_refresh_arg = num_ctu_row;
+ }
+}
+
+static int initialize_sequence(struct vpu_instance *inst)
+{
+ struct enc_initial_info initial_info;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ret = wave5_vpu_enc_issue_seq_init(inst);
+ if (ret) {
+ dev_err(inst->dev->dev, "%s: wave5_vpu_enc_issue_seq_init, fail: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0) {
+ dev_err(inst->dev->dev, "%s: wave5_vpu_wait_interrupt failed\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = wave5_vpu_enc_complete_seq_init(inst, &initial_info);
+ if (ret)
+ return ret;
+
+ dev_dbg(inst->dev->dev, "%s: min_frame_buffer: %u | min_source_buffer: %u\n",
+ __func__, initial_info.min_frame_buffer_count,
+ initial_info.min_src_frame_count);
+ inst->min_src_buf_count = initial_info.min_src_frame_count +
+ COMMAND_QUEUE_DEPTH;
+
+ ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT);
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, inst->min_src_buf_count);
+
+ inst->fbc_buf_count = initial_info.min_frame_buffer_count;
+ inst->src_buf_count = inst->min_src_buf_count;
+
+ return 0;
+}
+
+static int prepare_fb(struct vpu_instance *inst)
+{
+ u32 fb_stride = ALIGN(inst->dst_fmt.width, 32);
+ u32 fb_height = ALIGN(inst->dst_fmt.height, 32);
+ int i, ret = 0;
+
+ for (i = 0; i < inst->fbc_buf_count; i++) {
+ u32 luma_size = fb_stride * fb_height;
+ u32 chroma_size = ALIGN(fb_stride / 2, 16) * fb_height;
+
+ inst->frame_vbuf[i].size = luma_size + chroma_size;
+ ret = wave5_vdi_allocate_dma_memory(inst->dev, &inst->frame_vbuf[i]);
+ if (ret < 0) {
+ dev_err(inst->dev->dev, "%s: failed to allocate FBC buffer %zu\n",
+ __func__, inst->frame_vbuf[i].size);
+ goto free_buffers;
+ }
+
+ inst->frame_buf[i].buf_y = inst->frame_vbuf[i].daddr;
+ inst->frame_buf[i].buf_cb = (dma_addr_t)-1;
+ inst->frame_buf[i].buf_cr = (dma_addr_t)-1;
+ inst->frame_buf[i].update_fb_info = true;
+ inst->frame_buf[i].size = inst->frame_vbuf[i].size;
+ }
+
+ ret = wave5_vpu_enc_register_frame_buffer(inst, inst->fbc_buf_count, fb_stride,
+ fb_height, COMPRESSED_FRAME_MAP);
+ if (ret) {
+ dev_err(inst->dev->dev,
+ "%s: wave5_vpu_enc_register_frame_buffer, fail: %d\n",
+ __func__, ret);
+ goto free_buffers;
+ }
+
+ return 0;
+free_buffers:
+ for (i = 0; i < inst->fbc_buf_count; i++)
+ wave5_vpu_dec_reset_framebuffer(inst, i);
+ return ret;
+}
+
+static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ int ret;
+
+ v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
+
+ if (inst->state == VPU_INST_STATE_NONE && q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ struct enc_open_param open_param;
+
+ memset(&open_param, 0, sizeof(struct enc_open_param));
+
+ inst->std = wave5_to_vpu_std(inst->dst_fmt.pixelformat, inst->type);
+ if (inst->std == STD_UNKNOWN) {
+ dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
+ (char *)&inst->dst_fmt.pixelformat);
+ return -EINVAL;
+ }
+
+ wave5_set_enc_openparam(&open_param, inst);
+
+ ret = wave5_vpu_enc_open(inst, &open_param);
+ if (ret) {
+ dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_open, fail: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (inst->mirror_direction) {
+ wave5_vpu_enc_give_command(inst, ENABLE_MIRRORING, NULL);
+ wave5_vpu_enc_give_command(inst, SET_MIRROR_DIRECTION,
+ &inst->mirror_direction);
+ }
+ if (inst->rot_angle) {
+ wave5_vpu_enc_give_command(inst, ENABLE_ROTATION, NULL);
+ wave5_vpu_enc_give_command(inst, SET_ROTATION_ANGLE, &inst->rot_angle);
+ }
+
+ switch_state(inst, VPU_INST_STATE_OPEN);
+ }
+ if (inst->state == VPU_INST_STATE_OPEN && m2m_ctx->cap_q_ctx.q.streaming) {
+ ret = initialize_sequence(inst);
+ if (ret) {
+ dev_warn(inst->dev->dev, "Sequence not found: %d\n", ret);
+ return ret;
+ }
+ switch_state(inst, VPU_INST_STATE_INIT_SEQ);
+ /*
+ * The sequence must be analyzed first to calculate the proper
+ * size of the auxiliary buffers.
+ */
+ ret = prepare_fb(inst);
+ if (ret) {
+ dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret);
+ return ret;
+ }
+
+ switch_state(inst, VPU_INST_STATE_PIC_RUN);
+ }
+
+ return 0;
+}
+
+static void streamoff_output(struct vpu_instance *inst, struct vb2_queue *q)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_v4l2_buffer *buf;
+
+ while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
+ dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
+ __func__, buf->vb2_buf.type, buf->vb2_buf.index);
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static void streamoff_capture(struct vpu_instance *inst, struct vb2_queue *q)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct vb2_v4l2_buffer *buf;
+
+ while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) {
+ dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
+ __func__, buf->vb2_buf.type, buf->vb2_buf.index);
+ vb2_set_plane_payload(&buf->vb2_buf, 0, 0);
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
+ }
+
+ v4l2_m2m_clear_state(m2m_ctx);
+}
+
+static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q)
+{
+ struct vpu_instance *inst = vb2_get_drv_priv(q);
+ bool check_cmd = true;
+
+ /*
+ * Note that we don't need m2m_ctx->next_buf_last for this driver, so we
+ * don't call v4l2_m2m_update_stop_streaming_state().
+ */
+
+ dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
+
+ if (wave5_vpu_both_queues_are_streaming(inst))
+ switch_state(inst, VPU_INST_STATE_STOP);
+
+ while (check_cmd) {
+ struct queue_status_info q_status;
+ struct enc_output_info enc_output_info;
+
+ wave5_vpu_enc_give_command(inst, ENC_GET_QUEUE_STATUS, &q_status);
+
+ if (q_status.report_queue_count == 0)
+ break;
+
+ if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0)
+ break;
+
+ if (wave5_vpu_enc_get_output_info(inst, &enc_output_info))
+ dev_dbg(inst->dev->dev, "Getting encoding results from fw, fail\n");
+ }
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ streamoff_output(inst, q);
+ else
+ streamoff_capture(inst, q);
+}
+
+static const struct vb2_ops wave5_vpu_enc_vb2_ops = {
+ .queue_setup = wave5_vpu_enc_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_queue = wave5_vpu_enc_buf_queue,
+ .start_streaming = wave5_vpu_enc_start_streaming,
+ .stop_streaming = wave5_vpu_enc_stop_streaming,
+};
+
+static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
+ struct v4l2_pix_format_mplane *dst_fmt)
+{
+ unsigned int src_pix_fmt = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
+ const struct v4l2_format_info *src_fmt_info = v4l2_format_info(src_pix_fmt);
+
+ src_fmt->pixelformat = src_pix_fmt;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->flags = 0;
+ src_fmt->num_planes = src_fmt_info->mem_planes;
+ wave5_update_pix_fmt(src_fmt, 416, 240);
+
+ dst_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
+ dst_fmt->field = V4L2_FIELD_NONE;
+ dst_fmt->flags = 0;
+ dst_fmt->num_planes = 1;
+ wave5_update_pix_fmt(dst_fmt, 416, 240);
+}
+
+static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_enc_vb2_ops);
+}
+
+static const struct vpu_instance_ops wave5_vpu_enc_inst_ops = {
+ .finish_process = wave5_vpu_enc_finish_encode,
+};
+
+static void wave5_vpu_enc_device_run(void *priv)
+{
+ struct vpu_instance *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ u32 fail_res = 0;
+ int ret = 0;
+
+ switch (inst->state) {
+ case VPU_INST_STATE_PIC_RUN:
+ ret = start_encode(inst, &fail_res);
+ if (ret) {
+ if (ret == -EINVAL)
+ dev_err(inst->dev->dev,
+ "Frame encoding on m2m context (%p), fail: %d (res: %d)\n",
+ m2m_ctx, ret, fail_res);
+ else if (ret == -EAGAIN)
+ dev_dbg(inst->dev->dev, "Missing buffers for encode, try again\n");
+ break;
+ }
+ dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
+ return;
+ default:
+ WARN(1, "Execution of a job in state %s is invalid.\n",
+ state_to_str(inst->state));
+ break;
+ }
+ dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
+ v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
+}
+
+static int wave5_vpu_enc_job_ready(void *priv)
+{
+ struct vpu_instance *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+
+ switch (inst->state) {
+ case VPU_INST_STATE_NONE:
+ dev_dbg(inst->dev->dev, "Encoder must be open to start queueing M2M jobs!\n");
+ return false;
+ case VPU_INST_STATE_PIC_RUN:
+ if (m2m_ctx->is_draining || v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
+ dev_dbg(inst->dev->dev, "Encoder ready for a job, state: %s\n",
+ state_to_str(inst->state));
+ return true;
+ }
+ fallthrough;
+ default:
+ dev_dbg(inst->dev->dev,
+ "Encoder not ready for a job, state: %s, %s draining, %d src bufs ready\n",
+ state_to_str(inst->state), m2m_ctx->is_draining ? "is" : "is not",
+ v4l2_m2m_num_src_bufs_ready(m2m_ctx));
+ break;
+ }
+ return false;
+}
+
+static const struct v4l2_m2m_ops wave5_vpu_enc_m2m_ops = {
+ .device_run = wave5_vpu_enc_device_run,
+ .job_ready = wave5_vpu_enc_job_ready,
+};
+
+static int wave5_vpu_open_enc(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct vpu_device *dev = video_drvdata(filp);
+ struct vpu_instance *inst = NULL;
+ struct v4l2_ctrl_handler *v4l2_ctrl_hdl;
+ int ret = 0;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+ v4l2_ctrl_hdl = &inst->v4l2_ctrl_hdl;
+
+ inst->dev = dev;
+ inst->type = VPU_INST_TYPE_ENC;
+ inst->ops = &wave5_vpu_enc_inst_ops;
+
+ inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
+ if (!inst->codec_info)
+ return -ENOMEM;
+
+ v4l2_fh_init(&inst->v4l2_fh, vdev);
+ filp->private_data = &inst->v4l2_fh;
+ v4l2_fh_add(&inst->v4l2_fh);
+
+ INIT_LIST_HEAD(&inst->list);
+ list_add_tail(&inst->list, &dev->instances);
+
+ inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_enc_dev;
+ inst->v4l2_fh.m2m_ctx =
+ v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_enc_queue_init);
+ if (IS_ERR(inst->v4l2_fh.m2m_ctx)) {
+ ret = PTR_ERR(inst->v4l2_fh.m2m_ctx);
+ goto cleanup_inst;
+ }
+ v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, true);
+
+ v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, 0,
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_1);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
+ 0, 63, 1, 8);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
+ 0, 63, 1, 51);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
+ 0, 63, 1, 30);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE,
+ V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0,
+ V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2,
+ -6, 6, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2,
+ -6, 6, 1, 0);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE,
+ V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, 0,
+ V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD,
+ 0, 2047, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING,
+ 0, 1, 1, 1);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1,
+ 1, 2, 1, 2);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION,
+ 0, 1, 1, 1);
+
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE, 0,
+ V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 0,
+ V4L2_MPEG_VIDEO_H264_LEVEL_1_0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+ 0, 63, 1, 8);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+ 0, 63, 1, 51);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+ 0, 63, 1, 30);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0,
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+ -6, 6, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+ -6, 6, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+ 0, 1, 1, 1);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET,
+ -12, 12, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+ 0, 2047, 1, 0);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, 0,
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_AU_DELIMITER,
+ 0, 1, 1, 1);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_HFLIP,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_VFLIP,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_ROTATE,
+ 0, 270, 90, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VBV_SIZE,
+ 10, 3000, 1, 1000);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0, 700000000, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ 0, 2047, 1, 0);
+ v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB, 0,
+ V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+ 0, 0xFFFF, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1);
+
+ if (v4l2_ctrl_hdl->error) {
+ ret = -ENODEV;
+ goto cleanup_inst;
+ }
+
+ inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl;
+ v4l2_ctrl_handler_setup(v4l2_ctrl_hdl);
+
+ wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
+ inst->colorspace = V4L2_COLORSPACE_REC709;
+ inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ inst->hsv_enc = 0;
+ inst->quantization = V4L2_QUANTIZATION_DEFAULT;
+ inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ inst->frame_rate = 30;
+
+ init_completion(&inst->irq_done);
+ ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
+ if (ret) {
+ dev_err(inst->dev->dev, "Allocating fifo, fail: %d\n", ret);
+ goto cleanup_inst;
+ }
+
+ inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
+ if (inst->id < 0) {
+ dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id);
+ ret = inst->id;
+ goto cleanup_inst;
+ }
+
+ wave5_vdi_allocate_sram(inst->dev);
+
+ return 0;
+
+cleanup_inst:
+ wave5_cleanup_instance(inst);
+ return ret;
+}
+
+static int wave5_vpu_enc_release(struct file *filp)
+{
+ return wave5_vpu_release_device(filp, wave5_vpu_enc_close, "encoder");
+}
+
+static const struct v4l2_file_operations wave5_vpu_enc_fops = {
+ .owner = THIS_MODULE,
+ .open = wave5_vpu_open_enc,
+ .release = wave5_vpu_enc_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+int wave5_vpu_enc_register_device(struct vpu_device *dev)
+{
+ struct video_device *vdev_enc;
+ int ret;
+
+ vdev_enc = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_enc), GFP_KERNEL);
+ if (!vdev_enc)
+ return -ENOMEM;
+
+ dev->v4l2_m2m_enc_dev = v4l2_m2m_init(&wave5_vpu_enc_m2m_ops);
+ if (IS_ERR(dev->v4l2_m2m_enc_dev)) {
+ ret = PTR_ERR(dev->v4l2_m2m_enc_dev);
+ dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret);
+ return -EINVAL;
+ }
+
+ dev->video_dev_enc = vdev_enc;
+
+ strscpy(vdev_enc->name, VPU_ENC_DEV_NAME, sizeof(vdev_enc->name));
+ vdev_enc->fops = &wave5_vpu_enc_fops;
+ vdev_enc->ioctl_ops = &wave5_vpu_enc_ioctl_ops;
+ vdev_enc->release = video_device_release_empty;
+ vdev_enc->v4l2_dev = &dev->v4l2_dev;
+ vdev_enc->vfl_dir = VFL_DIR_M2M;
+ vdev_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ vdev_enc->lock = &dev->dev_lock;
+
+ ret = video_register_device(vdev_enc, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ return ret;
+
+ video_set_drvdata(vdev_enc, dev);
+
+ return 0;
+}
+
+void wave5_vpu_enc_unregister_device(struct vpu_device *dev)
+{
+ video_unregister_device(dev->video_dev_enc);
+ if (dev->v4l2_m2m_enc_dev)
+ v4l2_m2m_release(dev->v4l2_m2m_enc_dev);
+}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
new file mode 100644
index 000000000000..a13d968f5d04
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * Wave5 series multi-standard codec IP - platform driver
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include "wave5-vpu.h"
+#include "wave5-regdefine.h"
+#include "wave5-vpuconfig.h"
+#include "wave5.h"
+
+#define VPU_PLATFORM_DEVICE_NAME "vdec"
+#define VPU_CLK_NAME "vcodec"
+
+#define WAVE5_IS_ENC BIT(0)
+#define WAVE5_IS_DEC BIT(1)
+
+struct wave5_match_data {
+ int flags;
+ const char *fw_name;
+};
+
+int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
+{
+ int ret;
+
+ ret = wait_for_completion_timeout(&inst->irq_done,
+ msecs_to_jiffies(timeout));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ reinit_completion(&inst->irq_done);
+
+ return 0;
+}
+
+static void wave5_vpu_get_interrupt_for_inst(struct vpu_instance *inst, u32 status)
+{
+ struct vpu_device *dev = inst->dev;
+ u32 seq_done;
+ u32 cmd_done;
+ int val;
+
+ seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
+ cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
+
+ if (status & BIT(INT_WAVE5_INIT_SEQ)) {
+ if (seq_done & BIT(inst->id)) {
+ seq_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
+ val = BIT(INT_WAVE5_INIT_SEQ);
+ kfifo_in(&inst->irq_status, &val, sizeof(int));
+ }
+ }
+ if (status & BIT(INT_WAVE5_ENC_SET_PARAM)) {
+ if (seq_done & BIT(inst->id)) {
+ seq_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
+ val = BIT(INT_WAVE5_ENC_SET_PARAM);
+ kfifo_in(&inst->irq_status, &val, sizeof(int));
+ }
+ }
+ if (status & BIT(INT_WAVE5_DEC_PIC) ||
+ status & BIT(INT_WAVE5_ENC_PIC)) {
+ if (cmd_done & BIT(inst->id)) {
+ cmd_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, cmd_done);
+ val = BIT(INT_WAVE5_DEC_PIC);
+ kfifo_in(&inst->irq_status, &val, sizeof(int));
+ }
+ }
+}
+
+static irqreturn_t wave5_vpu_irq(int irq, void *dev_id)
+{
+ struct vpu_device *dev = dev_id;
+
+ if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
+ struct vpu_instance *inst;
+ u32 irq_status = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
+
+ list_for_each_entry(inst, &dev->instances, list) {
+ wave5_vpu_get_interrupt_for_inst(inst, irq_status);
+ }
+
+ wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_status);
+ wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
+
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
+{
+ struct vpu_device *dev = dev_id;
+ struct vpu_instance *inst;
+ int irq_status, ret;
+
+ list_for_each_entry(inst, &dev->instances, list) {
+ while (kfifo_len(&inst->irq_status)) {
+ ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int));
+ if (!ret)
+ break;
+
+ if (irq_status == BIT(INT_WAVE5_INIT_SEQ) ||
+ irq_status == BIT(INT_WAVE5_ENC_SET_PARAM))
+ complete(&inst->irq_done);
+ else /* DEC/ENC_PIC */
+ inst->ops->finish_process(inst);
+
+ wave5_vpu_clear_interrupt(inst, irq_status);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
+ u32 *revision)
+{
+ const struct firmware *fw;
+ int ret;
+ unsigned int product_id;
+
+ ret = request_firmware(&fw, fw_name, dev);
+ if (ret) {
+ dev_err(dev, "request_firmware, fail: %d\n", ret);
+ return ret;
+ }
+
+ ret = wave5_vpu_init_with_bitcode(dev, (u8 *)fw->data, fw->size);
+ if (ret) {
+ dev_err(dev, "vpu_init_with_bitcode, fail: %d\n", ret);
+ goto release_fw;
+ }
+ release_firmware(fw);
+
+ ret = wave5_vpu_get_version_info(dev, revision, &product_id);
+ if (ret) {
+ dev_err(dev, "vpu_get_version_info fail: %d\n", ret);
+ goto err_without_release;
+ }
+
+ dev_dbg(dev, "%s: enum product_id: %08x, fw revision: %u\n",
+ __func__, product_id, *revision);
+
+ return 0;
+
+release_fw:
+ release_firmware(fw);
+err_without_release:
+ return ret;
+}
+
+static int wave5_vpu_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct vpu_device *dev;
+ const struct wave5_match_data *match_data;
+ u32 fw_revision;
+
+ match_data = device_get_match_data(&pdev->dev);
+ if (!match_data) {
+ dev_err(&pdev->dev, "missing device match data\n");
+ return -EINVAL;
+ }
+
+ /* physical addresses limited to 32 bits */
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->vdb_register = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dev->vdb_register))
+ return PTR_ERR(dev->vdb_register);
+ ida_init(&dev->inst_ida);
+
+ mutex_init(&dev->dev_lock);
+ mutex_init(&dev->hw_lock);
+ dev_set_drvdata(&pdev->dev, dev);
+ dev->dev = &pdev->dev;
+
+ ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks);
+
+ /* continue without clock, assume externally managed */
+ if (ret < 0) {
+ dev_warn(&pdev->dev, "Getting clocks, fail: %d\n", ret);
+ ret = 0;
+ }
+ dev->num_clks = ret;
+
+ ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks);
+ if (ret) {
+ dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret);
+ return ret;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "sram-size",
+ &dev->sram_size);
+ if (ret) {
+ dev_warn(&pdev->dev, "sram-size not found\n");
+ dev->sram_size = 0;
+ }
+
+ dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ if (!dev->sram_pool)
+ dev_warn(&pdev->dev, "sram node not found\n");
+
+ dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER);
+ ret = wave5_vdi_init(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "wave5_vdi_init, fail: %d\n", ret);
+ goto err_clk_dis;
+ }
+ dev->product = wave5_vpu_get_product_id(dev);
+
+ INIT_LIST_HEAD(&dev->instances);
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret);
+ goto err_vdi_release;
+ }
+
+ if (match_data->flags & WAVE5_IS_DEC) {
+ ret = wave5_vpu_dec_register_device(dev);
+ if (ret) {
+ dev_err(&pdev->dev, "wave5_vpu_dec_register_device, fail: %d\n", ret);
+ goto err_v4l2_unregister;
+ }
+ }
+ if (match_data->flags & WAVE5_IS_ENC) {
+ ret = wave5_vpu_enc_register_device(dev);
+ if (ret) {
+ dev_err(&pdev->dev, "wave5_vpu_enc_register_device, fail: %d\n", ret);
+ goto err_dec_unreg;
+ }
+ }
+
+ dev->irq = platform_get_irq(pdev, 0);
+ if (dev->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ ret = -ENXIO;
+ goto err_enc_unreg;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, dev->irq, wave5_vpu_irq,
+ wave5_vpu_irq_thread, 0, "vpu_irq", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
+ goto err_enc_unreg;
+ }
+
+ ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision);
+ if (ret) {
+ dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret);
+ goto err_enc_unreg;
+ }
+
+ dev_info(&pdev->dev, "Added wave5 driver with caps: %s %s\n",
+ (match_data->flags & WAVE5_IS_ENC) ? "'ENCODE'" : "",
+ (match_data->flags & WAVE5_IS_DEC) ? "'DECODE'" : "");
+ dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code);
+ dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision);
+ return 0;
+
+err_enc_unreg:
+ if (match_data->flags & WAVE5_IS_ENC)
+ wave5_vpu_enc_unregister_device(dev);
+err_dec_unreg:
+ if (match_data->flags & WAVE5_IS_DEC)
+ wave5_vpu_dec_unregister_device(dev);
+err_v4l2_unregister:
+ v4l2_device_unregister(&dev->v4l2_dev);
+err_vdi_release:
+ wave5_vdi_release(&pdev->dev);
+err_clk_dis:
+ clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
+
+ return ret;
+}
+
+static int wave5_vpu_remove(struct platform_device *pdev)
+{
+ struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&dev->dev_lock);
+ mutex_destroy(&dev->hw_lock);
+ clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
+ wave5_vpu_enc_unregister_device(dev);
+ wave5_vpu_dec_unregister_device(dev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ wave5_vdi_release(&pdev->dev);
+ ida_destroy(&dev->inst_ida);
+
+ return 0;
+}
+
+static const struct wave5_match_data wave521c_data = {
+ .flags = WAVE5_IS_ENC | WAVE5_IS_DEC,
+ .fw_name = "cnm/wave521c_k3_codec_fw.bin",
+};
+
+static const struct of_device_id wave5_dt_ids[] = {
+ { .compatible = "cnm,cm521c-vpu", .data = &wave521c_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, wave5_dt_ids);
+
+static struct platform_driver wave5_vpu_driver = {
+ .driver = {
+ .name = VPU_PLATFORM_DEVICE_NAME,
+ .of_match_table = of_match_ptr(wave5_dt_ids),
+ },
+ .probe = wave5_vpu_probe,
+ .remove = wave5_vpu_remove,
+};
+
+module_platform_driver(wave5_vpu_driver);
+MODULE_DESCRIPTION("chips&media VPU V4L2 driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
new file mode 100644
index 000000000000..f4117c079027
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Wave5 series multi-standard codec IP - basic types
+ *
+ * Copyright (C) 2021 CHIPS&MEDIA INC
+ */
+#ifndef __VPU_DRV_H__
+#define __VPU_DRV_H__
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include "wave5-vpuconfig.h"
+#include "wave5-vpuapi.h"
+
+#define VPU_BUF_SYNC_TO_DEVICE 0
+#define VPU_BUF_SYNC_FROM_DEVICE 1
+
+struct vpu_src_buffer {
+ struct v4l2_m2m_buffer v4l2_m2m_buf;
+ struct list_head list;
+ bool consumed;
+};
+
+struct vpu_dst_buffer {
+ struct v4l2_m2m_buffer v4l2_m2m_buf;
+ bool display;
+};
+
+enum vpu_fmt_type {
+ VPU_FMT_TYPE_CODEC = 0,
+ VPU_FMT_TYPE_RAW = 1
+};
+
+struct vpu_format {
+ unsigned int v4l2_pix_fmt;
+ unsigned int max_width;
+ unsigned int min_width;
+ unsigned int max_height;
+ unsigned int min_height;
+};
+
+static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh)
+{
+ return container_of(vfh, struct vpu_instance, v4l2_fh);
+}
+
+static inline struct vpu_instance *wave5_ctrl_to_vpu_inst(struct v4l2_ctrl *vctrl)
+{
+ return container_of(vctrl->handler, struct vpu_instance, v4l2_ctrl_hdl);
+}
+
+static inline struct vpu_src_buffer *wave5_to_vpu_src_buf(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct vpu_src_buffer, v4l2_m2m_buf.vb);
+}
+
+static inline struct vpu_dst_buffer *wave5_to_vpu_dst_buf(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct vpu_dst_buffer, v4l2_m2m_buf.vb);
+}
+
+int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout);
+
+int wave5_vpu_dec_register_device(struct vpu_device *dev);
+void wave5_vpu_dec_unregister_device(struct vpu_device *dev);
+int wave5_vpu_enc_register_device(struct vpu_device *dev);
+void wave5_vpu_enc_unregister_device(struct vpu_device *dev);
+static inline bool wave5_vpu_both_queues_are_streaming(struct vpu_instance *inst)
+{
+ struct vb2_queue *vq_cap =
+ v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ struct vb2_queue *vq_out =
+ v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out);
+}
+
+#endif

--
2.25.1

2023-09-16 07:57:22

by Sebastian Fricke

[permalink] [raw]
Subject: [PATCH v12 2/7] media: v4l2: Allow M2M job queuing w/o streaming CAP queue

Allow decoder drivers to enable set the ignore_streaming flag on their
CAPTURE queue, to allow queuing jobs to the M2M ready queue and perform
firmware sequence analysis with just a streaming OUTPUT queue and
available bitstream data.

Signed-off-by: Sebastian Fricke <[email protected]>
Signed-off-by: Nicolas Dufresne <[email protected]>
---
drivers/media/v4l2-core/v4l2-mem2mem.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 0cc30397fbad..2dbbe93be257 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -301,9 +301,12 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,

dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);

- if (!m2m_ctx->out_q_ctx.q.streaming
- || !m2m_ctx->cap_q_ctx.q.streaming) {
- dprintk("Streaming needs to be on for both queues\n");
+ if (!m2m_ctx->out_q_ctx.q.streaming ||
+ (!m2m_ctx->cap_q_ctx.q.streaming && !m2m_ctx->cap_q_ctx.ignore_streaming)) {
+ if (!m2m_ctx->cap_q_ctx.ignore_streaming)
+ dprintk("Streaming needs to be on for both queues\n");
+ else
+ dprintk("Streaming needs to be on for the OUTPUT queue\n");
return;
}


--
2.25.1

2023-09-16 09:12:30

by Sebastian Fricke

[permalink] [raw]
Subject: [PATCH v12 6/7] dt-bindings: media: wave5: add yaml devicetree bindings

From: Robert Beckett <[email protected]>

Add bindings for the wave5 chips&media codec driver

Signed-off-by: Robert Beckett <[email protected]>
Signed-off-by: Dafna Hirschfeld <[email protected]>
Signed-off-by: Sebastian Fricke <[email protected]>
---
.../devicetree/bindings/media/cnm,wave5.yaml | 66 ++++++++++++++++++++++
1 file changed, 66 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/cnm,wave5.yaml b/Documentation/devicetree/bindings/media/cnm,wave5.yaml
new file mode 100644
index 000000000000..b8f383621805
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/cnm,wave5.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/cnm,wave5.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Chips&Media Wave 5 Series multi-standard codec IP
+
+maintainers:
+ - Nas Chung <[email protected]>
+ - Jackson Lee <[email protected]>
+
+description: |-
+ The Chips&Media WAVE codec IP is a multi format video encoder/decoder
+
+properties:
+ compatible:
+ enum:
+ - cnm,cm521c-vpu
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: VCODEC clock
+
+ clock-names:
+ items:
+ - const: vcodec
+
+ interrupts:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ sram:
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+ description:
+ The VPU uses the SRAM to store some of the reference data instead of
+ storing it on DMA memory. It is mainly used for the purpose of reducing
+ bandwidth.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ vpu: video-codec@12345678 {
+ compatible = "cnm,cm521c-vpu";
+ reg = <0x12345678 0x1000>;
+ interrupts = <42>;
+ clocks = <&clks 42>;
+ clock-names = "vcodec";
+ sram = <&sram>;
+ };

--
2.25.1

2023-09-16 09:39:06

by Sebastian Fricke

[permalink] [raw]
Subject: [PATCH v12 3/7] media: platform: chips-media: Move Coda to separate folder

Prepare the folder structure for a second Chips&Media driver.
Move the Coda driver to a sub-directory.

Signed-off-by: Sebastian Fricke <[email protected]>
---
MAINTAINERS | 2 +-
drivers/media/platform/chips-media/Kconfig | 17 +----------------
drivers/media/platform/chips-media/Makefile | 5 +----
drivers/media/platform/chips-media/coda/Kconfig | 18 ++++++++++++++++++
drivers/media/platform/chips-media/coda/Makefile | 6 ++++++
.../media/platform/chips-media/{ => coda}/coda-bit.c | 0
.../platform/chips-media/{ => coda}/coda-common.c | 0
.../media/platform/chips-media/{ => coda}/coda-gdi.c | 0
.../media/platform/chips-media/{ => coda}/coda-h264.c | 0
.../media/platform/chips-media/{ => coda}/coda-jpeg.c | 0
.../media/platform/chips-media/{ => coda}/coda-mpeg2.c | 0
.../media/platform/chips-media/{ => coda}/coda-mpeg4.c | 0
drivers/media/platform/chips-media/{ => coda}/coda.h | 0
.../media/platform/chips-media/{ => coda}/coda_regs.h | 0
.../media/platform/chips-media/{ => coda}/imx-vdoa.c | 0
.../media/platform/chips-media/{ => coda}/imx-vdoa.h | 0
drivers/media/platform/chips-media/{ => coda}/trace.h | 2 +-
17 files changed, 28 insertions(+), 22 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 90f13281d297..063a11791bbf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5093,7 +5093,7 @@ M: Philipp Zabel <[email protected]>
L: [email protected]
S: Maintained
F: Documentation/devicetree/bindings/media/coda.yaml
-F: drivers/media/platform/chips-media/
+F: drivers/media/platform/chips-media/coda

CODE OF CONDUCT
M: Greg Kroah-Hartman <[email protected]>
diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig
index 57f8f8a22df8..f87a0d693df7 100644
--- a/drivers/media/platform/chips-media/Kconfig
+++ b/drivers/media/platform/chips-media/Kconfig
@@ -2,19 +2,4 @@

comment "Chips&Media media platform drivers"

-config VIDEO_CODA
- tristate "Chips&Media Coda multi-standard codec IP"
- depends on V4L_MEM2MEM_DRIVERS
- depends on VIDEO_DEV && OF && (ARCH_MXC || COMPILE_TEST)
- select SRAM
- select VIDEOBUF2_DMA_CONTIG
- select VIDEOBUF2_VMALLOC
- select V4L2_JPEG_HELPER
- select V4L2_MEM2MEM_DEV
- select GENERIC_ALLOCATOR
- help
- Coda is a range of video codec IPs that supports
- H.264, MPEG-4, and other video formats.
-
-config VIDEO_IMX_VDOA
- def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST
+source "drivers/media/platform/chips-media/coda/Kconfig"
diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile
index bbb16425a875..5ee693f651c1 100644
--- a/drivers/media/platform/chips-media/Makefile
+++ b/drivers/media/platform/chips-media/Makefile
@@ -1,6 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only

-coda-vpu-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o
-
-obj-$(CONFIG_VIDEO_CODA) += coda-vpu.o
-obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o
+obj-y += coda/
diff --git a/drivers/media/platform/chips-media/coda/Kconfig b/drivers/media/platform/chips-media/coda/Kconfig
new file mode 100644
index 000000000000..cb7b66c71380
--- /dev/null
+++ b/drivers/media/platform/chips-media/coda/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_CODA
+ tristate "Chips&Media Coda multi-standard codec IP"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV && OF && (ARCH_MXC || COMPILE_TEST)
+ select SRAM
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_JPEG_HELPER
+ select V4L2_MEM2MEM_DEV
+ select GENERIC_ALLOCATOR
+ help
+ Coda is a range of video codec IPs that supports
+ H.264, MPEG-4, and other video formats.
+
+config VIDEO_IMX_VDOA
+ def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST
diff --git a/drivers/media/platform/chips-media/coda/Makefile b/drivers/media/platform/chips-media/coda/Makefile
new file mode 100644
index 000000000000..bbb16425a875
--- /dev/null
+++ b/drivers/media/platform/chips-media/coda/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+coda-vpu-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o
+
+obj-$(CONFIG_VIDEO_CODA) += coda-vpu.o
+obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o
diff --git a/drivers/media/platform/chips-media/coda-bit.c b/drivers/media/platform/chips-media/coda/coda-bit.c
similarity index 100%
rename from drivers/media/platform/chips-media/coda-bit.c
rename to drivers/media/platform/chips-media/coda/coda-bit.c
diff --git a/drivers/media/platform/chips-media/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c
similarity index 100%
rename from drivers/media/platform/chips-media/coda-common.c
rename to drivers/media/platform/chips-media/coda/coda-common.c
diff --git a/drivers/media/platform/chips-media/coda-gdi.c b/drivers/media/platform/chips-media/coda/coda-gdi.c
similarity index 100%
rename from drivers/media/platform/chips-media/coda-gdi.c
rename to drivers/media/platform/chips-media/coda/coda-gdi.c
diff --git a/drivers/media/platform/chips-media/coda-h264.c b/drivers/media/platform/chips-media/coda/coda-h264.c
similarity index 100%
rename from drivers/media/platform/chips-media/coda-h264.c
rename to drivers/media/platform/chips-media/coda/coda-h264.c
diff --git a/drivers/media/platform/chips-media/coda-jpeg.c b/drivers/media/platform/chips-media/coda/coda-jpeg.c
similarity index 100%
rename from drivers/media/platform/chips-media/coda-jpeg.c
rename to drivers/media/platform/chips-media/coda/coda-jpeg.c
diff --git a/drivers/media/platform/chips-media/coda-mpeg2.c b/drivers/media/platform/chips-media/coda/coda-mpeg2.c
similarity index 100%
rename from drivers/media/platform/chips-media/coda-mpeg2.c
rename to drivers/media/platform/chips-media/coda/coda-mpeg2.c
diff --git a/drivers/media/platform/chips-media/coda-mpeg4.c b/drivers/media/platform/chips-media/coda/coda-mpeg4.c
similarity index 100%
rename from drivers/media/platform/chips-media/coda-mpeg4.c
rename to drivers/media/platform/chips-media/coda/coda-mpeg4.c
diff --git a/drivers/media/platform/chips-media/coda.h b/drivers/media/platform/chips-media/coda/coda.h
similarity index 100%
rename from drivers/media/platform/chips-media/coda.h
rename to drivers/media/platform/chips-media/coda/coda.h
diff --git a/drivers/media/platform/chips-media/coda_regs.h b/drivers/media/platform/chips-media/coda/coda_regs.h
similarity index 100%
rename from drivers/media/platform/chips-media/coda_regs.h
rename to drivers/media/platform/chips-media/coda/coda_regs.h
diff --git a/drivers/media/platform/chips-media/imx-vdoa.c b/drivers/media/platform/chips-media/coda/imx-vdoa.c
similarity index 100%
rename from drivers/media/platform/chips-media/imx-vdoa.c
rename to drivers/media/platform/chips-media/coda/imx-vdoa.c
diff --git a/drivers/media/platform/chips-media/imx-vdoa.h b/drivers/media/platform/chips-media/coda/imx-vdoa.h
similarity index 100%
rename from drivers/media/platform/chips-media/imx-vdoa.h
rename to drivers/media/platform/chips-media/coda/imx-vdoa.h
diff --git a/drivers/media/platform/chips-media/trace.h b/drivers/media/platform/chips-media/coda/trace.h
similarity index 99%
rename from drivers/media/platform/chips-media/trace.h
rename to drivers/media/platform/chips-media/coda/trace.h
index 19f98e6dafb9..abc6a01a74e9 100644
--- a/drivers/media/platform/chips-media/trace.h
+++ b/drivers/media/platform/chips-media/coda/trace.h
@@ -167,7 +167,7 @@ DEFINE_EVENT(coda_buf_class, coda_jpeg_done,
#endif /* __CODA_TRACE_H__ */

#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media
+#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media/coda
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace


--
2.25.1

2023-09-16 22:56:43

by Ivan Bornyakov

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

Hi, Sebastian,

On Fri, Sep 15, 2023 at 23:11:34 +0200, Sebastian Fricke wrote:
> From: Nas Chung <[email protected]>
>
> Add the decoder and encoder implementing the v4l2
> API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
>
> Signed-off-by: Sebastian Fricke <[email protected]>
> Signed-off-by: Nicolas Dufresne <[email protected]>
> Signed-off-by: Robert Beckett <[email protected]>
> Signed-off-by: Dafna Hirschfeld <[email protected]>
> Signed-off-by: Nas Chung <[email protected]>

[...]

> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> new file mode 100644
> index 000000000000..a13d968f5d04
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c

[...]

> +static void wave5_vpu_get_interrupt_for_inst(struct vpu_instance *inst, u32 status)
> +{
> + struct vpu_device *dev = inst->dev;
> + u32 seq_done;
> + u32 cmd_done;
> + int val;
> +
> + seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
> + cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
> +
> + if (status & BIT(INT_WAVE5_INIT_SEQ)) {
> + if (seq_done & BIT(inst->id)) {
> + seq_done &= ~BIT(inst->id);
> + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
> + val = BIT(INT_WAVE5_INIT_SEQ);
> + kfifo_in(&inst->irq_status, &val, sizeof(int));
> + }
> + }
> + if (status & BIT(INT_WAVE5_ENC_SET_PARAM)) {
> + if (seq_done & BIT(inst->id)) {
> + seq_done &= ~BIT(inst->id);
> + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
> + val = BIT(INT_WAVE5_ENC_SET_PARAM);
> + kfifo_in(&inst->irq_status, &val, sizeof(int));
> + }
> + }
> + if (status & BIT(INT_WAVE5_DEC_PIC) ||
> + status & BIT(INT_WAVE5_ENC_PIC)) {
> + if (cmd_done & BIT(inst->id)) {
> + cmd_done &= ~BIT(inst->id);
> + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, cmd_done);
> + val = BIT(INT_WAVE5_DEC_PIC);
> + kfifo_in(&inst->irq_status, &val, sizeof(int));
> + }
> + }
> +}
> +
> +static irqreturn_t wave5_vpu_irq(int irq, void *dev_id)
> +{
> + struct vpu_device *dev = dev_id;
> +
> + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
> + struct vpu_instance *inst;
> + u32 irq_status = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
> +
> + list_for_each_entry(inst, &dev->instances, list) {
> + wave5_vpu_get_interrupt_for_inst(inst, irq_status);
> + }
> +
> + wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_status);
> + wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
> +
> + return IRQ_WAKE_THREAD;
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
> +{
> + struct vpu_device *dev = dev_id;
> + struct vpu_instance *inst;
> + int irq_status, ret;
> +
> + list_for_each_entry(inst, &dev->instances, list) {
> + while (kfifo_len(&inst->irq_status)) {
> + ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int));
> + if (!ret)
> + break;
> +
> + if (irq_status == BIT(INT_WAVE5_INIT_SEQ) ||
> + irq_status == BIT(INT_WAVE5_ENC_SET_PARAM))
> + complete(&inst->irq_done);
> + else /* DEC/ENC_PIC */
> + inst->ops->finish_process(inst);
> +
> + wave5_vpu_clear_interrupt(inst, irq_status);
> + }
> + }
> +
> + return IRQ_HANDLED;
> +}

I believe, instead of
wave5_vpu_irq() + wave5_vpu_get_interrupt_for_inst() + wave5_vpu_irq_thread()
you can reduce interrupt handling to only threaded part with something like this:

static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
{
u32 irq_status, seq_done, cmd_done;
struct vpu_device *dev = dev_id;
struct vpu_instance *inst;

while (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
irq_status = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);

list_for_each_entry(inst, &dev->instances, list) {
if (irq_status & BIT(INT_WAVE5_INIT_SEQ) ||
irq_status & BIT(INT_WAVE5_ENC_SET_PARAM)) {
if (seq_done & BIT(inst->id)) {
seq_done &= ~BIT(inst->id);
wave5_vdi_write_register(dev,
W5_RET_SEQ_DONE_INSTANCE_INFO,
seq_done);
complete(&inst->irq_done);
}
}

if (status & BIT(INT_WAVE5_DEC_PIC) ||
status & BIT(INT_WAVE5_ENC_PIC)) {
if (cmd_done & BIT(inst->id)) {
cmd_done &= ~BIT(inst->id);
wave5_vdi_write_register(dev,
W5_RET_QUEUE_CMD_DONE_INST,
cmd_done);
inst->ops->finish_process(inst);
}
}

wave5_vpu_clear_interrupt(inst, irq_status);
}

wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_status);
wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
}

return IRQ_HANDLED;
}

Is it better?

[...]

> +static int wave5_vpu_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct vpu_device *dev;
> + const struct wave5_match_data *match_data;
> + u32 fw_revision;
> +
> + match_data = device_get_match_data(&pdev->dev);
> + if (!match_data) {
> + dev_err(&pdev->dev, "missing device match data\n");
> + return -EINVAL;
> + }
> +
> + /* physical addresses limited to 32 bits */
> + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
> + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));

dma_set_mask_and_coherent()? Also error check?

2023-09-17 11:01:50

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v12 6/7] dt-bindings: media: wave5: add yaml devicetree bindings

On 15/09/2023 23:11, Sebastian Fricke wrote:
> From: Robert Beckett <[email protected]>
>
> Add bindings for the wave5 chips&media codec driver
>
> Signed-off-by: Robert Beckett <[email protected]>
> Signed-off-by: Dafna Hirschfeld <[email protected]>
> Signed-off-by: Sebastian Fricke <[email protected]>

So this is v12 and still no tested?

A nit, subject: drop second/last, redundant "yaml devicetree indings".
The "dt-bindings" prefix is already stating that these are bindings.
Basically three words bringing zero information.

> ---
> .../devicetree/bindings/media/cnm,wave5.yaml | 66 ++++++++++++++++++++++
> 1 file changed, 66 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/media/cnm,wave5.yaml b/Documentation/devicetree/bindings/media/cnm,wave5.yaml
> new file mode 100644
> index 000000000000..b8f383621805
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/cnm,wave5.yaml
> @@ -0,0 +1,66 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/cnm,wave5.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Chips&Media Wave 5 Series multi-standard codec IP
> +
> +maintainers:
> + - Nas Chung <[email protected]>
> + - Jackson Lee <[email protected]>
> +
> +description: |-

Do not need '|-' unless you need to preserve formatting.

> + The Chips&Media WAVE codec IP is a multi format video encoder/decoder
> +
> +properties:
> + compatible:
> + enum:
> + - cnm,cm521c-vpu

It does not look like you tested the bindings, at least after quick
look. Please run `make dt_binding_check` (see
Documentation/devicetree/bindings/writing-schema.rst for instructions).
Maybe you need to update your dtschema and yamllint.

Missing blank line

> + reg:
> + maxItems: 1
> +
> + clocks:
> + items:
> + - description: VCODEC clock
> +
> + clock-names:
> + items:
> + - const: vcodec

Drop clock-names, not really useful for one entry.

> +
> + interrupts:
> + maxItems: 1
> +
> + power-domains:
> + maxItems: 1
> +
> + resets:
> + maxItems: 1
> +
> + sram:
> + $ref: /schemas/types.yaml#/definitions/phandle
> +

Drop blank line

> + description:
> + The VPU uses the SRAM to store some of the reference data instead of
> + storing it on DMA memory. It is mainly used for the purpose of reducing
> + bandwidth.
> +
> +required:
> + - compatible
> + - reg
> + - interrupts

Keep the same order as listed in properties:

> + - clocks
> + - clock-names
> +
> +additionalProperties: false
> +

Best regards,
Krzysztof

2023-09-18 12:29:31

by Sebastian Fricke

[permalink] [raw]
Subject: Re: [PATCH v12 6/7] dt-bindings: media: wave5: add yaml devicetree bindings

Hey Krzysztof,

thanks for your review.

On 17.09.2023 09:56, Krzysztof Kozlowski wrote:
>On 15/09/2023 23:11, Sebastian Fricke wrote:
>> From: Robert Beckett <[email protected]>
>>
>> Add bindings for the wave5 chips&media codec driver
>>
>> Signed-off-by: Robert Beckett <[email protected]>
>> Signed-off-by: Dafna Hirschfeld <[email protected]>
>> Signed-off-by: Sebastian Fricke <[email protected]>
>
>So this is v12 and still no tested?

I have tested it, multiple times actually since V11. (For some reason
that indentation issue slipped by me though ...)
If you mean the tested by tag, the patch was completely unnoticed until
v10 by the community, which was partially because me and the previous
commiters didn't use the right recipients for this patch. So from that
point of view this is more like v2.

>
>A nit, subject: drop second/last, redundant "yaml devicetree indings".
>The "dt-bindings" prefix is already stating that these are bindings.
>Basically three words bringing zero information.

Okay so:
`dt-bindings: media: wave5: add devicetree`
?

>
>> ---
>> .../devicetree/bindings/media/cnm,wave5.yaml | 66 ++++++++++++++++++++++
>> 1 file changed, 66 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/media/cnm,wave5.yaml b/Documentation/devicetree/bindings/media/cnm,wave5.yaml
>> new file mode 100644
>> index 000000000000..b8f383621805
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/media/cnm,wave5.yaml
>> @@ -0,0 +1,66 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/media/cnm,wave5.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Chips&Media Wave 5 Series multi-standard codec IP
>> +
>> +maintainers:
>> + - Nas Chung <[email protected]>
>> + - Jackson Lee <[email protected]>
>> +
>> +description: |-
>
>Do not need '|-' unless you need to preserve formatting.

Ack.

>
>> + The Chips&Media WAVE codec IP is a multi format video encoder/decoder
>> +
>> +properties:
>> + compatible:
>> + enum:
>> + - cnm,cm521c-vpu
>
>It does not look like you tested the bindings, at least after quick
>look. Please run `make dt_binding_check` (see
>Documentation/devicetree/bindings/writing-schema.rst for instructions).
>Maybe you need to update your dtschema and yamllint.

Here my testing output:
```
❯ make dt_binding_check DT_SCHEMA_FILES=cnm,wave5.yaml
HOSTCC scripts/basic/fixdep
HOSTCC scripts/dtc/dtc.o
HOSTCC scripts/dtc/flattree.o
HOSTCC scripts/dtc/fstree.o
HOSTCC scripts/dtc/data.o
HOSTCC scripts/dtc/livetree.o
HOSTCC scripts/dtc/treesource.o
HOSTCC scripts/dtc/srcpos.o
HOSTCC scripts/dtc/checks.o
HOSTCC scripts/dtc/util.o
LEX scripts/dtc/dtc-lexer.lex.c
YACC scripts/dtc/dtc-parser.tab.[ch]
HOSTCC scripts/dtc/dtc-lexer.lex.o
HOSTCC scripts/dtc/dtc-parser.tab.o
HOSTLD scripts/dtc/dtc
LINT Documentation/devicetree/bindings
./Documentation/devicetree/bindings/media/cnm,wave5.yaml:19:9: [warning] wrong indentation: expected 6 but found 8 (indentation)
CHKDT Documentation/devicetree/bindings/processed-schema.json
SCHEMA Documentation/devicetree/bindings/processed-schema.json
DTEX Documentation/devicetree/bindings/media/cnm,wave5.example.dts
DTC_CHK Documentation/devicetree/bindings/media/cnm,wave5.example.dtb
```

Again sorry about missing the indentation warning, but nothing else was
highlighted.

Both dtschema and yamllint seem to be up-to-date:
```
❯ python3 -m pip --version
pip 23.2.1 from /home/basti/.local/lib/python3.8/site-packages/pip (python 3.8)
❯ pip3 show dtschema
Name: dtschema
Version: 2023.7
Summary: DeviceTree validation schema and tools
Home-page: https://github.com/devicetree-org/dt-schema
Author: Rob Herring
Author-email: [email protected]
License: BSD
Location: /home/basti/.local/lib/python3.8/site-packages
Requires: jsonschema, pylibfdt, rfc3987, ruamel.yaml
Required-by:
❯ pip3 show yamllint
Name: yamllint
Version: 1.32.0
Summary: A linter for YAML files.
Home-page:
Author: Adrien Vergé
Author-email:
License: GPL-3.0-only
Location: /home/basti/.local/lib/python3.8/site-packages
Requires: pathspec, pyyaml
Required-by:
```

>
>Missing blank line

Ack, will add that.
>
>> + reg:
>> + maxItems: 1
>> +
>> + clocks:
>> + items:
>> + - description: VCODEC clock
>> +
>> + clock-names:
>> + items:
>> + - const: vcodec
>
>Drop clock-names, not really useful for one entry.

Ack

>
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + power-domains:
>> + maxItems: 1
>> +
>> + resets:
>> + maxItems: 1
>> +
>> + sram:
>> + $ref: /schemas/types.yaml#/definitions/phandle
>> +
>
>Drop blank line

Ack

>
>> + description:
>> + The VPU uses the SRAM to store some of the reference data instead of
>> + storing it on DMA memory. It is mainly used for the purpose of reducing
>> + bandwidth.
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - interrupts
>
>Keep the same order as listed in properties:

Ack

>
>> + - clocks
>> + - clock-names
>> +
>> +additionalProperties: false
>> +
>
>Best regards,
>Krzysztof

Sincerely,
Sebastian

>
>_______________________________________________
>Kernel mailing list -- [email protected]
>To unsubscribe send an email to [email protected]

2023-09-19 03:52:59

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v12 6/7] dt-bindings: media: wave5: add yaml devicetree bindings

On 18/09/2023 21:16, Nicolas Dufresne wrote:
>>>>
>>>> A nit, subject: drop second/last, redundant "yaml devicetree indings".
>>>> The "dt-bindings" prefix is already stating that these are bindings.
>>>> Basically three words bringing zero information.
>>>
>>> Okay so:
>>> `dt-bindings: media: wave5: add devicetree`
>>
>> Still not, because devicetree is duplicating "dt". It's redundant.
>>
>> Instead should be (with correct order of prefixes):
>>
>> media: dt-bindings: wave5: add AzureWaveFooBar XYL ABC10 (whatever
>> company and full product name it is)
>
> So maybe this one ?
>
> media: dt-bindings: wave5: add Chips&Media 521c codec IP support

Sure, sounds good for me.

Best regards,
Krzysztof

2023-09-19 10:29:13

by Nicolas Dufresne

[permalink] [raw]
Subject: Re: [PATCH v12 6/7] dt-bindings: media: wave5: add yaml devicetree bindings

Le lundi 18 septembre 2023 à 14:02 +0200, Krzysztof Kozlowski a écrit :
> On 18/09/2023 08:49, Sebastian Fricke wrote:
> > Hey Krzysztof,
> >
> > thanks for your review.
> >
> > On 17.09.2023 09:56, Krzysztof Kozlowski wrote:
> > > On 15/09/2023 23:11, Sebastian Fricke wrote:
> > > > From: Robert Beckett <[email protected]>
> > > >
> > > > Add bindings for the wave5 chips&media codec driver
> > > >
> > > > Signed-off-by: Robert Beckett <[email protected]>
> > > > Signed-off-by: Dafna Hirschfeld <[email protected]>
> > > > Signed-off-by: Sebastian Fricke <[email protected]>
> > >
> > > So this is v12 and still no tested?
> >
> > I have tested it, multiple times actually since V11. (For some reason
> > that indentation issue slipped by me though ...)
> > If you mean the tested by tag, the patch was completely unnoticed until
> > v10 by the community, which was partially because me and the previous
> > commiters didn't use the right recipients for this patch. So from that
> > point of view this is more like v2.
> >
> > >
> > > A nit, subject: drop second/last, redundant "yaml devicetree indings".
> > > The "dt-bindings" prefix is already stating that these are bindings.
> > > Basically three words bringing zero information.
> >
> > Okay so:
> > `dt-bindings: media: wave5: add devicetree`
>
> Still not, because devicetree is duplicating "dt". It's redundant.
>
> Instead should be (with correct order of prefixes):
>
> media: dt-bindings: wave5: add AzureWaveFooBar XYL ABC10 (whatever
> company and full product name it is)

So maybe this one ?

media: dt-bindings: wave5: add Chips&Media 521c codec IP support

>
>
> Best regards,
> Krzysztof
>

2023-09-19 21:30:47

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v12 6/7] dt-bindings: media: wave5: add yaml devicetree bindings

On 18/09/2023 08:49, Sebastian Fricke wrote:
> Hey Krzysztof,
>
> thanks for your review.
>
> On 17.09.2023 09:56, Krzysztof Kozlowski wrote:
>> On 15/09/2023 23:11, Sebastian Fricke wrote:
>>> From: Robert Beckett <[email protected]>
>>>
>>> Add bindings for the wave5 chips&media codec driver
>>>
>>> Signed-off-by: Robert Beckett <[email protected]>
>>> Signed-off-by: Dafna Hirschfeld <[email protected]>
>>> Signed-off-by: Sebastian Fricke <[email protected]>
>>
>> So this is v12 and still no tested?
>
> I have tested it, multiple times actually since V11. (For some reason
> that indentation issue slipped by me though ...)
> If you mean the tested by tag, the patch was completely unnoticed until
> v10 by the community, which was partially because me and the previous
> commiters didn't use the right recipients for this patch. So from that
> point of view this is more like v2.
>
>>
>> A nit, subject: drop second/last, redundant "yaml devicetree indings".
>> The "dt-bindings" prefix is already stating that these are bindings.
>> Basically three words bringing zero information.
>
> Okay so:
> `dt-bindings: media: wave5: add devicetree`

Still not, because devicetree is duplicating "dt". It's redundant.

Instead should be (with correct order of prefixes):

media: dt-bindings: wave5: add AzureWaveFooBar XYL ABC10 (whatever
company and full product name it is)


Best regards,
Krzysztof

2023-09-20 15:23:14

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

On 15/09/2023 23:11, Sebastian Fricke wrote:
> From: Nas Chung <[email protected]>
>
> Add the decoder and encoder implementing the v4l2
> API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
>
> Signed-off-by: Sebastian Fricke <[email protected]>
> Signed-off-by: Nicolas Dufresne <[email protected]>
> Signed-off-by: Robert Beckett <[email protected]>
> Signed-off-by: Dafna Hirschfeld <[email protected]>
> Signed-off-by: Nas Chung <[email protected]>
> ---
> drivers/media/platform/chips-media/Kconfig | 1 +
> drivers/media/platform/chips-media/Makefile | 1 +
> drivers/media/platform/chips-media/wave5/Kconfig | 12 +
> drivers/media/platform/chips-media/wave5/Makefile | 10 +
> .../platform/chips-media/wave5/wave5-helper.c | 196 ++
> .../platform/chips-media/wave5/wave5-helper.h | 30 +
> .../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
> .../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
> .../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
> .../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
> 10 files changed, 4454 insertions(+)
>
> diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig
> index f87a0d693df7..ad350eb6b1fc 100644
> --- a/drivers/media/platform/chips-media/Kconfig
> +++ b/drivers/media/platform/chips-media/Kconfig
> @@ -3,3 +3,4 @@
> comment "Chips&Media media platform drivers"
>
> source "drivers/media/platform/chips-media/coda/Kconfig"
> +source "drivers/media/platform/chips-media/wave5/Kconfig"
> diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile
> index 5ee693f651c1..6b5d99de8b54 100644
> --- a/drivers/media/platform/chips-media/Makefile
> +++ b/drivers/media/platform/chips-media/Makefile
> @@ -1,3 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0-only
>
> obj-y += coda/
> +obj-y += wave5/
> diff --git a/drivers/media/platform/chips-media/wave5/Kconfig b/drivers/media/platform/chips-media/wave5/Kconfig
> new file mode 100644
> index 000000000000..a3b949356cd5
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config VIDEO_WAVE_VPU
> + tristate "Chips&Media Wave Codec Driver"
> + depends on VIDEO_DEV
> + select VIDEOBUF2_DMA_CONTIG
> + select VIDEOBUF2_VMALLOC
> + select V4L2_MEM2MEM_DEV
> + help
> + Chips&Media stateful encoder and decoder driver.
> + The driver supports HEVC and H264 formats.
> + To compile this driver as modules, choose M here: the
> + modules will be called wave5.
> diff --git a/drivers/media/platform/chips-media/wave5/Makefile b/drivers/media/platform/chips-media/wave5/Makefile
> new file mode 100644
> index 000000000000..3d738a03bd8e
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/Makefile
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_VIDEO_WAVE_VPU) += wave5.o
> +wave5-objs += wave5-hw.o \
> + wave5-vpuapi.o \
> + wave5-vdi.o \
> + wave5-vpu-dec.o \
> + wave5-vpu.o \
> + wave5-vpu-enc.o \
> + wave5-helper.o
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c
> new file mode 100644
> index 000000000000..07b6910bbfee
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
> @@ -0,0 +1,196 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - decoder interface
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC

2021? Should that be 2021-2023?

> + */
> +
> +#include "wave5-helper.h"
> +
> +const char *state_to_str(enum vpu_instance_state state)
> +{
> + switch (state) {
> + case VPU_INST_STATE_NONE:
> + return "NONE";
> + case VPU_INST_STATE_OPEN:
> + return "OPEN";
> + case VPU_INST_STATE_INIT_SEQ:
> + return "INIT_SEQ";
> + case VPU_INST_STATE_PIC_RUN:
> + return "PIC_RUN";
> + case VPU_INST_STATE_STOP:
> + return "STOP";
> + default:
> + return "UNKNOWN";
> + }
> +}
> +
> +void wave5_cleanup_instance(struct vpu_instance *inst)
> +{
> + int i;
> +
> + if (list_is_singular(&inst->list))
> + wave5_vdi_free_sram(inst->dev);
> +
> + for (i = 0; i < inst->fbc_buf_count; i++)
> + wave5_vpu_dec_reset_framebuffer(inst, i);
> +
> + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
> + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl);
> + if (inst->v4l2_fh.vdev) {
> + v4l2_fh_del(&inst->v4l2_fh);
> + v4l2_fh_exit(&inst->v4l2_fh);
> + }
> + list_del_init(&inst->list);
> + kfifo_free(&inst->irq_status);
> + ida_free(&inst->dev->inst_ida, inst->id);
> + kfree(inst->codec_info);
> + kfree(inst);
> +}
> +
> +int wave5_vpu_release_device(struct file *filp,
> + int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
> + char *name)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data);
> +
> + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
> + if (inst->state != VPU_INST_STATE_NONE) {
> + u32 fail_res;
> + int ret;
> +
> + ret = close_func(inst, &fail_res);
> + if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) {
> + dev_err(inst->dev->dev, "%s close failed, device is still running\n",
> + name);
> + return -EBUSY;
> + }
> + if (ret && ret != -EIO) {
> + dev_err(inst->dev->dev, "%s close, fail: %d\n", name, ret);
> + return ret;
> + }
> + }
> +
> + wave5_cleanup_instance(inst);
> +
> + return 0;
> +}
> +
> +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
> + const struct vb2_ops *ops)
> +{
> + struct vpu_instance *inst = priv;
> + int ret;
> +
> + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> + src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> + src_vq->mem_ops = &vb2_dma_contig_memops;
> + src_vq->ops = ops;
> + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + src_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
> + src_vq->drv_priv = inst;
> + src_vq->lock = &inst->dev->dev_lock;
> + src_vq->dev = inst->dev->v4l2_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->mem_ops = &vb2_dma_contig_memops;
> + dst_vq->ops = ops;
> + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> + dst_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
> + dst_vq->drv_priv = inst;
> + dst_vq->lock = &inst->dev->dev_lock;
> + dst_vq->dev = inst->dev->v4l2_dev.dev;
> + ret = vb2_queue_init(dst_vq);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + bool is_decoder = inst->type == VPU_INST_TYPE_DEC;
> +
> + dev_dbg(inst->dev->dev, "%s: [%s] type: %u id: %u | flags: %u\n", __func__,
> + is_decoder ? "decoder" : "encoder", sub->type, sub->id, sub->flags);
> +
> + switch (sub->type) {
> + case V4L2_EVENT_EOS:
> + return v4l2_event_subscribe(fh, sub, 0, NULL);
> + case V4L2_EVENT_SOURCE_CHANGE:
> + if (is_decoder)
> + return v4l2_src_change_event_subscribe(fh, sub);
> + return -EINVAL;
> + case V4L2_EVENT_CTRL:
> + return v4l2_ctrl_subscribe_event(fh, sub);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + int i;
> +
> + f->fmt.pix_mp.width = inst->src_fmt.width;
> + f->fmt.pix_mp.height = inst->src_fmt.height;
> + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
> + f->fmt.pix_mp.field = inst->src_fmt.field;
> + f->fmt.pix_mp.flags = inst->src_fmt.flags;
> + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
> + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
> + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline;
> + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage;
> + }
> +
> + f->fmt.pix_mp.colorspace = inst->colorspace;
> + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
> + f->fmt.pix_mp.quantization = inst->quantization;
> + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> +
> + return 0;
> +}
> +
> +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
> + const struct vpu_format fmt_list[MAX_FMTS])
> +{
> + unsigned int index;
> +
> + for (index = 0; index < MAX_FMTS; index++) {
> + if (fmt_list[index].v4l2_pix_fmt == v4l2_pix_fmt)
> + return &fmt_list[index];
> + }
> +
> + return NULL;
> +}
> +
> +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
> + const struct vpu_format fmt_list[MAX_FMTS])
> +{
> + if (idx >= MAX_FMTS)
> + return NULL;
> +
> + if (!fmt_list[idx].v4l2_pix_fmt)
> + return NULL;
> +
> + return &fmt_list[idx];
> +}
> +
> +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type)
> +{
> + switch (v4l2_pix_fmt) {
> + case V4L2_PIX_FMT_H264:
> + return type == VPU_INST_TYPE_DEC ? W_AVC_DEC : W_AVC_ENC;
> + case V4L2_PIX_FMT_HEVC:
> + return type == VPU_INST_TYPE_DEC ? W_HEVC_DEC : W_HEVC_ENC;
> + default:
> + return STD_UNKNOWN;
> + }
> +}
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h
> new file mode 100644
> index 000000000000..a55d5503f5b2
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - basic types
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#ifndef __WAVE_HELPER_H__
> +#define __WAVE_HELPER_H__
> +
> +#include "wave5-vpu.h"
> +
> +#define FMT_TYPES 2
> +#define MAX_FMTS 12
> +
> +const char *state_to_str(enum vpu_instance_state state);
> +void wave5_cleanup_instance(struct vpu_instance *inst);
> +int wave5_vpu_release_device(struct file *filp,
> + int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
> + char *name);
> +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
> + const struct vb2_ops *ops);
> +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);
> +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f);
> +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
> + const struct vpu_format fmt_list[MAX_FMTS]);
> +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
> + const struct vpu_format fmt_list[MAX_FMTS]);
> +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type);
> +#endif
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
> new file mode 100644
> index 000000000000..41257a26504b
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
> @@ -0,0 +1,1965 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - decoder interface
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#include "wave5-helper.h"
> +
> +#define VPU_DEC_DEV_NAME "C&M Wave5 VPU decoder"
> +#define VPU_DEC_DRV_NAME "wave5-dec"
> +
> +#define DEFAULT_SRC_SIZE(width, height) ({ \
> + (width) * (height) / 8 * 3; \
> +})
> +
> +static const struct vpu_format dec_fmt_list[FMT_TYPES][MAX_FMTS] = {
> + [VPU_FMT_TYPE_CODEC] = {
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_H264,
> + .max_width = 8192,
> + .min_width = 32,
> + .max_height = 4320,
> + .min_height = 32,
> + },
> + },
> + [VPU_FMT_TYPE_RAW] = {
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M,
> + .max_width = 8192,
> + .min_width = 8,
> + .max_height = 4320,
> + .min_height = 8,
> + },
> + }
> +};
> +
> +/*
> + * Make sure that the state switch is allowed and add logging for debugging
> + * purposes
> + */
> +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state)
> +{
> + switch (state) {
> + case VPU_INST_STATE_NONE:
> + break;
> + case VPU_INST_STATE_OPEN:
> + if (inst->state != VPU_INST_STATE_NONE)
> + goto invalid_state_switch;
> + goto valid_state_switch;
> + case VPU_INST_STATE_INIT_SEQ:
> + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP)
> + goto invalid_state_switch;
> + goto valid_state_switch;
> + case VPU_INST_STATE_PIC_RUN:
> + if (inst->state != VPU_INST_STATE_INIT_SEQ)
> + goto invalid_state_switch;
> + goto valid_state_switch;
> + case VPU_INST_STATE_STOP:
> + goto valid_state_switch;
> + };

Spurious ;

Odd, wouldn't checkpatch.pl --strict warn about that?

> +invalid_state_switch:
> + WARN(1, "Invalid state switch from %s to %s.\n",
> + state_to_str(inst->state), state_to_str(state));
> + return -EINVAL;
> +valid_state_switch:
> + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n",
> + state_to_str(inst->state), state_to_str(state));
> + inst->state = state;
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
> +{
> + int ret;
> +
> + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
> + if (ret) {
> + dev_err(inst->dev->dev,
> + "Setting EOS for the bitstream, fail: %d\n", ret);

Is this an error due to a driver problem, or because a bad bitstream is
fed from userspace? In the first case, dev_err would be right, in the
second dev_dbg would be more appropriate. Bad userspace input should not
spam the kernel log in general.

> + return ret;
> + }
> + return 0;
> +}
> +
> +static bool wave5_last_src_buffer_consumed(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> + struct vpu_src_buffer *vpu_buf;
> +
> + if (!m2m_ctx->last_src_buf)
> + return false;
> +
> + vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf);
> + return vpu_buf->consumed;
> +}
> +
> +static void wave5_handle_src_buffer(struct vpu_instance *inst, dma_addr_t rd_ptr)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct v4l2_m2m_buffer *buf, *n;
> + size_t consumed_bytes = 0;
> +
> + if (rd_ptr >= inst->last_rd_ptr) {
> + consumed_bytes = rd_ptr - inst->last_rd_ptr;
> + } else {
> + size_t rd_offs = rd_ptr - inst->bitstream_vbuf.daddr;
> + size_t last_rd_offs = inst->last_rd_ptr - inst->bitstream_vbuf.daddr;
> +
> + consumed_bytes = rd_offs + (inst->bitstream_vbuf.size - last_rd_offs);
> + }
> +
> + inst->last_rd_ptr = rd_ptr;
> + consumed_bytes += inst->remaining_consumed_bytes;
> +
> + dev_dbg(inst->dev->dev, "%s: %zu bytes of bitstream was consumed", __func__,
> + consumed_bytes);
> +
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) {
> + struct vb2_v4l2_buffer *src_buf = &buf->vb;
> + size_t src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
> +
> + if (src_size > consumed_bytes)
> + break;
> +
> + dev_dbg(inst->dev->dev, "%s: removing src buffer %i",
> + __func__, src_buf->vb2_buf.index);
> + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
> + inst->timestamp = src_buf->vb2_buf.timestamp;
> + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> + consumed_bytes -= src_size;
> +
> + /* Handle the case the last bitstream buffer has been picked */
> + if (src_buf == m2m_ctx->last_src_buf) {
> + int ret;
> +
> + m2m_ctx->last_src_buf = NULL;
> + ret = wave5_vpu_dec_set_eos_on_firmware(inst);
> + if (ret)
> + dev_warn(inst->dev->dev,
> + "Setting EOS for the bitstream, fail: %d\n", ret);
> + break;
> + }
> + }
> +
> + inst->remaining_consumed_bytes = consumed_bytes;
> +}
> +
> +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
> + unsigned int height)
> +{
> + switch (pix_mp->pixelformat) {
> + case V4L2_PIX_FMT_YUV420:
> + case V4L2_PIX_FMT_NV12:
> + case V4L2_PIX_FMT_NV21:
> + pix_mp->width = round_up(width, 32);
> + pix_mp->height = round_up(height, 16);
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = width * height * 3 / 2;
> + break;
> + case V4L2_PIX_FMT_YUV422P:
> + case V4L2_PIX_FMT_NV16:
> + case V4L2_PIX_FMT_NV61:
> + pix_mp->width = round_up(width, 32);
> + pix_mp->height = round_up(height, 16);
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = width * height * 2;
> + break;
> + case V4L2_PIX_FMT_YUV420M:
> + pix_mp->width = round_up(width, 32);
> + pix_mp->height = round_up(height, 16);
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = width * height;
> + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
> + pix_mp->plane_fmt[1].sizeimage = width * height / 4;
> + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
> + pix_mp->plane_fmt[2].sizeimage = width * height / 4;
> + break;
> + case V4L2_PIX_FMT_NV12M:
> + case V4L2_PIX_FMT_NV21M:
> + pix_mp->width = round_up(width, 32);
> + pix_mp->height = round_up(height, 16);
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = width * height;
> + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[1].sizeimage = width * height / 2;
> + break;
> + case V4L2_PIX_FMT_YUV422M:
> + pix_mp->width = round_up(width, 32);
> + pix_mp->height = round_up(height, 16);
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = width * height;
> + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
> + pix_mp->plane_fmt[1].sizeimage = width * height / 2;
> + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
> + pix_mp->plane_fmt[2].sizeimage = width * height / 2;
> + break;
> + case V4L2_PIX_FMT_NV16M:
> + case V4L2_PIX_FMT_NV61M:
> + pix_mp->width = round_up(width, 32);
> + pix_mp->height = round_up(height, 16);
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = width * height;
> + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[1].sizeimage = width * height;
> + break;
> + default:
> + pix_mp->width = width;
> + pix_mp->height = height;
> + pix_mp->plane_fmt[0].bytesperline = 0;
> + pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_SRC_SIZE(width, height),
> + pix_mp->plane_fmt[0].sizeimage);
> + break;
> + }
> +}
> +
> +static int start_decode(struct vpu_instance *inst, u32 *fail_res)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + int ret = 0;
> +
> + ret = wave5_vpu_dec_start_one_frame(inst, fail_res);
> + if (ret) {
> + struct vb2_v4l2_buffer *src_buf;
> +
> + /* FIXME this will have no effect on the firmware at all, and we
> + * are moving to STOP state, so the remaming src buffer won't be
> + * consumed. Maybe best here is just to make sure we can avoid
> + * deadlocking the app ?
> + */
> + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
> + if (src_buf)
> + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> + switch_state(inst, VPU_INST_STATE_STOP);
> +
> + dev_dbg(inst->dev->dev, "%s: pic run failed / finish job", __func__);
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> + }
> +
> + return ret;
> +}
> +
> +/* Must be called with state_spinlock */
> +static void flag_last_buffer_done(struct vpu_instance *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_v4l2_buffer *vb;
> + int i;
> +
> + vb = v4l2_m2m_dst_buf_remove(m2m_ctx);
> + if (!vb) {
> + m2m_ctx->is_draining = true;
> + m2m_ctx->next_buf_last = true;
> + return;
> + }
> +
> + for (i = 0; i < vb->vb2_buf.num_planes; i++)
> + vb2_set_plane_payload(&vb->vb2_buf, i, 0);
> + vb->field = V4L2_FIELD_NONE;
> +
> + v4l2_m2m_last_buffer_done(m2m_ctx, vb);
> +}
> +
> +/* Must be called with state_spinlock */
> +static void send_eos_event(struct vpu_instance *inst)
> +{
> + static const struct v4l2_event vpu_event_eos = {
> + .type = V4L2_EVENT_EOS
> + };
> +
> + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos);
> + inst->eos = false;
> +}
> +
> +/* Must be called with state_spinlock */
> +static int handle_dynamic_resolution_change(struct vpu_instance *inst)
> +{
> + struct v4l2_fh *fh = &inst->v4l2_fh;
> + static const struct v4l2_event vpu_event_src_ch = {
> + .type = V4L2_EVENT_SOURCE_CHANGE,
> + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
> + };
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + struct dec_initial_info *initial_info = &inst->codec_info->dec_info.initial_info;
> +
> + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad", __func__, &initial_info->rd_ptr);
> +
> + dev_dbg(inst->dev->dev, "%s: width: %u height: %u profile: %u | minbuffer: %u\n",
> + __func__, initial_info->pic_width, initial_info->pic_height,
> + initial_info->profile, initial_info->min_frame_buffer_count);
> +
> + inst->needs_reallocation = true;
> + inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1;
> + if (inst->dst_buf_count != inst->fbc_buf_count) {
> + struct v4l2_ctrl *ctrl;
> +
> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
> + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
> + if (ctrl)
> + v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count);
> +
> + inst->dst_buf_count = inst->fbc_buf_count;
> + }
> +
> + if (p_dec_info->initial_info_obtained) {
> + inst->conf_win.left = initial_info->pic_crop_rect.left;
> + inst->conf_win.top = initial_info->pic_crop_rect.top;
> + inst->conf_win.width = initial_info->pic_width -
> + initial_info->pic_crop_rect.left - initial_info->pic_crop_rect.right;
> + inst->conf_win.height = initial_info->pic_height -
> + initial_info->pic_crop_rect.top - initial_info->pic_crop_rect.bottom;
> +
> + wave5_update_pix_fmt(&inst->src_fmt, initial_info->pic_width,
> + initial_info->pic_height);
> + wave5_update_pix_fmt(&inst->dst_fmt, initial_info->pic_width,
> + initial_info->pic_height);
> + }
> +
> + v4l2_event_queue_fh(fh, &vpu_event_src_ch);
> +
> + return 0;
> +}
> +
> +static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct dec_output_info dec_info;
> + int ret;
> + struct vb2_v4l2_buffer *dec_buf = NULL;
> + struct vb2_v4l2_buffer *disp_buf = NULL;
> + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> + struct queue_status_info q_status;
> +
> + dev_dbg(inst->dev->dev, "%s: Fetch output info from firmware.", __func__);
> +
> + ret = wave5_vpu_dec_get_output_info(inst, &dec_info);
> + if (ret) {
> + dev_warn(inst->dev->dev, "%s: could not get output info.", __func__);
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> + return;
> + }
> +
> + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &dec_info.rd_ptr,
> + &dec_info.wr_ptr);
> + wave5_handle_src_buffer(inst, dec_info.rd_ptr);
> +
> + dev_dbg(inst->dev->dev, "%s: dec_info dec_idx %i disp_idx %i", __func__,
> + dec_info.index_frame_decoded, dec_info.index_frame_display);
> +
> + if (!vb2_is_streaming(dst_vq)) {
> + dev_dbg(inst->dev->dev, "%s: capture is not streaming..", __func__);
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> + return;
> + }
> +
> + /* Remove decoded buffer from the ready queue now that it has been
> + * decoded.
> + */
> + if (dec_info.index_frame_decoded >= 0) {
> + struct vb2_buffer *vb = vb2_get_buffer(dst_vq,
> + dec_info.index_frame_decoded);
> + if (vb)
> + dec_buf = to_vb2_v4l2_buffer(vb);
> + else
> + dev_warn(inst->dev->dev, "%s: invalid decoded frame index %i",
> + __func__, dec_info.index_frame_decoded);
> + }
> +
> + if (dec_buf)
> + dec_buf->vb2_buf.timestamp = inst->timestamp;
> +
> + if (dec_info.index_frame_display >= 0) {
> + disp_buf = v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, dec_info.index_frame_display);
> + if (!disp_buf)
> + dev_warn(inst->dev->dev, "%s: invalid display frame index %i",
> + __func__, dec_info.index_frame_display);
> + }
> +
> + /* If there is anything to display, do that now */
> + if (disp_buf) {
> + struct vpu_dst_buffer *dst_vpu_buf = wave5_to_vpu_dst_buf(disp_buf);
> + int stride = dec_info.disp_frame.stride;
> + int height = dec_info.disp_pic_height;
> +
> + /* FIXME we should calculate that once, and use v4l2-common if
> + * possible
> + */
> + if (inst->dst_fmt.num_planes == 1) {
> + vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
> + (stride * height * 3 / 2));
> + } else if (inst->dst_fmt.num_planes == 2) {
> + vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
> + (stride * height));
> + vb2_set_plane_payload(&disp_buf->vb2_buf, 1,
> + ((stride / 2) * height));
> + } else if (inst->dst_fmt.num_planes == 3) {
> + vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
> + (stride * height));
> + vb2_set_plane_payload(&disp_buf->vb2_buf, 1,
> + ((stride / 2) * (height / 2)));
> + vb2_set_plane_payload(&disp_buf->vb2_buf, 2,
> + ((stride / 2) * (height / 2)));
> + }
> +
> + /* TODO implement interlace support */
> + disp_buf->field = V4L2_FIELD_NONE;
> + dst_vpu_buf->display = true;
> + v4l2_m2m_buf_done(disp_buf, VB2_BUF_STATE_DONE);
> +
> + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u (payload %lu stride %d height %d)\n",
> + __func__, dec_info.frame_cycle,
> + vb2_get_plane_payload(&disp_buf->vb2_buf, 0), stride, height);
> + }
> +
> + if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END ||
> + dec_info.sequence_changed)) {
> + unsigned long flags;
> +
> + spin_lock_irqsave(&inst->state_spinlock, flags);
> + if (!v4l2_m2m_has_stopped(m2m_ctx)) {
> + switch_state(inst, VPU_INST_STATE_STOP);
> +
> + if (dec_info.sequence_changed)
> + handle_dynamic_resolution_change(inst);
> + else
> + send_eos_event(inst);
> +
> + flag_last_buffer_done(inst);
> + }
> + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> + }
> +
> + /*
> + * During a resolution change and while draining, the firmware may flush
> + * the reorder queue regardless of having a matching decoding operation
> + * pending. Only terminate the job if there are no more IRQ coming.
> + */
> + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
> + if (q_status.report_queue_count == 0 &&
> + (q_status.instance_queue_count == 0 || dec_info.sequence_changed)) {
> + dev_dbg(inst->dev->dev, "%s: finishing job.\n", __func__);
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> + }
> +}
> +
> +static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
> +{
> + strscpy(cap->driver, VPU_DEC_DRV_NAME, sizeof(cap->driver));
> + strscpy(cap->card, VPU_DEC_DRV_NAME, sizeof(cap->card));
> + strscpy(cap->bus_info, "platform:" VPU_DEC_DRV_NAME, sizeof(cap->bus_info));

This shouldn't be needed, bus_info is filled by the v4l2 core.

> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize)
> +{
> + const struct vpu_format *vpu_fmt;
> +
> + if (fsize->index)
> + return -EINVAL;
> +
> + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
> + if (!vpu_fmt) {
> + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_RAW]);
> + if (!vpu_fmt)
> + return -EINVAL;
> + }
> +
> + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> + fsize->stepwise.min_width = vpu_fmt->min_width;
> + fsize->stepwise.max_width = vpu_fmt->max_width;
> + fsize->stepwise.step_width = 1;
> + fsize->stepwise.min_height = vpu_fmt->min_height;
> + fsize->stepwise.max_height = vpu_fmt->max_height;
> + fsize->stepwise.step_height = 1;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> +{
> + const struct vpu_format *vpu_fmt;
> +
> + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_RAW]);
> + if (!vpu_fmt)
> + return -EINVAL;
> +
> + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->flags = 0;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + const struct vpu_format *vpu_fmt;
> + int width, height;
> +
> + dev_dbg(inst->dev->dev,
> + "%s: fourcc: %u width: %u height: %u nm planes: %u colorspace: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;

This check isn't needed.

> +
> + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_RAW]);
> + if (!vpu_fmt) {
> + width = inst->dst_fmt.width;
> + height = inst->dst_fmt.height;
> + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> + } else {
> + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
> +
> + width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> + height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->fmt.pix_mp.num_planes = info->mem_planes;
> + }
> +
> + if (p_dec_info->initial_info_obtained) {
> + width = inst->dst_fmt.width;
> + height = inst->dst_fmt.height;
> + }
> +
> + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> + f->fmt.pix_mp.flags = 0;
> + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> + f->fmt.pix_mp.colorspace = inst->colorspace;
> + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;

Drop this. hsv_enc and ycbcr_enc are in a union, so copying one is
sufficient. This might be done elsewhere, so grep for hsv_enc.

> + f->fmt.pix_mp.quantization = inst->quantization;
> + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));

Not needed, cleared by the core.

> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + int i, ret;
> +
> + dev_dbg(inst->dev->dev,
> + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
> +
> + ret = wave5_vpu_dec_try_fmt_cap(file, fh, f);
> + if (ret)
> + return ret;
> +
> + inst->dst_fmt.width = f->fmt.pix_mp.width;
> + inst->dst_fmt.height = f->fmt.pix_mp.height;
> + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> + inst->dst_fmt.field = f->fmt.pix_mp.field;
> + inst->dst_fmt.flags = f->fmt.pix_mp.flags;
> + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes;
> + for (i = 0; i < inst->dst_fmt.num_planes; i++) {
> + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> + }
> +
> + if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12 ||
> + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12M) {
> + inst->cbcr_interleave = true;
> + inst->nv21 = false;
> + inst->output_format = FORMAT_420;
> + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21 ||
> + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21M) {
> + inst->cbcr_interleave = true;
> + inst->nv21 = true;
> + inst->output_format = FORMAT_420;
> + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16 ||
> + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16M) {
> + inst->cbcr_interleave = true;
> + inst->nv21 = false;
> + inst->output_format = FORMAT_422;
> + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61 ||
> + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61M) {
> + inst->cbcr_interleave = true;
> + inst->nv21 = true;
> + inst->output_format = FORMAT_422;
> + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422P ||
> + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422M) {
> + inst->cbcr_interleave = false;
> + inst->nv21 = false;
> + inst->output_format = FORMAT_422;
> + } else {
> + inst->cbcr_interleave = false;
> + inst->nv21 = false;
> + inst->output_format = FORMAT_420;
> + }
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + int i;
> +
> + f->fmt.pix_mp.width = inst->dst_fmt.width;
> + f->fmt.pix_mp.height = inst->dst_fmt.height;
> + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> + f->fmt.pix_mp.field = inst->dst_fmt.field;
> + f->fmt.pix_mp.flags = inst->dst_fmt.flags;
> + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
> + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline;
> + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage;
> + }
> +
> + f->fmt.pix_mp.colorspace = inst->colorspace;
> + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;

Ah, another hsv_enc. Just drop all references for hsv_enc, it's not relevant
for this driver.

> + f->fmt.pix_mp.quantization = inst->quantization;
> + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + const struct vpu_format *vpu_fmt;
> +
> + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
> +
> + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
> + if (!vpu_fmt)
> + return -EINVAL;
> +
> + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + const struct vpu_format *vpu_fmt;
> +
> + dev_dbg(inst->dev->dev,
> + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + return -EINVAL;

Can be dropped.

> +
> + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
> + if (!vpu_fmt) {
> + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
> + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
> + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
> + } else {
> + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> +
> + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->fmt.pix_mp.num_planes = 1;
> + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> + }
> +
> + f->fmt.pix_mp.flags = 0;
> + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));

Drop this.

> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + int i, ret;
> +
> + dev_dbg(inst->dev->dev,
> + "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> +
> + ret = wave5_vpu_dec_try_fmt_out(file, fh, f);
> + if (ret)
> + return ret;
> +
> + inst->std = wave5_to_vpu_std(f->fmt.pix_mp.pixelformat, inst->type);
> + if (inst->std == STD_UNKNOWN) {
> + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
> + (char *)&f->fmt.pix_mp.pixelformat);
> + return -EINVAL;
> + }
> +
> + inst->src_fmt.width = f->fmt.pix_mp.width;
> + inst->src_fmt.height = f->fmt.pix_mp.height;
> + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> + inst->src_fmt.field = f->fmt.pix_mp.field;
> + inst->src_fmt.flags = f->fmt.pix_mp.flags;
> + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes;
> + for (i = 0; i < inst->src_fmt.num_planes; i++) {
> + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> + }
> +
> + inst->colorspace = f->fmt.pix_mp.colorspace;
> + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
> + inst->hsv_enc = f->fmt.pix_mp.hsv_enc;
> + inst->quantization = f->fmt.pix_mp.quantization;
> + inst->xfer_func = f->fmt.pix_mp.xfer_func;
> +
> + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> +
> + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target);
> +
> + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> + switch (s->target) {
> + case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> + case V4L2_SEL_TGT_COMPOSE_PADDED:
> + s->r.left = 0;
> + s->r.top = 0;
> + s->r.width = inst->dst_fmt.width;
> + s->r.height = inst->dst_fmt.height;
> + break;
> + case V4L2_SEL_TGT_COMPOSE:
> + case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> + s->r.left = 0;
> + s->r.top = 0;
> + if (inst->state > VPU_INST_STATE_OPEN) {
> + s->r = inst->conf_win;
> + } else {
> + s->r.width = inst->src_fmt.width;
> + s->r.height = inst->src_fmt.height;
> + }
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> +
> + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -EINVAL;
> +
> + if (s->target != V4L2_SEL_TGT_COMPOSE)
> + return -EINVAL;
> +
> + dev_dbg(inst->dev->dev, "V4L2_SEL_TGT_COMPOSE w: %u h: %u\n",
> + s->r.width, s->r.height);
> +
> + s->r.left = 0;
> + s->r.top = 0;
> + s->r.width = inst->dst_fmt.width;
> + s->r.height = inst->dst_fmt.height;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
> + struct v4l2_create_buffers *create)
> +{
> + struct v4l2_format *f = &create->format;
> +
> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> + return -ENOTTY;

Huh? Why is this needed?

> +
> + return v4l2_m2m_ioctl_create_bufs(file, priv, create);
> +}
> +
> +/* Must be called with state_spinlock */
> +static int wave5_vpu_dec_stop_unlocked(struct vpu_instance *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> +
> + if (m2m_ctx->is_draining)
> + return -EBUSY;
> +
> + if (inst->state != VPU_INST_STATE_NONE) {
> + int ret;
> +
> + ret = wave5_vpu_dec_set_eos_on_firmware(inst);
> + if (ret)
> + return ret;
> + }
> +
> + /*
> + * Used to remember the EOS state after the streamoff/on transition on
> + * the capture queue.
> + */
> + inst->eos = true;
> +
> + if (m2m_ctx->has_stopped)
> + return 0;
> +
> + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
> + m2m_ctx->is_draining = true;
> +
> + /*
> + * Deferred to device run in case it wasn't in the ring buffer
> + * yet. In other case, we have to send the EOS signal to the
> + * firmware so that any pending PIC_RUN ends without new
> + * bitstream buffer.
> + */
> + if (m2m_ctx->last_src_buf)
> + return 0;
> +
> + if (inst->state == VPU_INST_STATE_NONE) {
> + send_eos_event(inst);
> + flag_last_buffer_done(inst);
> + }
> +
> + return 0;
> +}
> +
> +/* Must be called with state_spinlock */
> +static int wave5_vpu_dec_start_unlocked(struct vpu_instance *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> +
> + if (m2m_ctx->is_draining)
> + return -EBUSY;
> +
> + if (m2m_ctx->has_stopped)
> + m2m_ctx->has_stopped = false;
> +
> + vb2_clear_last_buffer_dequeued(dst_vq);
> + inst->eos = false;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + unsigned long flags;
> + int ret;
> +
> + dev_dbg(inst->dev->dev, "decoder command: %u\n", dc->cmd);
> +
> + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc);
> + if (ret)
> + return ret;
> +
> + switch (dc->cmd) {
> + case V4L2_DEC_CMD_STOP:
> + spin_lock_irqsave(&inst->state_spinlock, flags);
> + ret = wave5_vpu_dec_stop_unlocked(inst);
> + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> +
> + /* Just in case we don't have anything to decode anymore */
> + v4l2_m2m_try_schedule(m2m_ctx);
> + break;
> + case V4L2_DEC_CMD_START:
> + spin_lock_irqsave(&inst->state_spinlock, flags);
> + ret = wave5_vpu_dec_start_unlocked(inst);
> + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static const struct v4l2_ioctl_ops wave5_vpu_dec_ioctl_ops = {
> + .vidioc_querycap = wave5_vpu_dec_querycap,
> + .vidioc_enum_framesizes = wave5_vpu_dec_enum_framesizes,
> +
> + .vidioc_enum_fmt_vid_cap = wave5_vpu_dec_enum_fmt_cap,
> + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_dec_s_fmt_cap,
> + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_dec_g_fmt_cap,
> + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_dec_try_fmt_cap,
> +
> + .vidioc_enum_fmt_vid_out = wave5_vpu_dec_enum_fmt_out,
> + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_dec_s_fmt_out,
> + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out,
> + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_dec_try_fmt_out,
> +
> + .vidioc_g_selection = wave5_vpu_dec_g_selection,
> + .vidioc_s_selection = wave5_vpu_dec_s_selection,
> +
> + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> + .vidioc_create_bufs = wave5_vpu_dec_create_bufs,
> + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> +
> + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
> + .vidioc_decoder_cmd = wave5_vpu_dec_decoder_cmd,
> +
> + .vidioc_subscribe_event = wave5_vpu_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
> + unsigned int *num_planes, unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + struct v4l2_pix_format_mplane inst_format =
> + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
> + unsigned int i;
> +
> + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
> + *num_buffers, *num_planes, q->type);
> +
> + /* the CREATE_BUFS case */
> + if (*num_planes) {
> + if (inst_format.num_planes != *num_planes)
> + return -EINVAL;
> +
> + for (i = 0; i < *num_planes; i++) {
> + if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
> + return -EINVAL;
> + }
> + /* the REQBUFS case */
> + } else {
> + *num_planes = inst_format.num_planes;
> +
> + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + sizes[0] = inst_format.plane_fmt[0].sizeimage;
> + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
> + } else if (*num_planes == 1) {
> + if (inst->output_format == FORMAT_422)
> + sizes[0] = inst_format.width * inst_format.height * 2;
> + else
> + sizes[0] = inst_format.width * inst_format.height * 3 / 2;
> + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
> + } else if (*num_planes == 2) {
> + sizes[0] = inst_format.width * inst_format.height;
> + if (inst->output_format == FORMAT_422)
> + sizes[1] = inst_format.width * inst_format.height;
> + else
> + sizes[1] = inst_format.width * inst_format.height / 2;
> + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n",
> + __func__, sizes[0], sizes[1]);
> + } else if (*num_planes == 3) {
> + sizes[0] = inst_format.width * inst_format.height;
> + if (inst->output_format == FORMAT_422) {
> + sizes[1] = inst_format.width * inst_format.height / 2;
> + sizes[2] = inst_format.width * inst_format.height / 2;
> + } else {
> + sizes[1] = inst_format.width * inst_format.height / 4;
> + sizes[2] = inst_format.width * inst_format.height / 4;
> + }
> + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n",
> + __func__, sizes[0], sizes[1], sizes[2]);
> + }
> + }
> +
> + if (inst->state == VPU_INST_STATE_INIT_SEQ &&
> + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> + if (*num_buffers > inst->dst_buf_count &&
> + *num_buffers < WAVE5_MAX_FBS)
> + inst->dst_buf_count = *num_buffers;

In the create_bufs case, *num_buffers is the number of buffers you are
adding to the already existing buffers. Frankly, the logic here is
dubious. I'm not sure what the intent is. Why do you need to keep track
of the buf count at all when the vb2_queue already does that?

WAVE5_MAX_FBS == 32, which is VIDEO_MAX_FRAMES. You can just drop the check
against WAVE5_MAX_FBS since the core ensures already it will never allocate
more than VIDEO_MAX_FRAMES.

I'm not sure why WAVE5_MAX_FBS is defined at all, when it is just equal to
VIDEO_MAX_FRAMES. Perhaps it can be replaced everywhere with VIDEO_MAX_FRAMES?

> +
> + *num_buffers = inst->dst_buf_count;
> + }
> +
> + return 0;
> +}
> +
> +static int wave5_prepare_fb(struct vpu_instance *inst)
> +{
> + int linear_num;
> + int non_linear_num;
> + int fb_stride = 0, fb_height = 0;
> + int luma_size, chroma_size;
> + int ret, i;
> + struct v4l2_m2m_buffer *buf, *n;
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> +
> + linear_num = inst->dst_buf_count;
> + non_linear_num = inst->fbc_buf_count;
> +
> + for (i = 0; i < non_linear_num; i++) {
> + struct frame_buffer *frame = &inst->frame_buf[i];
> + struct vpu_buf *vframe = &inst->frame_vbuf[i];
> +
> + fb_stride = inst->dst_fmt.width;
> + fb_height = ALIGN(inst->dst_fmt.height, 32);
> + luma_size = fb_stride * fb_height;
> +
> + /* FIXME shall we do /4 in case of 422 ?*/
> + chroma_size = ALIGN(fb_stride / 2, 16) * fb_height;
> +
> + if (vframe->size) {
> + if (vframe->size != (luma_size + chroma_size))
> + wave5_vpu_dec_reset_framebuffer(inst, i);
> + else
> + continue;
> + }
> +
> + vframe->size = luma_size + chroma_size;
> + ret = wave5_vdi_allocate_dma_memory(inst->dev, vframe);
> + if (ret) {
> + dev_dbg(inst->dev->dev,
> + "%s: Allocating FBC buf of size %zu, fail: %d\n",
> + __func__, vframe->size, ret);
> + return ret;
> + }
> +
> + frame->buf_y = vframe->daddr;
> + frame->buf_cb = vframe->daddr + luma_size;
> + frame->buf_cr = (dma_addr_t)-1;
> + frame->size = vframe->size;
> + frame->width = inst->src_fmt.width;
> + frame->stride = fb_stride;
> + frame->map_type = COMPRESSED_FRAME_MAP;
> + frame->update_fb_info = true;
> + }
> + /* In case the count has reduced, clean up leftover framebuffer memory */
> + for (i = non_linear_num; i < MAX_REG_FRAME; i++) {
> + ret = wave5_vpu_dec_reset_framebuffer(inst, i);
> + if (ret)
> + break;
> + }
> +
> + for (i = 0; i < linear_num; i++) {
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> + struct vb2_buffer *vb = vb2_get_buffer(dst_vq, i);
> + struct frame_buffer *frame = &inst->frame_buf[non_linear_num + i];
> + dma_addr_t buf_addr_y = 0, buf_addr_cb = 0, buf_addr_cr = 0;
> + u32 buf_size = 0;
> + u32 fb_stride = inst->dst_fmt.width;
> + u32 luma_size = fb_stride * inst->dst_fmt.height;
> + u32 chroma_size;
> +
> + if (inst->output_format == FORMAT_422)
> + chroma_size = fb_stride * inst->dst_fmt.height / 2;
> + else
> + chroma_size = fb_stride * inst->dst_fmt.height / 4;
> +
> + if (inst->dst_fmt.num_planes == 1) {
> + buf_size = vb2_plane_size(vb, 0);
> + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
> + buf_addr_cb = buf_addr_y + luma_size;
> + buf_addr_cr = buf_addr_cb + chroma_size;
> + } else if (inst->dst_fmt.num_planes == 2) {
> + buf_size = vb2_plane_size(vb, 0) +
> + vb2_plane_size(vb, 1);
> + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
> + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1);
> + buf_addr_cr = buf_addr_cb + chroma_size;
> + } else if (inst->dst_fmt.num_planes == 3) {
> + buf_size = vb2_plane_size(vb, 0) +
> + vb2_plane_size(vb, 1) +
> + vb2_plane_size(vb, 2);
> + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
> + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1);
> + buf_addr_cr = vb2_dma_contig_plane_dma_addr(vb, 2);
> + }
> +
> + frame->buf_y = buf_addr_y;
> + frame->buf_cb = buf_addr_cb;
> + frame->buf_cr = buf_addr_cr;
> + frame->size = buf_size;
> + frame->width = inst->src_fmt.width;
> + frame->stride = fb_stride;
> + frame->map_type = LINEAR_FRAME_MAP;
> + frame->update_fb_info = true;
> + }
> +
> + ret = wave5_vpu_dec_register_frame_buffer_ex(inst, non_linear_num, linear_num,
> + fb_stride, inst->dst_fmt.height);
> + if (ret) {
> + dev_dbg(inst->dev->dev, "%s: vpu_dec_register_frame_buffer_ex fail: %d",
> + __func__, ret);
> + return ret;
> + }
> +
> + /*
> + * Mark all frame buffers as out of display, to avoid using them before
> + * the application have them queued.
> + */
> + for (i = 0; i < inst->dst_buf_count; i++) {
> + ret = wave5_vpu_dec_set_disp_flag(inst, i);
> + if (ret) {
> + dev_dbg(inst->dev->dev,
> + "%s: Setting display flag of buf index: %u, fail: %d\n",
> + __func__, i, ret);
> + }
> + }
> +
> + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) {
> + struct vb2_v4l2_buffer *vbuf = &buf->vb;
> +
> + ret = wave5_vpu_dec_clr_disp_flag(inst, vbuf->vb2_buf.index);
> + if (ret) {
> + dev_dbg(inst->dev->dev,
> + "%s: Clearing display flag of buf index: %u, fail: %d\n",
> + __func__, i, ret);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int write_to_ringbuffer(struct vpu_instance *inst, void *buffer, size_t buffer_size,
> + struct vpu_buf *ring_buffer, dma_addr_t wr_ptr)
> +{
> + size_t size;
> + size_t offset = wr_ptr - ring_buffer->daddr;
> + int ret;
> +
> + if (wr_ptr + buffer_size > ring_buffer->daddr + ring_buffer->size) {
> + size = ring_buffer->daddr + ring_buffer->size - wr_ptr;
> + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer, size);
> + if (ret < 0)
> + return ret;
> +
> + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, 0, (u8 *)buffer + size,
> + buffer_size - size);
> + if (ret < 0)
> + return ret;
> + } else {
> + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer,
> + buffer_size);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int fill_ringbuffer(struct vpu_instance *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct v4l2_m2m_buffer *buf, *n;
> + int ret;
> +
> + if (m2m_ctx->last_src_buf) {
> + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf);
> +
> + if (vpu_buf->consumed) {
> + dev_dbg(inst->dev->dev, "last src buffer already written\n");
> + return 0;
> + }
> + }
> +
> + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) {
> + struct vb2_v4l2_buffer *vbuf = &buf->vb;
> + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf);
> + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf;
> + size_t src_size = vb2_get_plane_payload(&vbuf->vb2_buf, 0);
> + void *src_buf = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> + dma_addr_t rd_ptr = 0;
> + dma_addr_t wr_ptr = 0;
> + size_t remain_size = 0;
> +
> + if (vpu_buf->consumed) {
> + dev_dbg(inst->dev->dev, "already copied src buf (%u) to the ring buffer\n",
> + vbuf->vb2_buf.index);
> + continue;
> + }
> +
> + if (!src_buf) {
> + dev_dbg(inst->dev->dev,
> + "%s: Acquiring kernel pointer to src buf (%u), fail\n",
> + __func__, vbuf->vb2_buf.index);
> + break;
> + }
> +
> + ret = wave5_vpu_dec_get_bitstream_buffer(inst, &rd_ptr, &wr_ptr, &remain_size);
> + if (ret) {
> + /* Unable to acquire the mutex */
> + dev_err(inst->dev->dev, "Getting the bitstream buffer, fail: %d\n",
> + ret);
> + return ret;
> + }
> +
> + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &rd_ptr, &wr_ptr);
> +
> + if (remain_size < src_size) {
> + dev_dbg(inst->dev->dev,
> + "%s: remaining size: %zu < source size: %zu for src buf (%u)\n",
> + __func__, remain_size, src_size, vbuf->vb2_buf.index);
> + break;
> + }
> +
> + ret = write_to_ringbuffer(inst, src_buf, src_size, ring_buffer, wr_ptr);
> + if (ret) {
> + dev_err(inst->dev->dev, "Write src buf (%u) to ring buffer, fail: %d\n",
> + vbuf->vb2_buf.index, ret);
> + return ret;
> + }
> +
> + ret = wave5_vpu_dec_update_bitstream_buffer(inst, src_size);
> + if (ret) {
> + dev_dbg(inst->dev->dev,
> + "update_bitstream_buffer fail: %d for src buf (%u)\n",
> + ret, vbuf->vb2_buf.index);
> + break;
> + }
> +
> + vpu_buf->consumed = true;
> +
> + /* Don't write buffers passed the last one while draining. */
> + if (v4l2_m2m_is_last_draining_src_buf(m2m_ctx, vbuf)) {
> + dev_dbg(inst->dev->dev, "last src buffer written to the ring bufferur\n");
> + break;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb)
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf);
> +
> + vpu_buf->consumed = false;
> + vbuf->sequence = inst->queued_src_buf_num++;
> +
> + v4l2_m2m_buf_queue(m2m_ctx, vbuf);
> +}
> +
> +static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> +
> + vbuf->sequence = inst->queued_dst_buf_num++;
> +
> + if (inst->state == VPU_INST_STATE_PIC_RUN) {
> + struct vpu_dst_buffer *vpu_buf = wave5_to_vpu_dst_buf(vbuf);
> + int ret;
> +
> + /*
> + * The buffer is already register, just clear the display flag

register -> registered

> + * to let the firmware know it can be used.
> + */
> + vpu_buf->display = false;
> + ret = wave5_vpu_dec_clr_disp_flag(inst, vb->index);
> + if (ret) {
> + dev_dbg(inst->dev->dev,
> + "%s: Clearing the display flag of buffer index: %u, fail: %d\n",
> + __func__, vb->index, ret);
> + }
> + }
> +
> + if (vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(m2m_ctx)) {
> + unsigned int i;
> +
> + for (i = 0; i < vb->num_planes; i++)
> + vb2_set_plane_payload(vb, i, 0);
> +
> + vbuf->field = V4L2_FIELD_NONE;
> +
> + send_eos_event(inst);
> + v4l2_m2m_last_buffer_done(m2m_ctx, vbuf);
> + } else {
> + v4l2_m2m_buf_queue(m2m_ctx, vbuf);
> + }
> +}
> +
> +static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> +
> + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n",
> + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0),
> + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2));
> +
> + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + wave5_vpu_dec_buf_queue_src(vb);
> + else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + wave5_vpu_dec_buf_queue_dst(vb);
> +}
> +
> +static int wave5_vpu_dec_allocate_ring_buffer(struct vpu_instance *inst)
> +{
> + int ret;
> + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf;
> +
> + ring_buffer->size = ALIGN(inst->src_fmt.plane_fmt[0].sizeimage, 1024) * 4;
> + ret = wave5_vdi_allocate_dma_memory(inst->dev, ring_buffer);
> + if (ret) {
> + dev_dbg(inst->dev->dev, "%s: allocate ring buffer of size %zu fail: %d\n",
> + __func__, ring_buffer->size, ret);
> + return ret;
> + }
> +
> + inst->last_rd_ptr = ring_buffer->daddr;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_v4l2_buffer *buf;
> + int ret = 0;
> +
> + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
> +
> + v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
> +
> + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && inst->state == VPU_INST_STATE_NONE) {
> + struct dec_open_param open_param;
> +
> + memset(&open_param, 0, sizeof(struct dec_open_param));
> +
> + ret = wave5_vpu_dec_allocate_ring_buffer(inst);
> + if (ret)
> + return ret;

If there is an error during start_streaming, then all buffers of the queue must
be returned to state DEQUEUED. That doesn't happen here.

> +
> + inst->std = wave5_to_vpu_std(inst->src_fmt.pixelformat, inst->type);
> + if (inst->std == STD_UNKNOWN) {
> + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
> + (char *)&inst->src_fmt.pixelformat);

How can this happen? This is very weird to see inside a start_streaming callback.

> + goto free_bitstream_vbuf;
> + }
> + open_param.bitstream_buffer = inst->bitstream_vbuf.daddr;
> + open_param.bitstream_buffer_size = inst->bitstream_vbuf.size;
> +
> + ret = wave5_vpu_dec_open(inst, &open_param);
> + if (ret) {
> + dev_dbg(inst->dev->dev, "%s: decoder opening, fail: %d\n",
> + __func__, ret);
> + goto free_bitstream_vbuf;
> + }
> +
> + ret = switch_state(inst, VPU_INST_STATE_OPEN);
> + if (ret)
> + goto free_bitstream_vbuf;
> + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> + struct dec_initial_info *initial_info =
> + &inst->codec_info->dec_info.initial_info;
> +
> + if (inst->state == VPU_INST_STATE_STOP)
> + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> +
> + if (inst->state == VPU_INST_STATE_INIT_SEQ) {
> + if (initial_info->luma_bitdepth != 8) {
> + dev_info(inst->dev->dev, "%s: no support for %d bit depth",
> + __func__, initial_info->luma_bitdepth);
> + goto cleanup_dst_buffers;
> + }
> + }
> + }
> +
> + return ret;
> +
> +free_bitstream_vbuf:
> + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
> +cleanup_dst_buffers:
> + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx)))

This doesn't look right. This just clears the capture queue, instead it
should clear the capture or output queue, depending on V4L2_TYPE_IS_OUTPUT(q->type).

> + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
> + return -EINVAL;
> +}
> +
> +static int streamoff_output(struct vb2_queue *q)
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_v4l2_buffer *buf;
> + int ret;
> + dma_addr_t new_rd_ptr;
> +
> + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
> + dev_dbg(inst->dev->dev, "%s: (Multiplanar) buf type %4u | index %4u\n",
> + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> + }
> +
> + ret = wave5_vpu_flush_instance(inst);
> + if (ret)
> + return ret;
> +
> + /* Reset the ring buffer information */
> + new_rd_ptr = wave5_vpu_dec_get_rd_ptr(inst);
> + inst->last_rd_ptr = new_rd_ptr;
> + inst->codec_info->dec_info.stream_rd_ptr = new_rd_ptr;
> + inst->codec_info->dec_info.stream_wr_ptr = new_rd_ptr;
> +
> + if (v4l2_m2m_has_stopped(m2m_ctx))
> + send_eos_event(inst);
> +
> + /* streamoff on output cancels any draining operation */
> + inst->eos = false;
> +
> + return ret;
> +}
> +
> +static int streamoff_capture(struct vb2_queue *q)
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_v4l2_buffer *buf;
> + unsigned int i;
> + int ret = 0;
> +
> + /* TOOD unregister the framebuffers to restrict access from the firmware to
> + * buffers that are freed up
> + */
> + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) {
> + u32 plane;
> +
> + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
> + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> +
> + for (plane = 0; plane < inst->dst_fmt.num_planes; plane++)
> + vb2_set_plane_payload(&buf->vb2_buf, plane, 0);
> +
> + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> + }
> +
> + for (i = 0; i < inst->dst_buf_count; i++) {
> + ret = wave5_vpu_dec_set_disp_flag(inst, i);
> + if (ret) {
> + dev_dbg(inst->dev->dev,
> + "%s: Setting display flag of buf index: %u, fail: %d\n",
> + __func__, i, ret);
> + }
> + }
> +
> + if (inst->needs_reallocation) {
> + wave5_vpu_dec_give_command(inst, DEC_RESET_FRAMEBUF_INFO, NULL);
> + inst->needs_reallocation = false;
> + }
> +
> + if (v4l2_m2m_has_stopped(m2m_ctx)) {
> + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> + if (ret)
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + bool check_cmd = TRUE;
> +
> + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
> +
> + while (check_cmd) {
> + struct queue_status_info q_status;
> + struct dec_output_info dec_output_info;
> +
> + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
> +
> + if (q_status.report_queue_count == 0)
> + break;
> +
> + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0)
> + break;
> +
> + if (wave5_vpu_dec_get_output_info(inst, &dec_output_info))
> + dev_dbg(inst->dev->dev, "Getting decoding results from fw, fail\n");
> + }
> +
> + v4l2_m2m_update_stop_streaming_state(m2m_ctx, q);
> +
> + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + streamoff_output(q);
> + else
> + streamoff_capture(q);
> +}
> +
> +static const struct vb2_ops wave5_vpu_dec_vb2_ops = {
> + .queue_setup = wave5_vpu_dec_queue_setup,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .buf_queue = wave5_vpu_dec_buf_queue,
> + .start_streaming = wave5_vpu_dec_start_streaming,
> + .stop_streaming = wave5_vpu_dec_stop_streaming,
> +};
> +
> +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
> + struct v4l2_pix_format_mplane *dst_fmt)
> +{
> + unsigned int dst_pix_fmt = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
> + const struct v4l2_format_info *dst_fmt_info = v4l2_format_info(dst_pix_fmt);
> +
> + src_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
> + src_fmt->field = V4L2_FIELD_NONE;
> + src_fmt->flags = 0;
> + src_fmt->num_planes = 1;
> + wave5_update_pix_fmt(src_fmt, 720, 480);
> +
> + dst_fmt->pixelformat = dst_pix_fmt;
> + dst_fmt->field = V4L2_FIELD_NONE;
> + dst_fmt->flags = 0;
> + dst_fmt->num_planes = dst_fmt_info->mem_planes;
> + wave5_update_pix_fmt(dst_fmt, 736, 480);
> +}
> +
> +static int wave5_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
> +{
> + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_dec_vb2_ops);
> +}
> +
> +static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = {
> + .finish_process = wave5_vpu_dec_finish_decode,
> +};
> +
> +static int initialize_sequence(struct vpu_instance *inst)
> +{
> + struct dec_initial_info initial_info;
> + int ret = 0;
> +
> + memset(&initial_info, 0, sizeof(struct dec_initial_info));
> +
> + ret = wave5_vpu_dec_issue_seq_init(inst);
> + if (ret) {
> + dev_dbg(inst->dev->dev, "%s: wave5_vpu_dec_issue_seq_init, fail: %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0)
> + dev_dbg(inst->dev->dev, "%s: failed to call vpu_wait_interrupt()\n", __func__);
> +
> + ret = wave5_vpu_dec_complete_seq_init(inst, &initial_info);
> + if (ret) {
> + dev_dbg(inst->dev->dev, "%s: vpu_dec_complete_seq_init, fail: %d, reason: %u\n",
> + __func__, ret, initial_info.seq_init_err_reason);
> + wave5_handle_src_buffer(inst, initial_info.rd_ptr);
> + } else {
> + handle_dynamic_resolution_change(inst);
> + }
> +
> + return ret;
> +}
> +
> +/* Must be called with state_spinlock */
> +static bool wave5_is_draining_or_eos(struct vpu_instance *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> +
> + return m2m_ctx->is_draining || inst->eos;
> +}
> +
> +static void wave5_vpu_dec_device_run(void *priv)
> +{
> + struct vpu_instance *inst = priv;
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct queue_status_info q_status;
> + u32 fail_res = 0;
> + int ret = 0;
> +
> + dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__);
> +
> + ret = fill_ringbuffer(inst);
> + if (ret) {
> + dev_warn(inst->dev->dev, "Filling ring buffer failed\n");
> + goto finish_job_and_return;
> + }
> +
> + switch (inst->state) {
> + case VPU_INST_STATE_OPEN:
> + ret = initialize_sequence(inst);
> + if (ret) {
> + unsigned long flags;
> +
> + spin_lock_irqsave(&inst->state_spinlock, flags);
> + if (wave5_is_draining_or_eos(inst) &&
> + wave5_last_src_buffer_consumed(m2m_ctx)) {
> + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> +
> + switch_state(inst, VPU_INST_STATE_STOP);
> +
> + if (vb2_is_streaming(dst_vq))
> + send_eos_event(inst);
> + else
> + handle_dynamic_resolution_change(inst);
> +
> + flag_last_buffer_done(inst);
> + }
> + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> + } else {
> + switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> + }
> +
> + break;
> +
> + case VPU_INST_STATE_INIT_SEQ:
> + /*
> + * Do this early, preparing the fb can trigger an IRQ before
> + * we had a chance to switch, which leads to an invalid state
> + * change.
> + */
> + switch_state(inst, VPU_INST_STATE_PIC_RUN);
> +
> + /*
> + * During DRC, the picture decoding remains pending, so just leave the job
> + * active until this decode operation completes.
> + */
> + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
> +
> + /*
> + * The sequence must be analyzed first to calculate the proper
> + * size of the auxiliary buffers.
> + */
> + ret = wave5_prepare_fb(inst);
> + if (ret) {
> + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret);
> + switch_state(inst, VPU_INST_STATE_STOP);
> + break;
> + }
> +
> + if (q_status.instance_queue_count) {
> + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
> + return;
> + }
> +
> + fallthrough;
> + case VPU_INST_STATE_PIC_RUN:
> + ret = start_decode(inst, &fail_res);
> + if (ret) {
> + dev_err(inst->dev->dev,
> + "Frame decoding on m2m context (%p), fail: %d (result: %d)\n",
> + m2m_ctx, ret, fail_res);
> + break;
> + }
> + /* Return so that we leave this job active */
> + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
> + return;
> + default:
> + WARN(1, "Execution of a job in state %s illegal.\n", state_to_str(inst->state));
> + break;
> + }
> +
> +finish_job_and_return:
> + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> +}
> +
> +static void wave5_vpu_dec_job_abort(void *priv)
> +{
> + struct vpu_instance *inst = priv;
> + int ret;
> +
> + ret = switch_state(inst, VPU_INST_STATE_STOP);
> + if (ret)
> + return;
> +
> + ret = wave5_vpu_dec_set_eos_on_firmware(inst);
> + if (ret)
> + dev_warn(inst->dev->dev,
> + "Setting EOS for the bitstream, fail: %d\n", ret);
> +}
> +
> +static int wave5_vpu_dec_job_ready(void *priv)
> +{
> + struct vpu_instance *inst = priv;
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + unsigned long flags;
> + bool ret = false;
> +
> + spin_lock_irqsave(&inst->state_spinlock, flags);
> +
> + switch (inst->state) {
> + case VPU_INST_STATE_NONE:
> + dev_dbg(inst->dev->dev, "Decoder must be open to start queueing M2M jobs!\n");
> + break;
> + case VPU_INST_STATE_OPEN:
> + if (wave5_is_draining_or_eos(inst) || !v4l2_m2m_has_stopped(m2m_ctx) ||
> + v4l2_m2m_num_src_bufs_ready(m2m_ctx) > 0) {
> + ret = true;
> + break;
> + }
> +
> + dev_dbg(inst->dev->dev,
> + "Decoder must be draining or >= 1 OUTPUT queue buffer must be queued!\n");
> + break;
> + case VPU_INST_STATE_INIT_SEQ:
> + case VPU_INST_STATE_PIC_RUN:
> + if (!m2m_ctx->cap_q_ctx.q.streaming) {
> + dev_dbg(inst->dev->dev, "CAPTURE queue must be streaming to queue jobs!\n");
> + break;
> + } else if (v4l2_m2m_num_dst_bufs_ready(m2m_ctx) < (inst->fbc_buf_count - 1)) {
> + dev_dbg(inst->dev->dev,
> + "No capture buffer ready to decode!\n");
> + break;
> + } else if (!wave5_is_draining_or_eos(inst) &&
> + !v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
> + dev_dbg(inst->dev->dev,
> + "No bitstream data to decode!\n");
> + break;
> + }
> + ret = true;
> + break;
> + case VPU_INST_STATE_STOP:
> + dev_dbg(inst->dev->dev, "Decoder is stopped, not running.\n");
> + break;
> + }
> +
> + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> +
> + return ret;
> +}
> +
> +static const struct v4l2_m2m_ops wave5_vpu_dec_m2m_ops = {
> + .device_run = wave5_vpu_dec_device_run,
> + .job_abort = wave5_vpu_dec_job_abort,
> + .job_ready = wave5_vpu_dec_job_ready,
> +};
> +
> +static int wave5_vpu_open_dec(struct file *filp)
> +{
> + struct video_device *vdev = video_devdata(filp);
> + struct vpu_device *dev = video_drvdata(filp);
> + struct vpu_instance *inst = NULL;
> + struct v4l2_m2m_ctx *m2m_ctx;
> + int ret = 0;
> +
> + inst = kzalloc(sizeof(*inst), GFP_KERNEL);
> + if (!inst)
> + return -ENOMEM;
> +
> + inst->dev = dev;
> + inst->type = VPU_INST_TYPE_DEC;
> + inst->ops = &wave5_vpu_dec_inst_ops;
> +
> + spin_lock_init(&inst->state_spinlock);
> +
> + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
> + if (!inst->codec_info)
> + return -ENOMEM;
> +
> + v4l2_fh_init(&inst->v4l2_fh, vdev);
> + filp->private_data = &inst->v4l2_fh;
> + v4l2_fh_add(&inst->v4l2_fh);
> +
> + INIT_LIST_HEAD(&inst->list);
> + list_add_tail(&inst->list, &dev->instances);
> +
> + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_dec_dev;
> + inst->v4l2_fh.m2m_ctx =
> + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_dec_queue_init);
> + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) {
> + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx);
> + goto cleanup_inst;
> + }
> + m2m_ctx = inst->v4l2_fh.m2m_ctx;
> +
> + v4l2_m2m_set_src_buffered(m2m_ctx, true);
> + v4l2_m2m_set_dst_buffered(m2m_ctx, true);
> + /*
> + * We use the M2M job queue to ensure synchronization of steps where
> + * needed, as IOCTLs can occur at anytime and we need to run commands on
> + * the firmware in a specified order.
> + * In order to initialize the sequence on the firmware within an M2M
> + * job, the M2M framework needs to be able to queue jobs before
> + * the CAPTURE queue has been started, because we need the results of the
> + * initialization to properly prepare the CAPTURE queue with the correct
> + * amount of buffers

Add something along the lines of:

By setting ignore_cap_streaming to true the m2m framework will call
job_ready as soon as the OUTPUT queue is streaming, instead of waiting
until both the CAPTURE and OUTPUT queues are streaming.

> + */
> + v4l2_m2m_set_dst_ignore_streaming(m2m_ctx, true);
> +
> + v4l2_ctrl_handler_init(&inst->v4l2_ctrl_hdl, 10);
> + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, NULL,
> + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1);
> +
> + if (inst->v4l2_ctrl_hdl.error) {
> + ret = -ENODEV;
> + goto cleanup_inst;
> + }
> +
> + inst->v4l2_fh.ctrl_handler = &inst->v4l2_ctrl_hdl;
> + v4l2_ctrl_handler_setup(&inst->v4l2_ctrl_hdl);
> +
> + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
> + inst->colorspace = V4L2_COLORSPACE_REC709;
> + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> + inst->hsv_enc = 0;
> + inst->quantization = V4L2_QUANTIZATION_DEFAULT;
> + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> +
> + init_completion(&inst->irq_done);
> + ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
> + if (ret) {
> + dev_err(inst->dev->dev, "failed to allocate fifo\n");
> + goto cleanup_inst;
> + }
> +
> + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
> + if (inst->id < 0) {
> + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id);
> + ret = inst->id;
> + goto cleanup_inst;
> + }
> +
> + wave5_vdi_allocate_sram(inst->dev);
> +
> + return 0;
> +
> +cleanup_inst:
> + wave5_cleanup_instance(inst);
> + return ret;
> +}
> +
> +static int wave5_vpu_dec_release(struct file *filp)
> +{
> + return wave5_vpu_release_device(filp, wave5_vpu_dec_close, "decoder");
> +}
> +
> +static const struct v4l2_file_operations wave5_vpu_dec_fops = {
> + .owner = THIS_MODULE,
> + .open = wave5_vpu_open_dec,
> + .release = wave5_vpu_dec_release,
> + .unlocked_ioctl = video_ioctl2,
> + .poll = v4l2_m2m_fop_poll,
> + .mmap = v4l2_m2m_fop_mmap,
> +};
> +
> +int wave5_vpu_dec_register_device(struct vpu_device *dev)
> +{
> + struct video_device *vdev_dec;
> + int ret;
> +
> + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL);
> + if (!vdev_dec)
> + return -ENOMEM;
> +
> + dev->v4l2_m2m_dec_dev = v4l2_m2m_init(&wave5_vpu_dec_m2m_ops);
> + if (IS_ERR(dev->v4l2_m2m_dec_dev)) {
> + ret = PTR_ERR(dev->v4l2_m2m_dec_dev);
> + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret);
> + return -EINVAL;
> + }
> +
> + dev->video_dev_dec = vdev_dec;
> +
> + strscpy(vdev_dec->name, VPU_DEC_DEV_NAME, sizeof(vdev_dec->name));
> + vdev_dec->fops = &wave5_vpu_dec_fops;
> + vdev_dec->ioctl_ops = &wave5_vpu_dec_ioctl_ops;
> + vdev_dec->release = video_device_release_empty;
> + vdev_dec->v4l2_dev = &dev->v4l2_dev;
> + vdev_dec->vfl_dir = VFL_DIR_M2M;
> + vdev_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
> + vdev_dec->lock = &dev->dev_lock;
> +
> + ret = video_register_device(vdev_dec, VFL_TYPE_VIDEO, -1);
> + if (ret)
> + return ret;
> +
> + video_set_drvdata(vdev_dec, dev);
> +
> + return 0;
> +}
> +
> +void wave5_vpu_dec_unregister_device(struct vpu_device *dev)
> +{
> + video_unregister_device(dev->video_dev_dec);
> + if (dev->v4l2_m2m_dec_dev)
> + v4l2_m2m_release(dev->v4l2_m2m_dec_dev);
> +}
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
> new file mode 100644
> index 000000000000..e8d8e31b0cbe
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
> @@ -0,0 +1,1825 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - encoder interface
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#include "wave5-helper.h"
> +
> +#define VPU_ENC_DEV_NAME "C&M Wave5 VPU encoder"
> +#define VPU_ENC_DRV_NAME "wave5-enc"
> +
> +static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = {
> + [VPU_FMT_TYPE_CODEC] = {
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_H264,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + },
> + [VPU_FMT_TYPE_RAW] = {
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + {
> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
> + .max_width = W5_MAX_ENC_PIC_WIDTH,
> + .min_width = W5_MIN_ENC_PIC_WIDTH,
> + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> + },
> + }
> +};
> +
> +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state)
> +{
> + switch (state) {
> + case VPU_INST_STATE_NONE:
> + goto invalid_state_switch;
> + case VPU_INST_STATE_OPEN:
> + if (inst->state != VPU_INST_STATE_NONE)
> + goto invalid_state_switch;
> + break;
> + case VPU_INST_STATE_INIT_SEQ:
> + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP)
> + goto invalid_state_switch;
> + break;
> + case VPU_INST_STATE_PIC_RUN:
> + if (inst->state != VPU_INST_STATE_INIT_SEQ)
> + goto invalid_state_switch;
> + break;
> + case VPU_INST_STATE_STOP:
> + break;
> + };
> +
> + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n",
> + state_to_str(inst->state), state_to_str(state));
> + inst->state = state;
> + return 0;
> +
> +invalid_state_switch:
> + WARN(1, "Invalid state switch from %s to %s.\n",
> + state_to_str(inst->state), state_to_str(state));
> + return -EINVAL;
> +}
> +
> +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
> + unsigned int height)
> +{
> + switch (pix_mp->pixelformat) {
> + case V4L2_PIX_FMT_YUV420:
> + case V4L2_PIX_FMT_NV12:
> + case V4L2_PIX_FMT_NV21:
> + pix_mp->width = width;
> + pix_mp->height = height;
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height * 3 / 2;
> + break;
> + case V4L2_PIX_FMT_YUV420M:
> + pix_mp->width = width;
> + pix_mp->height = height;
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
> + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
> + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 4;
> + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
> + pix_mp->plane_fmt[2].sizeimage = round_up(width, 32) * height / 4;
> + break;
> + case V4L2_PIX_FMT_NV12M:
> + case V4L2_PIX_FMT_NV21M:
> + pix_mp->width = width;
> + pix_mp->height = height;
> + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
> + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
> + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 2;
> + break;
> + default:
> + pix_mp->width = width;
> + pix_mp->height = height;
> + pix_mp->plane_fmt[0].bytesperline = 0;
> + pix_mp->plane_fmt[0].sizeimage = width * height / 8 * 3;
> + break;
> + }
> +}
> +
> +static int start_encode(struct vpu_instance *inst, u32 *fail_res)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + int ret;
> + struct vb2_v4l2_buffer *src_buf;
> + struct vb2_v4l2_buffer *dst_buf;
> + struct frame_buffer frame_buf;
> + struct enc_param pic_param;
> + u32 stride = ALIGN(inst->dst_fmt.width, 32);
> + u32 luma_size = (stride * inst->dst_fmt.height);
> + u32 chroma_size = ((stride / 2) * (inst->dst_fmt.height / 2));
> +
> + memset(&pic_param, 0, sizeof(struct enc_param));
> + memset(&frame_buf, 0, sizeof(struct frame_buffer));
> +
> + dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
> + if (!dst_buf) {
> + dev_dbg(inst->dev->dev, "%s: No destination buffer found\n", __func__);
> + return -EAGAIN;
> + }
> +
> + pic_param.pic_stream_buffer_addr =
> + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
> + pic_param.pic_stream_buffer_size =
> + vb2_plane_size(&dst_buf->vb2_buf, 0);
> +
> + src_buf = v4l2_m2m_next_src_buf(m2m_ctx);
> + if (!src_buf) {
> + dev_dbg(inst->dev->dev, "%s: No source buffer found\n", __func__);
> + if (m2m_ctx->is_draining)
> + pic_param.src_end_flag = 1;
> + else
> + return -EAGAIN;
> + } else {
> + if (inst->src_fmt.num_planes == 1) {
> + frame_buf.buf_y =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> + frame_buf.buf_cb = frame_buf.buf_y + luma_size;
> + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size;
> + } else if (inst->src_fmt.num_planes == 2) {
> + frame_buf.buf_y =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> + frame_buf.buf_cb =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
> + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size;
> + } else if (inst->src_fmt.num_planes == 3) {
> + frame_buf.buf_y =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> + frame_buf.buf_cb =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
> + frame_buf.buf_cr =
> + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 2);
> + }
> + frame_buf.stride = stride;
> + pic_param.src_idx = src_buf->vb2_buf.index;
> + }
> +
> + pic_param.source_frame = &frame_buf;
> + pic_param.code_option.implicit_header_encode = 1;
> + pic_param.code_option.encode_aud = inst->encode_aud;
> + ret = wave5_vpu_enc_start_one_frame(inst, &pic_param, fail_res);
> + if (ret) {
> + if (*fail_res == WAVE5_SYSERR_QUEUEING_FAIL)
> + return -EINVAL;
> +
> + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame fail: %d\n",
> + __func__, ret);
> + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
> + if (!src_buf) {
> + dev_dbg(inst->dev->dev,
> + "%s: Removing src buf failed, the queue is empty\n",
> + __func__);
> + return -EINVAL;
> + }
> + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
> + if (!dst_buf) {
> + dev_dbg(inst->dev->dev,
> + "%s: Removing dst buf failed, the queue is empty\n",
> + __func__);
> + return -EINVAL;
> + }
> + switch_state(inst, VPU_INST_STATE_STOP);
> + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
> + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
> + } else {
> + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n",
> + __func__);
> + /*
> + * Remove the source buffer from the ready-queue now and finish
> + * it in the videobuf2 framework once the index is returned by the
> + * firmware in finish_encode
> + */
> + if (src_buf)
> + v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index);
> + }
> +
> + return 0;
> +}
> +
> +static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + int ret;
> + struct enc_output_info enc_output_info;
> + struct vb2_v4l2_buffer *src_buf = NULL;
> + struct vb2_v4l2_buffer *dst_buf = NULL;
> +
> + ret = wave5_vpu_enc_get_output_info(inst, &enc_output_info);
> + if (ret) {
> + dev_dbg(inst->dev->dev,
> + "%s: vpu_enc_get_output_info fail: %d reason: %u | info: %u\n",
> + __func__, ret, enc_output_info.error_reason, enc_output_info.warn_info);
> + return;
> + }
> +
> + dev_dbg(inst->dev->dev,
> + "%s: pic_type %i recon_idx %i src_idx %i pic_byte %u pts %llu\n",
> + __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index,
> + enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts);
> +
> + /*
> + * The source buffer will not be found in the ready-queue as it has been
> + * dropped after sending of the encode firmware command, locate it in
> + * the videobuf2 queue directly
> + */
> + if (enc_output_info.enc_src_idx >= 0) {
> + struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx),
> + enc_output_info.enc_src_idx);
> + if (vb->state != VB2_BUF_STATE_ACTIVE)
> + dev_warn(inst->dev->dev,
> + "%s: encoded buffer (%d) was not in ready queue %i.",
> + __func__, enc_output_info.enc_src_idx, vb->state);
> + else
> + src_buf = to_vb2_v4l2_buffer(vb);
> +
> + if (src_buf) {
> + inst->timestamp = src_buf->vb2_buf.timestamp;
> + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> + } else {
> + dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n",
> + __func__, enc_output_info.enc_src_idx);
> + }
> + }
> +
> + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
> + if (enc_output_info.recon_frame_index == RECON_IDX_FLAG_ENC_END) {
> + static const struct v4l2_event vpu_event_eos = {
> + .type = V4L2_EVENT_EOS
> + };
> +
> + if (dst_buf) {
> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
> + dst_buf->field = V4L2_FIELD_NONE;
> + v4l2_m2m_last_buffer_done(m2m_ctx, dst_buf);
> + } else {
> + WARN(1, "A job have been run without destination buffer.\n");
> + }
> +
> + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos);
> +
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> + } else {
> + if (!dst_buf) {
> + dev_warn(inst->dev->dev, "No bitstream buffer.");
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> + return;
> + }
> +
> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_output_info.bitstream_size);
> +
> + dst_buf->vb2_buf.timestamp = inst->timestamp;
> + dst_buf->field = V4L2_FIELD_NONE;
> + if (enc_output_info.pic_type == PIC_TYPE_I) {
> + if (enc_output_info.enc_vcl_nut == 19 ||
> + enc_output_info.enc_vcl_nut == 20)
> + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
> + else
> + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
> + } else if (enc_output_info.pic_type == PIC_TYPE_P) {
> + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
> + } else if (enc_output_info.pic_type == PIC_TYPE_B) {
> + dst_buf->flags |= V4L2_BUF_FLAG_BFRAME;
> + }
> +
> + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
> +
> + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u\n",
> + __func__, enc_output_info.frame_cycle);
> +
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> + }
> +}
> +
> +static int wave5_vpu_enc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
> +{
> + strscpy(cap->driver, VPU_ENC_DRV_NAME, sizeof(cap->driver));
> + strscpy(cap->card, VPU_ENC_DRV_NAME, sizeof(cap->card));
> + strscpy(cap->bus_info, "platform:" VPU_ENC_DRV_NAME, sizeof(cap->bus_info));

This last line should not be needed, I think.

> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize)
> +{
> + const struct vpu_format *vpu_fmt;
> +
> + if (fsize->index)
> + return -EINVAL;
> +
> + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
> + if (!vpu_fmt) {
> + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_RAW]);
> + if (!vpu_fmt)
> + return -EINVAL;
> + }
> +
> + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> + fsize->stepwise.min_width = vpu_fmt->min_width;
> + fsize->stepwise.max_width = vpu_fmt->max_width;
> + fsize->stepwise.step_width = 1;
> + fsize->stepwise.min_height = vpu_fmt->min_height;
> + fsize->stepwise.max_height = vpu_fmt->max_height;
> + fsize->stepwise.step_height = 1;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + const struct vpu_format *vpu_fmt;
> +
> + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
> +
> + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
> + if (!vpu_fmt)
> + return -EINVAL;
> +
> + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->flags = 0;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + const struct vpu_format *vpu_fmt;
> +
> + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
> +
> + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
> + if (!vpu_fmt) {
> + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->dst_fmt.width, inst->dst_fmt.height);
> + } else {
> + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> +
> + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->fmt.pix_mp.num_planes = 1;
> + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> + }
> +
> + f->fmt.pix_mp.flags = 0;
> + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> + f->fmt.pix_mp.colorspace = inst->colorspace;
> + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;

Same issue here with hsv_enc. I won't mention that again.

> + f->fmt.pix_mp.quantization = inst->quantization;
> + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));

Not needed.

> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + int i, ret;
> +
> + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> +
> + ret = wave5_vpu_enc_try_fmt_cap(file, fh, f);
> + if (ret)
> + return ret;
> +
> + inst->dst_fmt.width = f->fmt.pix_mp.width;
> + inst->dst_fmt.height = f->fmt.pix_mp.height;
> + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> + inst->dst_fmt.field = f->fmt.pix_mp.field;
> + inst->dst_fmt.flags = f->fmt.pix_mp.flags;
> + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes;
> + for (i = 0; i < inst->dst_fmt.num_planes; i++) {
> + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> + }
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + int i;
> +
> + f->fmt.pix_mp.width = inst->dst_fmt.width;
> + f->fmt.pix_mp.height = inst->dst_fmt.height;
> + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> + f->fmt.pix_mp.field = inst->dst_fmt.field;
> + f->fmt.pix_mp.flags = inst->dst_fmt.flags;
> + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
> + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline;
> + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage;
> + }
> +
> + f->fmt.pix_mp.colorspace = inst->colorspace;
> + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
> + f->fmt.pix_mp.quantization = inst->quantization;
> + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + const struct vpu_format *vpu_fmt;
> +
> + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
> +
> + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_RAW]);
> + if (!vpu_fmt)
> + return -EINVAL;
> +
> + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->flags = 0;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + const struct vpu_format *vpu_fmt;
> +
> + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> +
> + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + return -EINVAL;
> +
> + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_RAW]);
> + if (!vpu_fmt) {
> + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
> + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
> + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
> + } else {
> + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
> +
> + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> + f->fmt.pix_mp.num_planes = info->mem_planes;
> + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> + }
> +
> + f->fmt.pix_mp.flags = 0;
> + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));

Not needed.

> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + int i, ret;
> +
> + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> +
> + ret = wave5_vpu_enc_try_fmt_out(file, fh, f);
> + if (ret)
> + return ret;
> +
> + inst->src_fmt.width = f->fmt.pix_mp.width;
> + inst->src_fmt.height = f->fmt.pix_mp.height;
> + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> + inst->src_fmt.field = f->fmt.pix_mp.field;
> + inst->src_fmt.flags = f->fmt.pix_mp.flags;
> + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes;
> + for (i = 0; i < inst->src_fmt.num_planes; i++) {
> + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> + }
> +
> + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 ||
> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M) {
> + inst->cbcr_interleave = true;
> + inst->nv21 = false;
> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 ||
> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) {
> + inst->cbcr_interleave = true;
> + inst->nv21 = true;
> + } else {
> + inst->cbcr_interleave = false;
> + inst->nv21 = false;
> + }
> +
> + inst->colorspace = f->fmt.pix_mp.colorspace;
> + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
> + inst->hsv_enc = f->fmt.pix_mp.hsv_enc;
> + inst->quantization = f->fmt.pix_mp.quantization;
> + inst->xfer_func = f->fmt.pix_mp.xfer_func;
> +
> + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> +
> + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target);
> +
> + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> + switch (s->target) {
> + case V4L2_SEL_TGT_CROP_DEFAULT:
> + case V4L2_SEL_TGT_CROP_BOUNDS:
> + s->r.left = 0;
> + s->r.top = 0;
> + s->r.width = inst->dst_fmt.width;
> + s->r.height = inst->dst_fmt.height;
> + break;
> + case V4L2_SEL_TGT_CROP:
> + s->r.left = 0;
> + s->r.top = 0;
> + s->r.width = inst->dst_fmt.width;
> + s->r.height = inst->dst_fmt.height;
> + dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n",
> + __func__, s->r.width, s->r.height);
> + break;
> + default:
> + return -EINVAL;
> + }
> + } else {
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> +
> + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
> + return -EINVAL;
> +
> + if (s->target != V4L2_SEL_TGT_CROP)
> + return -EINVAL;
> +
> + dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n",
> + __func__, s->r.width, s->r.height);
> +
> + s->r.left = 0;
> + s->r.top = 0;
> + s->r.width = inst->src_fmt.width;
> + s->r.height = inst->src_fmt.height;
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + int ret;
> +
> + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec);
> + if (ret)
> + return ret;
> +
> + if (!wave5_vpu_both_queues_are_streaming(inst))
> + return 0;
> +
> + switch (ec->cmd) {
> + case V4L2_ENC_CMD_STOP:
> + if (m2m_ctx->is_draining)
> + return -EBUSY;
> +
> + if (m2m_ctx->has_stopped)
> + return 0;
> +
> + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
> + m2m_ctx->is_draining = true;
> + break;
> + case V4L2_ENC_CMD_START:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> +
> + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type);
> +
> + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + return -EINVAL;
> +
> + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> + a->parm.output.timeperframe.numerator = 1;
> + a->parm.output.timeperframe.denominator = inst->frame_rate;
> +
> + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n",
> + __func__, a->parm.output.timeperframe.numerator,
> + a->parm.output.timeperframe.denominator);
> +
> + return 0;
> +}
> +
> +static int wave5_vpu_enc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> +
> + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type);
> +
> + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + return -EINVAL;
> +
> + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> + if (a->parm.output.timeperframe.denominator && a->parm.output.timeperframe.numerator) {
> + inst->frame_rate = a->parm.output.timeperframe.denominator /
> + a->parm.output.timeperframe.numerator;
> + } else {
> + a->parm.output.timeperframe.numerator = 1;
> + a->parm.output.timeperframe.denominator = inst->frame_rate;
> + }
> +
> + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n",
> + __func__, a->parm.output.timeperframe.numerator,
> + a->parm.output.timeperframe.denominator);
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops wave5_vpu_enc_ioctl_ops = {
> + .vidioc_querycap = wave5_vpu_enc_querycap,
> + .vidioc_enum_framesizes = wave5_vpu_enc_enum_framesizes,
> +
> + .vidioc_enum_fmt_vid_cap = wave5_vpu_enc_enum_fmt_cap,
> + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_enc_s_fmt_cap,
> + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_enc_g_fmt_cap,
> + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_enc_try_fmt_cap,
> +
> + .vidioc_enum_fmt_vid_out = wave5_vpu_enc_enum_fmt_out,
> + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_enc_s_fmt_out,
> + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out,
> + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_enc_try_fmt_out,
> +
> + .vidioc_g_selection = wave5_vpu_enc_g_selection,
> + .vidioc_s_selection = wave5_vpu_enc_s_selection,
> +
> + .vidioc_g_parm = wave5_vpu_enc_g_parm,
> + .vidioc_s_parm = wave5_vpu_enc_s_parm,
> +
> + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> +
> + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
> + .vidioc_encoder_cmd = wave5_vpu_enc_encoder_cmd,
> +
> + .vidioc_subscribe_event = wave5_vpu_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct vpu_instance *inst = wave5_ctrl_to_vpu_inst(ctrl);
> +
> + dev_dbg(inst->dev->dev, "%s: name: %s | value: %d\n", __func__, ctrl->name, ctrl->val);
> +
> + switch (ctrl->id) {
> + case V4L2_CID_MPEG_VIDEO_AU_DELIMITER:
> + inst->encode_aud = ctrl->val;
> + break;
> + case V4L2_CID_HFLIP:
> + inst->mirror_direction |= (ctrl->val << 1);
> + break;
> + case V4L2_CID_VFLIP:
> + inst->mirror_direction |= ctrl->val;
> + break;
> + case V4L2_CID_ROTATE:
> + inst->rot_angle = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
> + inst->vbv_buf_size = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
> + inst->rc_mode = 0;
> + break;
> + case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
> + inst->rc_mode = 1;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_BITRATE:
> + inst->bit_rate = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
> + inst->enc_param.avc_idr_period = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
> + inst->enc_param.independ_slice_mode = ctrl->val;
> + inst->enc_param.avc_slice_mode = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
> + inst->enc_param.independ_slice_mode_arg = ctrl->val;
> + inst->enc_param.avc_slice_arg = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
> + inst->rc_enable = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
> + inst->enc_param.mb_level_rc_enable = ctrl->val;
> + inst->enc_param.cu_level_rc_enable = ctrl->val;
> + inst->enc_param.hvs_qp_enable = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
> + inst->enc_param.profile = HEVC_PROFILE_MAIN;
> + inst->bit_depth = 8;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
> + inst->enc_param.profile = HEVC_PROFILE_STILLPICTURE;
> + inst->enc_param.en_still_picture = 1;
> + inst->bit_depth = 8;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
> + inst->enc_param.profile = HEVC_PROFILE_MAIN10;
> + inst->bit_depth = 10;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
> + inst->enc_param.level = 10 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
> + inst->enc_param.level = 20 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
> + inst->enc_param.level = 21 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
> + inst->enc_param.level = 30 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
> + inst->enc_param.level = 31 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
> + inst->enc_param.level = 40 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
> + inst->enc_param.level = 41 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
> + inst->enc_param.level = 50 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
> + inst->enc_param.level = 51 * 3;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2:
> + inst->enc_param.level = 52 * 3;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
> + inst->enc_param.min_qp_i = ctrl->val;
> + inst->enc_param.min_qp_p = ctrl->val;
> + inst->enc_param.min_qp_b = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
> + inst->enc_param.max_qp_i = ctrl->val;
> + inst->enc_param.max_qp_p = ctrl->val;
> + inst->enc_param.max_qp_b = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
> + inst->enc_param.intra_qp = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED:
> + inst->enc_param.disable_deblk = 1;
> + inst->enc_param.sao_enable = 0;
> + inst->enc_param.lf_cross_slice_boundary_enable = 0;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED:
> + inst->enc_param.disable_deblk = 0;
> + inst->enc_param.sao_enable = 1;
> + inst->enc_param.lf_cross_slice_boundary_enable = 1;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
> + inst->enc_param.disable_deblk = 0;
> + inst->enc_param.sao_enable = 1;
> + inst->enc_param.lf_cross_slice_boundary_enable = 0;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2:
> + inst->enc_param.beta_offset_div2 = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2:
> + inst->enc_param.tc_offset_div2 = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE:
> + inst->enc_param.decoding_refresh_type = 0;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA:
> + inst->enc_param.decoding_refresh_type = 1;
> + break;
> + case V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR:
> + inst->enc_param.decoding_refresh_type = 2;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD:
> + inst->enc_param.intra_period = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU:
> + inst->enc_param.lossless_enable = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED:
> + inst->enc_param.const_intra_pred_flag = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT:
> + inst->enc_param.wpp_enable = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING:
> + inst->enc_param.strong_intra_smooth_enable = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1:
> + inst->enc_param.max_num_merge = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION:
> + inst->enc_param.tmvp_enable = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
> + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
> + inst->enc_param.profile = H264_PROFILE_BP;
> + inst->bit_depth = 8;
> + break;
> + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
> + inst->enc_param.profile = H264_PROFILE_MP;
> + inst->bit_depth = 8;
> + break;
> + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
> + inst->enc_param.profile = H264_PROFILE_EXTENDED;
> + inst->bit_depth = 8;
> + break;
> + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
> + inst->enc_param.profile = H264_PROFILE_HP;
> + inst->bit_depth = 8;
> + break;
> + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
> + inst->enc_param.profile = H264_PROFILE_HIGH10;
> + inst->bit_depth = 10;
> + break;
> + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
> + inst->enc_param.profile = H264_PROFILE_HIGH422;
> + inst->bit_depth = 10;
> + break;
> + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
> + inst->enc_param.profile = H264_PROFILE_HIGH444;
> + inst->bit_depth = 10;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
> + inst->enc_param.level = 10;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
> + inst->enc_param.level = 9;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
> + inst->enc_param.level = 11;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
> + inst->enc_param.level = 12;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
> + inst->enc_param.level = 13;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
> + inst->enc_param.level = 20;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
> + inst->enc_param.level = 21;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
> + inst->enc_param.level = 22;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
> + inst->enc_param.level = 30;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
> + inst->enc_param.level = 31;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
> + inst->enc_param.level = 32;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
> + inst->enc_param.level = 40;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
> + inst->enc_param.level = 41;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
> + inst->enc_param.level = 42;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
> + inst->enc_param.level = 50;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
> + inst->enc_param.level = 51;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
> + inst->enc_param.min_qp_i = ctrl->val;
> + inst->enc_param.min_qp_p = ctrl->val;
> + inst->enc_param.min_qp_b = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
> + inst->enc_param.max_qp_i = ctrl->val;
> + inst->enc_param.max_qp_p = ctrl->val;
> + inst->enc_param.max_qp_b = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
> + inst->enc_param.intra_qp = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
> + switch (ctrl->val) {
> + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED:
> + inst->enc_param.disable_deblk = 1;
> + inst->enc_param.lf_cross_slice_boundary_enable = 1;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED:
> + inst->enc_param.disable_deblk = 0;
> + inst->enc_param.lf_cross_slice_boundary_enable = 1;
> + break;
> + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
> + inst->enc_param.disable_deblk = 0;
> + inst->enc_param.lf_cross_slice_boundary_enable = 0;
> + break;
> + default:
> + return -EINVAL;
> + }
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
> + inst->enc_param.beta_offset_div2 = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
> + inst->enc_param.tc_offset_div2 = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
> + inst->enc_param.transform8x8_enable = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION:
> + inst->enc_param.const_intra_pred_flag = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET:
> + inst->enc_param.chroma_cb_qp_offset = ctrl->val;
> + inst->enc_param.chroma_cr_qp_offset = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
> + inst->enc_param.intra_period = ctrl->val;
> + break;
> + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
> + inst->enc_param.entropy_coding_mode = ctrl->val;
> + break;
> + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops wave5_vpu_enc_ctrl_ops = {
> + .s_ctrl = wave5_vpu_enc_s_ctrl,
> +};
> +
> +static int wave5_vpu_enc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
> + unsigned int *num_planes, unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + struct v4l2_pix_format_mplane inst_format =
> + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
> + unsigned int i;
> +
> + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
> + *num_buffers, *num_planes, q->type);
> +
> + if (*num_planes) {
> + if (inst_format.num_planes != *num_planes)
> + return -EINVAL;
> +
> + for (i = 0; i < *num_planes; i++) {
> + if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
> + return -EINVAL;
> + }
> + } else {
> + *num_planes = inst_format.num_planes;
> + for (i = 0; i < *num_planes; i++) {
> + sizes[i] = inst_format.plane_fmt[i].sizeimage;
> + dev_dbg(inst->dev->dev, "%s: size[%u]: %u\n", __func__, i, sizes[i]);
> + }
> + }
> +
> + dev_dbg(inst->dev->dev, "%s: size: %u\n", __func__, sizes[0]);
> +
> + if (inst->state == VPU_INST_STATE_INIT_SEQ &&
> + q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + if (*num_buffers > inst->src_buf_count &&
> + *num_buffers < WAVE5_MAX_FBS)
> + inst->src_buf_count = *num_buffers;

Same issue as with the decoder. Why is src_buf_count needed at all?

> +
> + *num_buffers = inst->src_buf_count;
> + dev_dbg(inst->dev->dev, "%s: src buf num: %u", __func__, *num_buffers);
> + }
> +
> + return 0;
> +}
> +
> +static void wave5_vpu_enc_buf_queue(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> +
> + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n",
> + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0),
> + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2));
> +
> + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + vbuf->sequence = inst->queued_src_buf_num++;
> + else
> + vbuf->sequence = inst->queued_dst_buf_num++;
> +
> + v4l2_m2m_buf_queue(m2m_ctx, vbuf);
> +}
> +
> +static void wave5_set_enc_openparam(struct enc_open_param *open_param,
> + struct vpu_instance *inst)
> +{
> + struct enc_wave_param input = inst->enc_param;
> + u32 num_ctu_row = ALIGN(inst->dst_fmt.height, 64) / 64;
> + u32 num_mb_row = ALIGN(inst->dst_fmt.height, 16) / 16;
> +
> + open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE;
> + open_param->wave_param.hvs_qp_scale = 2;
> + open_param->wave_param.hvs_max_delta_qp = 10;
> + open_param->wave_param.skip_intra_trans = 1;
> + open_param->wave_param.intra_nx_n_enable = 1;
> + open_param->wave_param.nr_intra_weight_y = 7;
> + open_param->wave_param.nr_intra_weight_cb = 7;
> + open_param->wave_param.nr_intra_weight_cr = 7;
> + open_param->wave_param.nr_inter_weight_y = 4;
> + open_param->wave_param.nr_inter_weight_cb = 4;
> + open_param->wave_param.nr_inter_weight_cr = 4;
> + open_param->wave_param.rdo_skip = 1;
> + open_param->wave_param.lambda_scaling_enable = 1;
> +
> + open_param->line_buf_int_en = true;
> + open_param->pic_width = inst->dst_fmt.width;
> + open_param->pic_height = inst->dst_fmt.height;
> + open_param->frame_rate_info = inst->frame_rate;
> + open_param->rc_enable = inst->rc_enable;
> + if (inst->rc_enable) {
> + open_param->wave_param.initial_rc_qp = -1;
> + open_param->wave_param.rc_weight_param = 16;
> + open_param->wave_param.rc_weight_buf = 128;
> + }
> + open_param->wave_param.mb_level_rc_enable = input.mb_level_rc_enable;
> + open_param->wave_param.cu_level_rc_enable = input.cu_level_rc_enable;
> + open_param->wave_param.hvs_qp_enable = input.hvs_qp_enable;
> + open_param->bit_rate = inst->bit_rate;
> + open_param->vbv_buffer_size = inst->vbv_buf_size;
> + if (inst->rc_mode == 0)
> + open_param->vbv_buffer_size = 3000;
> + open_param->wave_param.profile = input.profile;
> + open_param->wave_param.en_still_picture = input.en_still_picture;
> + open_param->wave_param.level = input.level;
> + open_param->wave_param.internal_bit_depth = inst->bit_depth;
> + open_param->wave_param.intra_qp = input.intra_qp;
> + open_param->wave_param.min_qp_i = input.min_qp_i;
> + open_param->wave_param.max_qp_i = input.max_qp_i;
> + open_param->wave_param.min_qp_p = input.min_qp_p;
> + open_param->wave_param.max_qp_p = input.max_qp_p;
> + open_param->wave_param.min_qp_b = input.min_qp_b;
> + open_param->wave_param.max_qp_b = input.max_qp_b;
> + open_param->wave_param.disable_deblk = input.disable_deblk;
> + open_param->wave_param.lf_cross_slice_boundary_enable =
> + input.lf_cross_slice_boundary_enable;
> + open_param->wave_param.tc_offset_div2 = input.tc_offset_div2;
> + open_param->wave_param.beta_offset_div2 = input.beta_offset_div2;
> + open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type;
> + open_param->wave_param.intra_period = input.intra_period;
> + if (inst->std == W_HEVC_ENC) {
> + if (input.intra_period == 0) {
> + open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR;
> + open_param->wave_param.intra_period = input.avc_idr_period;
> + }
> + } else {
> + open_param->wave_param.avc_idr_period = input.avc_idr_period;
> + }
> + open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode;
> + open_param->wave_param.lossless_enable = input.lossless_enable;
> + open_param->wave_param.const_intra_pred_flag = input.const_intra_pred_flag;
> + open_param->wave_param.wpp_enable = input.wpp_enable;
> + open_param->wave_param.strong_intra_smooth_enable = input.strong_intra_smooth_enable;
> + open_param->wave_param.max_num_merge = input.max_num_merge;
> + open_param->wave_param.tmvp_enable = input.tmvp_enable;
> + open_param->wave_param.transform8x8_enable = input.transform8x8_enable;
> + open_param->wave_param.chroma_cb_qp_offset = input.chroma_cb_qp_offset;
> + open_param->wave_param.chroma_cr_qp_offset = input.chroma_cr_qp_offset;
> + open_param->wave_param.independ_slice_mode = input.independ_slice_mode;
> + open_param->wave_param.independ_slice_mode_arg = input.independ_slice_mode_arg;
> + open_param->wave_param.avc_slice_mode = input.avc_slice_mode;
> + open_param->wave_param.avc_slice_arg = input.avc_slice_arg;
> + open_param->wave_param.intra_mb_refresh_mode = input.intra_mb_refresh_mode;
> + if (input.intra_mb_refresh_mode != REFRESH_MB_MODE_NONE) {
> + if (num_mb_row >= input.intra_mb_refresh_arg)
> + open_param->wave_param.intra_mb_refresh_arg =
> + num_mb_row / input.intra_mb_refresh_arg;
> + else
> + open_param->wave_param.intra_mb_refresh_arg = num_mb_row;
> + }
> + open_param->wave_param.intra_refresh_mode = input.intra_refresh_mode;
> + if (input.intra_refresh_mode != 0) {
> + if (num_ctu_row >= input.intra_refresh_arg)
> + open_param->wave_param.intra_refresh_arg =
> + num_ctu_row / input.intra_refresh_arg;
> + else
> + open_param->wave_param.intra_refresh_arg = num_ctu_row;
> + }
> +}
> +
> +static int initialize_sequence(struct vpu_instance *inst)
> +{
> + struct enc_initial_info initial_info;
> + struct v4l2_ctrl *ctrl;
> + int ret;
> +
> + ret = wave5_vpu_enc_issue_seq_init(inst);
> + if (ret) {
> + dev_err(inst->dev->dev, "%s: wave5_vpu_enc_issue_seq_init, fail: %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0) {
> + dev_err(inst->dev->dev, "%s: wave5_vpu_wait_interrupt failed\n", __func__);
> + return -EINVAL;
> + }
> +
> + ret = wave5_vpu_enc_complete_seq_init(inst, &initial_info);
> + if (ret)
> + return ret;
> +
> + dev_dbg(inst->dev->dev, "%s: min_frame_buffer: %u | min_source_buffer: %u\n",
> + __func__, initial_info.min_frame_buffer_count,
> + initial_info.min_src_frame_count);
> + inst->min_src_buf_count = initial_info.min_src_frame_count +
> + COMMAND_QUEUE_DEPTH;
> +
> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
> + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT);
> + if (ctrl)
> + v4l2_ctrl_s_ctrl(ctrl, inst->min_src_buf_count);
> +
> + inst->fbc_buf_count = initial_info.min_frame_buffer_count;
> + inst->src_buf_count = inst->min_src_buf_count;
> +
> + return 0;
> +}
> +
> +static int prepare_fb(struct vpu_instance *inst)
> +{
> + u32 fb_stride = ALIGN(inst->dst_fmt.width, 32);
> + u32 fb_height = ALIGN(inst->dst_fmt.height, 32);
> + int i, ret = 0;
> +
> + for (i = 0; i < inst->fbc_buf_count; i++) {
> + u32 luma_size = fb_stride * fb_height;
> + u32 chroma_size = ALIGN(fb_stride / 2, 16) * fb_height;
> +
> + inst->frame_vbuf[i].size = luma_size + chroma_size;
> + ret = wave5_vdi_allocate_dma_memory(inst->dev, &inst->frame_vbuf[i]);
> + if (ret < 0) {
> + dev_err(inst->dev->dev, "%s: failed to allocate FBC buffer %zu\n",
> + __func__, inst->frame_vbuf[i].size);
> + goto free_buffers;
> + }
> +
> + inst->frame_buf[i].buf_y = inst->frame_vbuf[i].daddr;
> + inst->frame_buf[i].buf_cb = (dma_addr_t)-1;
> + inst->frame_buf[i].buf_cr = (dma_addr_t)-1;
> + inst->frame_buf[i].update_fb_info = true;
> + inst->frame_buf[i].size = inst->frame_vbuf[i].size;
> + }
> +
> + ret = wave5_vpu_enc_register_frame_buffer(inst, inst->fbc_buf_count, fb_stride,
> + fb_height, COMPRESSED_FRAME_MAP);
> + if (ret) {
> + dev_err(inst->dev->dev,
> + "%s: wave5_vpu_enc_register_frame_buffer, fail: %d\n",
> + __func__, ret);
> + goto free_buffers;
> + }
> +
> + return 0;
> +free_buffers:
> + for (i = 0; i < inst->fbc_buf_count; i++)
> + wave5_vpu_dec_reset_framebuffer(inst, i);
> + return ret;
> +}
> +
> +static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count)
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + int ret;
> +
> + v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
> +
> + if (inst->state == VPU_INST_STATE_NONE && q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> + struct enc_open_param open_param;
> +
> + memset(&open_param, 0, sizeof(struct enc_open_param));
> +
> + inst->std = wave5_to_vpu_std(inst->dst_fmt.pixelformat, inst->type);
> + if (inst->std == STD_UNKNOWN) {
> + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
> + (char *)&inst->dst_fmt.pixelformat);

This should call buf_done(DEQUEUED) for all buffers in the queue on error.

> + return -EINVAL;
> + }
> +
> + wave5_set_enc_openparam(&open_param, inst);
> +
> + ret = wave5_vpu_enc_open(inst, &open_param);
> + if (ret) {
> + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_open, fail: %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + if (inst->mirror_direction) {
> + wave5_vpu_enc_give_command(inst, ENABLE_MIRRORING, NULL);
> + wave5_vpu_enc_give_command(inst, SET_MIRROR_DIRECTION,
> + &inst->mirror_direction);
> + }
> + if (inst->rot_angle) {
> + wave5_vpu_enc_give_command(inst, ENABLE_ROTATION, NULL);
> + wave5_vpu_enc_give_command(inst, SET_ROTATION_ANGLE, &inst->rot_angle);
> + }
> +
> + switch_state(inst, VPU_INST_STATE_OPEN);
> + }
> + if (inst->state == VPU_INST_STATE_OPEN && m2m_ctx->cap_q_ctx.q.streaming) {
> + ret = initialize_sequence(inst);
> + if (ret) {
> + dev_warn(inst->dev->dev, "Sequence not found: %d\n", ret);
> + return ret;
> + }
> + switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> + /*
> + * The sequence must be analyzed first to calculate the proper
> + * size of the auxiliary buffers.
> + */
> + ret = prepare_fb(inst);
> + if (ret) {
> + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret);
> + return ret;
> + }
> +
> + switch_state(inst, VPU_INST_STATE_PIC_RUN);
> + }
> +

The DEQUEUED sequence is done (although not quite correctly) in the decoder's start_streaming,
but not at all here.

> + return 0;
> +}
> +
> +static void streamoff_output(struct vpu_instance *inst, struct vb2_queue *q)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_v4l2_buffer *buf;
> +
> + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
> + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
> + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> + }
> +}
> +
> +static void streamoff_capture(struct vpu_instance *inst, struct vb2_queue *q)
> +{
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + struct vb2_v4l2_buffer *buf;
> +
> + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) {
> + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
> + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> + vb2_set_plane_payload(&buf->vb2_buf, 0, 0);
> + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> + }
> +
> + v4l2_m2m_clear_state(m2m_ctx);
> +}
> +
> +static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q)
> +{
> + struct vpu_instance *inst = vb2_get_drv_priv(q);
> + bool check_cmd = true;
> +
> + /*
> + * Note that we don't need m2m_ctx->next_buf_last for this driver, so we
> + * don't call v4l2_m2m_update_stop_streaming_state().
> + */
> +
> + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
> +
> + if (wave5_vpu_both_queues_are_streaming(inst))
> + switch_state(inst, VPU_INST_STATE_STOP);
> +
> + while (check_cmd) {
> + struct queue_status_info q_status;
> + struct enc_output_info enc_output_info;
> +
> + wave5_vpu_enc_give_command(inst, ENC_GET_QUEUE_STATUS, &q_status);
> +
> + if (q_status.report_queue_count == 0)
> + break;
> +
> + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0)
> + break;
> +
> + if (wave5_vpu_enc_get_output_info(inst, &enc_output_info))
> + dev_dbg(inst->dev->dev, "Getting encoding results from fw, fail\n");
> + }
> +
> + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> + streamoff_output(inst, q);
> + else
> + streamoff_capture(inst, q);
> +}
> +
> +static const struct vb2_ops wave5_vpu_enc_vb2_ops = {
> + .queue_setup = wave5_vpu_enc_queue_setup,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .buf_queue = wave5_vpu_enc_buf_queue,
> + .start_streaming = wave5_vpu_enc_start_streaming,
> + .stop_streaming = wave5_vpu_enc_stop_streaming,
> +};
> +
> +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
> + struct v4l2_pix_format_mplane *dst_fmt)
> +{
> + unsigned int src_pix_fmt = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
> + const struct v4l2_format_info *src_fmt_info = v4l2_format_info(src_pix_fmt);
> +
> + src_fmt->pixelformat = src_pix_fmt;
> + src_fmt->field = V4L2_FIELD_NONE;
> + src_fmt->flags = 0;
> + src_fmt->num_planes = src_fmt_info->mem_planes;
> + wave5_update_pix_fmt(src_fmt, 416, 240);
> +
> + dst_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
> + dst_fmt->field = V4L2_FIELD_NONE;
> + dst_fmt->flags = 0;
> + dst_fmt->num_planes = 1;
> + wave5_update_pix_fmt(dst_fmt, 416, 240);
> +}
> +
> +static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
> +{
> + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_enc_vb2_ops);
> +}
> +
> +static const struct vpu_instance_ops wave5_vpu_enc_inst_ops = {
> + .finish_process = wave5_vpu_enc_finish_encode,
> +};
> +
> +static void wave5_vpu_enc_device_run(void *priv)
> +{
> + struct vpu_instance *inst = priv;
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> + u32 fail_res = 0;
> + int ret = 0;
> +
> + switch (inst->state) {
> + case VPU_INST_STATE_PIC_RUN:
> + ret = start_encode(inst, &fail_res);
> + if (ret) {
> + if (ret == -EINVAL)
> + dev_err(inst->dev->dev,
> + "Frame encoding on m2m context (%p), fail: %d (res: %d)\n",
> + m2m_ctx, ret, fail_res);
> + else if (ret == -EAGAIN)
> + dev_dbg(inst->dev->dev, "Missing buffers for encode, try again\n");
> + break;
> + }
> + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
> + return;
> + default:
> + WARN(1, "Execution of a job in state %s is invalid.\n",
> + state_to_str(inst->state));
> + break;
> + }
> + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
> + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> +}
> +
> +static int wave5_vpu_enc_job_ready(void *priv)
> +{
> + struct vpu_instance *inst = priv;
> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> +
> + switch (inst->state) {
> + case VPU_INST_STATE_NONE:
> + dev_dbg(inst->dev->dev, "Encoder must be open to start queueing M2M jobs!\n");
> + return false;
> + case VPU_INST_STATE_PIC_RUN:
> + if (m2m_ctx->is_draining || v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
> + dev_dbg(inst->dev->dev, "Encoder ready for a job, state: %s\n",
> + state_to_str(inst->state));
> + return true;
> + }
> + fallthrough;
> + default:
> + dev_dbg(inst->dev->dev,
> + "Encoder not ready for a job, state: %s, %s draining, %d src bufs ready\n",
> + state_to_str(inst->state), m2m_ctx->is_draining ? "is" : "is not",
> + v4l2_m2m_num_src_bufs_ready(m2m_ctx));
> + break;
> + }
> + return false;
> +}
> +
> +static const struct v4l2_m2m_ops wave5_vpu_enc_m2m_ops = {
> + .device_run = wave5_vpu_enc_device_run,
> + .job_ready = wave5_vpu_enc_job_ready,
> +};
> +
> +static int wave5_vpu_open_enc(struct file *filp)
> +{
> + struct video_device *vdev = video_devdata(filp);
> + struct vpu_device *dev = video_drvdata(filp);
> + struct vpu_instance *inst = NULL;
> + struct v4l2_ctrl_handler *v4l2_ctrl_hdl;
> + int ret = 0;
> +
> + inst = kzalloc(sizeof(*inst), GFP_KERNEL);
> + if (!inst)
> + return -ENOMEM;
> + v4l2_ctrl_hdl = &inst->v4l2_ctrl_hdl;
> +
> + inst->dev = dev;
> + inst->type = VPU_INST_TYPE_ENC;
> + inst->ops = &wave5_vpu_enc_inst_ops;
> +
> + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
> + if (!inst->codec_info)
> + return -ENOMEM;
> +
> + v4l2_fh_init(&inst->v4l2_fh, vdev);
> + filp->private_data = &inst->v4l2_fh;
> + v4l2_fh_add(&inst->v4l2_fh);
> +
> + INIT_LIST_HEAD(&inst->list);
> + list_add_tail(&inst->list, &dev->instances);
> +
> + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_enc_dev;
> + inst->v4l2_fh.m2m_ctx =
> + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_enc_queue_init);
> + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) {
> + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx);
> + goto cleanup_inst;
> + }
> + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, true);
> +
> + v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
> + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, 0,
> + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
> + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0,
> + V4L2_MPEG_VIDEO_HEVC_LEVEL_1);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
> + 0, 63, 1, 8);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
> + 0, 63, 1, 51);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
> + 0, 63, 1, 30);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE,
> + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0,
> + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2,
> + -6, 6, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2,
> + -6, 6, 1, 0);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE,
> + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, 0,
> + V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD,
> + 0, 2047, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING,
> + 0, 1, 1, 1);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1,
> + 1, 2, 1, 2);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION,
> + 0, 1, 1, 1);
> +
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_PROFILE,
> + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE, 0,
> + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_LEVEL,
> + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 0,
> + V4L2_MPEG_VIDEO_H264_LEVEL_1_0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
> + 0, 63, 1, 8);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
> + 0, 63, 1, 51);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
> + 0, 63, 1, 30);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
> + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0,
> + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
> + -6, 6, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
> + -6, 6, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
> + 0, 1, 1, 1);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET,
> + -12, 12, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
> + 0, 2047, 1, 0);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
> + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, 0,
> + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_AU_DELIMITER,
> + 0, 1, 1, 1);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_HFLIP,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_VFLIP,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_ROTATE,
> + 0, 270, 90, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_VBV_SIZE,
> + 10, 3000, 1, 1000);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
> + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
> + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_BITRATE,
> + 0, 700000000, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_GOP_SIZE,
> + 0, 2047, 1, 0);
> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
> + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB, 0,
> + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
> + 0, 0xFFFF, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
> + 0, 1, 1, 0);
> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1);
> +
> + if (v4l2_ctrl_hdl->error) {
> + ret = -ENODEV;
> + goto cleanup_inst;
> + }
> +
> + inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl;
> + v4l2_ctrl_handler_setup(v4l2_ctrl_hdl);
> +
> + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
> + inst->colorspace = V4L2_COLORSPACE_REC709;
> + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> + inst->hsv_enc = 0;
> + inst->quantization = V4L2_QUANTIZATION_DEFAULT;
> + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> + inst->frame_rate = 30;
> +
> + init_completion(&inst->irq_done);
> + ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
> + if (ret) {
> + dev_err(inst->dev->dev, "Allocating fifo, fail: %d\n", ret);
> + goto cleanup_inst;
> + }
> +
> + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
> + if (inst->id < 0) {
> + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id);
> + ret = inst->id;
> + goto cleanup_inst;
> + }
> +
> + wave5_vdi_allocate_sram(inst->dev);
> +
> + return 0;
> +
> +cleanup_inst:
> + wave5_cleanup_instance(inst);
> + return ret;
> +}
> +
> +static int wave5_vpu_enc_release(struct file *filp)
> +{
> + return wave5_vpu_release_device(filp, wave5_vpu_enc_close, "encoder");
> +}
> +
> +static const struct v4l2_file_operations wave5_vpu_enc_fops = {
> + .owner = THIS_MODULE,
> + .open = wave5_vpu_open_enc,
> + .release = wave5_vpu_enc_release,
> + .unlocked_ioctl = video_ioctl2,
> + .poll = v4l2_m2m_fop_poll,
> + .mmap = v4l2_m2m_fop_mmap,
> +};
> +
> +int wave5_vpu_enc_register_device(struct vpu_device *dev)
> +{
> + struct video_device *vdev_enc;
> + int ret;
> +
> + vdev_enc = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_enc), GFP_KERNEL);
> + if (!vdev_enc)
> + return -ENOMEM;
> +
> + dev->v4l2_m2m_enc_dev = v4l2_m2m_init(&wave5_vpu_enc_m2m_ops);
> + if (IS_ERR(dev->v4l2_m2m_enc_dev)) {
> + ret = PTR_ERR(dev->v4l2_m2m_enc_dev);
> + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret);
> + return -EINVAL;
> + }
> +
> + dev->video_dev_enc = vdev_enc;
> +
> + strscpy(vdev_enc->name, VPU_ENC_DEV_NAME, sizeof(vdev_enc->name));
> + vdev_enc->fops = &wave5_vpu_enc_fops;
> + vdev_enc->ioctl_ops = &wave5_vpu_enc_ioctl_ops;
> + vdev_enc->release = video_device_release_empty;
> + vdev_enc->v4l2_dev = &dev->v4l2_dev;
> + vdev_enc->vfl_dir = VFL_DIR_M2M;
> + vdev_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
> + vdev_enc->lock = &dev->dev_lock;
> +
> + ret = video_register_device(vdev_enc, VFL_TYPE_VIDEO, -1);
> + if (ret)
> + return ret;
> +
> + video_set_drvdata(vdev_enc, dev);
> +
> + return 0;
> +}
> +
> +void wave5_vpu_enc_unregister_device(struct vpu_device *dev)
> +{
> + video_unregister_device(dev->video_dev_enc);
> + if (dev->v4l2_m2m_enc_dev)
> + v4l2_m2m_release(dev->v4l2_m2m_enc_dev);
> +}
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> new file mode 100644
> index 000000000000..a13d968f5d04
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> @@ -0,0 +1,331 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - platform driver
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include "wave5-vpu.h"
> +#include "wave5-regdefine.h"
> +#include "wave5-vpuconfig.h"
> +#include "wave5.h"
> +
> +#define VPU_PLATFORM_DEVICE_NAME "vdec"
> +#define VPU_CLK_NAME "vcodec"
> +
> +#define WAVE5_IS_ENC BIT(0)
> +#define WAVE5_IS_DEC BIT(1)
> +
> +struct wave5_match_data {
> + int flags;
> + const char *fw_name;
> +};
> +
> +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
> +{
> + int ret;
> +
> + ret = wait_for_completion_timeout(&inst->irq_done,
> + msecs_to_jiffies(timeout));
> + if (!ret)
> + return -ETIMEDOUT;
> +
> + reinit_completion(&inst->irq_done);
> +
> + return 0;
> +}
> +
> +static void wave5_vpu_get_interrupt_for_inst(struct vpu_instance *inst, u32 status)
> +{
> + struct vpu_device *dev = inst->dev;
> + u32 seq_done;
> + u32 cmd_done;
> + int val;
> +
> + seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
> + cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
> +
> + if (status & BIT(INT_WAVE5_INIT_SEQ)) {
> + if (seq_done & BIT(inst->id)) {
> + seq_done &= ~BIT(inst->id);
> + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
> + val = BIT(INT_WAVE5_INIT_SEQ);
> + kfifo_in(&inst->irq_status, &val, sizeof(int));
> + }
> + }
> + if (status & BIT(INT_WAVE5_ENC_SET_PARAM)) {
> + if (seq_done & BIT(inst->id)) {
> + seq_done &= ~BIT(inst->id);
> + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
> + val = BIT(INT_WAVE5_ENC_SET_PARAM);
> + kfifo_in(&inst->irq_status, &val, sizeof(int));
> + }
> + }
> + if (status & BIT(INT_WAVE5_DEC_PIC) ||
> + status & BIT(INT_WAVE5_ENC_PIC)) {
> + if (cmd_done & BIT(inst->id)) {
> + cmd_done &= ~BIT(inst->id);
> + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, cmd_done);
> + val = BIT(INT_WAVE5_DEC_PIC);
> + kfifo_in(&inst->irq_status, &val, sizeof(int));
> + }
> + }
> +}
> +
> +static irqreturn_t wave5_vpu_irq(int irq, void *dev_id)
> +{
> + struct vpu_device *dev = dev_id;
> +
> + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
> + struct vpu_instance *inst;
> + u32 irq_status = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
> +
> + list_for_each_entry(inst, &dev->instances, list) {
> + wave5_vpu_get_interrupt_for_inst(inst, irq_status);
> + }
> +
> + wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_status);
> + wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
> +
> + return IRQ_WAKE_THREAD;
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
> +{
> + struct vpu_device *dev = dev_id;
> + struct vpu_instance *inst;
> + int irq_status, ret;
> +
> + list_for_each_entry(inst, &dev->instances, list) {
> + while (kfifo_len(&inst->irq_status)) {
> + ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int));
> + if (!ret)
> + break;
> +
> + if (irq_status == BIT(INT_WAVE5_INIT_SEQ) ||
> + irq_status == BIT(INT_WAVE5_ENC_SET_PARAM))
> + complete(&inst->irq_done);
> + else /* DEC/ENC_PIC */
> + inst->ops->finish_process(inst);
> +
> + wave5_vpu_clear_interrupt(inst, irq_status);
> + }
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
> + u32 *revision)
> +{
> + const struct firmware *fw;
> + int ret;
> + unsigned int product_id;
> +
> + ret = request_firmware(&fw, fw_name, dev);
> + if (ret) {
> + dev_err(dev, "request_firmware, fail: %d\n", ret);
> + return ret;
> + }
> +
> + ret = wave5_vpu_init_with_bitcode(dev, (u8 *)fw->data, fw->size);
> + if (ret) {
> + dev_err(dev, "vpu_init_with_bitcode, fail: %d\n", ret);
> + goto release_fw;
> + }
> + release_firmware(fw);
> +
> + ret = wave5_vpu_get_version_info(dev, revision, &product_id);
> + if (ret) {
> + dev_err(dev, "vpu_get_version_info fail: %d\n", ret);
> + goto err_without_release;
> + }
> +
> + dev_dbg(dev, "%s: enum product_id: %08x, fw revision: %u\n",
> + __func__, product_id, *revision);
> +
> + return 0;
> +
> +release_fw:
> + release_firmware(fw);
> +err_without_release:
> + return ret;
> +}
> +
> +static int wave5_vpu_probe(struct platform_device *pdev)
> +{
> + int ret;
> + struct vpu_device *dev;
> + const struct wave5_match_data *match_data;
> + u32 fw_revision;
> +
> + match_data = device_get_match_data(&pdev->dev);
> + if (!match_data) {
> + dev_err(&pdev->dev, "missing device match data\n");
> + return -EINVAL;
> + }
> +
> + /* physical addresses limited to 32 bits */
> + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
> + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
> +
> + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + dev->vdb_register = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(dev->vdb_register))
> + return PTR_ERR(dev->vdb_register);
> + ida_init(&dev->inst_ida);
> +
> + mutex_init(&dev->dev_lock);
> + mutex_init(&dev->hw_lock);
> + dev_set_drvdata(&pdev->dev, dev);
> + dev->dev = &pdev->dev;
> +
> + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks);
> +
> + /* continue without clock, assume externally managed */
> + if (ret < 0) {
> + dev_warn(&pdev->dev, "Getting clocks, fail: %d\n", ret);
> + ret = 0;
> + }
> + dev->num_clks = ret;
> +
> + ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks);
> + if (ret) {
> + dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret);
> + return ret;
> + }
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "sram-size",
> + &dev->sram_size);
> + if (ret) {
> + dev_warn(&pdev->dev, "sram-size not found\n");
> + dev->sram_size = 0;
> + }
> +
> + dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
> + if (!dev->sram_pool)
> + dev_warn(&pdev->dev, "sram node not found\n");
> +
> + dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER);
> + ret = wave5_vdi_init(&pdev->dev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "wave5_vdi_init, fail: %d\n", ret);
> + goto err_clk_dis;
> + }
> + dev->product = wave5_vpu_get_product_id(dev);
> +
> + INIT_LIST_HEAD(&dev->instances);
> + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret);
> + goto err_vdi_release;
> + }
> +
> + if (match_data->flags & WAVE5_IS_DEC) {
> + ret = wave5_vpu_dec_register_device(dev);
> + if (ret) {
> + dev_err(&pdev->dev, "wave5_vpu_dec_register_device, fail: %d\n", ret);
> + goto err_v4l2_unregister;
> + }
> + }
> + if (match_data->flags & WAVE5_IS_ENC) {
> + ret = wave5_vpu_enc_register_device(dev);
> + if (ret) {
> + dev_err(&pdev->dev, "wave5_vpu_enc_register_device, fail: %d\n", ret);
> + goto err_dec_unreg;
> + }
> + }
> +
> + dev->irq = platform_get_irq(pdev, 0);
> + if (dev->irq < 0) {
> + dev_err(&pdev->dev, "failed to get irq resource\n");
> + ret = -ENXIO;
> + goto err_enc_unreg;
> + }
> +
> + ret = devm_request_threaded_irq(&pdev->dev, dev->irq, wave5_vpu_irq,
> + wave5_vpu_irq_thread, 0, "vpu_irq", dev);
> + if (ret) {
> + dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
> + goto err_enc_unreg;
> + }
> +
> + ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision);
> + if (ret) {
> + dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret);
> + goto err_enc_unreg;
> + }
> +
> + dev_info(&pdev->dev, "Added wave5 driver with caps: %s %s\n",
> + (match_data->flags & WAVE5_IS_ENC) ? "'ENCODE'" : "",
> + (match_data->flags & WAVE5_IS_DEC) ? "'DECODE'" : "");
> + dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code);
> + dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision);
> + return 0;
> +
> +err_enc_unreg:
> + if (match_data->flags & WAVE5_IS_ENC)
> + wave5_vpu_enc_unregister_device(dev);
> +err_dec_unreg:
> + if (match_data->flags & WAVE5_IS_DEC)
> + wave5_vpu_dec_unregister_device(dev);
> +err_v4l2_unregister:
> + v4l2_device_unregister(&dev->v4l2_dev);
> +err_vdi_release:
> + wave5_vdi_release(&pdev->dev);
> +err_clk_dis:
> + clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
> +
> + return ret;
> +}
> +
> +static int wave5_vpu_remove(struct platform_device *pdev)
> +{
> + struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
> +
> + mutex_destroy(&dev->dev_lock);
> + mutex_destroy(&dev->hw_lock);
> + clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
> + wave5_vpu_enc_unregister_device(dev);
> + wave5_vpu_dec_unregister_device(dev);
> + v4l2_device_unregister(&dev->v4l2_dev);
> + wave5_vdi_release(&pdev->dev);
> + ida_destroy(&dev->inst_ida);
> +
> + return 0;
> +}
> +
> +static const struct wave5_match_data wave521c_data = {
> + .flags = WAVE5_IS_ENC | WAVE5_IS_DEC,
> + .fw_name = "cnm/wave521c_k3_codec_fw.bin",
> +};
> +
> +static const struct of_device_id wave5_dt_ids[] = {
> + { .compatible = "cnm,cm521c-vpu", .data = &wave521c_data },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, wave5_dt_ids);
> +
> +static struct platform_driver wave5_vpu_driver = {
> + .driver = {
> + .name = VPU_PLATFORM_DEVICE_NAME,
> + .of_match_table = of_match_ptr(wave5_dt_ids),
> + },
> + .probe = wave5_vpu_probe,
> + .remove = wave5_vpu_remove,
> +};
> +
> +module_platform_driver(wave5_vpu_driver);
> +MODULE_DESCRIPTION("chips&media VPU V4L2 driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
> new file mode 100644
> index 000000000000..f4117c079027
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
> @@ -0,0 +1,83 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - basic types
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +#ifndef __VPU_DRV_H__
> +#define __VPU_DRV_H__
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fh.h>
> +#include <media/videobuf2-v4l2.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/videobuf2-vmalloc.h>
> +#include "wave5-vpuconfig.h"
> +#include "wave5-vpuapi.h"
> +
> +#define VPU_BUF_SYNC_TO_DEVICE 0
> +#define VPU_BUF_SYNC_FROM_DEVICE 1
> +
> +struct vpu_src_buffer {
> + struct v4l2_m2m_buffer v4l2_m2m_buf;
> + struct list_head list;
> + bool consumed;
> +};
> +
> +struct vpu_dst_buffer {
> + struct v4l2_m2m_buffer v4l2_m2m_buf;
> + bool display;
> +};
> +
> +enum vpu_fmt_type {
> + VPU_FMT_TYPE_CODEC = 0,
> + VPU_FMT_TYPE_RAW = 1
> +};
> +
> +struct vpu_format {
> + unsigned int v4l2_pix_fmt;
> + unsigned int max_width;
> + unsigned int min_width;
> + unsigned int max_height;
> + unsigned int min_height;
> +};
> +
> +static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh)
> +{
> + return container_of(vfh, struct vpu_instance, v4l2_fh);
> +}
> +
> +static inline struct vpu_instance *wave5_ctrl_to_vpu_inst(struct v4l2_ctrl *vctrl)
> +{
> + return container_of(vctrl->handler, struct vpu_instance, v4l2_ctrl_hdl);
> +}
> +
> +static inline struct vpu_src_buffer *wave5_to_vpu_src_buf(struct vb2_v4l2_buffer *vbuf)
> +{
> + return container_of(vbuf, struct vpu_src_buffer, v4l2_m2m_buf.vb);
> +}
> +
> +static inline struct vpu_dst_buffer *wave5_to_vpu_dst_buf(struct vb2_v4l2_buffer *vbuf)
> +{
> + return container_of(vbuf, struct vpu_dst_buffer, v4l2_m2m_buf.vb);
> +}
> +
> +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout);
> +
> +int wave5_vpu_dec_register_device(struct vpu_device *dev);
> +void wave5_vpu_dec_unregister_device(struct vpu_device *dev);
> +int wave5_vpu_enc_register_device(struct vpu_device *dev);
> +void wave5_vpu_enc_unregister_device(struct vpu_device *dev);
> +static inline bool wave5_vpu_both_queues_are_streaming(struct vpu_instance *inst)
> +{
> + struct vb2_queue *vq_cap =
> + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> + struct vb2_queue *vq_out =
> + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
> +
> + return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out);
> +}
> +
> +#endif
>

Regards,

Hans

2023-09-22 04:09:23

by Nicolas Dufresne

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

Le mercredi 20 septembre 2023 à 17:13 +0200, Hans Verkuil a écrit :
> On 15/09/2023 23:11, Sebastian Fricke wrote:
> > From: Nas Chung <[email protected]>
> >
> > Add the decoder and encoder implementing the v4l2
> > API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
> >
> > Signed-off-by: Sebastian Fricke <[email protected]>
> > Signed-off-by: Nicolas Dufresne <[email protected]>
> > Signed-off-by: Robert Beckett <[email protected]>
> > Signed-off-by: Dafna Hirschfeld <[email protected]>
> > Signed-off-by: Nas Chung <[email protected]>
> > ---
> > drivers/media/platform/chips-media/Kconfig | 1 +
> > drivers/media/platform/chips-media/Makefile | 1 +
> > drivers/media/platform/chips-media/wave5/Kconfig | 12 +
> > drivers/media/platform/chips-media/wave5/Makefile | 10 +
> > .../platform/chips-media/wave5/wave5-helper.c | 196 ++
> > .../platform/chips-media/wave5/wave5-helper.h | 30 +
> > .../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
> > .../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
> > .../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
> > .../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
> > 10 files changed, 4454 insertions(+)
> >
> > diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig
> > index f87a0d693df7..ad350eb6b1fc 100644
> > --- a/drivers/media/platform/chips-media/Kconfig
> > +++ b/drivers/media/platform/chips-media/Kconfig
> > @@ -3,3 +3,4 @@
> > comment "Chips&Media media platform drivers"
> >
> > source "drivers/media/platform/chips-media/coda/Kconfig"
> > +source "drivers/media/platform/chips-media/wave5/Kconfig"
> > diff --git a/drivers/media/platform/chips-media/Makefile b/drivers/media/platform/chips-media/Makefile
> > index 5ee693f651c1..6b5d99de8b54 100644
> > --- a/drivers/media/platform/chips-media/Makefile
> > +++ b/drivers/media/platform/chips-media/Makefile
> > @@ -1,3 +1,4 @@
> > # SPDX-License-Identifier: GPL-2.0-only
> >
> > obj-y += coda/
> > +obj-y += wave5/
> > diff --git a/drivers/media/platform/chips-media/wave5/Kconfig b/drivers/media/platform/chips-media/wave5/Kconfig
> > new file mode 100644
> > index 000000000000..a3b949356cd5
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/Kconfig
> > @@ -0,0 +1,12 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +config VIDEO_WAVE_VPU
> > + tristate "Chips&Media Wave Codec Driver"
> > + depends on VIDEO_DEV
> > + select VIDEOBUF2_DMA_CONTIG
> > + select VIDEOBUF2_VMALLOC
> > + select V4L2_MEM2MEM_DEV
> > + help
> > + Chips&Media stateful encoder and decoder driver.
> > + The driver supports HEVC and H264 formats.
> > + To compile this driver as modules, choose M here: the
> > + modules will be called wave5.
> > diff --git a/drivers/media/platform/chips-media/wave5/Makefile b/drivers/media/platform/chips-media/wave5/Makefile
> > new file mode 100644
> > index 000000000000..3d738a03bd8e
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/Makefile
> > @@ -0,0 +1,10 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-$(CONFIG_VIDEO_WAVE_VPU) += wave5.o
> > +wave5-objs += wave5-hw.o \
> > + wave5-vpuapi.o \
> > + wave5-vdi.o \
> > + wave5-vpu-dec.o \
> > + wave5-vpu.o \
> > + wave5-vpu-enc.o \
> > + wave5-helper.o
> > diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c
> > new file mode 100644
> > index 000000000000..07b6910bbfee
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
> > @@ -0,0 +1,196 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> > +/*
> > + * Wave5 series multi-standard codec IP - decoder interface
> > + *
> > + * Copyright (C) 2021 CHIPS&MEDIA INC
>
> 2021? Should that be 2021-2023?
>
> > + */
> > +
> > +#include "wave5-helper.h"
> > +
> > +const char *state_to_str(enum vpu_instance_state state)
> > +{
> > + switch (state) {
> > + case VPU_INST_STATE_NONE:
> > + return "NONE";
> > + case VPU_INST_STATE_OPEN:
> > + return "OPEN";
> > + case VPU_INST_STATE_INIT_SEQ:
> > + return "INIT_SEQ";
> > + case VPU_INST_STATE_PIC_RUN:
> > + return "PIC_RUN";
> > + case VPU_INST_STATE_STOP:
> > + return "STOP";
> > + default:
> > + return "UNKNOWN";
> > + }
> > +}
> > +
> > +void wave5_cleanup_instance(struct vpu_instance *inst)
> > +{
> > + int i;
> > +
> > + if (list_is_singular(&inst->list))
> > + wave5_vdi_free_sram(inst->dev);
> > +
> > + for (i = 0; i < inst->fbc_buf_count; i++)
> > + wave5_vpu_dec_reset_framebuffer(inst, i);
> > +
> > + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
> > + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl);
> > + if (inst->v4l2_fh.vdev) {
> > + v4l2_fh_del(&inst->v4l2_fh);
> > + v4l2_fh_exit(&inst->v4l2_fh);
> > + }
> > + list_del_init(&inst->list);
> > + kfifo_free(&inst->irq_status);
> > + ida_free(&inst->dev->inst_ida, inst->id);
> > + kfree(inst->codec_info);
> > + kfree(inst);
> > +}
> > +
> > +int wave5_vpu_release_device(struct file *filp,
> > + int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
> > + char *name)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data);
> > +
> > + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
> > + if (inst->state != VPU_INST_STATE_NONE) {
> > + u32 fail_res;
> > + int ret;
> > +
> > + ret = close_func(inst, &fail_res);
> > + if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) {
> > + dev_err(inst->dev->dev, "%s close failed, device is still running\n",
> > + name);
> > + return -EBUSY;
> > + }
> > + if (ret && ret != -EIO) {
> > + dev_err(inst->dev->dev, "%s close, fail: %d\n", name, ret);
> > + return ret;
> > + }
> > + }
> > +
> > + wave5_cleanup_instance(inst);
> > +
> > + return 0;
> > +}
> > +
> > +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
> > + const struct vb2_ops *ops)
> > +{
> > + struct vpu_instance *inst = priv;
> > + int ret;
> > +
> > + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> > + src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
> > + src_vq->mem_ops = &vb2_dma_contig_memops;
> > + src_vq->ops = ops;
> > + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + src_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
> > + src_vq->drv_priv = inst;
> > + src_vq->lock = &inst->dev->dev_lock;
> > + src_vq->dev = inst->dev->v4l2_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->mem_ops = &vb2_dma_contig_memops;
> > + dst_vq->ops = ops;
> > + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
> > + dst_vq->buf_struct_size = sizeof(struct vpu_src_buffer);
> > + dst_vq->drv_priv = inst;
> > + dst_vq->lock = &inst->dev->dev_lock;
> > + dst_vq->dev = inst->dev->v4l2_dev.dev;
> > + ret = vb2_queue_init(dst_vq);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > +
> > +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + bool is_decoder = inst->type == VPU_INST_TYPE_DEC;
> > +
> > + dev_dbg(inst->dev->dev, "%s: [%s] type: %u id: %u | flags: %u\n", __func__,
> > + is_decoder ? "decoder" : "encoder", sub->type, sub->id, sub->flags);
> > +
> > + switch (sub->type) {
> > + case V4L2_EVENT_EOS:
> > + return v4l2_event_subscribe(fh, sub, 0, NULL);
> > + case V4L2_EVENT_SOURCE_CHANGE:
> > + if (is_decoder)
> > + return v4l2_src_change_event_subscribe(fh, sub);
> > + return -EINVAL;
> > + case V4L2_EVENT_CTRL:
> > + return v4l2_ctrl_subscribe_event(fh, sub);
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + int i;
> > +
> > + f->fmt.pix_mp.width = inst->src_fmt.width;
> > + f->fmt.pix_mp.height = inst->src_fmt.height;
> > + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
> > + f->fmt.pix_mp.field = inst->src_fmt.field;
> > + f->fmt.pix_mp.flags = inst->src_fmt.flags;
> > + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
> > + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
> > + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->src_fmt.plane_fmt[i].bytesperline;
> > + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->src_fmt.plane_fmt[i].sizeimage;
> > + }
> > +
> > + f->fmt.pix_mp.colorspace = inst->colorspace;
> > + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> > + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
> > + f->fmt.pix_mp.quantization = inst->quantization;
> > + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> > +
> > + return 0;
> > +}
> > +
> > +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
> > + const struct vpu_format fmt_list[MAX_FMTS])
> > +{
> > + unsigned int index;
> > +
> > + for (index = 0; index < MAX_FMTS; index++) {
> > + if (fmt_list[index].v4l2_pix_fmt == v4l2_pix_fmt)
> > + return &fmt_list[index];
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
> > + const struct vpu_format fmt_list[MAX_FMTS])
> > +{
> > + if (idx >= MAX_FMTS)
> > + return NULL;
> > +
> > + if (!fmt_list[idx].v4l2_pix_fmt)
> > + return NULL;
> > +
> > + return &fmt_list[idx];
> > +}
> > +
> > +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type)
> > +{
> > + switch (v4l2_pix_fmt) {
> > + case V4L2_PIX_FMT_H264:
> > + return type == VPU_INST_TYPE_DEC ? W_AVC_DEC : W_AVC_ENC;
> > + case V4L2_PIX_FMT_HEVC:
> > + return type == VPU_INST_TYPE_DEC ? W_HEVC_DEC : W_HEVC_ENC;
> > + default:
> > + return STD_UNKNOWN;
> > + }
> > +}
> > diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h
> > new file mode 100644
> > index 000000000000..a55d5503f5b2
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h
> > @@ -0,0 +1,30 @@
> > +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> > +/*
> > + * Wave5 series multi-standard codec IP - basic types
> > + *
> > + * Copyright (C) 2021 CHIPS&MEDIA INC
> > + */
> > +
> > +#ifndef __WAVE_HELPER_H__
> > +#define __WAVE_HELPER_H__
> > +
> > +#include "wave5-vpu.h"
> > +
> > +#define FMT_TYPES 2
> > +#define MAX_FMTS 12
> > +
> > +const char *state_to_str(enum vpu_instance_state state);
> > +void wave5_cleanup_instance(struct vpu_instance *inst);
> > +int wave5_vpu_release_device(struct file *filp,
> > + int (*close_func)(struct vpu_instance *inst, u32 *fail_res),
> > + char *name);
> > +int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
> > + const struct vb2_ops *ops);
> > +int wave5_vpu_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);
> > +int wave5_vpu_g_fmt_out(struct file *file, void *fh, struct v4l2_format *f);
> > +const struct vpu_format *wave5_find_vpu_fmt(unsigned int v4l2_pix_fmt,
> > + const struct vpu_format fmt_list[MAX_FMTS]);
> > +const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
> > + const struct vpu_format fmt_list[MAX_FMTS]);
> > +enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type);
> > +#endif
> > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
> > new file mode 100644
> > index 000000000000..41257a26504b
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
> > @@ -0,0 +1,1965 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> > +/*
> > + * Wave5 series multi-standard codec IP - decoder interface
> > + *
> > + * Copyright (C) 2021 CHIPS&MEDIA INC
> > + */
> > +
> > +#include "wave5-helper.h"
> > +
> > +#define VPU_DEC_DEV_NAME "C&M Wave5 VPU decoder"
> > +#define VPU_DEC_DRV_NAME "wave5-dec"
> > +
> > +#define DEFAULT_SRC_SIZE(width, height) ({ \
> > + (width) * (height) / 8 * 3; \
> > +})
> > +
> > +static const struct vpu_format dec_fmt_list[FMT_TYPES][MAX_FMTS] = {
> > + [VPU_FMT_TYPE_CODEC] = {
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_H264,
> > + .max_width = 8192,
> > + .min_width = 32,
> > + .max_height = 4320,
> > + .min_height = 32,
> > + },
> > + },
> > + [VPU_FMT_TYPE_RAW] = {
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M,
> > + .max_width = 8192,
> > + .min_width = 8,
> > + .max_height = 4320,
> > + .min_height = 8,
> > + },
> > + }
> > +};
> > +
> > +/*
> > + * Make sure that the state switch is allowed and add logging for debugging
> > + * purposes
> > + */
> > +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state)
> > +{
> > + switch (state) {
> > + case VPU_INST_STATE_NONE:
> > + break;
> > + case VPU_INST_STATE_OPEN:
> > + if (inst->state != VPU_INST_STATE_NONE)
> > + goto invalid_state_switch;
> > + goto valid_state_switch;
> > + case VPU_INST_STATE_INIT_SEQ:
> > + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP)
> > + goto invalid_state_switch;
> > + goto valid_state_switch;
> > + case VPU_INST_STATE_PIC_RUN:
> > + if (inst->state != VPU_INST_STATE_INIT_SEQ)
> > + goto invalid_state_switch;
> > + goto valid_state_switch;
> > + case VPU_INST_STATE_STOP:
> > + goto valid_state_switch;
> > + };
>
> Spurious ;
>
> Odd, wouldn't checkpatch.pl --strict warn about that?
>
> > +invalid_state_switch:
> > + WARN(1, "Invalid state switch from %s to %s.\n",
> > + state_to_str(inst->state), state_to_str(state));
> > + return -EINVAL;
> > +valid_state_switch:
> > + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n",
> > + state_to_str(inst->state), state_to_str(state));
> > + inst->state = state;
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
> > +{
> > + int ret;
> > +
> > + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
> > + if (ret) {
> > + dev_err(inst->dev->dev,
> > + "Setting EOS for the bitstream, fail: %d\n", ret);
>
> Is this an error due to a driver problem, or because a bad bitstream is
> fed from userspace? In the first case, dev_err would be right, in the
> second dev_dbg would be more appropriate. Bad userspace input should not
> spam the kernel log in general.

Its the first. To set the EOS flag, a command is sent to the firmware. That
command may never return (timeout) or may report an error. For this specific
command, if that happens we are likely facing firmware of driver problem (or
both).

>
> > + return ret;
> > + }
> > + return 0;
> > +}
> > +
> > +static bool wave5_last_src_buffer_consumed(struct v4l2_m2m_ctx *m2m_ctx)
> > +{
> > + struct vpu_src_buffer *vpu_buf;
> > +
> > + if (!m2m_ctx->last_src_buf)
> > + return false;
> > +
> > + vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf);
> > + return vpu_buf->consumed;
> > +}
> > +
> > +static void wave5_handle_src_buffer(struct vpu_instance *inst, dma_addr_t rd_ptr)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct v4l2_m2m_buffer *buf, *n;
> > + size_t consumed_bytes = 0;
> > +
> > + if (rd_ptr >= inst->last_rd_ptr) {
> > + consumed_bytes = rd_ptr - inst->last_rd_ptr;
> > + } else {
> > + size_t rd_offs = rd_ptr - inst->bitstream_vbuf.daddr;
> > + size_t last_rd_offs = inst->last_rd_ptr - inst->bitstream_vbuf.daddr;
> > +
> > + consumed_bytes = rd_offs + (inst->bitstream_vbuf.size - last_rd_offs);
> > + }
> > +
> > + inst->last_rd_ptr = rd_ptr;
> > + consumed_bytes += inst->remaining_consumed_bytes;
> > +
> > + dev_dbg(inst->dev->dev, "%s: %zu bytes of bitstream was consumed", __func__,
> > + consumed_bytes);
> > +
> > + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) {
> > + struct vb2_v4l2_buffer *src_buf = &buf->vb;
> > + size_t src_size = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
> > +
> > + if (src_size > consumed_bytes)
> > + break;
> > +
> > + dev_dbg(inst->dev->dev, "%s: removing src buffer %i",
> > + __func__, src_buf->vb2_buf.index);
> > + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
> > + inst->timestamp = src_buf->vb2_buf.timestamp;
> > + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> > + consumed_bytes -= src_size;
> > +
> > + /* Handle the case the last bitstream buffer has been picked */
> > + if (src_buf == m2m_ctx->last_src_buf) {
> > + int ret;
> > +
> > + m2m_ctx->last_src_buf = NULL;
> > + ret = wave5_vpu_dec_set_eos_on_firmware(inst);
> > + if (ret)
> > + dev_warn(inst->dev->dev,
> > + "Setting EOS for the bitstream, fail: %d\n", ret);
> > + break;
> > + }
> > + }
> > +
> > + inst->remaining_consumed_bytes = consumed_bytes;
> > +}
> > +
> > +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
> > + unsigned int height)
> > +{
> > + switch (pix_mp->pixelformat) {
> > + case V4L2_PIX_FMT_YUV420:
> > + case V4L2_PIX_FMT_NV12:
> > + case V4L2_PIX_FMT_NV21:
> > + pix_mp->width = round_up(width, 32);
> > + pix_mp->height = round_up(height, 16);
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = width * height * 3 / 2;
> > + break;
> > + case V4L2_PIX_FMT_YUV422P:
> > + case V4L2_PIX_FMT_NV16:
> > + case V4L2_PIX_FMT_NV61:
> > + pix_mp->width = round_up(width, 32);
> > + pix_mp->height = round_up(height, 16);
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = width * height * 2;
> > + break;
> > + case V4L2_PIX_FMT_YUV420M:
> > + pix_mp->width = round_up(width, 32);
> > + pix_mp->height = round_up(height, 16);
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = width * height;
> > + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
> > + pix_mp->plane_fmt[1].sizeimage = width * height / 4;
> > + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
> > + pix_mp->plane_fmt[2].sizeimage = width * height / 4;
> > + break;
> > + case V4L2_PIX_FMT_NV12M:
> > + case V4L2_PIX_FMT_NV21M:
> > + pix_mp->width = round_up(width, 32);
> > + pix_mp->height = round_up(height, 16);
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = width * height;
> > + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[1].sizeimage = width * height / 2;
> > + break;
> > + case V4L2_PIX_FMT_YUV422M:
> > + pix_mp->width = round_up(width, 32);
> > + pix_mp->height = round_up(height, 16);
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = width * height;
> > + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
> > + pix_mp->plane_fmt[1].sizeimage = width * height / 2;
> > + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
> > + pix_mp->plane_fmt[2].sizeimage = width * height / 2;
> > + break;
> > + case V4L2_PIX_FMT_NV16M:
> > + case V4L2_PIX_FMT_NV61M:
> > + pix_mp->width = round_up(width, 32);
> > + pix_mp->height = round_up(height, 16);
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = width * height;
> > + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[1].sizeimage = width * height;
> > + break;
> > + default:
> > + pix_mp->width = width;
> > + pix_mp->height = height;
> > + pix_mp->plane_fmt[0].bytesperline = 0;
> > + pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_SRC_SIZE(width, height),
> > + pix_mp->plane_fmt[0].sizeimage);
> > + break;
> > + }
> > +}
> > +
> > +static int start_decode(struct vpu_instance *inst, u32 *fail_res)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + int ret = 0;
> > +
> > + ret = wave5_vpu_dec_start_one_frame(inst, fail_res);
> > + if (ret) {
> > + struct vb2_v4l2_buffer *src_buf;
> > +
> > + /* FIXME this will have no effect on the firmware at all, and we
> > + * are moving to STOP state, so the remaming src buffer won't be
> > + * consumed. Maybe best here is just to make sure we can avoid
> > + * deadlocking the app ?
> > + */
> > + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
> > + if (src_buf)
> > + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> > + switch_state(inst, VPU_INST_STATE_STOP);
> > +
> > + dev_dbg(inst->dev->dev, "%s: pic run failed / finish job", __func__);
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +/* Must be called with state_spinlock */
> > +static void flag_last_buffer_done(struct vpu_instance *inst)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_v4l2_buffer *vb;
> > + int i;
> > +
> > + vb = v4l2_m2m_dst_buf_remove(m2m_ctx);
> > + if (!vb) {
> > + m2m_ctx->is_draining = true;
> > + m2m_ctx->next_buf_last = true;
> > + return;
> > + }
> > +
> > + for (i = 0; i < vb->vb2_buf.num_planes; i++)
> > + vb2_set_plane_payload(&vb->vb2_buf, i, 0);
> > + vb->field = V4L2_FIELD_NONE;
> > +
> > + v4l2_m2m_last_buffer_done(m2m_ctx, vb);
> > +}
> > +
> > +/* Must be called with state_spinlock */
> > +static void send_eos_event(struct vpu_instance *inst)
> > +{
> > + static const struct v4l2_event vpu_event_eos = {
> > + .type = V4L2_EVENT_EOS
> > + };
> > +
> > + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos);
> > + inst->eos = false;
> > +}
> > +
> > +/* Must be called with state_spinlock */
> > +static int handle_dynamic_resolution_change(struct vpu_instance *inst)
> > +{
> > + struct v4l2_fh *fh = &inst->v4l2_fh;
> > + static const struct v4l2_event vpu_event_src_ch = {
> > + .type = V4L2_EVENT_SOURCE_CHANGE,
> > + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
> > + };
> > + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> > + struct dec_initial_info *initial_info = &inst->codec_info->dec_info.initial_info;
> > +
> > + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad", __func__, &initial_info->rd_ptr);
> > +
> > + dev_dbg(inst->dev->dev, "%s: width: %u height: %u profile: %u | minbuffer: %u\n",
> > + __func__, initial_info->pic_width, initial_info->pic_height,
> > + initial_info->profile, initial_info->min_frame_buffer_count);
> > +
> > + inst->needs_reallocation = true;
> > + inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1;
> > + if (inst->dst_buf_count != inst->fbc_buf_count) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
> > + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
> > + if (ctrl)
> > + v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count);
> > +
> > + inst->dst_buf_count = inst->fbc_buf_count;
> > + }
> > +
> > + if (p_dec_info->initial_info_obtained) {
> > + inst->conf_win.left = initial_info->pic_crop_rect.left;
> > + inst->conf_win.top = initial_info->pic_crop_rect.top;
> > + inst->conf_win.width = initial_info->pic_width -
> > + initial_info->pic_crop_rect.left - initial_info->pic_crop_rect.right;
> > + inst->conf_win.height = initial_info->pic_height -
> > + initial_info->pic_crop_rect.top - initial_info->pic_crop_rect.bottom;
> > +
> > + wave5_update_pix_fmt(&inst->src_fmt, initial_info->pic_width,
> > + initial_info->pic_height);
> > + wave5_update_pix_fmt(&inst->dst_fmt, initial_info->pic_width,
> > + initial_info->pic_height);
> > + }
> > +
> > + v4l2_event_queue_fh(fh, &vpu_event_src_ch);
> > +
> > + return 0;
> > +}
> > +
> > +static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct dec_output_info dec_info;
> > + int ret;
> > + struct vb2_v4l2_buffer *dec_buf = NULL;
> > + struct vb2_v4l2_buffer *disp_buf = NULL;
> > + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> > + struct queue_status_info q_status;
> > +
> > + dev_dbg(inst->dev->dev, "%s: Fetch output info from firmware.", __func__);
> > +
> > + ret = wave5_vpu_dec_get_output_info(inst, &dec_info);
> > + if (ret) {
> > + dev_warn(inst->dev->dev, "%s: could not get output info.", __func__);
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > + return;
> > + }
> > +
> > + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &dec_info.rd_ptr,
> > + &dec_info.wr_ptr);
> > + wave5_handle_src_buffer(inst, dec_info.rd_ptr);
> > +
> > + dev_dbg(inst->dev->dev, "%s: dec_info dec_idx %i disp_idx %i", __func__,
> > + dec_info.index_frame_decoded, dec_info.index_frame_display);
> > +
> > + if (!vb2_is_streaming(dst_vq)) {
> > + dev_dbg(inst->dev->dev, "%s: capture is not streaming..", __func__);
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > + return;
> > + }
> > +
> > + /* Remove decoded buffer from the ready queue now that it has been
> > + * decoded.
> > + */
> > + if (dec_info.index_frame_decoded >= 0) {
> > + struct vb2_buffer *vb = vb2_get_buffer(dst_vq,
> > + dec_info.index_frame_decoded);
> > + if (vb)
> > + dec_buf = to_vb2_v4l2_buffer(vb);
> > + else
> > + dev_warn(inst->dev->dev, "%s: invalid decoded frame index %i",
> > + __func__, dec_info.index_frame_decoded);
> > + }
> > +
> > + if (dec_buf)
> > + dec_buf->vb2_buf.timestamp = inst->timestamp;
> > +
> > + if (dec_info.index_frame_display >= 0) {
> > + disp_buf = v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, dec_info.index_frame_display);
> > + if (!disp_buf)
> > + dev_warn(inst->dev->dev, "%s: invalid display frame index %i",
> > + __func__, dec_info.index_frame_display);
> > + }
> > +
> > + /* If there is anything to display, do that now */
> > + if (disp_buf) {
> > + struct vpu_dst_buffer *dst_vpu_buf = wave5_to_vpu_dst_buf(disp_buf);
> > + int stride = dec_info.disp_frame.stride;
> > + int height = dec_info.disp_pic_height;
> > +
> > + /* FIXME we should calculate that once, and use v4l2-common if
> > + * possible
> > + */
> > + if (inst->dst_fmt.num_planes == 1) {
> > + vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
> > + (stride * height * 3 / 2));
> > + } else if (inst->dst_fmt.num_planes == 2) {
> > + vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
> > + (stride * height));
> > + vb2_set_plane_payload(&disp_buf->vb2_buf, 1,
> > + ((stride / 2) * height));
> > + } else if (inst->dst_fmt.num_planes == 3) {
> > + vb2_set_plane_payload(&disp_buf->vb2_buf, 0,
> > + (stride * height));
> > + vb2_set_plane_payload(&disp_buf->vb2_buf, 1,
> > + ((stride / 2) * (height / 2)));
> > + vb2_set_plane_payload(&disp_buf->vb2_buf, 2,
> > + ((stride / 2) * (height / 2)));
> > + }
> > +
> > + /* TODO implement interlace support */
> > + disp_buf->field = V4L2_FIELD_NONE;
> > + dst_vpu_buf->display = true;
> > + v4l2_m2m_buf_done(disp_buf, VB2_BUF_STATE_DONE);
> > +
> > + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u (payload %lu stride %d height %d)\n",
> > + __func__, dec_info.frame_cycle,
> > + vb2_get_plane_payload(&disp_buf->vb2_buf, 0), stride, height);
> > + }
> > +
> > + if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END ||
> > + dec_info.sequence_changed)) {
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&inst->state_spinlock, flags);
> > + if (!v4l2_m2m_has_stopped(m2m_ctx)) {
> > + switch_state(inst, VPU_INST_STATE_STOP);
> > +
> > + if (dec_info.sequence_changed)
> > + handle_dynamic_resolution_change(inst);
> > + else
> > + send_eos_event(inst);
> > +
> > + flag_last_buffer_done(inst);
> > + }
> > + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> > + }
> > +
> > + /*
> > + * During a resolution change and while draining, the firmware may flush
> > + * the reorder queue regardless of having a matching decoding operation
> > + * pending. Only terminate the job if there are no more IRQ coming.
> > + */
> > + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
> > + if (q_status.report_queue_count == 0 &&
> > + (q_status.instance_queue_count == 0 || dec_info.sequence_changed)) {
> > + dev_dbg(inst->dev->dev, "%s: finishing job.\n", __func__);
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > + }
> > +}
> > +
> > +static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
> > +{
> > + strscpy(cap->driver, VPU_DEC_DRV_NAME, sizeof(cap->driver));
> > + strscpy(cap->card, VPU_DEC_DRV_NAME, sizeof(cap->card));
> > + strscpy(cap->bus_info, "platform:" VPU_DEC_DRV_NAME, sizeof(cap->bus_info));
>
> This shouldn't be needed, bus_info is filled by the v4l2 core.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize)
> > +{
> > + const struct vpu_format *vpu_fmt;
> > +
> > + if (fsize->index)
> > + return -EINVAL;
> > +
> > + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
> > + if (!vpu_fmt) {
> > + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, dec_fmt_list[VPU_FMT_TYPE_RAW]);
> > + if (!vpu_fmt)
> > + return -EINVAL;
> > + }
> > +
> > + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> > + fsize->stepwise.min_width = vpu_fmt->min_width;
> > + fsize->stepwise.max_width = vpu_fmt->max_width;
> > + fsize->stepwise.step_width = 1;
> > + fsize->stepwise.min_height = vpu_fmt->min_height;
> > + fsize->stepwise.max_height = vpu_fmt->max_height;
> > + fsize->stepwise.step_height = 1;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> > +{
> > + const struct vpu_format *vpu_fmt;
> > +
> > + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_RAW]);
> > + if (!vpu_fmt)
> > + return -EINVAL;
> > +
> > + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->flags = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> > + const struct vpu_format *vpu_fmt;
> > + int width, height;
> > +
> > + dev_dbg(inst->dev->dev,
> > + "%s: fourcc: %u width: %u height: %u nm planes: %u colorspace: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
> > +
> > + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> > + return -EINVAL;
>
> This check isn't needed.
>
> > +
> > + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_RAW]);
> > + if (!vpu_fmt) {
> > + width = inst->dst_fmt.width;
> > + height = inst->dst_fmt.height;
> > + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> > + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> > + } else {
> > + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
> > +
> > + width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> > + height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> > + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->fmt.pix_mp.num_planes = info->mem_planes;
> > + }
> > +
> > + if (p_dec_info->initial_info_obtained) {
> > + width = inst->dst_fmt.width;
> > + height = inst->dst_fmt.height;
> > + }
> > +
> > + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> > + f->fmt.pix_mp.flags = 0;
> > + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> > + f->fmt.pix_mp.colorspace = inst->colorspace;
> > + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> > + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
>
> Drop this. hsv_enc and ycbcr_enc are in a union, so copying one is
> sufficient. This might be done elsewhere, so grep for hsv_enc.
>
> > + f->fmt.pix_mp.quantization = inst->quantization;
> > + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> > + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
>
> Not needed, cleared by the core.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + int i, ret;
> > +
> > + dev_dbg(inst->dev->dev,
> > + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
> > +
> > + ret = wave5_vpu_dec_try_fmt_cap(file, fh, f);
> > + if (ret)
> > + return ret;
> > +
> > + inst->dst_fmt.width = f->fmt.pix_mp.width;
> > + inst->dst_fmt.height = f->fmt.pix_mp.height;
> > + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> > + inst->dst_fmt.field = f->fmt.pix_mp.field;
> > + inst->dst_fmt.flags = f->fmt.pix_mp.flags;
> > + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes;
> > + for (i = 0; i < inst->dst_fmt.num_planes; i++) {
> > + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> > + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> > + }
> > +
> > + if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12 ||
> > + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12M) {
> > + inst->cbcr_interleave = true;
> > + inst->nv21 = false;
> > + inst->output_format = FORMAT_420;
> > + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21 ||
> > + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21M) {
> > + inst->cbcr_interleave = true;
> > + inst->nv21 = true;
> > + inst->output_format = FORMAT_420;
> > + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16 ||
> > + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV16M) {
> > + inst->cbcr_interleave = true;
> > + inst->nv21 = false;
> > + inst->output_format = FORMAT_422;
> > + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61 ||
> > + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV61M) {
> > + inst->cbcr_interleave = true;
> > + inst->nv21 = true;
> > + inst->output_format = FORMAT_422;
> > + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422P ||
> > + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_YUV422M) {
> > + inst->cbcr_interleave = false;
> > + inst->nv21 = false;
> > + inst->output_format = FORMAT_422;
> > + } else {
> > + inst->cbcr_interleave = false;
> > + inst->nv21 = false;
> > + inst->output_format = FORMAT_420;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + int i;
> > +
> > + f->fmt.pix_mp.width = inst->dst_fmt.width;
> > + f->fmt.pix_mp.height = inst->dst_fmt.height;
> > + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> > + f->fmt.pix_mp.field = inst->dst_fmt.field;
> > + f->fmt.pix_mp.flags = inst->dst_fmt.flags;
> > + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> > + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
> > + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline;
> > + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage;
> > + }
> > +
> > + f->fmt.pix_mp.colorspace = inst->colorspace;
> > + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> > + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
>
> Ah, another hsv_enc. Just drop all references for hsv_enc, it's not relevant
> for this driver.
>
> > + f->fmt.pix_mp.quantization = inst->quantization;
> > + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + const struct vpu_format *vpu_fmt;
> > +
> > + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
> > +
> > + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
> > + if (!vpu_fmt)
> > + return -EINVAL;
> > +
> > + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | V4L2_FMT_FLAG_COMPRESSED;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + const struct vpu_format *vpu_fmt;
> > +
> > + dev_dbg(inst->dev->dev,
> > + "%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.colorspace, f->fmt.pix_mp.field);
> > +
> > + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + return -EINVAL;
>
> Can be dropped.
>
> > +
> > + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
> > + if (!vpu_fmt) {
> > + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
> > + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
> > + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
> > + } else {
> > + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> > + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> > +
> > + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->fmt.pix_mp.num_planes = 1;
> > + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> > + }
> > +
> > + f->fmt.pix_mp.flags = 0;
> > + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> > + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
>
> Drop this.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + int i, ret;
> > +
> > + dev_dbg(inst->dev->dev,
> > + "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> > +
> > + ret = wave5_vpu_dec_try_fmt_out(file, fh, f);
> > + if (ret)
> > + return ret;
> > +
> > + inst->std = wave5_to_vpu_std(f->fmt.pix_mp.pixelformat, inst->type);
> > + if (inst->std == STD_UNKNOWN) {
> > + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
> > + (char *)&f->fmt.pix_mp.pixelformat);
> > + return -EINVAL;
> > + }
> > +
> > + inst->src_fmt.width = f->fmt.pix_mp.width;
> > + inst->src_fmt.height = f->fmt.pix_mp.height;
> > + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> > + inst->src_fmt.field = f->fmt.pix_mp.field;
> > + inst->src_fmt.flags = f->fmt.pix_mp.flags;
> > + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes;
> > + for (i = 0; i < inst->src_fmt.num_planes; i++) {
> > + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> > + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> > + }
> > +
> > + inst->colorspace = f->fmt.pix_mp.colorspace;
> > + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
> > + inst->hsv_enc = f->fmt.pix_mp.hsv_enc;
> > + inst->quantization = f->fmt.pix_mp.quantization;
> > + inst->xfer_func = f->fmt.pix_mp.xfer_func;
> > +
> > + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target);
> > +
> > + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > + return -EINVAL;
> > + switch (s->target) {
> > + case V4L2_SEL_TGT_COMPOSE_BOUNDS:
> > + case V4L2_SEL_TGT_COMPOSE_PADDED:
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + s->r.width = inst->dst_fmt.width;
> > + s->r.height = inst->dst_fmt.height;
> > + break;
> > + case V4L2_SEL_TGT_COMPOSE:
> > + case V4L2_SEL_TGT_COMPOSE_DEFAULT:
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + if (inst->state > VPU_INST_STATE_OPEN) {
> > + s->r = inst->conf_win;
> > + } else {
> > + s->r.width = inst->src_fmt.width;
> > + s->r.height = inst->src_fmt.height;
> > + }
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > +
> > + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > + return -EINVAL;
> > +
> > + if (s->target != V4L2_SEL_TGT_COMPOSE)
> > + return -EINVAL;
> > +
> > + dev_dbg(inst->dev->dev, "V4L2_SEL_TGT_COMPOSE w: %u h: %u\n",
> > + s->r.width, s->r.height);
> > +
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + s->r.width = inst->dst_fmt.width;
> > + s->r.height = inst->dst_fmt.height;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
> > + struct v4l2_create_buffers *create)
> > +{
> > + struct v4l2_format *f = &create->format;
> > +
> > + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > + return -ENOTTY;
>
> Huh? Why is this needed?

Minimally a comment should be added. The why is that we support CREATE_BUF for
OUTPUT queue (bitstream) but not for CAPTURE queues. This is simply not
supported by Wave5 firmware. Do you have any suggestion how this asymmetry can
be implemented better ?

>
> > +
> > + return v4l2_m2m_ioctl_create_bufs(file, priv, create);
> > +}
> > +
> > +/* Must be called with state_spinlock */
> > +static int wave5_vpu_dec_stop_unlocked(struct vpu_instance *inst)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > +
> > + if (m2m_ctx->is_draining)
> > + return -EBUSY;
> > +
> > + if (inst->state != VPU_INST_STATE_NONE) {
> > + int ret;
> > +
> > + ret = wave5_vpu_dec_set_eos_on_firmware(inst);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + /*
> > + * Used to remember the EOS state after the streamoff/on transition on
> > + * the capture queue.
> > + */
> > + inst->eos = true;
> > +
> > + if (m2m_ctx->has_stopped)
> > + return 0;
> > +
> > + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
> > + m2m_ctx->is_draining = true;
> > +
> > + /*
> > + * Deferred to device run in case it wasn't in the ring buffer
> > + * yet. In other case, we have to send the EOS signal to the
> > + * firmware so that any pending PIC_RUN ends without new
> > + * bitstream buffer.
> > + */
> > + if (m2m_ctx->last_src_buf)
> > + return 0;
> > +
> > + if (inst->state == VPU_INST_STATE_NONE) {
> > + send_eos_event(inst);
> > + flag_last_buffer_done(inst);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/* Must be called with state_spinlock */
> > +static int wave5_vpu_dec_start_unlocked(struct vpu_instance *inst)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> > +
> > + if (m2m_ctx->is_draining)
> > + return -EBUSY;
> > +
> > + if (m2m_ctx->has_stopped)
> > + m2m_ctx->has_stopped = false;
> > +
> > + vb2_clear_last_buffer_dequeued(dst_vq);
> > + inst->eos = false;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + unsigned long flags;
> > + int ret;
> > +
> > + dev_dbg(inst->dev->dev, "decoder command: %u\n", dc->cmd);
> > +
> > + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc);
> > + if (ret)
> > + return ret;
> > +
> > + switch (dc->cmd) {
> > + case V4L2_DEC_CMD_STOP:
> > + spin_lock_irqsave(&inst->state_spinlock, flags);
> > + ret = wave5_vpu_dec_stop_unlocked(inst);
> > + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> > +
> > + /* Just in case we don't have anything to decode anymore */
> > + v4l2_m2m_try_schedule(m2m_ctx);
> > + break;
> > + case V4L2_DEC_CMD_START:
> > + spin_lock_irqsave(&inst->state_spinlock, flags);
> > + ret = wave5_vpu_dec_start_unlocked(inst);
> > + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static const struct v4l2_ioctl_ops wave5_vpu_dec_ioctl_ops = {
> > + .vidioc_querycap = wave5_vpu_dec_querycap,
> > + .vidioc_enum_framesizes = wave5_vpu_dec_enum_framesizes,
> > +
> > + .vidioc_enum_fmt_vid_cap = wave5_vpu_dec_enum_fmt_cap,
> > + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_dec_s_fmt_cap,
> > + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_dec_g_fmt_cap,
> > + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_dec_try_fmt_cap,
> > +
> > + .vidioc_enum_fmt_vid_out = wave5_vpu_dec_enum_fmt_out,
> > + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_dec_s_fmt_out,
> > + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out,
> > + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_dec_try_fmt_out,
> > +
> > + .vidioc_g_selection = wave5_vpu_dec_g_selection,
> > + .vidioc_s_selection = wave5_vpu_dec_s_selection,
> > +
> > + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> > + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> > + .vidioc_create_bufs = wave5_vpu_dec_create_bufs,
> > + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> > + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> > + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> > + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> > + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> > + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> > +
> > + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
> > + .vidioc_decoder_cmd = wave5_vpu_dec_decoder_cmd,
> > +
> > + .vidioc_subscribe_event = wave5_vpu_subscribe_event,
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > +};
> > +
> > +static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
> > + unsigned int *num_planes, unsigned int sizes[],
> > + struct device *alloc_devs[])
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + struct v4l2_pix_format_mplane inst_format =
> > + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
> > + unsigned int i;
> > +
> > + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
> > + *num_buffers, *num_planes, q->type);
> > +
> > + /* the CREATE_BUFS case */
> > + if (*num_planes) {
> > + if (inst_format.num_planes != *num_planes)
> > + return -EINVAL;
> > +
> > + for (i = 0; i < *num_planes; i++) {
> > + if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
> > + return -EINVAL;
> > + }
> > + /* the REQBUFS case */
> > + } else {
> > + *num_planes = inst_format.num_planes;
> > +
> > + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> > + sizes[0] = inst_format.plane_fmt[0].sizeimage;
> > + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
> > + } else if (*num_planes == 1) {
> > + if (inst->output_format == FORMAT_422)
> > + sizes[0] = inst_format.width * inst_format.height * 2;
> > + else
> > + sizes[0] = inst_format.width * inst_format.height * 3 / 2;
> > + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
> > + } else if (*num_planes == 2) {
> > + sizes[0] = inst_format.width * inst_format.height;
> > + if (inst->output_format == FORMAT_422)
> > + sizes[1] = inst_format.width * inst_format.height;
> > + else
> > + sizes[1] = inst_format.width * inst_format.height / 2;
> > + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n",
> > + __func__, sizes[0], sizes[1]);
> > + } else if (*num_planes == 3) {
> > + sizes[0] = inst_format.width * inst_format.height;
> > + if (inst->output_format == FORMAT_422) {
> > + sizes[1] = inst_format.width * inst_format.height / 2;
> > + sizes[2] = inst_format.width * inst_format.height / 2;
> > + } else {
> > + sizes[1] = inst_format.width * inst_format.height / 4;
> > + sizes[2] = inst_format.width * inst_format.height / 4;
> > + }
> > + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n",
> > + __func__, sizes[0], sizes[1], sizes[2]);
> > + }
> > + }
> > +
> > + if (inst->state == VPU_INST_STATE_INIT_SEQ &&
> > + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> > + if (*num_buffers > inst->dst_buf_count &&
> > + *num_buffers < WAVE5_MAX_FBS)
> > + inst->dst_buf_count = *num_buffers;
>
> In the create_bufs case, *num_buffers is the number of buffers you are
> adding to the already existing buffers. Frankly, the logic here is
> dubious. I'm not sure what the intent is. Why do you need to keep track
> of the buf count at all when the vb2_queue already does that?

This needs to be cleaned up. CREATE_BUFS case is not supported for capture, and
so there is no such weirdo case second calls for that queue at least. Meanwhile,
inst->dst_buf_count is used a MIN_BUFFERS_FOR_CAPTURE initially, and the number
of allocated buffer later. I think it would be better to simply store the min,
and use the queue to track the number of allocated buffers.

In this diver, the reference frame are stored separately, and compressed. The
capture queue contains the display frame. There is a gap when comes time to
transfer timestamp, and for this reason we had to keep the two fbc_count equal.
We classified this as hardware issue and moved on.

I think the dst_buf_count can be dropped now and the "*num_buffers > inst-
>dst_buf_count" not longer make any sense.

>
> WAVE5_MAX_FBS == 32, which is VIDEO_MAX_FRAMES. You can just drop the check
> against WAVE5_MAX_FBS since the core ensures already it will never allocate
> more than VIDEO_MAX_FRAMES.
>
> I'm not sure why WAVE5_MAX_FBS is defined at all, when it is just equal to
> VIDEO_MAX_FRAMES. Perhaps it can be replaced everywhere with VIDEO_MAX_FRAMES?

That is more challenging changes, since VIDEO_MAX_FRAMES is a software
limitation, but WAVE5_MAX_FBS is a hardware limitation. Buffer index only have 4
bits on this hardware. And the marking of frame being used for display is using
a 32bit flag. Considering there is effort to lift that software limitation, it
seems ill advised to completely stop ensuring this HW limit is respected.

I'm open for suggestions.

>
> > +
> > + *num_buffers = inst->dst_buf_count;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_prepare_fb(struct vpu_instance *inst)
> > +{
> > + int linear_num;
> > + int non_linear_num;
> > + int fb_stride = 0, fb_height = 0;
> > + int luma_size, chroma_size;
> > + int ret, i;
> > + struct v4l2_m2m_buffer *buf, *n;
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > +
> > + linear_num = inst->dst_buf_count;
> > + non_linear_num = inst->fbc_buf_count;
> > +
> > + for (i = 0; i < non_linear_num; i++) {
> > + struct frame_buffer *frame = &inst->frame_buf[i];
> > + struct vpu_buf *vframe = &inst->frame_vbuf[i];
> > +
> > + fb_stride = inst->dst_fmt.width;
> > + fb_height = ALIGN(inst->dst_fmt.height, 32);
> > + luma_size = fb_stride * fb_height;
> > +
> > + /* FIXME shall we do /4 in case of 422 ?*/
> > + chroma_size = ALIGN(fb_stride / 2, 16) * fb_height;
> > +
> > + if (vframe->size) {
> > + if (vframe->size != (luma_size + chroma_size))
> > + wave5_vpu_dec_reset_framebuffer(inst, i);
> > + else
> > + continue;
> > + }
> > +
> > + vframe->size = luma_size + chroma_size;
> > + ret = wave5_vdi_allocate_dma_memory(inst->dev, vframe);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Allocating FBC buf of size %zu, fail: %d\n",
> > + __func__, vframe->size, ret);
> > + return ret;
> > + }
> > +
> > + frame->buf_y = vframe->daddr;
> > + frame->buf_cb = vframe->daddr + luma_size;
> > + frame->buf_cr = (dma_addr_t)-1;
> > + frame->size = vframe->size;
> > + frame->width = inst->src_fmt.width;
> > + frame->stride = fb_stride;
> > + frame->map_type = COMPRESSED_FRAME_MAP;
> > + frame->update_fb_info = true;
> > + }
> > + /* In case the count has reduced, clean up leftover framebuffer memory */
> > + for (i = non_linear_num; i < MAX_REG_FRAME; i++) {
> > + ret = wave5_vpu_dec_reset_framebuffer(inst, i);
> > + if (ret)
> > + break;
> > + }
> > +
> > + for (i = 0; i < linear_num; i++) {
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> > + struct vb2_buffer *vb = vb2_get_buffer(dst_vq, i);
> > + struct frame_buffer *frame = &inst->frame_buf[non_linear_num + i];
> > + dma_addr_t buf_addr_y = 0, buf_addr_cb = 0, buf_addr_cr = 0;
> > + u32 buf_size = 0;
> > + u32 fb_stride = inst->dst_fmt.width;
> > + u32 luma_size = fb_stride * inst->dst_fmt.height;
> > + u32 chroma_size;
> > +
> > + if (inst->output_format == FORMAT_422)
> > + chroma_size = fb_stride * inst->dst_fmt.height / 2;
> > + else
> > + chroma_size = fb_stride * inst->dst_fmt.height / 4;
> > +
> > + if (inst->dst_fmt.num_planes == 1) {
> > + buf_size = vb2_plane_size(vb, 0);
> > + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
> > + buf_addr_cb = buf_addr_y + luma_size;
> > + buf_addr_cr = buf_addr_cb + chroma_size;
> > + } else if (inst->dst_fmt.num_planes == 2) {
> > + buf_size = vb2_plane_size(vb, 0) +
> > + vb2_plane_size(vb, 1);
> > + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
> > + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1);
> > + buf_addr_cr = buf_addr_cb + chroma_size;
> > + } else if (inst->dst_fmt.num_planes == 3) {
> > + buf_size = vb2_plane_size(vb, 0) +
> > + vb2_plane_size(vb, 1) +
> > + vb2_plane_size(vb, 2);
> > + buf_addr_y = vb2_dma_contig_plane_dma_addr(vb, 0);
> > + buf_addr_cb = vb2_dma_contig_plane_dma_addr(vb, 1);
> > + buf_addr_cr = vb2_dma_contig_plane_dma_addr(vb, 2);
> > + }
> > +
> > + frame->buf_y = buf_addr_y;
> > + frame->buf_cb = buf_addr_cb;
> > + frame->buf_cr = buf_addr_cr;
> > + frame->size = buf_size;
> > + frame->width = inst->src_fmt.width;
> > + frame->stride = fb_stride;
> > + frame->map_type = LINEAR_FRAME_MAP;
> > + frame->update_fb_info = true;
> > + }
> > +
> > + ret = wave5_vpu_dec_register_frame_buffer_ex(inst, non_linear_num, linear_num,
> > + fb_stride, inst->dst_fmt.height);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev, "%s: vpu_dec_register_frame_buffer_ex fail: %d",
> > + __func__, ret);
> > + return ret;
> > + }
> > +
> > + /*
> > + * Mark all frame buffers as out of display, to avoid using them before
> > + * the application have them queued.
> > + */
> > + for (i = 0; i < inst->dst_buf_count; i++) {
> > + ret = wave5_vpu_dec_set_disp_flag(inst, i);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Setting display flag of buf index: %u, fail: %d\n",
> > + __func__, i, ret);
> > + }
> > + }
> > +
> > + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) {
> > + struct vb2_v4l2_buffer *vbuf = &buf->vb;
> > +
> > + ret = wave5_vpu_dec_clr_disp_flag(inst, vbuf->vb2_buf.index);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Clearing display flag of buf index: %u, fail: %d\n",
> > + __func__, i, ret);
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int write_to_ringbuffer(struct vpu_instance *inst, void *buffer, size_t buffer_size,
> > + struct vpu_buf *ring_buffer, dma_addr_t wr_ptr)
> > +{
> > + size_t size;
> > + size_t offset = wr_ptr - ring_buffer->daddr;
> > + int ret;
> > +
> > + if (wr_ptr + buffer_size > ring_buffer->daddr + ring_buffer->size) {
> > + size = ring_buffer->daddr + ring_buffer->size - wr_ptr;
> > + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer, size);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, 0, (u8 *)buffer + size,
> > + buffer_size - size);
> > + if (ret < 0)
> > + return ret;
> > + } else {
> > + ret = wave5_vdi_write_memory(inst->dev, ring_buffer, offset, (u8 *)buffer,
> > + buffer_size);
> > + if (ret < 0)
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int fill_ringbuffer(struct vpu_instance *inst)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct v4l2_m2m_buffer *buf, *n;
> > + int ret;
> > +
> > + if (m2m_ctx->last_src_buf) {
> > + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf);
> > +
> > + if (vpu_buf->consumed) {
> > + dev_dbg(inst->dev->dev, "last src buffer already written\n");
> > + return 0;
> > + }
> > + }
> > +
> > + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) {
> > + struct vb2_v4l2_buffer *vbuf = &buf->vb;
> > + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf);
> > + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf;
> > + size_t src_size = vb2_get_plane_payload(&vbuf->vb2_buf, 0);
> > + void *src_buf = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
> > + dma_addr_t rd_ptr = 0;
> > + dma_addr_t wr_ptr = 0;
> > + size_t remain_size = 0;
> > +
> > + if (vpu_buf->consumed) {
> > + dev_dbg(inst->dev->dev, "already copied src buf (%u) to the ring buffer\n",
> > + vbuf->vb2_buf.index);
> > + continue;
> > + }
> > +
> > + if (!src_buf) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Acquiring kernel pointer to src buf (%u), fail\n",
> > + __func__, vbuf->vb2_buf.index);
> > + break;
> > + }
> > +
> > + ret = wave5_vpu_dec_get_bitstream_buffer(inst, &rd_ptr, &wr_ptr, &remain_size);
> > + if (ret) {
> > + /* Unable to acquire the mutex */
> > + dev_err(inst->dev->dev, "Getting the bitstream buffer, fail: %d\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + dev_dbg(inst->dev->dev, "%s: rd_ptr %pad wr_ptr %pad", __func__, &rd_ptr, &wr_ptr);
> > +
> > + if (remain_size < src_size) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: remaining size: %zu < source size: %zu for src buf (%u)\n",
> > + __func__, remain_size, src_size, vbuf->vb2_buf.index);
> > + break;
> > + }
> > +
> > + ret = write_to_ringbuffer(inst, src_buf, src_size, ring_buffer, wr_ptr);
> > + if (ret) {
> > + dev_err(inst->dev->dev, "Write src buf (%u) to ring buffer, fail: %d\n",
> > + vbuf->vb2_buf.index, ret);
> > + return ret;
> > + }
> > +
> > + ret = wave5_vpu_dec_update_bitstream_buffer(inst, src_size);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev,
> > + "update_bitstream_buffer fail: %d for src buf (%u)\n",
> > + ret, vbuf->vb2_buf.index);
> > + break;
> > + }
> > +
> > + vpu_buf->consumed = true;
> > +
> > + /* Don't write buffers passed the last one while draining. */
> > + if (v4l2_m2m_is_last_draining_src_buf(m2m_ctx, vbuf)) {
> > + dev_dbg(inst->dev->dev, "last src buffer written to the ring bufferur\n");
> > + break;
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb)
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > + struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf);
> > +
> > + vpu_buf->consumed = false;
> > + vbuf->sequence = inst->queued_src_buf_num++;
> > +
> > + v4l2_m2m_buf_queue(m2m_ctx, vbuf);
> > +}
> > +
> > +static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb)
> > +{
> > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > +
> > + vbuf->sequence = inst->queued_dst_buf_num++;
> > +
> > + if (inst->state == VPU_INST_STATE_PIC_RUN) {
> > + struct vpu_dst_buffer *vpu_buf = wave5_to_vpu_dst_buf(vbuf);
> > + int ret;
> > +
> > + /*
> > + * The buffer is already register, just clear the display flag
>
> register -> registered
>
> > + * to let the firmware know it can be used.
> > + */
> > + vpu_buf->display = false;
> > + ret = wave5_vpu_dec_clr_disp_flag(inst, vb->index);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Clearing the display flag of buffer index: %u, fail: %d\n",
> > + __func__, vb->index, ret);
> > + }
> > + }
> > +
> > + if (vb2_is_streaming(vb->vb2_queue) && v4l2_m2m_dst_buf_is_last(m2m_ctx)) {
> > + unsigned int i;
> > +
> > + for (i = 0; i < vb->num_planes; i++)
> > + vb2_set_plane_payload(vb, i, 0);
> > +
> > + vbuf->field = V4L2_FIELD_NONE;
> > +
> > + send_eos_event(inst);
> > + v4l2_m2m_last_buffer_done(m2m_ctx, vbuf);
> > + } else {
> > + v4l2_m2m_buf_queue(m2m_ctx, vbuf);
> > + }
> > +}
> > +
> > +static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb)
> > +{
> > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n",
> > + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0),
> > + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2));
> > +
> > + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + wave5_vpu_dec_buf_queue_src(vb);
> > + else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> > + wave5_vpu_dec_buf_queue_dst(vb);
> > +}
> > +
> > +static int wave5_vpu_dec_allocate_ring_buffer(struct vpu_instance *inst)
> > +{
> > + int ret;
> > + struct vpu_buf *ring_buffer = &inst->bitstream_vbuf;
> > +
> > + ring_buffer->size = ALIGN(inst->src_fmt.plane_fmt[0].sizeimage, 1024) * 4;
> > + ret = wave5_vdi_allocate_dma_memory(inst->dev, ring_buffer);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev, "%s: allocate ring buffer of size %zu fail: %d\n",
> > + __func__, ring_buffer->size, ret);
> > + return ret;
> > + }
> > +
> > + inst->last_rd_ptr = ring_buffer->daddr;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count)
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_v4l2_buffer *buf;
> > + int ret = 0;
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
> > +
> > + v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
> > +
> > + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && inst->state == VPU_INST_STATE_NONE) {
> > + struct dec_open_param open_param;
> > +
> > + memset(&open_param, 0, sizeof(struct dec_open_param));
> > +
> > + ret = wave5_vpu_dec_allocate_ring_buffer(inst);
> > + if (ret)
> > + return ret;
>
> If there is an error during start_streaming, then all buffers of the queue must
> be returned to state DEQUEUED. That doesn't happen here.
>
> > +
> > + inst->std = wave5_to_vpu_std(inst->src_fmt.pixelformat, inst->type);
> > + if (inst->std == STD_UNKNOWN) {
> > + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
> > + (char *)&inst->src_fmt.pixelformat);
>
> How can this happen? This is very weird to see inside a start_streaming callback.
>
> > + goto free_bitstream_vbuf;
> > + }
> > + open_param.bitstream_buffer = inst->bitstream_vbuf.daddr;
> > + open_param.bitstream_buffer_size = inst->bitstream_vbuf.size;
> > +
> > + ret = wave5_vpu_dec_open(inst, &open_param);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev, "%s: decoder opening, fail: %d\n",
> > + __func__, ret);
> > + goto free_bitstream_vbuf;
> > + }
> > +
> > + ret = switch_state(inst, VPU_INST_STATE_OPEN);
> > + if (ret)
> > + goto free_bitstream_vbuf;
> > + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> > + struct dec_initial_info *initial_info =
> > + &inst->codec_info->dec_info.initial_info;
> > +
> > + if (inst->state == VPU_INST_STATE_STOP)
> > + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> > +
> > + if (inst->state == VPU_INST_STATE_INIT_SEQ) {
> > + if (initial_info->luma_bitdepth != 8) {
> > + dev_info(inst->dev->dev, "%s: no support for %d bit depth",
> > + __func__, initial_info->luma_bitdepth);
> > + goto cleanup_dst_buffers;
> > + }
> > + }
> > + }
> > +
> > + return ret;
> > +
> > +free_bitstream_vbuf:
> > + wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
> > +cleanup_dst_buffers:
> > + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx)))
>
> This doesn't look right. This just clears the capture queue, instead it
> should clear the capture or output queue, depending on V4L2_TYPE_IS_OUTPUT(q->type).

Good catch.

>
> > + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
> > + return -EINVAL;
> > +}
> > +
> > +static int streamoff_output(struct vb2_queue *q)
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_v4l2_buffer *buf;
> > + int ret;
> > + dma_addr_t new_rd_ptr;
> > +
> > + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
> > + dev_dbg(inst->dev->dev, "%s: (Multiplanar) buf type %4u | index %4u\n",
> > + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> > + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> > + }
> > +
> > + ret = wave5_vpu_flush_instance(inst);
> > + if (ret)
> > + return ret;
> > +
> > + /* Reset the ring buffer information */
> > + new_rd_ptr = wave5_vpu_dec_get_rd_ptr(inst);
> > + inst->last_rd_ptr = new_rd_ptr;
> > + inst->codec_info->dec_info.stream_rd_ptr = new_rd_ptr;
> > + inst->codec_info->dec_info.stream_wr_ptr = new_rd_ptr;
> > +
> > + if (v4l2_m2m_has_stopped(m2m_ctx))
> > + send_eos_event(inst);
> > +
> > + /* streamoff on output cancels any draining operation */
> > + inst->eos = false;
> > +
> > + return ret;
> > +}
> > +
> > +static int streamoff_capture(struct vb2_queue *q)
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_v4l2_buffer *buf;
> > + unsigned int i;
> > + int ret = 0;
> > +
> > + /* TOOD unregister the framebuffers to restrict access from the firmware to
> > + * buffers that are freed up
> > + */
> > + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) {
> > + u32 plane;
> > +
> > + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
> > + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> > +
> > + for (plane = 0; plane < inst->dst_fmt.num_planes; plane++)
> > + vb2_set_plane_payload(&buf->vb2_buf, plane, 0);
> > +
> > + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> > + }
> > +
> > + for (i = 0; i < inst->dst_buf_count; i++) {
> > + ret = wave5_vpu_dec_set_disp_flag(inst, i);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Setting display flag of buf index: %u, fail: %d\n",
> > + __func__, i, ret);
> > + }
> > + }
> > +
> > + if (inst->needs_reallocation) {
> > + wave5_vpu_dec_give_command(inst, DEC_RESET_FRAMEBUF_INFO, NULL);
> > + inst->needs_reallocation = false;
> > + }
> > +
> > + if (v4l2_m2m_has_stopped(m2m_ctx)) {
> > + ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + bool check_cmd = TRUE;
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
> > +
> > + while (check_cmd) {
> > + struct queue_status_info q_status;
> > + struct dec_output_info dec_output_info;
> > +
> > + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
> > +
> > + if (q_status.report_queue_count == 0)
> > + break;
> > +
> > + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0)
> > + break;
> > +
> > + if (wave5_vpu_dec_get_output_info(inst, &dec_output_info))
> > + dev_dbg(inst->dev->dev, "Getting decoding results from fw, fail\n");
> > + }
> > +
> > + v4l2_m2m_update_stop_streaming_state(m2m_ctx, q);
> > +
> > + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + streamoff_output(q);
> > + else
> > + streamoff_capture(q);
> > +}
> > +
> > +static const struct vb2_ops wave5_vpu_dec_vb2_ops = {
> > + .queue_setup = wave5_vpu_dec_queue_setup,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > + .buf_queue = wave5_vpu_dec_buf_queue,
> > + .start_streaming = wave5_vpu_dec_start_streaming,
> > + .stop_streaming = wave5_vpu_dec_stop_streaming,
> > +};
> > +
> > +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
> > + struct v4l2_pix_format_mplane *dst_fmt)
> > +{
> > + unsigned int dst_pix_fmt = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
> > + const struct v4l2_format_info *dst_fmt_info = v4l2_format_info(dst_pix_fmt);
> > +
> > + src_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
> > + src_fmt->field = V4L2_FIELD_NONE;
> > + src_fmt->flags = 0;
> > + src_fmt->num_planes = 1;
> > + wave5_update_pix_fmt(src_fmt, 720, 480);
> > +
> > + dst_fmt->pixelformat = dst_pix_fmt;
> > + dst_fmt->field = V4L2_FIELD_NONE;
> > + dst_fmt->flags = 0;
> > + dst_fmt->num_planes = dst_fmt_info->mem_planes;
> > + wave5_update_pix_fmt(dst_fmt, 736, 480);
> > +}
> > +
> > +static int wave5_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
> > +{
> > + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_dec_vb2_ops);
> > +}
> > +
> > +static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = {
> > + .finish_process = wave5_vpu_dec_finish_decode,
> > +};
> > +
> > +static int initialize_sequence(struct vpu_instance *inst)
> > +{
> > + struct dec_initial_info initial_info;
> > + int ret = 0;
> > +
> > + memset(&initial_info, 0, sizeof(struct dec_initial_info));
> > +
> > + ret = wave5_vpu_dec_issue_seq_init(inst);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev, "%s: wave5_vpu_dec_issue_seq_init, fail: %d\n",
> > + __func__, ret);
> > + return ret;
> > + }
> > +
> > + if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0)
> > + dev_dbg(inst->dev->dev, "%s: failed to call vpu_wait_interrupt()\n", __func__);
> > +
> > + ret = wave5_vpu_dec_complete_seq_init(inst, &initial_info);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev, "%s: vpu_dec_complete_seq_init, fail: %d, reason: %u\n",
> > + __func__, ret, initial_info.seq_init_err_reason);
> > + wave5_handle_src_buffer(inst, initial_info.rd_ptr);
> > + } else {
> > + handle_dynamic_resolution_change(inst);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +/* Must be called with state_spinlock */
> > +static bool wave5_is_draining_or_eos(struct vpu_instance *inst)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > +
> > + return m2m_ctx->is_draining || inst->eos;
> > +}
> > +
> > +static void wave5_vpu_dec_device_run(void *priv)
> > +{
> > + struct vpu_instance *inst = priv;
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct queue_status_info q_status;
> > + u32 fail_res = 0;
> > + int ret = 0;
> > +
> > + dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__);
> > +
> > + ret = fill_ringbuffer(inst);
> > + if (ret) {
> > + dev_warn(inst->dev->dev, "Filling ring buffer failed\n");
> > + goto finish_job_and_return;
> > + }
> > +
> > + switch (inst->state) {
> > + case VPU_INST_STATE_OPEN:
> > + ret = initialize_sequence(inst);
> > + if (ret) {
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&inst->state_spinlock, flags);
> > + if (wave5_is_draining_or_eos(inst) &&
> > + wave5_last_src_buffer_consumed(m2m_ctx)) {
> > + struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> > +
> > + switch_state(inst, VPU_INST_STATE_STOP);
> > +
> > + if (vb2_is_streaming(dst_vq))
> > + send_eos_event(inst);
> > + else
> > + handle_dynamic_resolution_change(inst);
> > +
> > + flag_last_buffer_done(inst);
> > + }
> > + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> > + } else {
> > + switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> > + }
> > +
> > + break;
> > +
> > + case VPU_INST_STATE_INIT_SEQ:
> > + /*
> > + * Do this early, preparing the fb can trigger an IRQ before
> > + * we had a chance to switch, which leads to an invalid state
> > + * change.
> > + */
> > + switch_state(inst, VPU_INST_STATE_PIC_RUN);
> > +
> > + /*
> > + * During DRC, the picture decoding remains pending, so just leave the job
> > + * active until this decode operation completes.
> > + */
> > + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
> > +
> > + /*
> > + * The sequence must be analyzed first to calculate the proper
> > + * size of the auxiliary buffers.
> > + */
> > + ret = wave5_prepare_fb(inst);
> > + if (ret) {
> > + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret);
> > + switch_state(inst, VPU_INST_STATE_STOP);
> > + break;
> > + }
> > +
> > + if (q_status.instance_queue_count) {
> > + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
> > + return;
> > + }
> > +
> > + fallthrough;
> > + case VPU_INST_STATE_PIC_RUN:
> > + ret = start_decode(inst, &fail_res);
> > + if (ret) {
> > + dev_err(inst->dev->dev,
> > + "Frame decoding on m2m context (%p), fail: %d (result: %d)\n",
> > + m2m_ctx, ret, fail_res);
> > + break;
> > + }
> > + /* Return so that we leave this job active */
> > + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
> > + return;
> > + default:
> > + WARN(1, "Execution of a job in state %s illegal.\n", state_to_str(inst->state));
> > + break;
> > + }
> > +
> > +finish_job_and_return:
> > + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > +}
> > +
> > +static void wave5_vpu_dec_job_abort(void *priv)
> > +{
> > + struct vpu_instance *inst = priv;
> > + int ret;
> > +
> > + ret = switch_state(inst, VPU_INST_STATE_STOP);
> > + if (ret)
> > + return;
> > +
> > + ret = wave5_vpu_dec_set_eos_on_firmware(inst);
> > + if (ret)
> > + dev_warn(inst->dev->dev,
> > + "Setting EOS for the bitstream, fail: %d\n", ret);
> > +}
> > +
> > +static int wave5_vpu_dec_job_ready(void *priv)
> > +{
> > + struct vpu_instance *inst = priv;
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + unsigned long flags;
> > + bool ret = false;
> > +
> > + spin_lock_irqsave(&inst->state_spinlock, flags);
> > +
> > + switch (inst->state) {
> > + case VPU_INST_STATE_NONE:
> > + dev_dbg(inst->dev->dev, "Decoder must be open to start queueing M2M jobs!\n");
> > + break;
> > + case VPU_INST_STATE_OPEN:
> > + if (wave5_is_draining_or_eos(inst) || !v4l2_m2m_has_stopped(m2m_ctx) ||
> > + v4l2_m2m_num_src_bufs_ready(m2m_ctx) > 0) {
> > + ret = true;
> > + break;
> > + }
> > +
> > + dev_dbg(inst->dev->dev,
> > + "Decoder must be draining or >= 1 OUTPUT queue buffer must be queued!\n");
> > + break;
> > + case VPU_INST_STATE_INIT_SEQ:
> > + case VPU_INST_STATE_PIC_RUN:
> > + if (!m2m_ctx->cap_q_ctx.q.streaming) {
> > + dev_dbg(inst->dev->dev, "CAPTURE queue must be streaming to queue jobs!\n");
> > + break;
> > + } else if (v4l2_m2m_num_dst_bufs_ready(m2m_ctx) < (inst->fbc_buf_count - 1)) {
> > + dev_dbg(inst->dev->dev,
> > + "No capture buffer ready to decode!\n");
> > + break;
> > + } else if (!wave5_is_draining_or_eos(inst) &&
> > + !v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
> > + dev_dbg(inst->dev->dev,
> > + "No bitstream data to decode!\n");
> > + break;
> > + }
> > + ret = true;
> > + break;
> > + case VPU_INST_STATE_STOP:
> > + dev_dbg(inst->dev->dev, "Decoder is stopped, not running.\n");
> > + break;
> > + }
> > +
> > + spin_unlock_irqrestore(&inst->state_spinlock, flags);
> > +
> > + return ret;
> > +}
> > +
> > +static const struct v4l2_m2m_ops wave5_vpu_dec_m2m_ops = {
> > + .device_run = wave5_vpu_dec_device_run,
> > + .job_abort = wave5_vpu_dec_job_abort,
> > + .job_ready = wave5_vpu_dec_job_ready,
> > +};
> > +
> > +static int wave5_vpu_open_dec(struct file *filp)
> > +{
> > + struct video_device *vdev = video_devdata(filp);
> > + struct vpu_device *dev = video_drvdata(filp);
> > + struct vpu_instance *inst = NULL;
> > + struct v4l2_m2m_ctx *m2m_ctx;
> > + int ret = 0;
> > +
> > + inst = kzalloc(sizeof(*inst), GFP_KERNEL);
> > + if (!inst)
> > + return -ENOMEM;
> > +
> > + inst->dev = dev;
> > + inst->type = VPU_INST_TYPE_DEC;
> > + inst->ops = &wave5_vpu_dec_inst_ops;
> > +
> > + spin_lock_init(&inst->state_spinlock);
> > +
> > + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
> > + if (!inst->codec_info)
> > + return -ENOMEM;
> > +
> > + v4l2_fh_init(&inst->v4l2_fh, vdev);
> > + filp->private_data = &inst->v4l2_fh;
> > + v4l2_fh_add(&inst->v4l2_fh);
> > +
> > + INIT_LIST_HEAD(&inst->list);
> > + list_add_tail(&inst->list, &dev->instances);
> > +
> > + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_dec_dev;
> > + inst->v4l2_fh.m2m_ctx =
> > + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_dec_queue_init);
> > + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) {
> > + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx);
> > + goto cleanup_inst;
> > + }
> > + m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > +
> > + v4l2_m2m_set_src_buffered(m2m_ctx, true);
> > + v4l2_m2m_set_dst_buffered(m2m_ctx, true);
> > + /*
> > + * We use the M2M job queue to ensure synchronization of steps where
> > + * needed, as IOCTLs can occur at anytime and we need to run commands on
> > + * the firmware in a specified order.
> > + * In order to initialize the sequence on the firmware within an M2M
> > + * job, the M2M framework needs to be able to queue jobs before
> > + * the CAPTURE queue has been started, because we need the results of the
> > + * initialization to properly prepare the CAPTURE queue with the correct
> > + * amount of buffers
>
> Add something along the lines of:
>
> By setting ignore_cap_streaming to true the m2m framework will call
> job_ready as soon as the OUTPUT queue is streaming, instead of waiting
> until both the CAPTURE and OUTPUT queues are streaming.
>
> > + */
> > + v4l2_m2m_set_dst_ignore_streaming(m2m_ctx, true);
> > +
> > + v4l2_ctrl_handler_init(&inst->v4l2_ctrl_hdl, 10);
> > + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, NULL,
> > + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 1);
> > +
> > + if (inst->v4l2_ctrl_hdl.error) {
> > + ret = -ENODEV;
> > + goto cleanup_inst;
> > + }
> > +
> > + inst->v4l2_fh.ctrl_handler = &inst->v4l2_ctrl_hdl;
> > + v4l2_ctrl_handler_setup(&inst->v4l2_ctrl_hdl);
> > +
> > + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
> > + inst->colorspace = V4L2_COLORSPACE_REC709;
> > + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> > + inst->hsv_enc = 0;
> > + inst->quantization = V4L2_QUANTIZATION_DEFAULT;
> > + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> > +
> > + init_completion(&inst->irq_done);
> > + ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
> > + if (ret) {
> > + dev_err(inst->dev->dev, "failed to allocate fifo\n");
> > + goto cleanup_inst;
> > + }
> > +
> > + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
> > + if (inst->id < 0) {
> > + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id);
> > + ret = inst->id;
> > + goto cleanup_inst;
> > + }
> > +
> > + wave5_vdi_allocate_sram(inst->dev);
> > +
> > + return 0;
> > +
> > +cleanup_inst:
> > + wave5_cleanup_instance(inst);
> > + return ret;
> > +}
> > +
> > +static int wave5_vpu_dec_release(struct file *filp)
> > +{
> > + return wave5_vpu_release_device(filp, wave5_vpu_dec_close, "decoder");
> > +}
> > +
> > +static const struct v4l2_file_operations wave5_vpu_dec_fops = {
> > + .owner = THIS_MODULE,
> > + .open = wave5_vpu_open_dec,
> > + .release = wave5_vpu_dec_release,
> > + .unlocked_ioctl = video_ioctl2,
> > + .poll = v4l2_m2m_fop_poll,
> > + .mmap = v4l2_m2m_fop_mmap,
> > +};
> > +
> > +int wave5_vpu_dec_register_device(struct vpu_device *dev)
> > +{
> > + struct video_device *vdev_dec;
> > + int ret;
> > +
> > + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL);
> > + if (!vdev_dec)
> > + return -ENOMEM;
> > +
> > + dev->v4l2_m2m_dec_dev = v4l2_m2m_init(&wave5_vpu_dec_m2m_ops);
> > + if (IS_ERR(dev->v4l2_m2m_dec_dev)) {
> > + ret = PTR_ERR(dev->v4l2_m2m_dec_dev);
> > + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret);
> > + return -EINVAL;
> > + }
> > +
> > + dev->video_dev_dec = vdev_dec;
> > +
> > + strscpy(vdev_dec->name, VPU_DEC_DEV_NAME, sizeof(vdev_dec->name));
> > + vdev_dec->fops = &wave5_vpu_dec_fops;
> > + vdev_dec->ioctl_ops = &wave5_vpu_dec_ioctl_ops;
> > + vdev_dec->release = video_device_release_empty;
> > + vdev_dec->v4l2_dev = &dev->v4l2_dev;
> > + vdev_dec->vfl_dir = VFL_DIR_M2M;
> > + vdev_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
> > + vdev_dec->lock = &dev->dev_lock;
> > +
> > + ret = video_register_device(vdev_dec, VFL_TYPE_VIDEO, -1);
> > + if (ret)
> > + return ret;
> > +
> > + video_set_drvdata(vdev_dec, dev);
> > +
> > + return 0;
> > +}
> > +
> > +void wave5_vpu_dec_unregister_device(struct vpu_device *dev)
> > +{
> > + video_unregister_device(dev->video_dev_dec);
> > + if (dev->v4l2_m2m_dec_dev)
> > + v4l2_m2m_release(dev->v4l2_m2m_dec_dev);
> > +}
> > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
> > new file mode 100644
> > index 000000000000..e8d8e31b0cbe
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
> > @@ -0,0 +1,1825 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> > +/*
> > + * Wave5 series multi-standard codec IP - encoder interface
> > + *
> > + * Copyright (C) 2021 CHIPS&MEDIA INC
> > + */
> > +
> > +#include "wave5-helper.h"
> > +
> > +#define VPU_ENC_DEV_NAME "C&M Wave5 VPU encoder"
> > +#define VPU_ENC_DRV_NAME "wave5-enc"
> > +
> > +static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = {
> > + [VPU_FMT_TYPE_CODEC] = {
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_H264,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + },
> > + [VPU_FMT_TYPE_RAW] = {
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + {
> > + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
> > + .max_width = W5_MAX_ENC_PIC_WIDTH,
> > + .min_width = W5_MIN_ENC_PIC_WIDTH,
> > + .max_height = W5_MAX_ENC_PIC_HEIGHT,
> > + .min_height = W5_MIN_ENC_PIC_HEIGHT,
> > + },
> > + }
> > +};
> > +
> > +static int switch_state(struct vpu_instance *inst, enum vpu_instance_state state)
> > +{
> > + switch (state) {
> > + case VPU_INST_STATE_NONE:
> > + goto invalid_state_switch;
> > + case VPU_INST_STATE_OPEN:
> > + if (inst->state != VPU_INST_STATE_NONE)
> > + goto invalid_state_switch;
> > + break;
> > + case VPU_INST_STATE_INIT_SEQ:
> > + if (inst->state != VPU_INST_STATE_OPEN && inst->state != VPU_INST_STATE_STOP)
> > + goto invalid_state_switch;
> > + break;
> > + case VPU_INST_STATE_PIC_RUN:
> > + if (inst->state != VPU_INST_STATE_INIT_SEQ)
> > + goto invalid_state_switch;
> > + break;
> > + case VPU_INST_STATE_STOP:
> > + break;
> > + };
> > +
> > + dev_dbg(inst->dev->dev, "Switch state from %s to %s.\n",
> > + state_to_str(inst->state), state_to_str(state));
> > + inst->state = state;
> > + return 0;
> > +
> > +invalid_state_switch:
> > + WARN(1, "Invalid state switch from %s to %s.\n",
> > + state_to_str(inst->state), state_to_str(state));
> > + return -EINVAL;
> > +}
> > +
> > +static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
> > + unsigned int height)
> > +{
> > + switch (pix_mp->pixelformat) {
> > + case V4L2_PIX_FMT_YUV420:
> > + case V4L2_PIX_FMT_NV12:
> > + case V4L2_PIX_FMT_NV21:
> > + pix_mp->width = width;
> > + pix_mp->height = height;
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height * 3 / 2;
> > + break;
> > + case V4L2_PIX_FMT_YUV420M:
> > + pix_mp->width = width;
> > + pix_mp->height = height;
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
> > + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
> > + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 4;
> > + pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
> > + pix_mp->plane_fmt[2].sizeimage = round_up(width, 32) * height / 4;
> > + break;
> > + case V4L2_PIX_FMT_NV12M:
> > + case V4L2_PIX_FMT_NV21M:
> > + pix_mp->width = width;
> > + pix_mp->height = height;
> > + pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
> > + pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
> > + pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 2;
> > + break;
> > + default:
> > + pix_mp->width = width;
> > + pix_mp->height = height;
> > + pix_mp->plane_fmt[0].bytesperline = 0;
> > + pix_mp->plane_fmt[0].sizeimage = width * height / 8 * 3;
> > + break;
> > + }
> > +}
> > +
> > +static int start_encode(struct vpu_instance *inst, u32 *fail_res)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + int ret;
> > + struct vb2_v4l2_buffer *src_buf;
> > + struct vb2_v4l2_buffer *dst_buf;
> > + struct frame_buffer frame_buf;
> > + struct enc_param pic_param;
> > + u32 stride = ALIGN(inst->dst_fmt.width, 32);
> > + u32 luma_size = (stride * inst->dst_fmt.height);
> > + u32 chroma_size = ((stride / 2) * (inst->dst_fmt.height / 2));
> > +
> > + memset(&pic_param, 0, sizeof(struct enc_param));
> > + memset(&frame_buf, 0, sizeof(struct frame_buffer));
> > +
> > + dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
> > + if (!dst_buf) {
> > + dev_dbg(inst->dev->dev, "%s: No destination buffer found\n", __func__);
> > + return -EAGAIN;
> > + }
> > +
> > + pic_param.pic_stream_buffer_addr =
> > + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
> > + pic_param.pic_stream_buffer_size =
> > + vb2_plane_size(&dst_buf->vb2_buf, 0);
> > +
> > + src_buf = v4l2_m2m_next_src_buf(m2m_ctx);
> > + if (!src_buf) {
> > + dev_dbg(inst->dev->dev, "%s: No source buffer found\n", __func__);
> > + if (m2m_ctx->is_draining)
> > + pic_param.src_end_flag = 1;
> > + else
> > + return -EAGAIN;
> > + } else {
> > + if (inst->src_fmt.num_planes == 1) {
> > + frame_buf.buf_y =
> > + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> > + frame_buf.buf_cb = frame_buf.buf_y + luma_size;
> > + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size;
> > + } else if (inst->src_fmt.num_planes == 2) {
> > + frame_buf.buf_y =
> > + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> > + frame_buf.buf_cb =
> > + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
> > + frame_buf.buf_cr = frame_buf.buf_cb + chroma_size;
> > + } else if (inst->src_fmt.num_planes == 3) {
> > + frame_buf.buf_y =
> > + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
> > + frame_buf.buf_cb =
> > + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 1);
> > + frame_buf.buf_cr =
> > + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 2);
> > + }
> > + frame_buf.stride = stride;
> > + pic_param.src_idx = src_buf->vb2_buf.index;
> > + }
> > +
> > + pic_param.source_frame = &frame_buf;
> > + pic_param.code_option.implicit_header_encode = 1;
> > + pic_param.code_option.encode_aud = inst->encode_aud;
> > + ret = wave5_vpu_enc_start_one_frame(inst, &pic_param, fail_res);
> > + if (ret) {
> > + if (*fail_res == WAVE5_SYSERR_QUEUEING_FAIL)
> > + return -EINVAL;
> > +
> > + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame fail: %d\n",
> > + __func__, ret);
> > + src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
> > + if (!src_buf) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Removing src buf failed, the queue is empty\n",
> > + __func__);
> > + return -EINVAL;
> > + }
> > + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
> > + if (!dst_buf) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: Removing dst buf failed, the queue is empty\n",
> > + __func__);
> > + return -EINVAL;
> > + }
> > + switch_state(inst, VPU_INST_STATE_STOP);
> > + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
> > + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
> > + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
> > + } else {
> > + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n",
> > + __func__);
> > + /*
> > + * Remove the source buffer from the ready-queue now and finish
> > + * it in the videobuf2 framework once the index is returned by the
> > + * firmware in finish_encode
> > + */
> > + if (src_buf)
> > + v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + int ret;
> > + struct enc_output_info enc_output_info;
> > + struct vb2_v4l2_buffer *src_buf = NULL;
> > + struct vb2_v4l2_buffer *dst_buf = NULL;
> > +
> > + ret = wave5_vpu_enc_get_output_info(inst, &enc_output_info);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev,
> > + "%s: vpu_enc_get_output_info fail: %d reason: %u | info: %u\n",
> > + __func__, ret, enc_output_info.error_reason, enc_output_info.warn_info);
> > + return;
> > + }
> > +
> > + dev_dbg(inst->dev->dev,
> > + "%s: pic_type %i recon_idx %i src_idx %i pic_byte %u pts %llu\n",
> > + __func__, enc_output_info.pic_type, enc_output_info.recon_frame_index,
> > + enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts);
> > +
> > + /*
> > + * The source buffer will not be found in the ready-queue as it has been
> > + * dropped after sending of the encode firmware command, locate it in
> > + * the videobuf2 queue directly
> > + */
> > + if (enc_output_info.enc_src_idx >= 0) {
> > + struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx),
> > + enc_output_info.enc_src_idx);
> > + if (vb->state != VB2_BUF_STATE_ACTIVE)
> > + dev_warn(inst->dev->dev,
> > + "%s: encoded buffer (%d) was not in ready queue %i.",
> > + __func__, enc_output_info.enc_src_idx, vb->state);
> > + else
> > + src_buf = to_vb2_v4l2_buffer(vb);
> > +
> > + if (src_buf) {
> > + inst->timestamp = src_buf->vb2_buf.timestamp;
> > + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
> > + } else {
> > + dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n",
> > + __func__, enc_output_info.enc_src_idx);
> > + }
> > + }
> > +
> > + dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
> > + if (enc_output_info.recon_frame_index == RECON_IDX_FLAG_ENC_END) {
> > + static const struct v4l2_event vpu_event_eos = {
> > + .type = V4L2_EVENT_EOS
> > + };
> > +
> > + if (dst_buf) {
> > + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
> > + dst_buf->field = V4L2_FIELD_NONE;
> > + v4l2_m2m_last_buffer_done(m2m_ctx, dst_buf);
> > + } else {
> > + WARN(1, "A job have been run without destination buffer.\n");
> > + }
> > +
> > + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos);
> > +
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > + } else {
> > + if (!dst_buf) {
> > + dev_warn(inst->dev->dev, "No bitstream buffer.");
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > + return;
> > + }
> > +
> > + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_output_info.bitstream_size);
> > +
> > + dst_buf->vb2_buf.timestamp = inst->timestamp;
> > + dst_buf->field = V4L2_FIELD_NONE;
> > + if (enc_output_info.pic_type == PIC_TYPE_I) {
> > + if (enc_output_info.enc_vcl_nut == 19 ||
> > + enc_output_info.enc_vcl_nut == 20)
> > + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
> > + else
> > + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
> > + } else if (enc_output_info.pic_type == PIC_TYPE_P) {
> > + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
> > + } else if (enc_output_info.pic_type == PIC_TYPE_B) {
> > + dst_buf->flags |= V4L2_BUF_FLAG_BFRAME;
> > + }
> > +
> > + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
> > +
> > + dev_dbg(inst->dev->dev, "%s: frame_cycle %8u\n",
> > + __func__, enc_output_info.frame_cycle);
> > +
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > + }
> > +}
> > +
> > +static int wave5_vpu_enc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
> > +{
> > + strscpy(cap->driver, VPU_ENC_DRV_NAME, sizeof(cap->driver));
> > + strscpy(cap->card, VPU_ENC_DRV_NAME, sizeof(cap->card));
> > + strscpy(cap->bus_info, "platform:" VPU_ENC_DRV_NAME, sizeof(cap->bus_info));
>
> This last line should not be needed, I think.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_frmsizeenum *fsize)
> > +{
> > + const struct vpu_format *vpu_fmt;
> > +
> > + if (fsize->index)
> > + return -EINVAL;
> > +
> > + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
> > + if (!vpu_fmt) {
> > + vpu_fmt = wave5_find_vpu_fmt(fsize->pixel_format, enc_fmt_list[VPU_FMT_TYPE_RAW]);
> > + if (!vpu_fmt)
> > + return -EINVAL;
> > + }
> > +
> > + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
> > + fsize->stepwise.min_width = vpu_fmt->min_width;
> > + fsize->stepwise.max_width = vpu_fmt->max_width;
> > + fsize->stepwise.step_width = 1;
> > + fsize->stepwise.min_height = vpu_fmt->min_height;
> > + fsize->stepwise.max_height = vpu_fmt->max_height;
> > + fsize->stepwise.step_height = 1;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + const struct vpu_format *vpu_fmt;
> > +
> > + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
> > +
> > + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
> > + if (!vpu_fmt)
> > + return -EINVAL;
> > +
> > + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->flags = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + const struct vpu_format *vpu_fmt;
> > +
> > + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> > +
> > + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> > + return -EINVAL;
> > +
> > + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
> > + if (!vpu_fmt) {
> > + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> > + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> > + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->dst_fmt.width, inst->dst_fmt.height);
> > + } else {
> > + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> > + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> > +
> > + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->fmt.pix_mp.num_planes = 1;
> > + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> > + }
> > +
> > + f->fmt.pix_mp.flags = 0;
> > + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> > + f->fmt.pix_mp.colorspace = inst->colorspace;
> > + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> > + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
>
> Same issue here with hsv_enc. I won't mention that again.
>
> > + f->fmt.pix_mp.quantization = inst->quantization;
> > + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> > + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
>
> Not needed.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + int i, ret;
> > +
> > + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> > +
> > + ret = wave5_vpu_enc_try_fmt_cap(file, fh, f);
> > + if (ret)
> > + return ret;
> > +
> > + inst->dst_fmt.width = f->fmt.pix_mp.width;
> > + inst->dst_fmt.height = f->fmt.pix_mp.height;
> > + inst->dst_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> > + inst->dst_fmt.field = f->fmt.pix_mp.field;
> > + inst->dst_fmt.flags = f->fmt.pix_mp.flags;
> > + inst->dst_fmt.num_planes = f->fmt.pix_mp.num_planes;
> > + for (i = 0; i < inst->dst_fmt.num_planes; i++) {
> > + inst->dst_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> > + inst->dst_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + int i;
> > +
> > + f->fmt.pix_mp.width = inst->dst_fmt.width;
> > + f->fmt.pix_mp.height = inst->dst_fmt.height;
> > + f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
> > + f->fmt.pix_mp.field = inst->dst_fmt.field;
> > + f->fmt.pix_mp.flags = inst->dst_fmt.flags;
> > + f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
> > + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
> > + f->fmt.pix_mp.plane_fmt[i].bytesperline = inst->dst_fmt.plane_fmt[i].bytesperline;
> > + f->fmt.pix_mp.plane_fmt[i].sizeimage = inst->dst_fmt.plane_fmt[i].sizeimage;
> > + }
> > +
> > + f->fmt.pix_mp.colorspace = inst->colorspace;
> > + f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
> > + f->fmt.pix_mp.hsv_enc = inst->hsv_enc;
> > + f->fmt.pix_mp.quantization = inst->quantization;
> > + f->fmt.pix_mp.xfer_func = inst->xfer_func;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + const struct vpu_format *vpu_fmt;
> > +
> > + dev_dbg(inst->dev->dev, "%s: index: %u\n", __func__, f->index);
> > +
> > + vpu_fmt = wave5_find_vpu_fmt_by_idx(f->index, enc_fmt_list[VPU_FMT_TYPE_RAW]);
> > + if (!vpu_fmt)
> > + return -EINVAL;
> > +
> > + f->pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->flags = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + const struct vpu_format *vpu_fmt;
> > +
> > + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> > +
> > + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + return -EINVAL;
> > +
> > + vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_RAW]);
> > + if (!vpu_fmt) {
> > + f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
> > + f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
> > + wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
> > + } else {
> > + int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
> > + int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
> > + const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
> > +
> > + f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
> > + f->fmt.pix_mp.num_planes = info->mem_planes;
> > + wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
> > + }
> > +
> > + f->fmt.pix_mp.flags = 0;
> > + f->fmt.pix_mp.field = V4L2_FIELD_NONE;
> > + memset(&f->fmt.pix_mp.reserved, 0, sizeof(f->fmt.pix_mp.reserved));
>
> Not needed.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + int i, ret;
> > +
> > + dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
> > + __func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
> > + f->fmt.pix_mp.num_planes, f->fmt.pix_mp.field);
> > +
> > + ret = wave5_vpu_enc_try_fmt_out(file, fh, f);
> > + if (ret)
> > + return ret;
> > +
> > + inst->src_fmt.width = f->fmt.pix_mp.width;
> > + inst->src_fmt.height = f->fmt.pix_mp.height;
> > + inst->src_fmt.pixelformat = f->fmt.pix_mp.pixelformat;
> > + inst->src_fmt.field = f->fmt.pix_mp.field;
> > + inst->src_fmt.flags = f->fmt.pix_mp.flags;
> > + inst->src_fmt.num_planes = f->fmt.pix_mp.num_planes;
> > + for (i = 0; i < inst->src_fmt.num_planes; i++) {
> > + inst->src_fmt.plane_fmt[i].bytesperline = f->fmt.pix_mp.plane_fmt[i].bytesperline;
> > + inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
> > + }
> > +
> > + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 ||
> > + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M) {
> > + inst->cbcr_interleave = true;
> > + inst->nv21 = false;
> > + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 ||
> > + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) {
> > + inst->cbcr_interleave = true;
> > + inst->nv21 = true;
> > + } else {
> > + inst->cbcr_interleave = false;
> > + inst->nv21 = false;
> > + }
> > +
> > + inst->colorspace = f->fmt.pix_mp.colorspace;
> > + inst->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
> > + inst->hsv_enc = f->fmt.pix_mp.hsv_enc;
> > + inst->quantization = f->fmt.pix_mp.quantization;
> > + inst->xfer_func = f->fmt.pix_mp.xfer_func;
> > +
> > + wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %u | target: %u\n", __func__, s->type, s->target);
> > +
> > + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
> > + switch (s->target) {
> > + case V4L2_SEL_TGT_CROP_DEFAULT:
> > + case V4L2_SEL_TGT_CROP_BOUNDS:
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + s->r.width = inst->dst_fmt.width;
> > + s->r.height = inst->dst_fmt.height;
> > + break;
> > + case V4L2_SEL_TGT_CROP:
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + s->r.width = inst->dst_fmt.width;
> > + s->r.height = inst->dst_fmt.height;
> > + dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n",
> > + __func__, s->r.width, s->r.height);
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + } else {
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > +
> > + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
> > + return -EINVAL;
> > +
> > + if (s->target != V4L2_SEL_TGT_CROP)
> > + return -EINVAL;
> > +
> > + dev_dbg(inst->dev->dev, "%s: V4L2_SEL_TGT_CROP width: %u | height: %u\n",
> > + __func__, s->r.width, s->r.height);
> > +
> > + s->r.left = 0;
> > + s->r.top = 0;
> > + s->r.width = inst->src_fmt.width;
> > + s->r.height = inst->src_fmt.height;
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + int ret;
> > +
> > + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec);
> > + if (ret)
> > + return ret;
> > +
> > + if (!wave5_vpu_both_queues_are_streaming(inst))
> > + return 0;
> > +
> > + switch (ec->cmd) {
> > + case V4L2_ENC_CMD_STOP:
> > + if (m2m_ctx->is_draining)
> > + return -EBUSY;
> > +
> > + if (m2m_ctx->has_stopped)
> > + return 0;
> > +
> > + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx);
> > + m2m_ctx->is_draining = true;
> > + break;
> > + case V4L2_ENC_CMD_START:
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type);
> > +
> > + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + return -EINVAL;
> > +
> > + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> > + a->parm.output.timeperframe.numerator = 1;
> > + a->parm.output.timeperframe.denominator = inst->frame_rate;
> > +
> > + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n",
> > + __func__, a->parm.output.timeperframe.numerator,
> > + a->parm.output.timeperframe.denominator);
> > +
> > + return 0;
> > +}
> > +
> > +static int wave5_vpu_enc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
> > +{
> > + struct vpu_instance *inst = wave5_to_vpu_inst(fh);
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, a->type);
> > +
> > + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + return -EINVAL;
> > +
> > + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> > + if (a->parm.output.timeperframe.denominator && a->parm.output.timeperframe.numerator) {
> > + inst->frame_rate = a->parm.output.timeperframe.denominator /
> > + a->parm.output.timeperframe.numerator;
> > + } else {
> > + a->parm.output.timeperframe.numerator = 1;
> > + a->parm.output.timeperframe.denominator = inst->frame_rate;
> > + }
> > +
> > + dev_dbg(inst->dev->dev, "%s: numerator: %u | denominator: %u\n",
> > + __func__, a->parm.output.timeperframe.numerator,
> > + a->parm.output.timeperframe.denominator);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct v4l2_ioctl_ops wave5_vpu_enc_ioctl_ops = {
> > + .vidioc_querycap = wave5_vpu_enc_querycap,
> > + .vidioc_enum_framesizes = wave5_vpu_enc_enum_framesizes,
> > +
> > + .vidioc_enum_fmt_vid_cap = wave5_vpu_enc_enum_fmt_cap,
> > + .vidioc_s_fmt_vid_cap_mplane = wave5_vpu_enc_s_fmt_cap,
> > + .vidioc_g_fmt_vid_cap_mplane = wave5_vpu_enc_g_fmt_cap,
> > + .vidioc_try_fmt_vid_cap_mplane = wave5_vpu_enc_try_fmt_cap,
> > +
> > + .vidioc_enum_fmt_vid_out = wave5_vpu_enc_enum_fmt_out,
> > + .vidioc_s_fmt_vid_out_mplane = wave5_vpu_enc_s_fmt_out,
> > + .vidioc_g_fmt_vid_out_mplane = wave5_vpu_g_fmt_out,
> > + .vidioc_try_fmt_vid_out_mplane = wave5_vpu_enc_try_fmt_out,
> > +
> > + .vidioc_g_selection = wave5_vpu_enc_g_selection,
> > + .vidioc_s_selection = wave5_vpu_enc_s_selection,
> > +
> > + .vidioc_g_parm = wave5_vpu_enc_g_parm,
> > + .vidioc_s_parm = wave5_vpu_enc_s_parm,
> > +
> > + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
> > + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
> > + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
> > + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
> > + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
> > + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
> > + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
> > + .vidioc_streamon = v4l2_m2m_ioctl_streamon,
> > + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
> > +
> > + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
> > + .vidioc_encoder_cmd = wave5_vpu_enc_encoder_cmd,
> > +
> > + .vidioc_subscribe_event = wave5_vpu_subscribe_event,
> > + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> > +};
> > +
> > +static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + struct vpu_instance *inst = wave5_ctrl_to_vpu_inst(ctrl);
> > +
> > + dev_dbg(inst->dev->dev, "%s: name: %s | value: %d\n", __func__, ctrl->name, ctrl->val);
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_MPEG_VIDEO_AU_DELIMITER:
> > + inst->encode_aud = ctrl->val;
> > + break;
> > + case V4L2_CID_HFLIP:
> > + inst->mirror_direction |= (ctrl->val << 1);
> > + break;
> > + case V4L2_CID_VFLIP:
> > + inst->mirror_direction |= ctrl->val;
> > + break;
> > + case V4L2_CID_ROTATE:
> > + inst->rot_angle = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
> > + inst->vbv_buf_size = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
> > + inst->rc_mode = 0;
> > + break;
> > + case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
> > + inst->rc_mode = 1;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_BITRATE:
> > + inst->bit_rate = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
> > + inst->enc_param.avc_idr_period = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
> > + inst->enc_param.independ_slice_mode = ctrl->val;
> > + inst->enc_param.avc_slice_mode = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
> > + inst->enc_param.independ_slice_mode_arg = ctrl->val;
> > + inst->enc_param.avc_slice_arg = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
> > + inst->rc_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
> > + inst->enc_param.mb_level_rc_enable = ctrl->val;
> > + inst->enc_param.cu_level_rc_enable = ctrl->val;
> > + inst->enc_param.hvs_qp_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
> > + inst->enc_param.profile = HEVC_PROFILE_MAIN;
> > + inst->bit_depth = 8;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
> > + inst->enc_param.profile = HEVC_PROFILE_STILLPICTURE;
> > + inst->enc_param.en_still_picture = 1;
> > + inst->bit_depth = 8;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
> > + inst->enc_param.profile = HEVC_PROFILE_MAIN10;
> > + inst->bit_depth = 10;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
> > + inst->enc_param.level = 10 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
> > + inst->enc_param.level = 20 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
> > + inst->enc_param.level = 21 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
> > + inst->enc_param.level = 30 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
> > + inst->enc_param.level = 31 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
> > + inst->enc_param.level = 40 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
> > + inst->enc_param.level = 41 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
> > + inst->enc_param.level = 50 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
> > + inst->enc_param.level = 51 * 3;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2:
> > + inst->enc_param.level = 52 * 3;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP:
> > + inst->enc_param.min_qp_i = ctrl->val;
> > + inst->enc_param.min_qp_p = ctrl->val;
> > + inst->enc_param.min_qp_b = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP:
> > + inst->enc_param.max_qp_i = ctrl->val;
> > + inst->enc_param.max_qp_p = ctrl->val;
> > + inst->enc_param.max_qp_b = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP:
> > + inst->enc_param.intra_qp = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED:
> > + inst->enc_param.disable_deblk = 1;
> > + inst->enc_param.sao_enable = 0;
> > + inst->enc_param.lf_cross_slice_boundary_enable = 0;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED:
> > + inst->enc_param.disable_deblk = 0;
> > + inst->enc_param.sao_enable = 1;
> > + inst->enc_param.lf_cross_slice_boundary_enable = 1;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
> > + inst->enc_param.disable_deblk = 0;
> > + inst->enc_param.sao_enable = 1;
> > + inst->enc_param.lf_cross_slice_boundary_enable = 0;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2:
> > + inst->enc_param.beta_offset_div2 = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2:
> > + inst->enc_param.tc_offset_div2 = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE:
> > + inst->enc_param.decoding_refresh_type = 0;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA:
> > + inst->enc_param.decoding_refresh_type = 1;
> > + break;
> > + case V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR:
> > + inst->enc_param.decoding_refresh_type = 2;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD:
> > + inst->enc_param.intra_period = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU:
> > + inst->enc_param.lossless_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED:
> > + inst->enc_param.const_intra_pred_flag = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT:
> > + inst->enc_param.wpp_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING:
> > + inst->enc_param.strong_intra_smooth_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1:
> > + inst->enc_param.max_num_merge = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION:
> > + inst->enc_param.tmvp_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
> > + inst->enc_param.profile = H264_PROFILE_BP;
> > + inst->bit_depth = 8;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
> > + inst->enc_param.profile = H264_PROFILE_MP;
> > + inst->bit_depth = 8;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
> > + inst->enc_param.profile = H264_PROFILE_EXTENDED;
> > + inst->bit_depth = 8;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
> > + inst->enc_param.profile = H264_PROFILE_HP;
> > + inst->bit_depth = 8;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
> > + inst->enc_param.profile = H264_PROFILE_HIGH10;
> > + inst->bit_depth = 10;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
> > + inst->enc_param.profile = H264_PROFILE_HIGH422;
> > + inst->bit_depth = 10;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
> > + inst->enc_param.profile = H264_PROFILE_HIGH444;
> > + inst->bit_depth = 10;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
> > + inst->enc_param.level = 10;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
> > + inst->enc_param.level = 9;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
> > + inst->enc_param.level = 11;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
> > + inst->enc_param.level = 12;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
> > + inst->enc_param.level = 13;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
> > + inst->enc_param.level = 20;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
> > + inst->enc_param.level = 21;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
> > + inst->enc_param.level = 22;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
> > + inst->enc_param.level = 30;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
> > + inst->enc_param.level = 31;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
> > + inst->enc_param.level = 32;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
> > + inst->enc_param.level = 40;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
> > + inst->enc_param.level = 41;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
> > + inst->enc_param.level = 42;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
> > + inst->enc_param.level = 50;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
> > + inst->enc_param.level = 51;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
> > + inst->enc_param.min_qp_i = ctrl->val;
> > + inst->enc_param.min_qp_p = ctrl->val;
> > + inst->enc_param.min_qp_b = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
> > + inst->enc_param.max_qp_i = ctrl->val;
> > + inst->enc_param.max_qp_p = ctrl->val;
> > + inst->enc_param.max_qp_b = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
> > + inst->enc_param.intra_qp = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
> > + switch (ctrl->val) {
> > + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED:
> > + inst->enc_param.disable_deblk = 1;
> > + inst->enc_param.lf_cross_slice_boundary_enable = 1;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED:
> > + inst->enc_param.disable_deblk = 0;
> > + inst->enc_param.lf_cross_slice_boundary_enable = 1;
> > + break;
> > + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
> > + inst->enc_param.disable_deblk = 0;
> > + inst->enc_param.lf_cross_slice_boundary_enable = 0;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
> > + inst->enc_param.beta_offset_div2 = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
> > + inst->enc_param.tc_offset_div2 = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
> > + inst->enc_param.transform8x8_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION:
> > + inst->enc_param.const_intra_pred_flag = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET:
> > + inst->enc_param.chroma_cb_qp_offset = ctrl->val;
> > + inst->enc_param.chroma_cr_qp_offset = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
> > + inst->enc_param.intra_period = ctrl->val;
> > + break;
> > + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
> > + inst->enc_param.entropy_coding_mode = ctrl->val;
> > + break;
> > + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct v4l2_ctrl_ops wave5_vpu_enc_ctrl_ops = {
> > + .s_ctrl = wave5_vpu_enc_s_ctrl,
> > +};
> > +
> > +static int wave5_vpu_enc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
> > + unsigned int *num_planes, unsigned int sizes[],
> > + struct device *alloc_devs[])
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + struct v4l2_pix_format_mplane inst_format =
> > + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
> > + unsigned int i;
> > +
> > + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
> > + *num_buffers, *num_planes, q->type);
> > +
> > + if (*num_planes) {
> > + if (inst_format.num_planes != *num_planes)
> > + return -EINVAL;
> > +
> > + for (i = 0; i < *num_planes; i++) {
> > + if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
> > + return -EINVAL;
> > + }
> > + } else {
> > + *num_planes = inst_format.num_planes;
> > + for (i = 0; i < *num_planes; i++) {
> > + sizes[i] = inst_format.plane_fmt[i].sizeimage;
> > + dev_dbg(inst->dev->dev, "%s: size[%u]: %u\n", __func__, i, sizes[i]);
> > + }
> > + }
> > +
> > + dev_dbg(inst->dev->dev, "%s: size: %u\n", __func__, sizes[0]);
> > +
> > + if (inst->state == VPU_INST_STATE_INIT_SEQ &&
> > + q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> > + if (*num_buffers > inst->src_buf_count &&
> > + *num_buffers < WAVE5_MAX_FBS)
> > + inst->src_buf_count = *num_buffers;
>
> Same issue as with the decoder. Why is src_buf_count needed at all?
>
> > +
> > + *num_buffers = inst->src_buf_count;
> > + dev_dbg(inst->dev->dev, "%s: src buf num: %u", __func__, *num_buffers);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void wave5_vpu_enc_buf_queue(struct vb2_buffer *vb)
> > +{
> > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> > + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %4u index: %4u size: ([0]=%4lu, [1]=%4lu, [2]=%4lu)\n",
> > + __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0),
> > + vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2));
> > +
> > + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + vbuf->sequence = inst->queued_src_buf_num++;
> > + else
> > + vbuf->sequence = inst->queued_dst_buf_num++;
> > +
> > + v4l2_m2m_buf_queue(m2m_ctx, vbuf);
> > +}
> > +
> > +static void wave5_set_enc_openparam(struct enc_open_param *open_param,
> > + struct vpu_instance *inst)
> > +{
> > + struct enc_wave_param input = inst->enc_param;
> > + u32 num_ctu_row = ALIGN(inst->dst_fmt.height, 64) / 64;
> > + u32 num_mb_row = ALIGN(inst->dst_fmt.height, 16) / 16;
> > +
> > + open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE;
> > + open_param->wave_param.hvs_qp_scale = 2;
> > + open_param->wave_param.hvs_max_delta_qp = 10;
> > + open_param->wave_param.skip_intra_trans = 1;
> > + open_param->wave_param.intra_nx_n_enable = 1;
> > + open_param->wave_param.nr_intra_weight_y = 7;
> > + open_param->wave_param.nr_intra_weight_cb = 7;
> > + open_param->wave_param.nr_intra_weight_cr = 7;
> > + open_param->wave_param.nr_inter_weight_y = 4;
> > + open_param->wave_param.nr_inter_weight_cb = 4;
> > + open_param->wave_param.nr_inter_weight_cr = 4;
> > + open_param->wave_param.rdo_skip = 1;
> > + open_param->wave_param.lambda_scaling_enable = 1;
> > +
> > + open_param->line_buf_int_en = true;
> > + open_param->pic_width = inst->dst_fmt.width;
> > + open_param->pic_height = inst->dst_fmt.height;
> > + open_param->frame_rate_info = inst->frame_rate;
> > + open_param->rc_enable = inst->rc_enable;
> > + if (inst->rc_enable) {
> > + open_param->wave_param.initial_rc_qp = -1;
> > + open_param->wave_param.rc_weight_param = 16;
> > + open_param->wave_param.rc_weight_buf = 128;
> > + }
> > + open_param->wave_param.mb_level_rc_enable = input.mb_level_rc_enable;
> > + open_param->wave_param.cu_level_rc_enable = input.cu_level_rc_enable;
> > + open_param->wave_param.hvs_qp_enable = input.hvs_qp_enable;
> > + open_param->bit_rate = inst->bit_rate;
> > + open_param->vbv_buffer_size = inst->vbv_buf_size;
> > + if (inst->rc_mode == 0)
> > + open_param->vbv_buffer_size = 3000;
> > + open_param->wave_param.profile = input.profile;
> > + open_param->wave_param.en_still_picture = input.en_still_picture;
> > + open_param->wave_param.level = input.level;
> > + open_param->wave_param.internal_bit_depth = inst->bit_depth;
> > + open_param->wave_param.intra_qp = input.intra_qp;
> > + open_param->wave_param.min_qp_i = input.min_qp_i;
> > + open_param->wave_param.max_qp_i = input.max_qp_i;
> > + open_param->wave_param.min_qp_p = input.min_qp_p;
> > + open_param->wave_param.max_qp_p = input.max_qp_p;
> > + open_param->wave_param.min_qp_b = input.min_qp_b;
> > + open_param->wave_param.max_qp_b = input.max_qp_b;
> > + open_param->wave_param.disable_deblk = input.disable_deblk;
> > + open_param->wave_param.lf_cross_slice_boundary_enable =
> > + input.lf_cross_slice_boundary_enable;
> > + open_param->wave_param.tc_offset_div2 = input.tc_offset_div2;
> > + open_param->wave_param.beta_offset_div2 = input.beta_offset_div2;
> > + open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type;
> > + open_param->wave_param.intra_period = input.intra_period;
> > + if (inst->std == W_HEVC_ENC) {
> > + if (input.intra_period == 0) {
> > + open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR;
> > + open_param->wave_param.intra_period = input.avc_idr_period;
> > + }
> > + } else {
> > + open_param->wave_param.avc_idr_period = input.avc_idr_period;
> > + }
> > + open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode;
> > + open_param->wave_param.lossless_enable = input.lossless_enable;
> > + open_param->wave_param.const_intra_pred_flag = input.const_intra_pred_flag;
> > + open_param->wave_param.wpp_enable = input.wpp_enable;
> > + open_param->wave_param.strong_intra_smooth_enable = input.strong_intra_smooth_enable;
> > + open_param->wave_param.max_num_merge = input.max_num_merge;
> > + open_param->wave_param.tmvp_enable = input.tmvp_enable;
> > + open_param->wave_param.transform8x8_enable = input.transform8x8_enable;
> > + open_param->wave_param.chroma_cb_qp_offset = input.chroma_cb_qp_offset;
> > + open_param->wave_param.chroma_cr_qp_offset = input.chroma_cr_qp_offset;
> > + open_param->wave_param.independ_slice_mode = input.independ_slice_mode;
> > + open_param->wave_param.independ_slice_mode_arg = input.independ_slice_mode_arg;
> > + open_param->wave_param.avc_slice_mode = input.avc_slice_mode;
> > + open_param->wave_param.avc_slice_arg = input.avc_slice_arg;
> > + open_param->wave_param.intra_mb_refresh_mode = input.intra_mb_refresh_mode;
> > + if (input.intra_mb_refresh_mode != REFRESH_MB_MODE_NONE) {
> > + if (num_mb_row >= input.intra_mb_refresh_arg)
> > + open_param->wave_param.intra_mb_refresh_arg =
> > + num_mb_row / input.intra_mb_refresh_arg;
> > + else
> > + open_param->wave_param.intra_mb_refresh_arg = num_mb_row;
> > + }
> > + open_param->wave_param.intra_refresh_mode = input.intra_refresh_mode;
> > + if (input.intra_refresh_mode != 0) {
> > + if (num_ctu_row >= input.intra_refresh_arg)
> > + open_param->wave_param.intra_refresh_arg =
> > + num_ctu_row / input.intra_refresh_arg;
> > + else
> > + open_param->wave_param.intra_refresh_arg = num_ctu_row;
> > + }
> > +}
> > +
> > +static int initialize_sequence(struct vpu_instance *inst)
> > +{
> > + struct enc_initial_info initial_info;
> > + struct v4l2_ctrl *ctrl;
> > + int ret;
> > +
> > + ret = wave5_vpu_enc_issue_seq_init(inst);
> > + if (ret) {
> > + dev_err(inst->dev->dev, "%s: wave5_vpu_enc_issue_seq_init, fail: %d\n",
> > + __func__, ret);
> > + return ret;
> > + }
> > +
> > + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0) {
> > + dev_err(inst->dev->dev, "%s: wave5_vpu_wait_interrupt failed\n", __func__);
> > + return -EINVAL;
> > + }
> > +
> > + ret = wave5_vpu_enc_complete_seq_init(inst, &initial_info);
> > + if (ret)
> > + return ret;
> > +
> > + dev_dbg(inst->dev->dev, "%s: min_frame_buffer: %u | min_source_buffer: %u\n",
> > + __func__, initial_info.min_frame_buffer_count,
> > + initial_info.min_src_frame_count);
> > + inst->min_src_buf_count = initial_info.min_src_frame_count +
> > + COMMAND_QUEUE_DEPTH;
> > +
> > + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
> > + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT);
> > + if (ctrl)
> > + v4l2_ctrl_s_ctrl(ctrl, inst->min_src_buf_count);
> > +
> > + inst->fbc_buf_count = initial_info.min_frame_buffer_count;
> > + inst->src_buf_count = inst->min_src_buf_count;
> > +
> > + return 0;
> > +}
> > +
> > +static int prepare_fb(struct vpu_instance *inst)
> > +{
> > + u32 fb_stride = ALIGN(inst->dst_fmt.width, 32);
> > + u32 fb_height = ALIGN(inst->dst_fmt.height, 32);
> > + int i, ret = 0;
> > +
> > + for (i = 0; i < inst->fbc_buf_count; i++) {
> > + u32 luma_size = fb_stride * fb_height;
> > + u32 chroma_size = ALIGN(fb_stride / 2, 16) * fb_height;
> > +
> > + inst->frame_vbuf[i].size = luma_size + chroma_size;
> > + ret = wave5_vdi_allocate_dma_memory(inst->dev, &inst->frame_vbuf[i]);
> > + if (ret < 0) {
> > + dev_err(inst->dev->dev, "%s: failed to allocate FBC buffer %zu\n",
> > + __func__, inst->frame_vbuf[i].size);
> > + goto free_buffers;
> > + }
> > +
> > + inst->frame_buf[i].buf_y = inst->frame_vbuf[i].daddr;
> > + inst->frame_buf[i].buf_cb = (dma_addr_t)-1;
> > + inst->frame_buf[i].buf_cr = (dma_addr_t)-1;
> > + inst->frame_buf[i].update_fb_info = true;
> > + inst->frame_buf[i].size = inst->frame_vbuf[i].size;
> > + }
> > +
> > + ret = wave5_vpu_enc_register_frame_buffer(inst, inst->fbc_buf_count, fb_stride,
> > + fb_height, COMPRESSED_FRAME_MAP);
> > + if (ret) {
> > + dev_err(inst->dev->dev,
> > + "%s: wave5_vpu_enc_register_frame_buffer, fail: %d\n",
> > + __func__, ret);
> > + goto free_buffers;
> > + }
> > +
> > + return 0;
> > +free_buffers:
> > + for (i = 0; i < inst->fbc_buf_count; i++)
> > + wave5_vpu_dec_reset_framebuffer(inst, i);
> > + return ret;
> > +}
> > +
> > +static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count)
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + int ret;
> > +
> > + v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
> > +
> > + if (inst->state == VPU_INST_STATE_NONE && q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> > + struct enc_open_param open_param;
> > +
> > + memset(&open_param, 0, sizeof(struct enc_open_param));
> > +
> > + inst->std = wave5_to_vpu_std(inst->dst_fmt.pixelformat, inst->type);
> > + if (inst->std == STD_UNKNOWN) {
> > + dev_warn(inst->dev->dev, "unsupported pixelformat: %.4s\n",
> > + (char *)&inst->dst_fmt.pixelformat);
>
> This should call buf_done(DEQUEUED) for all buffers in the queue on error.
>
> > + return -EINVAL;
> > + }
> > +
> > + wave5_set_enc_openparam(&open_param, inst);
> > +
> > + ret = wave5_vpu_enc_open(inst, &open_param);
> > + if (ret) {
> > + dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_open, fail: %d\n",
> > + __func__, ret);
> > + return ret;
> > + }
> > +
> > + if (inst->mirror_direction) {
> > + wave5_vpu_enc_give_command(inst, ENABLE_MIRRORING, NULL);
> > + wave5_vpu_enc_give_command(inst, SET_MIRROR_DIRECTION,
> > + &inst->mirror_direction);
> > + }
> > + if (inst->rot_angle) {
> > + wave5_vpu_enc_give_command(inst, ENABLE_ROTATION, NULL);
> > + wave5_vpu_enc_give_command(inst, SET_ROTATION_ANGLE, &inst->rot_angle);
> > + }
> > +
> > + switch_state(inst, VPU_INST_STATE_OPEN);
> > + }
> > + if (inst->state == VPU_INST_STATE_OPEN && m2m_ctx->cap_q_ctx.q.streaming) {
> > + ret = initialize_sequence(inst);
> > + if (ret) {
> > + dev_warn(inst->dev->dev, "Sequence not found: %d\n", ret);
> > + return ret;
> > + }
> > + switch_state(inst, VPU_INST_STATE_INIT_SEQ);
> > + /*
> > + * The sequence must be analyzed first to calculate the proper
> > + * size of the auxiliary buffers.
> > + */
> > + ret = prepare_fb(inst);
> > + if (ret) {
> > + dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + switch_state(inst, VPU_INST_STATE_PIC_RUN);
> > + }
> > +
>
> The DEQUEUED sequence is done (although not quite correctly) in the decoder's start_streaming,
> but not at all here.
>
> > + return 0;
> > +}
> > +
> > +static void streamoff_output(struct vpu_instance *inst, struct vb2_queue *q)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_v4l2_buffer *buf;
> > +
> > + while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
> > + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
> > + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> > + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> > + }
> > +}
> > +
> > +static void streamoff_capture(struct vpu_instance *inst, struct vb2_queue *q)
> > +{
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + struct vb2_v4l2_buffer *buf;
> > +
> > + while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx))) {
> > + dev_dbg(inst->dev->dev, "%s: buf type %4u | index %4u\n",
> > + __func__, buf->vb2_buf.type, buf->vb2_buf.index);
> > + vb2_set_plane_payload(&buf->vb2_buf, 0, 0);
> > + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
> > + }
> > +
> > + v4l2_m2m_clear_state(m2m_ctx);
> > +}
> > +
> > +static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q)
> > +{
> > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > + bool check_cmd = true;
> > +
> > + /*
> > + * Note that we don't need m2m_ctx->next_buf_last for this driver, so we
> > + * don't call v4l2_m2m_update_stop_streaming_state().
> > + */
> > +
> > + dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
> > +
> > + if (wave5_vpu_both_queues_are_streaming(inst))
> > + switch_state(inst, VPU_INST_STATE_STOP);
> > +
> > + while (check_cmd) {
> > + struct queue_status_info q_status;
> > + struct enc_output_info enc_output_info;
> > +
> > + wave5_vpu_enc_give_command(inst, ENC_GET_QUEUE_STATUS, &q_status);
> > +
> > + if (q_status.report_queue_count == 0)
> > + break;
> > +
> > + if (wave5_vpu_wait_interrupt(inst, VPU_ENC_TIMEOUT) < 0)
> > + break;
> > +
> > + if (wave5_vpu_enc_get_output_info(inst, &enc_output_info))
> > + dev_dbg(inst->dev->dev, "Getting encoding results from fw, fail\n");
> > + }
> > +
> > + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> > + streamoff_output(inst, q);
> > + else
> > + streamoff_capture(inst, q);
> > +}
> > +
> > +static const struct vb2_ops wave5_vpu_enc_vb2_ops = {
> > + .queue_setup = wave5_vpu_enc_queue_setup,
> > + .wait_prepare = vb2_ops_wait_prepare,
> > + .wait_finish = vb2_ops_wait_finish,
> > + .buf_queue = wave5_vpu_enc_buf_queue,
> > + .start_streaming = wave5_vpu_enc_start_streaming,
> > + .stop_streaming = wave5_vpu_enc_stop_streaming,
> > +};
> > +
> > +static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
> > + struct v4l2_pix_format_mplane *dst_fmt)
> > +{
> > + unsigned int src_pix_fmt = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
> > + const struct v4l2_format_info *src_fmt_info = v4l2_format_info(src_pix_fmt);
> > +
> > + src_fmt->pixelformat = src_pix_fmt;
> > + src_fmt->field = V4L2_FIELD_NONE;
> > + src_fmt->flags = 0;
> > + src_fmt->num_planes = src_fmt_info->mem_planes;
> > + wave5_update_pix_fmt(src_fmt, 416, 240);
> > +
> > + dst_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
> > + dst_fmt->field = V4L2_FIELD_NONE;
> > + dst_fmt->flags = 0;
> > + dst_fmt->num_planes = 1;
> > + wave5_update_pix_fmt(dst_fmt, 416, 240);
> > +}
> > +
> > +static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
> > +{
> > + return wave5_vpu_queue_init(priv, src_vq, dst_vq, &wave5_vpu_enc_vb2_ops);
> > +}
> > +
> > +static const struct vpu_instance_ops wave5_vpu_enc_inst_ops = {
> > + .finish_process = wave5_vpu_enc_finish_encode,
> > +};
> > +
> > +static void wave5_vpu_enc_device_run(void *priv)
> > +{
> > + struct vpu_instance *inst = priv;
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > + u32 fail_res = 0;
> > + int ret = 0;
> > +
> > + switch (inst->state) {
> > + case VPU_INST_STATE_PIC_RUN:
> > + ret = start_encode(inst, &fail_res);
> > + if (ret) {
> > + if (ret == -EINVAL)
> > + dev_err(inst->dev->dev,
> > + "Frame encoding on m2m context (%p), fail: %d (res: %d)\n",
> > + m2m_ctx, ret, fail_res);
> > + else if (ret == -EAGAIN)
> > + dev_dbg(inst->dev->dev, "Missing buffers for encode, try again\n");
> > + break;
> > + }
> > + dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
> > + return;
> > + default:
> > + WARN(1, "Execution of a job in state %s is invalid.\n",
> > + state_to_str(inst->state));
> > + break;
> > + }
> > + dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
> > + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
> > +}
> > +
> > +static int wave5_vpu_enc_job_ready(void *priv)
> > +{
> > + struct vpu_instance *inst = priv;
> > + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
> > +
> > + switch (inst->state) {
> > + case VPU_INST_STATE_NONE:
> > + dev_dbg(inst->dev->dev, "Encoder must be open to start queueing M2M jobs!\n");
> > + return false;
> > + case VPU_INST_STATE_PIC_RUN:
> > + if (m2m_ctx->is_draining || v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
> > + dev_dbg(inst->dev->dev, "Encoder ready for a job, state: %s\n",
> > + state_to_str(inst->state));
> > + return true;
> > + }
> > + fallthrough;
> > + default:
> > + dev_dbg(inst->dev->dev,
> > + "Encoder not ready for a job, state: %s, %s draining, %d src bufs ready\n",
> > + state_to_str(inst->state), m2m_ctx->is_draining ? "is" : "is not",
> > + v4l2_m2m_num_src_bufs_ready(m2m_ctx));
> > + break;
> > + }
> > + return false;
> > +}
> > +
> > +static const struct v4l2_m2m_ops wave5_vpu_enc_m2m_ops = {
> > + .device_run = wave5_vpu_enc_device_run,
> > + .job_ready = wave5_vpu_enc_job_ready,
> > +};
> > +
> > +static int wave5_vpu_open_enc(struct file *filp)
> > +{
> > + struct video_device *vdev = video_devdata(filp);
> > + struct vpu_device *dev = video_drvdata(filp);
> > + struct vpu_instance *inst = NULL;
> > + struct v4l2_ctrl_handler *v4l2_ctrl_hdl;
> > + int ret = 0;
> > +
> > + inst = kzalloc(sizeof(*inst), GFP_KERNEL);
> > + if (!inst)
> > + return -ENOMEM;
> > + v4l2_ctrl_hdl = &inst->v4l2_ctrl_hdl;
> > +
> > + inst->dev = dev;
> > + inst->type = VPU_INST_TYPE_ENC;
> > + inst->ops = &wave5_vpu_enc_inst_ops;
> > +
> > + inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL);
> > + if (!inst->codec_info)
> > + return -ENOMEM;
> > +
> > + v4l2_fh_init(&inst->v4l2_fh, vdev);
> > + filp->private_data = &inst->v4l2_fh;
> > + v4l2_fh_add(&inst->v4l2_fh);
> > +
> > + INIT_LIST_HEAD(&inst->list);
> > + list_add_tail(&inst->list, &dev->instances);
> > +
> > + inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_enc_dev;
> > + inst->v4l2_fh.m2m_ctx =
> > + v4l2_m2m_ctx_init(inst->v4l2_m2m_dev, inst, wave5_vpu_enc_queue_init);
> > + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) {
> > + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx);
> > + goto cleanup_inst;
> > + }
> > + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, true);
> > +
> > + v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
> > + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, 0,
> > + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
> > + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0,
> > + V4L2_MPEG_VIDEO_HEVC_LEVEL_1);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP,
> > + 0, 63, 1, 8);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP,
> > + 0, 63, 1, 51);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP,
> > + 0, 63, 1, 30);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE,
> > + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0,
> > + V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2,
> > + -6, 6, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2,
> > + -6, 6, 1, 0);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE,
> > + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, 0,
> > + V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD,
> > + 0, 2047, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING,
> > + 0, 1, 1, 1);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1,
> > + 1, 2, 1, 2);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION,
> > + 0, 1, 1, 1);
> > +
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_PROFILE,
> > + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE, 0,
> > + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_LEVEL,
> > + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 0,
> > + V4L2_MPEG_VIDEO_H264_LEVEL_1_0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
> > + 0, 63, 1, 8);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
> > + 0, 63, 1, 51);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
> > + 0, 63, 1, 30);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
> > + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0,
> > + V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
> > + -6, 6, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
> > + -6, 6, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
> > + 0, 1, 1, 1);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET,
> > + -12, 12, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
> > + 0, 2047, 1, 0);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
> > + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, 0,
> > + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_AU_DELIMITER,
> > + 0, 1, 1, 1);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_HFLIP,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_VFLIP,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_ROTATE,
> > + 0, 270, 90, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_VBV_SIZE,
> > + 10, 3000, 1, 1000);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
> > + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
> > + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_BITRATE,
> > + 0, 700000000, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_GOP_SIZE,
> > + 0, 2047, 1, 0);
> > + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
> > + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB, 0,
> > + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
> > + 0, 0xFFFF, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
> > + 0, 1, 1, 0);
> > + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
> > + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1);
> > +
> > + if (v4l2_ctrl_hdl->error) {
> > + ret = -ENODEV;
> > + goto cleanup_inst;
> > + }
> > +
> > + inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl;
> > + v4l2_ctrl_handler_setup(v4l2_ctrl_hdl);
> > +
> > + wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
> > + inst->colorspace = V4L2_COLORSPACE_REC709;
> > + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> > + inst->hsv_enc = 0;
> > + inst->quantization = V4L2_QUANTIZATION_DEFAULT;
> > + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT;
> > + inst->frame_rate = 30;
> > +
> > + init_completion(&inst->irq_done);
> > + ret = kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL);
> > + if (ret) {
> > + dev_err(inst->dev->dev, "Allocating fifo, fail: %d\n", ret);
> > + goto cleanup_inst;
> > + }
> > +
> > + inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL);
> > + if (inst->id < 0) {
> > + dev_warn(inst->dev->dev, "Allocating instance ID, fail: %d\n", inst->id);
> > + ret = inst->id;
> > + goto cleanup_inst;
> > + }
> > +
> > + wave5_vdi_allocate_sram(inst->dev);
> > +
> > + return 0;
> > +
> > +cleanup_inst:
> > + wave5_cleanup_instance(inst);
> > + return ret;
> > +}
> > +
> > +static int wave5_vpu_enc_release(struct file *filp)
> > +{
> > + return wave5_vpu_release_device(filp, wave5_vpu_enc_close, "encoder");
> > +}
> > +
> > +static const struct v4l2_file_operations wave5_vpu_enc_fops = {
> > + .owner = THIS_MODULE,
> > + .open = wave5_vpu_open_enc,
> > + .release = wave5_vpu_enc_release,
> > + .unlocked_ioctl = video_ioctl2,
> > + .poll = v4l2_m2m_fop_poll,
> > + .mmap = v4l2_m2m_fop_mmap,
> > +};
> > +
> > +int wave5_vpu_enc_register_device(struct vpu_device *dev)
> > +{
> > + struct video_device *vdev_enc;
> > + int ret;
> > +
> > + vdev_enc = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_enc), GFP_KERNEL);
> > + if (!vdev_enc)
> > + return -ENOMEM;
> > +
> > + dev->v4l2_m2m_enc_dev = v4l2_m2m_init(&wave5_vpu_enc_m2m_ops);
> > + if (IS_ERR(dev->v4l2_m2m_enc_dev)) {
> > + ret = PTR_ERR(dev->v4l2_m2m_enc_dev);
> > + dev_err(dev->dev, "v4l2_m2m_init, fail: %d\n", ret);
> > + return -EINVAL;
> > + }
> > +
> > + dev->video_dev_enc = vdev_enc;
> > +
> > + strscpy(vdev_enc->name, VPU_ENC_DEV_NAME, sizeof(vdev_enc->name));
> > + vdev_enc->fops = &wave5_vpu_enc_fops;
> > + vdev_enc->ioctl_ops = &wave5_vpu_enc_ioctl_ops;
> > + vdev_enc->release = video_device_release_empty;
> > + vdev_enc->v4l2_dev = &dev->v4l2_dev;
> > + vdev_enc->vfl_dir = VFL_DIR_M2M;
> > + vdev_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
> > + vdev_enc->lock = &dev->dev_lock;
> > +
> > + ret = video_register_device(vdev_enc, VFL_TYPE_VIDEO, -1);
> > + if (ret)
> > + return ret;
> > +
> > + video_set_drvdata(vdev_enc, dev);
> > +
> > + return 0;
> > +}
> > +
> > +void wave5_vpu_enc_unregister_device(struct vpu_device *dev)
> > +{
> > + video_unregister_device(dev->video_dev_enc);
> > + if (dev->v4l2_m2m_enc_dev)
> > + v4l2_m2m_release(dev->v4l2_m2m_enc_dev);
> > +}
> > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> > new file mode 100644
> > index 000000000000..a13d968f5d04
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
> > @@ -0,0 +1,331 @@
> > +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> > +/*
> > + * Wave5 series multi-standard codec IP - platform driver
> > + *
> > + * Copyright (C) 2021 CHIPS&MEDIA INC
> > + */
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/clk.h>
> > +#include <linux/firmware.h>
> > +#include <linux/interrupt.h>
> > +#include "wave5-vpu.h"
> > +#include "wave5-regdefine.h"
> > +#include "wave5-vpuconfig.h"
> > +#include "wave5.h"
> > +
> > +#define VPU_PLATFORM_DEVICE_NAME "vdec"
> > +#define VPU_CLK_NAME "vcodec"
> > +
> > +#define WAVE5_IS_ENC BIT(0)
> > +#define WAVE5_IS_DEC BIT(1)
> > +
> > +struct wave5_match_data {
> > + int flags;
> > + const char *fw_name;
> > +};
> > +
> > +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
> > +{
> > + int ret;
> > +
> > + ret = wait_for_completion_timeout(&inst->irq_done,
> > + msecs_to_jiffies(timeout));
> > + if (!ret)
> > + return -ETIMEDOUT;
> > +
> > + reinit_completion(&inst->irq_done);
> > +
> > + return 0;
> > +}
> > +
> > +static void wave5_vpu_get_interrupt_for_inst(struct vpu_instance *inst, u32 status)
> > +{
> > + struct vpu_device *dev = inst->dev;
> > + u32 seq_done;
> > + u32 cmd_done;
> > + int val;
> > +
> > + seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
> > + cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
> > +
> > + if (status & BIT(INT_WAVE5_INIT_SEQ)) {
> > + if (seq_done & BIT(inst->id)) {
> > + seq_done &= ~BIT(inst->id);
> > + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
> > + val = BIT(INT_WAVE5_INIT_SEQ);
> > + kfifo_in(&inst->irq_status, &val, sizeof(int));
> > + }
> > + }
> > + if (status & BIT(INT_WAVE5_ENC_SET_PARAM)) {
> > + if (seq_done & BIT(inst->id)) {
> > + seq_done &= ~BIT(inst->id);
> > + wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done);
> > + val = BIT(INT_WAVE5_ENC_SET_PARAM);
> > + kfifo_in(&inst->irq_status, &val, sizeof(int));
> > + }
> > + }
> > + if (status & BIT(INT_WAVE5_DEC_PIC) ||
> > + status & BIT(INT_WAVE5_ENC_PIC)) {
> > + if (cmd_done & BIT(inst->id)) {
> > + cmd_done &= ~BIT(inst->id);
> > + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, cmd_done);
> > + val = BIT(INT_WAVE5_DEC_PIC);
> > + kfifo_in(&inst->irq_status, &val, sizeof(int));
> > + }
> > + }
> > +}
> > +
> > +static irqreturn_t wave5_vpu_irq(int irq, void *dev_id)
> > +{
> > + struct vpu_device *dev = dev_id;
> > +
> > + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
> > + struct vpu_instance *inst;
> > + u32 irq_status = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
> > +
> > + list_for_each_entry(inst, &dev->instances, list) {
> > + wave5_vpu_get_interrupt_for_inst(inst, irq_status);
> > + }
> > +
> > + wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_status);
> > + wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
> > +
> > + return IRQ_WAKE_THREAD;
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
> > +{
> > + struct vpu_device *dev = dev_id;
> > + struct vpu_instance *inst;
> > + int irq_status, ret;
> > +
> > + list_for_each_entry(inst, &dev->instances, list) {
> > + while (kfifo_len(&inst->irq_status)) {
> > + ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int));
> > + if (!ret)
> > + break;
> > +
> > + if (irq_status == BIT(INT_WAVE5_INIT_SEQ) ||
> > + irq_status == BIT(INT_WAVE5_ENC_SET_PARAM))
> > + complete(&inst->irq_done);
> > + else /* DEC/ENC_PIC */
> > + inst->ops->finish_process(inst);
> > +
> > + wave5_vpu_clear_interrupt(inst, irq_status);
> > + }
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
> > + u32 *revision)
> > +{
> > + const struct firmware *fw;
> > + int ret;
> > + unsigned int product_id;
> > +
> > + ret = request_firmware(&fw, fw_name, dev);
> > + if (ret) {
> > + dev_err(dev, "request_firmware, fail: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = wave5_vpu_init_with_bitcode(dev, (u8 *)fw->data, fw->size);
> > + if (ret) {
> > + dev_err(dev, "vpu_init_with_bitcode, fail: %d\n", ret);
> > + goto release_fw;
> > + }
> > + release_firmware(fw);
> > +
> > + ret = wave5_vpu_get_version_info(dev, revision, &product_id);
> > + if (ret) {
> > + dev_err(dev, "vpu_get_version_info fail: %d\n", ret);
> > + goto err_without_release;
> > + }
> > +
> > + dev_dbg(dev, "%s: enum product_id: %08x, fw revision: %u\n",
> > + __func__, product_id, *revision);
> > +
> > + return 0;
> > +
> > +release_fw:
> > + release_firmware(fw);
> > +err_without_release:
> > + return ret;
> > +}
> > +
> > +static int wave5_vpu_probe(struct platform_device *pdev)
> > +{
> > + int ret;
> > + struct vpu_device *dev;
> > + const struct wave5_match_data *match_data;
> > + u32 fw_revision;
> > +
> > + match_data = device_get_match_data(&pdev->dev);
> > + if (!match_data) {
> > + dev_err(&pdev->dev, "missing device match data\n");
> > + return -EINVAL;
> > + }
> > +
> > + /* physical addresses limited to 32 bits */
> > + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
> > + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
> > +
> > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
> > + if (!dev)
> > + return -ENOMEM;
> > +
> > + dev->vdb_register = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(dev->vdb_register))
> > + return PTR_ERR(dev->vdb_register);
> > + ida_init(&dev->inst_ida);
> > +
> > + mutex_init(&dev->dev_lock);
> > + mutex_init(&dev->hw_lock);
> > + dev_set_drvdata(&pdev->dev, dev);
> > + dev->dev = &pdev->dev;
> > +
> > + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks);
> > +
> > + /* continue without clock, assume externally managed */
> > + if (ret < 0) {
> > + dev_warn(&pdev->dev, "Getting clocks, fail: %d\n", ret);
> > + ret = 0;
> > + }
> > + dev->num_clks = ret;
> > +
> > + ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = of_property_read_u32(pdev->dev.of_node, "sram-size",
> > + &dev->sram_size);
> > + if (ret) {
> > + dev_warn(&pdev->dev, "sram-size not found\n");
> > + dev->sram_size = 0;
> > + }
> > +
> > + dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
> > + if (!dev->sram_pool)
> > + dev_warn(&pdev->dev, "sram node not found\n");
> > +
> > + dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER);
> > + ret = wave5_vdi_init(&pdev->dev);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "wave5_vdi_init, fail: %d\n", ret);
> > + goto err_clk_dis;
> > + }
> > + dev->product = wave5_vpu_get_product_id(dev);
> > +
> > + INIT_LIST_HEAD(&dev->instances);
> > + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
> > + if (ret) {
> > + dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret);
> > + goto err_vdi_release;
> > + }
> > +
> > + if (match_data->flags & WAVE5_IS_DEC) {
> > + ret = wave5_vpu_dec_register_device(dev);
> > + if (ret) {
> > + dev_err(&pdev->dev, "wave5_vpu_dec_register_device, fail: %d\n", ret);
> > + goto err_v4l2_unregister;
> > + }
> > + }
> > + if (match_data->flags & WAVE5_IS_ENC) {
> > + ret = wave5_vpu_enc_register_device(dev);
> > + if (ret) {
> > + dev_err(&pdev->dev, "wave5_vpu_enc_register_device, fail: %d\n", ret);
> > + goto err_dec_unreg;
> > + }
> > + }
> > +
> > + dev->irq = platform_get_irq(pdev, 0);
> > + if (dev->irq < 0) {
> > + dev_err(&pdev->dev, "failed to get irq resource\n");
> > + ret = -ENXIO;
> > + goto err_enc_unreg;
> > + }
> > +
> > + ret = devm_request_threaded_irq(&pdev->dev, dev->irq, wave5_vpu_irq,
> > + wave5_vpu_irq_thread, 0, "vpu_irq", dev);
> > + if (ret) {
> > + dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
> > + goto err_enc_unreg;
> > + }
> > +
> > + ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision);
> > + if (ret) {
> > + dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret);
> > + goto err_enc_unreg;
> > + }
> > +
> > + dev_info(&pdev->dev, "Added wave5 driver with caps: %s %s\n",
> > + (match_data->flags & WAVE5_IS_ENC) ? "'ENCODE'" : "",
> > + (match_data->flags & WAVE5_IS_DEC) ? "'DECODE'" : "");
> > + dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code);
> > + dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision);
> > + return 0;
> > +
> > +err_enc_unreg:
> > + if (match_data->flags & WAVE5_IS_ENC)
> > + wave5_vpu_enc_unregister_device(dev);
> > +err_dec_unreg:
> > + if (match_data->flags & WAVE5_IS_DEC)
> > + wave5_vpu_dec_unregister_device(dev);
> > +err_v4l2_unregister:
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > +err_vdi_release:
> > + wave5_vdi_release(&pdev->dev);
> > +err_clk_dis:
> > + clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
> > +
> > + return ret;
> > +}
> > +
> > +static int wave5_vpu_remove(struct platform_device *pdev)
> > +{
> > + struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
> > +
> > + mutex_destroy(&dev->dev_lock);
> > + mutex_destroy(&dev->hw_lock);
> > + clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
> > + wave5_vpu_enc_unregister_device(dev);
> > + wave5_vpu_dec_unregister_device(dev);
> > + v4l2_device_unregister(&dev->v4l2_dev);
> > + wave5_vdi_release(&pdev->dev);
> > + ida_destroy(&dev->inst_ida);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct wave5_match_data wave521c_data = {
> > + .flags = WAVE5_IS_ENC | WAVE5_IS_DEC,
> > + .fw_name = "cnm/wave521c_k3_codec_fw.bin",
> > +};
> > +
> > +static const struct of_device_id wave5_dt_ids[] = {
> > + { .compatible = "cnm,cm521c-vpu", .data = &wave521c_data },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, wave5_dt_ids);
> > +
> > +static struct platform_driver wave5_vpu_driver = {
> > + .driver = {
> > + .name = VPU_PLATFORM_DEVICE_NAME,
> > + .of_match_table = of_match_ptr(wave5_dt_ids),
> > + },
> > + .probe = wave5_vpu_probe,
> > + .remove = wave5_vpu_remove,
> > +};
> > +
> > +module_platform_driver(wave5_vpu_driver);
> > +MODULE_DESCRIPTION("chips&media VPU V4L2 driver");
> > +MODULE_LICENSE("Dual BSD/GPL");
> > diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
> > new file mode 100644
> > index 000000000000..f4117c079027
> > --- /dev/null
> > +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
> > @@ -0,0 +1,83 @@
> > +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> > +/*
> > + * Wave5 series multi-standard codec IP - basic types
> > + *
> > + * Copyright (C) 2021 CHIPS&MEDIA INC
> > + */
> > +#ifndef __VPU_DRV_H__
> > +#define __VPU_DRV_H__
> > +
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-fh.h>
> > +#include <media/videobuf2-v4l2.h>
> > +#include <media/videobuf2-dma-contig.h>
> > +#include <media/videobuf2-vmalloc.h>
> > +#include "wave5-vpuconfig.h"
> > +#include "wave5-vpuapi.h"
> > +
> > +#define VPU_BUF_SYNC_TO_DEVICE 0
> > +#define VPU_BUF_SYNC_FROM_DEVICE 1
> > +
> > +struct vpu_src_buffer {
> > + struct v4l2_m2m_buffer v4l2_m2m_buf;
> > + struct list_head list;
> > + bool consumed;
> > +};
> > +
> > +struct vpu_dst_buffer {
> > + struct v4l2_m2m_buffer v4l2_m2m_buf;
> > + bool display;
> > +};
> > +
> > +enum vpu_fmt_type {
> > + VPU_FMT_TYPE_CODEC = 0,
> > + VPU_FMT_TYPE_RAW = 1
> > +};
> > +
> > +struct vpu_format {
> > + unsigned int v4l2_pix_fmt;
> > + unsigned int max_width;
> > + unsigned int min_width;
> > + unsigned int max_height;
> > + unsigned int min_height;
> > +};
> > +
> > +static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh)
> > +{
> > + return container_of(vfh, struct vpu_instance, v4l2_fh);
> > +}
> > +
> > +static inline struct vpu_instance *wave5_ctrl_to_vpu_inst(struct v4l2_ctrl *vctrl)
> > +{
> > + return container_of(vctrl->handler, struct vpu_instance, v4l2_ctrl_hdl);
> > +}
> > +
> > +static inline struct vpu_src_buffer *wave5_to_vpu_src_buf(struct vb2_v4l2_buffer *vbuf)
> > +{
> > + return container_of(vbuf, struct vpu_src_buffer, v4l2_m2m_buf.vb);
> > +}
> > +
> > +static inline struct vpu_dst_buffer *wave5_to_vpu_dst_buf(struct vb2_v4l2_buffer *vbuf)
> > +{
> > + return container_of(vbuf, struct vpu_dst_buffer, v4l2_m2m_buf.vb);
> > +}
> > +
> > +int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout);
> > +
> > +int wave5_vpu_dec_register_device(struct vpu_device *dev);
> > +void wave5_vpu_dec_unregister_device(struct vpu_device *dev);
> > +int wave5_vpu_enc_register_device(struct vpu_device *dev);
> > +void wave5_vpu_enc_unregister_device(struct vpu_device *dev);
> > +static inline bool wave5_vpu_both_queues_are_streaming(struct vpu_instance *inst)
> > +{
> > + struct vb2_queue *vq_cap =
> > + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> > + struct vb2_queue *vq_out =
> > + v4l2_m2m_get_vq(inst->v4l2_fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
> > +
> > + return vb2_is_streaming(vq_cap) && vb2_is_streaming(vq_out);
> > +}
> > +
> > +#endif
> >
>
> Regards,
>
> Hans

Thank you so much or your review,

Nicolas

2023-09-22 09:09:30

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

On 21/09/2023 21:11, Nicolas Dufresne wrote:
> Le mercredi 20 septembre 2023 à 17:13 +0200, Hans Verkuil a écrit :
>> On 15/09/2023 23:11, Sebastian Fricke wrote:
>>> From: Nas Chung <[email protected]>
>>>
>>> Add the decoder and encoder implementing the v4l2
>>> API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
>>>
>>> Signed-off-by: Sebastian Fricke <[email protected]>
>>> Signed-off-by: Nicolas Dufresne <[email protected]>
>>> Signed-off-by: Robert Beckett <[email protected]>
>>> Signed-off-by: Dafna Hirschfeld <[email protected]>
>>> Signed-off-by: Nas Chung <[email protected]>
>>> ---
>>> drivers/media/platform/chips-media/Kconfig | 1 +
>>> drivers/media/platform/chips-media/Makefile | 1 +
>>> drivers/media/platform/chips-media/wave5/Kconfig | 12 +
>>> drivers/media/platform/chips-media/wave5/Makefile | 10 +
>>> .../platform/chips-media/wave5/wave5-helper.c | 196 ++
>>> .../platform/chips-media/wave5/wave5-helper.h | 30 +
>>> .../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
>>> .../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
>>> .../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
>>> .../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
>>> 10 files changed, 4454 insertions(+)
>>>

<snip>

>>> +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
>>> +{
>>> + int ret;
>>> +
>>> + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
>>> + if (ret) {
>>> + dev_err(inst->dev->dev,
>>> + "Setting EOS for the bitstream, fail: %d\n", ret);
>>
>> Is this an error due to a driver problem, or because a bad bitstream is
>> fed from userspace? In the first case, dev_err would be right, in the
>> second dev_dbg would be more appropriate. Bad userspace input should not
>> spam the kernel log in general.
>
> Its the first. To set the EOS flag, a command is sent to the firmware. That
> command may never return (timeout) or may report an error. For this specific
> command, if that happens we are likely facing firmware of driver problem (or
> both).

OK, I'd add that as a comment here as this is unexpected behavior.

>
>>
>>> + return ret;
>>> + }
>>> + return 0;
>>> +}

<snip>

>>> +static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
>>> + struct v4l2_create_buffers *create)
>>> +{
>>> + struct v4l2_format *f = &create->format;
>>> +
>>> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>> + return -ENOTTY;
>>
>> Huh? Why is this needed?
>
> Minimally a comment should be added. The why is that we support CREATE_BUF for
> OUTPUT queue (bitstream) but not for CAPTURE queues. This is simply not
> supported by Wave5 firmware. Do you have any suggestion how this asymmetry can
> be implemented better ?

Certainly not with ENOTTY: the ioctl exists, it is just not supported for
CAPTURE queues.

How about -EPERM? And document this error as well in the VIDIOC_CREATE_BUFS
documentation. And you want a dev_dbg here too.

So I would propose that EPERM is returned if CREATE_BUFS is only supported
for for one of the two queues of an M2M device.

>
>>
>>> +
>>> + return v4l2_m2m_ioctl_create_bufs(file, priv, create);
>>> +}

<snip>

>>> +static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
>>> + unsigned int *num_planes, unsigned int sizes[],
>>> + struct device *alloc_devs[])
>>> +{
>>> + struct vpu_instance *inst = vb2_get_drv_priv(q);
>>> + struct v4l2_pix_format_mplane inst_format =
>>> + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
>>> + unsigned int i;
>>> +
>>> + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
>>> + *num_buffers, *num_planes, q->type);
>>> +
>>> + /* the CREATE_BUFS case */
>>> + if (*num_planes) {
>>> + if (inst_format.num_planes != *num_planes)
>>> + return -EINVAL;
>>> +
>>> + for (i = 0; i < *num_planes; i++) {
>>> + if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
>>> + return -EINVAL;
>>> + }
>>> + /* the REQBUFS case */
>>> + } else {
>>> + *num_planes = inst_format.num_planes;
>>> +
>>> + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
>>> + sizes[0] = inst_format.plane_fmt[0].sizeimage;
>>> + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
>>> + } else if (*num_planes == 1) {
>>> + if (inst->output_format == FORMAT_422)
>>> + sizes[0] = inst_format.width * inst_format.height * 2;
>>> + else
>>> + sizes[0] = inst_format.width * inst_format.height * 3 / 2;
>>> + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
>>> + } else if (*num_planes == 2) {
>>> + sizes[0] = inst_format.width * inst_format.height;
>>> + if (inst->output_format == FORMAT_422)
>>> + sizes[1] = inst_format.width * inst_format.height;
>>> + else
>>> + sizes[1] = inst_format.width * inst_format.height / 2;
>>> + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n",
>>> + __func__, sizes[0], sizes[1]);
>>> + } else if (*num_planes == 3) {
>>> + sizes[0] = inst_format.width * inst_format.height;
>>> + if (inst->output_format == FORMAT_422) {
>>> + sizes[1] = inst_format.width * inst_format.height / 2;
>>> + sizes[2] = inst_format.width * inst_format.height / 2;
>>> + } else {
>>> + sizes[1] = inst_format.width * inst_format.height / 4;
>>> + sizes[2] = inst_format.width * inst_format.height / 4;
>>> + }
>>> + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n",
>>> + __func__, sizes[0], sizes[1], sizes[2]);
>>> + }
>>> + }
>>> +
>>> + if (inst->state == VPU_INST_STATE_INIT_SEQ &&
>>> + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
>>> + if (*num_buffers > inst->dst_buf_count &&
>>> + *num_buffers < WAVE5_MAX_FBS)
>>> + inst->dst_buf_count = *num_buffers;
>>
>> In the create_bufs case, *num_buffers is the number of buffers you are
>> adding to the already existing buffers. Frankly, the logic here is
>> dubious. I'm not sure what the intent is. Why do you need to keep track
>> of the buf count at all when the vb2_queue already does that?
>
> This needs to be cleaned up. CREATE_BUFS case is not supported for capture, and
> so there is no such weirdo case second calls for that queue at least. Meanwhile,
> inst->dst_buf_count is used a MIN_BUFFERS_FOR_CAPTURE initially, and the number
> of allocated buffer later. I think it would be better to simply store the min,
> and use the queue to track the number of allocated buffers.
>
> In this diver, the reference frame are stored separately, and compressed. The
> capture queue contains the display frame. There is a gap when comes time to
> transfer timestamp, and for this reason we had to keep the two fbc_count equal.
> We classified this as hardware issue and moved on.
>
> I think the dst_buf_count can be dropped now and the "*num_buffers > inst-
>> dst_buf_count" not longer make any sense.
>
>>
>> WAVE5_MAX_FBS == 32, which is VIDEO_MAX_FRAMES. You can just drop the check
>> against WAVE5_MAX_FBS since the core ensures already it will never allocate
>> more than VIDEO_MAX_FRAMES.
>>
>> I'm not sure why WAVE5_MAX_FBS is defined at all, when it is just equal to
>> VIDEO_MAX_FRAMES. Perhaps it can be replaced everywhere with VIDEO_MAX_FRAMES?
>
> That is more challenging changes, since VIDEO_MAX_FRAMES is a software
> limitation, but WAVE5_MAX_FBS is a hardware limitation. Buffer index only have 4
> bits on this hardware. And the marking of frame being used for display is using
> a 32bit flag. Considering there is effort to lift that software limitation, it
> seems ill advised to completely stop ensuring this HW limit is respected.

If there are only 4 bits for the buffer index, shouldn't WAVE5_MAX_FBS be 16? Or
did you mean '5 bits'? Assuming that you meant '5 bits', then that makes
WAVE5_MAX_FBS identical to VIDEO_MAX_FRAMES, but that is just luck, really.

In any case, you should document at the place where WAVE5_MAX_FBS is defined that
this is a hardware limitation, and not a random software limit.

I also think that the DELETE_BUFS series should allow drivers to set max_num_buffers
to a value less than 32 (currently the requirement is that it is at least 32).

I saw a few more drivers that limit the number of buffers, usually based on the
format (and so the buffer size) and some HW memory limitation. It might be interesting
to allow drivers to change max_num_buffers on the fly (provided no buffers are
allocated, of course). Limit checking is really something that vb2 should do, and
not the driver.

>
> I'm open for suggestions.
>
>>
>>> +
>>> + *num_buffers = inst->dst_buf_count;
>>> + }
>>> +
>>> + return 0;
>>> +}

Regards,

Hans

2023-09-25 19:41:59

by Benjamin Gaignard

[permalink] [raw]
Subject: Re: [PATCH v12 4/7] media: chips-media: wave5: Add vpuapi layer

Hi Sebastien,

Please find my comments below.

Regards,
Benjamin

Le 15/09/2023 à 23:11, Sebastian Fricke a écrit :
> From: Nas Chung <[email protected]>
>
> Add the vpuapi layer of the wave5 codec driver.
> This layer is used to configure the hardware according
> to the parameters.
>
> Signed-off-by: Sebastian Fricke <[email protected]>
> Signed-off-by: Nicolas Dufresne <[email protected]>
> Signed-off-by: Dafna Hirschfeld <[email protected]>
> Signed-off-by: Robert Beckett <[email protected]>
> Signed-off-by: Nas Chung <[email protected]>
> ---
> .../media/platform/chips-media/wave5/wave5-hw.c | 2553 ++++++++++++++++++++
> .../platform/chips-media/wave5/wave5-regdefine.h | 732 ++++++
> .../media/platform/chips-media/wave5/wave5-vdi.c | 208 ++
> .../media/platform/chips-media/wave5/wave5-vdi.h | 35 +
> .../platform/chips-media/wave5/wave5-vpuapi.c | 958 ++++++++
> .../platform/chips-media/wave5/wave5-vpuapi.h | 874 +++++++
> .../platform/chips-media/wave5/wave5-vpuconfig.h | 77 +
> .../platform/chips-media/wave5/wave5-vpuerror.h | 292 +++
> drivers/media/platform/chips-media/wave5/wave5.h | 116 +
> 9 files changed, 5845 insertions(+)
>
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c
> new file mode 100644
> index 000000000000..5d533d01b820
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c
> @@ -0,0 +1,2553 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - wave5 backend logic
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#include <linux/iopoll.h>
> +#include <linux/bitfield.h>
> +#include "wave5-vpu.h"
> +#include "wave5.h"
> +#include "wave5-regdefine.h"
> +
> +#define FIO_TIMEOUT 10000000
> +#define FIO_CTRL_READY BIT(31)
> +#define FIO_CTRL_WRITE BIT(16)
> +#define VPU_BUSY_CHECK_TIMEOUT 10000000
> +#define QUEUE_REPORT_MASK 0xffff
> +
> +/* Encoder support fields */
> +#define FEATURE_HEVC10BIT_ENC BIT(3)
> +#define FEATURE_AVC10BIT_ENC BIT(11)
> +#define FEATURE_AVC_ENCODER BIT(1)
> +#define FEATURE_HEVC_ENCODER BIT(0)
> +
> +/* Decoder support fields */
> +#define FEATURE_AVC_DECODER BIT(3)
> +#define FEATURE_HEVC_DECODER BIT(2)
> +
> +#define FEATURE_BACKBONE BIT(16)
> +#define FEATURE_VCORE_BACKBONE BIT(22)
> +#define FEATURE_VCPU_BACKBONE BIT(28)
> +
> +#define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff)
> +#define REMAP_CTRL_REGISTER_VALUE(index) ( \
> + (BIT(31) | (index << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS) \
> +)
> +
> +#define FASTIO_ADDRESS_MASK GENMASK(15, 0)
> +#define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24)
> +
> +static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason,
> + const char *func);
> +#define PRINT_REG_ERR(dev, reason) _wave5_print_reg_err((dev), (reason), __func__)
> +
> +static inline const char *cmd_to_str(int cmd, bool is_dec)
> +{
> + switch (cmd) {
> + case W5_INIT_VPU:
> + return "W5_INIT_VPU";
> + case W5_WAKEUP_VPU:
> + return "W5_WAKEUP_VPU";
> + case W5_SLEEP_VPU:
> + return "W5_SLEEP_VPU";
> + case W5_CREATE_INSTANCE:
> + return "W5_CREATE_INSTANCE";
> + case W5_FLUSH_INSTANCE:
> + return "W5_FLUSH_INSTANCE";
> + case W5_DESTROY_INSTANCE:
> + return "W5_DESTROY_INSTANCE";
> + case W5_INIT_SEQ:
> + return "W5_INIT_SEQ";
> + case W5_SET_FB:
> + return "W5_SET_FB";
> + case W5_DEC_ENC_PIC:
> + if (is_dec)
> + return "W5_DEC_PIC";
> + return "W5_ENC_PIC";
> + case W5_ENC_SET_PARAM:
> + return "W5_ENC_SET_PARAM";
> + case W5_QUERY:
> + return "W5_QUERY";
> + case W5_UPDATE_BS:
> + return "W5_UPDATE_BS";
> + case W5_MAX_VPU_COMD:
> + return "W5_MAX_VPU_COMD";
> + default:
> + return "UNKNOWN";
> + }
> +}
> +
> +static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason,
> + const char *func)
> +{
> + struct device *dev = vpu_dev->dev;
> + u32 reg_val;
> +
> + switch (reg_fail_reason) {
> + case WAVE5_SYSERR_QUEUEING_FAIL:
> + reg_val = vpu_read_reg(vpu_dev, W5_RET_QUEUE_FAIL_REASON);
> + dev_dbg(dev, "%s: queueing failure: 0x%x\n", func, reg_val);
> + break;
> + case WAVE5_SYSERR_RESULT_NOT_READY:
> + dev_err(dev, "%s: result not ready: 0x%x\n", func, reg_fail_reason);
> + break;
> + case WAVE5_SYSERR_ACCESS_VIOLATION_HW:
> + dev_err(dev, "%s: access violation: 0x%x\n", func, reg_fail_reason);
> + break;
> + case WAVE5_SYSERR_WATCHDOG_TIMEOUT:
> + dev_err(dev, "%s: watchdog timeout: 0x%x\n", func, reg_fail_reason);
> + break;
> + case WAVE5_SYSERR_BUS_ERROR:
> + dev_err(dev, "%s: bus error: 0x%x\n", func, reg_fail_reason);
> + break;
> + case WAVE5_SYSERR_DOUBLE_FAULT:
> + dev_err(dev, "%s: double fault: 0x%x\n", func, reg_fail_reason);
> + break;
> + case WAVE5_SYSERR_VPU_STILL_RUNNING:
> + dev_err(dev, "%s: still running: 0x%x\n", func, reg_fail_reason);
> + break;
> + case WAVE5_SYSERR_VLC_BUF_FULL:
> + dev_err(dev, "%s: vlc buf full: 0x%x\n", func, reg_fail_reason);
> + break;
> + default:
> + dev_err(dev, "%s: failure:: 0x%x\n", func, reg_fail_reason);
> + break;
> + }
> +}
> +
> +static int wave5_wait_fio_readl(struct vpu_device *vpu_dev, u32 addr, u32 val)
> +{
> + u32 ctrl;
> + int ret;
> +
> + ctrl = addr & 0xffff;
> + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl);
> + ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY,
> + 0, FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR);
> + if (ret)
> + return ret;
> +
> + if (wave5_vdi_read_register(vpu_dev, W5_VPU_FIO_DATA) != val)
> + return -ETIMEDOUT;
> +
> + return 0;
> +}
> +
> +static void wave5_fio_writel(struct vpu_device *vpu_dev, unsigned int addr, unsigned int data)
> +{
> + int ret;
> + unsigned int ctrl;
> +
> + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_DATA, data);
> + ctrl = FIELD_GET(FASTIO_ADDRESS_MASK, addr);
> + ctrl |= FIO_CTRL_WRITE;
> + wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl);
> + ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY, 0,
> + FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR);
> + if (ret)
> + dev_dbg_ratelimited(vpu_dev->dev, "FIO write timeout: addr=0x%x data=%x\n",
> + ctrl, data);
> +}
> +
> +static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr)
> +{
> + u32 gdi_status_check_value = 0x3f;
> +
> + if (vpu_dev->product_code == WAVE521C_CODE ||
> + vpu_dev->product_code == WAVE521_CODE ||
> + vpu_dev->product_code == WAVE521E1_CODE)
> + gdi_status_check_value = 0x00ff1f3f;
> +
> + return wave5_wait_fio_readl(vpu_dev, addr, gdi_status_check_value);
> +}
> +
> +static int wave5_wait_vpu_busy(struct vpu_device *vpu_dev, unsigned int addr)
> +{
> + u32 data;
> +
> + return read_poll_timeout(wave5_vdi_read_register, data, data == 0,
> + 0, VPU_BUSY_CHECK_TIMEOUT, false, vpu_dev, addr);
> +}
> +
> +static int wave5_wait_vcpu_bus_busy(struct vpu_device *vpu_dev, unsigned int addr)
> +{
> + return wave5_wait_fio_readl(vpu_dev, addr, 0);
> +}
> +
> +bool wave5_vpu_is_init(struct vpu_device *vpu_dev)
> +{
> + return vpu_read_reg(vpu_dev, W5_VCPU_CUR_PC) != 0;
> +}
> +
> +unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev)
> +{
> + u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER);
> +
> + switch (val) {
> + case WAVE521C_CODE:
> + return PRODUCT_ID_521;
> + case WAVE521_CODE:
> + case WAVE521C_DUAL_CODE:
> + case WAVE521E1_CODE:
> + case WAVE511_CODE:
> + case WAVE517_CODE:
> + case WAVE537_CODE:
> + dev_err(vpu_dev->dev, "Unsupported product id (%x)\n", val);
> + break;
> + default:
> + dev_err(vpu_dev->dev, "Invalid product id (%x)\n", val);
> + break;
> + }
> +
> + return PRODUCT_ID_NONE;
> +}
> +
> +void wave5_bit_issue_command(struct vpu_instance *inst, u32 cmd)
> +{
> + u32 instance_index = inst->id;
> + u32 codec_mode = inst->std;
> +
> + vpu_write_reg(inst->dev, W5_CMD_INSTANCE_INFO, (codec_mode << 16) |
> + (instance_index & 0xffff));
> + vpu_write_reg(inst->dev, W5_VPU_BUSY_STATUS, 1);
> + vpu_write_reg(inst->dev, W5_COMMAND, cmd);
> +
> + dev_dbg(inst->dev->dev, "%s: cmd=0x%x (%s)\n", __func__, cmd,
> + cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC));
> +
> + vpu_write_reg(inst->dev, W5_VPU_HOST_INT_REQ, 1);
> +}

I haven't found any to this functions outside this, you can make it static and
remove it prototype from wave.h

> +
> +static int wave5_vpu_firmware_command_queue_error_check(struct vpu_device *dev, u32 *fail_res)
> +{
> + /* Check if we were able to add a command into the VCPU QUEUE */
> + if (!vpu_read_reg(dev, W5_RET_SUCCESS)) {
> + if (!fail_res)
> + return -EIO;
> +
> + *fail_res = vpu_read_reg(dev, W5_RET_FAIL_REASON);
> + PRINT_REG_ERR(dev, *fail_res);
> + if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING)
> + return -EBUSY;
> + }
> + return 0;
> +}
> +
> +static int send_firmware_command(struct vpu_instance *inst, u32 cmd, bool check_success,
> + u32 *queue_status, u32 *fail_result)
> +{
> + int ret;
> +
> + wave5_bit_issue_command(inst, cmd);
> + ret = wave5_wait_vpu_busy(inst->dev, W5_VPU_BUSY_STATUS);
> + if (ret) {
> + dev_warn(inst->dev->dev, "%s: command: '%s', timed out\n", __func__,
> + cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC));
> + return -ETIMEDOUT;
> + }
> +
> + if (queue_status)
> + *queue_status = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
> +
> + /* In some cases we want to send multiple commands before checking
> + * whether they are queued properly
> + */
> + if (!check_success)
> + return 0;
> +
> + return wave5_vpu_firmware_command_queue_error_check(inst->dev, fail_result);
> +}
> +
> +static int wave5_send_query(struct vpu_instance *inst, enum query_opt query_opt)
> +{
> + int ret;
> +
> + vpu_write_reg(inst->dev, W5_QUERY_OPTION, query_opt);
> + vpu_write_reg(inst->dev, W5_VPU_BUSY_STATUS, 1);
> + wave5_bit_issue_command(inst, W5_QUERY);
> +
> + ret = wave5_wait_vpu_busy(inst->dev, W5_VPU_BUSY_STATUS);
> + if (ret) {
> + dev_warn(inst->dev->dev, "command: 'W5_QUERY', timed out opt=0x%x\n", query_opt);
> + return ret;
> + }
> +
> + return wave5_vpu_firmware_command_queue_error_check(inst->dev, NULL);
> +}
> +
> +static int setup_wave5_properties(struct device *dev)
> +{
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> + struct vpu_attr *p_attr = &vpu_dev->attr;
> + u32 reg_val;
> + u8 *str;
> + int ret;
> + u32 hw_config_def0, hw_config_def1, hw_config_feature;
> +
> + vpu_write_reg(vpu_dev, W5_QUERY_OPTION, GET_VPU_INFO);
> + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
> + vpu_write_reg(vpu_dev, W5_COMMAND, W5_QUERY);
> + vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
> + if (ret)
> + return ret;

You may want to replace this by:
wave5_send_query(..., GET_VPU_INFO);

> +
> + reg_val = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_NAME);
> + str = (u8 *)&reg_val;
> + p_attr->product_name[0] = str[3];
> + p_attr->product_name[1] = str[2];
> + p_attr->product_name[2] = str[1];
> + p_attr->product_name[3] = str[0];
> + p_attr->product_name[4] = 0;
> +
> + p_attr->product_id = wave5_vpu_get_product_id(vpu_dev);
> + p_attr->product_version = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_VERSION);
> + p_attr->fw_version = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION);
> + p_attr->customer_id = vpu_read_reg(vpu_dev, W5_RET_CUSTOMER_ID);
> + hw_config_def0 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF0);
> + hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1);
> + hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE);
> +
> + p_attr->support_hevc10bit_enc = FIELD_GET(FEATURE_HEVC10BIT_ENC, hw_config_feature);
> + p_attr->support_avc10bit_enc = FIELD_GET(FEATURE_AVC10BIT_ENC, hw_config_feature);
> +
> + p_attr->support_decoders = FIELD_GET(FEATURE_AVC_DECODER, hw_config_def1) << STD_AVC;
> + p_attr->support_decoders |= FIELD_GET(FEATURE_HEVC_DECODER, hw_config_def1) << STD_HEVC;
> + p_attr->support_encoders = FIELD_GET(FEATURE_AVC_ENCODER, hw_config_def1) << STD_AVC;
> + p_attr->support_encoders |= FIELD_GET(FEATURE_HEVC_ENCODER, hw_config_def1) << STD_HEVC;
> +
> + p_attr->support_backbone = FIELD_GET(FEATURE_BACKBONE, hw_config_def0);
> + p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0);
> + p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0);
> +
> + return 0;
> +}
> +
> +int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision)
> +{
> + u32 reg_val;
> + int ret;
> +
> + vpu_write_reg(vpu_dev, W5_QUERY_OPTION, GET_VPU_INFO);
> + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
> + vpu_write_reg(vpu_dev, W5_COMMAND, W5_QUERY);
> + vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
> + if (ret) {
> + dev_err(vpu_dev->dev, "%s: timeout\n", __func__);
> + return ret;
> + }
> +
> + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
> + if (ret) {
> + dev_err(vpu_dev->dev, "%s: failed\n", __func__);
> + return ret;
> + }

same here.

> +
> + reg_val = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION);
> + if (revision) {
> + *revision = reg_val;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static void remap_page(struct vpu_device *vpu_dev, dma_addr_t code_base, u32 index)
> +{
> + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CTRL, REMAP_CTRL_REGISTER_VALUE(index));
> + vpu_write_reg(vpu_dev, W5_VPU_REMAP_VADDR, index * W5_REMAP_MAX_SIZE);
> + vpu_write_reg(vpu_dev, W5_VPU_REMAP_PADDR, code_base + index * W5_REMAP_MAX_SIZE);
> +}
> +
> +int wave5_vpu_init(struct device *dev, u8 *fw, size_t size)
> +{
> + struct vpu_buf *common_vb;
> + dma_addr_t code_base, temp_base;
> + u32 code_size, temp_size;
> + u32 i, reg_val, reason_code;
> + int ret;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + common_vb = &vpu_dev->common_mem;
> +
> + code_base = common_vb->daddr;
> + /* ALIGN TO 4KB */
> + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
> + if (code_size < size * 2)
> + return -EINVAL;
> +
> + temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET;
> + temp_size = WAVE5_TEMPBUF_SIZE;
> +
> + ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size);
> + if (ret < 0) {
> + dev_err(vpu_dev->dev, "VPU init, Writing firmware to common buffer, fail: %d\n",
> + ret);
> + return ret;
> + }
> +
> + vpu_write_reg(vpu_dev, W5_PO_CONF, 0);
> +
> + /* clear registers */
> +
> + for (i = W5_CMD_REG_BASE; i < W5_CMD_REG_END; i += 4)
> + vpu_write_reg(vpu_dev, i, 0x00);
> +
> + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
> + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);
> +
> + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
> + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
> + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
> + vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base);
> + vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size);
> +
> + /* These register must be reset explicitly */
> + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
> + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
> +
> + /* Encoder interrupt */
> + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
> + reg_val |= BIT(INT_WAVE5_ENC_PIC);
> + reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
> + /* Decoder interrupt */
> + reg_val |= BIT(INT_WAVE5_INIT_SEQ);
> + reg_val |= BIT(INT_WAVE5_DEC_PIC);
> + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
> + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
> +
> + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
> + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
> + reg_val = ((WAVE5_PROC_AXI_ID << 28) |
> + (WAVE5_PRP_AXI_ID << 24) |
> + (WAVE5_FBD_Y_AXI_ID << 20) |
> + (WAVE5_FBC_Y_AXI_ID << 16) |
> + (WAVE5_FBD_C_AXI_ID << 12) |
> + (WAVE5_FBC_C_AXI_ID << 8) |
> + (WAVE5_PRI_AXI_ID << 4) |
> + WAVE5_SEC_AXI_ID);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
> + }
> +
> + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
> + vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
> + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
> + if (ret) {
> + dev_err(vpu_dev->dev, "VPU init(W5_VPU_REMAP_CORE_START) timeout\n");
> + return ret;
> + }
> +
> + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
> + if (ret)
> + return ret;
> +
> + return setup_wave5_properties(dev);
> +}
> +
> +int wave5_vpu_build_up_dec_param(struct vpu_instance *inst,
> + struct dec_open_param *param)
> +{
> + int ret;
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + p_dec_info->cycle_per_tick = 256;
> + if (vpu_dev->sram_buf.size) {
> + p_dec_info->sec_axi_info.use_bit_enable = 1;
> + p_dec_info->sec_axi_info.use_ip_enable = 1;
> + p_dec_info->sec_axi_info.use_lf_row_enable = 1;
> + }
> + switch (inst->std) {
> + case W_HEVC_DEC:
> + p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_HEVC;
> + break;
> + case W_AVC_DEC:
> + p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_AVC;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE;
> + ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work);
> + if (ret)
> + return ret;
> +
> + vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1);
> +
> + wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work);
> +
> + vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr);
> + vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
> + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
> +
> + vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size);
> +
> + /* NOTE: SDMA reads MSB first */
> + vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN);
> + /* This register must be reset explicitly */
> + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
> + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1));
> +
> + ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
> + if (ret) {
> + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
> + return ret;
> + }
> +
> + p_dec_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER);
> +
> + return 0;
> +}
> +
> +int wave5_vpu_hw_flush_instance(struct vpu_instance *inst)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + u32 instance_queue_count, report_queue_count;
> + u32 reg_val = 0;
> + u32 fail_res = 0;
> + int ret;
> +
> + ret = send_firmware_command(inst, W5_FLUSH_INSTANCE, true, &reg_val, &fail_res);
> + if (ret)
> + return ret;
> +
> + instance_queue_count = (reg_val >> 16) & 0xff;
> + report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> + if (instance_queue_count != 0 || report_queue_count != 0) {
> + dev_warn(inst->dev->dev,
> + "FLUSH_INSTANCE cmd didn't reset the amount of queued commands & reports");
> + }
> +
> + /* reset our local copy of the counts */
> + p_dec_info->instance_queue_count = 0;
> + p_dec_info->report_queue_count = 0;
> +
> + return 0;
> +}
> +
> +static u32 get_bitstream_options(struct dec_info *info)
> +{
> + u32 bs_option = BSOPTION_ENABLE_EXPLICIT_END;
> +
> + if (info->stream_endflag)
> + bs_option |= BSOPTION_HIGHLIGHT_STREAM_END;
> + return bs_option;
> +}
> +
> +int wave5_vpu_dec_init_seq(struct vpu_instance *inst)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + u32 cmd_option = INIT_SEQ_NORMAL;
> + u32 reg_val, fail_res;
> + int ret;
> +
> + if (!inst->codec_info)
> + return -EINVAL;
> +
> + vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr);
> + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);
> +
> + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
> +
> + vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable);
> +
> + ret = send_firmware_command(inst, W5_INIT_SEQ, true, &reg_val, &fail_res);
> + if (ret == -ETIMEDOUT)
> + return ret;

Why timeout error case if different than other errors case ?

> +
> + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
> + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> +
> + dev_dbg(inst->dev->dev, "%s: init seq sent (queue %u : %u)\n", __func__,
> + p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
> +
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initial_info *info)
> +{
> + u32 reg_val;
> + u32 profile_compatibility_flag;
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> +
> + p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst);
> + info->rd_ptr = p_dec_info->stream_rd_ptr;
> +
> + p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC);
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE);
> + info->pic_width = ((reg_val >> 16) & 0xffff);
> + info->pic_height = (reg_val & 0xffff);
> + info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_DEC_NUM_REQUIRED_FB);
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_LEFT_RIGHT);
> + info->pic_crop_rect.left = (reg_val >> 16) & 0xffff;
> + info->pic_crop_rect.right = reg_val & 0xffff;
> + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_TOP_BOTTOM);
> + info->pic_crop_rect.top = (reg_val >> 16) & 0xffff;
> + info->pic_crop_rect.bottom = reg_val & 0xffff;
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_COLOR_SAMPLE_INFO);
> + info->luma_bitdepth = reg_val & 0xf;
> + info->chroma_bitdepth = (reg_val >> 4) & 0xf;
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_SEQ_PARAM);
> + profile_compatibility_flag = (reg_val >> 12) & 0xff;
> + info->profile = (reg_val >> 24) & 0x1f;
> +
> + if (inst->std == W_HEVC_DEC) {
> + /* guessing profile */
> + if (!info->profile) {
> + if ((profile_compatibility_flag & 0x06) == 0x06)

if (profile_compatibility_flag & 0x06)
is enough to test what you want.
same below

> + info->profile = HEVC_PROFILE_MAIN; /* main profile */
> + else if ((profile_compatibility_flag & 0x04) == 0x04)
> + info->profile = HEVC_PROFILE_MAIN10; /* main10 profile */
> + else if ((profile_compatibility_flag & 0x08) == 0x08)
> + /* main still picture profile */
> + info->profile = HEVC_PROFILE_STILLPICTURE;
> + else
> + info->profile = HEVC_PROFILE_MAIN; /* for old version HM */
> + }
> + } else if (inst->std == W_AVC_DEC) {
> + info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val);
> + }
> +
> + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
> + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
> + p_dec_info->vlc_buf_size = info->vlc_buf_size;
> + p_dec_info->param_buf_size = info->param_buf_size;
> +}
> +
> +int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info)
> +{
> + int ret;
> + u32 reg_val;
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> +
> + vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN);
> +
> + /* send QUERY cmd */
> + ret = wave5_send_query(inst, GET_RESULT);
> + if (ret) {
> + if (ret == -EIO) {
> + reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
> + PRINT_REG_ERR(inst->dev, reg_val);
> + }
> + return ret;
> + }
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
> +
> + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
> + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> +
> + dev_dbg(inst->dev->dev, "%s: init seq complete (queue %u : %u)\n", __func__,
> + p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
> +
> + /* this is not a fatal error, set ret to -EIO but don't return immediately */
> + if (vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_SUCCESS) != 1) {
> + info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_DEC_ERR_INFO);
> + ret = -EIO;
> + }
> +
> + wave5_get_dec_seq_result(inst, info);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_buffer *fb_arr,
> + enum tiled_map_type map_type, unsigned int count)
> +{
> + int ret;
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + struct dec_initial_info *init_info = &p_dec_info->initial_info;
> + size_t remain, idx, j, i, cnt_8_chunk, size;
> + u32 start_no, end_no;
> + u32 reg_val, cbcr_interleave, nv21, pic_size;
> + u32 addr_y, addr_cb, addr_cr;
> + u32 mv_col_size, frame_width, frame_height, fbc_y_tbl_size, fbc_c_tbl_size;
> + struct vpu_buf vb_buf;
> + bool justified = WTL_RIGHT_JUSTIFIED;
> + u32 format_no = WTL_PIXEL_8BIT;
> + u32 color_format = 0;
> + u32 pixel_order = 1;
> + u32 bwb_flag = (map_type == LINEAR_FRAME_MAP) ? 1 : 0;
> +
> + cbcr_interleave = inst->cbcr_interleave;
> + nv21 = inst->nv21;
> + mv_col_size = 0;
> + fbc_y_tbl_size = 0;
> + fbc_c_tbl_size = 0;
> +
> + if (map_type >= COMPRESSED_FRAME_MAP) {
> + cbcr_interleave = 0;
> + nv21 = 0;
> +
> + switch (inst->std) {
> + case W_HEVC_DEC:
> + mv_col_size = WAVE5_DEC_HEVC_BUF_SIZE(init_info->pic_width,
> + init_info->pic_height);
> + break;
> + case W_AVC_DEC:
> + mv_col_size = WAVE5_DEC_AVC_BUF_SIZE(init_info->pic_width,
> + init_info->pic_height);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) {
> + size = ALIGN(ALIGN(mv_col_size, 16), BUFFER_MARGIN) + BUFFER_MARGIN;
> + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_mv, count, size);
> + if (ret)
> + goto free_mv_buffers;
> + }
> +
> + frame_width = init_info->pic_width;
> + frame_height = init_info->pic_height;
> + fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(frame_width, frame_height), 16);
> + fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(frame_width, frame_height), 16);
> +
> + size = ALIGN(fbc_y_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN;
> + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_y_tbl, count, size);
> + if (ret)
> + goto free_fbc_y_tbl_buffers;
> +
> + size = ALIGN(fbc_c_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN;
> + ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_c_tbl, count, size);
> + if (ret)
> + goto free_fbc_c_tbl_buffers;
> +
> + pic_size = (init_info->pic_width << 16) | (init_info->pic_height);
> +
> + vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) +
> + (p_dec_info->param_buf_size * COMMAND_QUEUE_DEPTH);
> + vb_buf.daddr = 0;
> +
> + if (vb_buf.size != p_dec_info->vb_task.size) {
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
> + ret = wave5_vdi_allocate_dma_memory(inst->dev, &vb_buf);
> + if (ret)
> + goto free_fbc_c_tbl_buffers;
> +
> + p_dec_info->vb_task = vb_buf;
> + }
> +
> + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
> + p_dec_info->vb_task.daddr);
> + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_buf.size);
> + } else {
> + pic_size = (init_info->pic_width << 16) | (init_info->pic_height);
> +
> + if (inst->output_format == FORMAT_422)
> + color_format = 1;
> + else
> + color_format = 0;

you can remove the else since color_format been initialize to 0.

> + }
> + vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size);
> +
> + reg_val = (bwb_flag << 28) |
> + (pixel_order << 23) |
> + (justified << 22) |
> + (format_no << 20) |
> + (color_format << 19) |
> + (nv21 << 17) |
> + (cbcr_interleave << 16) |
> + (fb_arr[0].stride);
> + vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, reg_val);
> +
> + remain = count;
> + cnt_8_chunk = DIV_ROUND_UP(count, 8);
> + idx = 0;
> + for (j = 0; j < cnt_8_chunk; j++) {
> + reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3);
> + vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val);
> + start_no = j * 8;
> + end_no = start_no + ((remain >= 8) ? 8 : remain) - 1;
> +
> + vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no);
> +
> + for (i = 0; i < 8 && i < remain; i++) {
> + addr_y = fb_arr[i + start_no].buf_y;
> + addr_cb = fb_arr[i + start_no].buf_cb;
> + addr_cr = fb_arr[i + start_no].buf_cr;
> + vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), addr_y);
> + vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4), addr_cb);
> + if (map_type >= COMPRESSED_FRAME_MAP) {
> + /* luma FBC offset table */
> + vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4),
> + p_dec_info->vb_fbc_y_tbl[idx].daddr);
> + /* chroma FBC offset table */
> + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4),
> + p_dec_info->vb_fbc_c_tbl[idx].daddr);
> + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2),
> + p_dec_info->vb_mv[idx].daddr);
> + } else {
> + vpu_write_reg(inst->dev, W5_ADDR_CR_BASE0 + (i << 4), addr_cr);
> + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), 0);
> + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), 0);
> + }
> + idx++;
> + }
> + remain -= i;
> +
> + ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL);
> + if (ret)
> + goto free_buffers;
> + }
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_SUCCESS);
> + if (!reg_val) {
> + ret = -EIO;
> + goto free_buffers;
> + }
> +
> + return 0;
> +
> +free_buffers:
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
> +free_fbc_c_tbl_buffers:
> + for (i = 0; i < count; i++)
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[i]);
> +free_fbc_y_tbl_buffers:
> + for (i = 0; i < count; i++)
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[i]);
> +free_mv_buffers:
> + for (i = 0; i < count; i++)
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[i]);
> + return ret;
> +}
> +
> +int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res)
> +{
> + u32 reg_val;
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret;
> +
> + vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr);
> + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);
> +
> + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
> +
> + /* secondary AXI */
> + reg_val = p_dec_info->sec_axi_info.use_bit_enable |
> + (p_dec_info->sec_axi_info.use_ip_enable << 9) |
> + (p_dec_info->sec_axi_info.use_lf_row_enable << 15);
> + vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val);
> +
> + /* set attributes of user buffer */
> + vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable);
> +
> + vpu_write_reg(inst->dev, W5_COMMAND_OPTION, DEC_PIC_NORMAL);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_TEMPORAL_ID_PLUS1,
> + (p_dec_info->target_spatial_id << 9) |
> + (p_dec_info->temp_id_select_mode << 8) | p_dec_info->target_temp_id);
> + vpu_write_reg(inst->dev, W5_CMD_SEQ_CHANGE_ENABLE_FLAG, p_dec_info->seq_change_mask);
> + /* When reordering is disabled we force the latency of the framebuffers */
> + vpu_write_reg(inst->dev, W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1, !p_dec_info->reorder_enable);
> +
> + ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, &reg_val, fail_res);
> + if (ret == -ETIMEDOUT)
> + return ret;
> +
> + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
> + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> +
> + dev_dbg(inst->dev->dev, "%s: dec pic sent (queue %u : %u)\n", __func__,
> + p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
> +
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result)
> +{
> + int ret;
> + u32 index, nal_unit_type, reg_val, sub_layer_info;
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN);
> +
> + /* send QUERY cmd */
> + ret = wave5_send_query(inst, GET_RESULT);
> + if (ret) {
> + if (ret == -EIO) {
> + reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
> + PRINT_REG_ERR(inst->dev, reg_val);

This is already done in wave5_vpu_firmware_command_queue_error_check() you can remove it.

> + }
> +
> + return ret;
> + }
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
> +
> + p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
> + p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> +
> + dev_dbg(inst->dev->dev, "%s: dec pic complete (queue %u : %u)\n", __func__,
> + p_dec_info->instance_queue_count, p_dec_info->report_queue_count);
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_TYPE);
> +
> + nal_unit_type = (reg_val >> 4) & 0x3f;
> +
> + if (inst->std == W_HEVC_DEC) {
> + if (reg_val & 0x04)
> + result->pic_type = PIC_TYPE_B;
> + else if (reg_val & 0x02)
> + result->pic_type = PIC_TYPE_P;
> + else if (reg_val & 0x01)
> + result->pic_type = PIC_TYPE_I;
> + else
> + result->pic_type = PIC_TYPE_MAX;
> + if ((nal_unit_type == 19 || nal_unit_type == 20) && result->pic_type == PIC_TYPE_I)
> + /* IDR_W_RADL, IDR_N_LP */
> + result->pic_type = PIC_TYPE_IDR;
> + } else if (inst->std == W_AVC_DEC) {
> + if (reg_val & 0x04)
> + result->pic_type = PIC_TYPE_B;
> + else if (reg_val & 0x02)
> + result->pic_type = PIC_TYPE_P;
> + else if (reg_val & 0x01)
> + result->pic_type = PIC_TYPE_I;
> + else
> + result->pic_type = PIC_TYPE_MAX;
> + if (nal_unit_type == 5 && result->pic_type == PIC_TYPE_I)
> + result->pic_type = PIC_TYPE_IDR;
> + }
> + index = vpu_read_reg(inst->dev, W5_RET_DEC_DISPLAY_INDEX);
> + result->index_frame_display = index;
> + index = vpu_read_reg(inst->dev, W5_RET_DEC_DECODED_INDEX);
> + result->index_frame_decoded = index;
> + result->index_frame_decoded_for_tiled = index;
> +
> + sub_layer_info = vpu_read_reg(inst->dev, W5_RET_DEC_SUB_LAYER_INFO);
> + result->temporal_id = sub_layer_info & 0x7;
> +
> + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) {
> + result->decoded_poc = -1;
> + if (result->index_frame_decoded >= 0 ||
> + result->index_frame_decoded == DECODED_IDX_FLAG_SKIP)
> + result->decoded_poc = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_POC);
> + }
> +
> + result->sequence_changed = vpu_read_reg(inst->dev, W5_RET_DEC_NOTIFICATION);
> + reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE);
> + result->dec_pic_width = reg_val >> 16;
> + result->dec_pic_height = reg_val & 0xffff;
> +
> + if (result->sequence_changed) {
> + memcpy((void *)&p_dec_info->new_seq_info, (void *)&p_dec_info->initial_info,
> + sizeof(struct dec_initial_info));
> + wave5_get_dec_seq_result(inst, &p_dec_info->new_seq_info);
> + }
> +
> + result->dec_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_DEC_HOST_CMD_TICK);
> + result->dec_decode_end_tick = vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_ENC_TICK);
> +
> + if (!p_dec_info->first_cycle_check) {
> + result->frame_cycle =
> + (result->dec_decode_end_tick - result->dec_host_cmd_tick) *
> + p_dec_info->cycle_per_tick;
> + vpu_dev->last_performance_cycles = result->dec_decode_end_tick;
> + p_dec_info->first_cycle_check = true;
> + } else if (result->index_frame_decoded_for_tiled != -1) {
> + result->frame_cycle =
> + (result->dec_decode_end_tick - vpu_dev->last_performance_cycles) *
> + p_dec_info->cycle_per_tick;
> + vpu_dev->last_performance_cycles = result->dec_decode_end_tick;
> + if (vpu_dev->last_performance_cycles < result->dec_host_cmd_tick)
> + result->frame_cycle =
> + (result->dec_decode_end_tick - result->dec_host_cmd_tick) *
> + p_dec_info->cycle_per_tick;
> + }
> +
> + /* no remaining command. reset frame cycle. */
> + if (p_dec_info->instance_queue_count == 0 && p_dec_info->report_queue_count == 0)
> + p_dec_info->first_cycle_check = false;
> +
> + return 0;
> +}
> +
> +int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size)
> +{
> + struct vpu_buf *common_vb;
> + dma_addr_t code_base, temp_base;
> + dma_addr_t old_code_base, temp_size;
> + u32 code_size, reason_code;
> + u32 reg_val;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + common_vb = &vpu_dev->common_mem;
> +
> + code_base = common_vb->daddr;
> + /* ALIGN TO 4KB */
> + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
> + if (code_size < size * 2)
> + return -EINVAL;
> + temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET;
> + temp_size = WAVE5_TEMPBUF_SIZE;
> +
> + old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR);
> +
> + if (old_code_base != code_base + W5_REMAP_INDEX1 * W5_REMAP_MAX_SIZE) {
> + int ret;
> +
> + ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size);
> + if (ret < 0) {
> + dev_err(vpu_dev->dev,
> + "VPU init, Writing firmware to common buffer, fail: %d\n", ret);
> + return ret;
> + }
> +
> + vpu_write_reg(vpu_dev, W5_PO_CONF, 0);
> +
> + ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT);
> + if (ret < 0) {
> + dev_err(vpu_dev->dev, "VPU init, Resetting the VPU, fail: %d\n", ret);
> + return ret;
> + }
> +
> + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
> + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);
> +
> + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
> + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
> + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
> + vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base);
> + vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size);
> +
> + /* These register must be reset explicitly */
> + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
> + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
> +
> + /* Encoder interrupt */
> + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
> + reg_val |= BIT(INT_WAVE5_ENC_PIC);
> + reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
> + /* Decoder interrupt */
> + reg_val |= BIT(INT_WAVE5_INIT_SEQ);
> + reg_val |= BIT(INT_WAVE5_DEC_PIC);
> + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
> + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
> +
> + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
> + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
> + reg_val = ((WAVE5_PROC_AXI_ID << 28) |
> + (WAVE5_PRP_AXI_ID << 24) |
> + (WAVE5_FBD_Y_AXI_ID << 20) |
> + (WAVE5_FBC_Y_AXI_ID << 16) |
> + (WAVE5_FBD_C_AXI_ID << 12) |
> + (WAVE5_FBC_C_AXI_ID << 8) |
> + (WAVE5_PRI_AXI_ID << 4) |
> + WAVE5_SEC_AXI_ID);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
> + }
> +
> + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
> + vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
> + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
> +
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
> + if (ret) {
> + dev_err(vpu_dev->dev, "VPU reinit(W5_VPU_REMAP_CORE_START) timeout\n");
> + return ret;
> + }
> +
> + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
> + if (ret)
> + return ret;
> + }
> +
> + return setup_wave5_properties(dev);
> +}
> +
> +static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code,
> + size_t size)
> +{
> + u32 reg_val;
> + struct vpu_buf *common_vb;
> + dma_addr_t code_base;
> + u32 code_size, reason_code;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> + int ret;
> +
> + if (i_sleep_wake) {
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
> + if (ret)
> + return ret;
> +
> + /*
> + * Declare who has ownership for the host interface access
> + * 1 = VPU
> + * 0 = Host processor
> + */
> + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
> + vpu_write_reg(vpu_dev, W5_COMMAND, W5_SLEEP_VPU);
> + /* Send an interrupt named HOST to the VPU */
> + vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);
> +
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
> + if (ret)
> + return ret;
> + } else { /* restore */
> + common_vb = &vpu_dev->common_mem;
> +
> + code_base = common_vb->daddr;
> + /* ALIGN TO 4KB */
> + code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
> + if (code_size < size * 2) {
> + dev_err(dev, "size too small\n");
> + return -EINVAL;
> + }
> +
> + /* Power on without DEBUG mode */
> + vpu_write_reg(vpu_dev, W5_PO_CONF, 0);
> +
> + remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
> + remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);
> +
> + vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
> + vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
> + vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
> +
> + /* These register must be reset explicitly */
> + vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
> + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
> +
> + /* Encoder interrupt */
> + reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
> + reg_val |= BIT(INT_WAVE5_ENC_PIC);
> + reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
> + /* Decoder interrupt */
> + reg_val |= BIT(INT_WAVE5_INIT_SEQ);
> + reg_val |= BIT(INT_WAVE5_DEC_PIC);
> + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
> + vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
> +
> + reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
> + if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
> + reg_val = ((WAVE5_PROC_AXI_ID << 28) |
> + (WAVE5_PRP_AXI_ID << 24) |
> + (WAVE5_FBD_Y_AXI_ID << 20) |
> + (WAVE5_FBC_Y_AXI_ID << 16) |
> + (WAVE5_FBD_C_AXI_ID << 12) |
> + (WAVE5_FBC_C_AXI_ID << 8) |
> + (WAVE5_PRI_AXI_ID << 4) |
> + WAVE5_SEC_AXI_ID);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
> + }
> +
> + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
> + vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU);
> + /* Start VPU after settings */
> + vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
> +
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
> + if (ret) {
> + dev_err(vpu_dev->dev, "VPU wakeup(W5_VPU_REMAP_CORE_START) timeout\n");
> + return ret;
> + }
> +
> + return wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
> + }
> +
> + return 0;
> +}
> +
> +int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode)
> +{
> + u32 val = 0;
> + int ret = 0;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> + struct vpu_attr *p_attr = &vpu_dev->attr;
> + /* VPU doesn't send response. force to set BUSY flag to 0. */
> + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 0);
> +
> + if (reset_mode == SW_RESET_SAFETY) {
> + ret = wave5_vpu_sleep_wake(dev, true, NULL, 0);
> + if (ret)
> + return ret;
> + }
> +
> + val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
> + if ((val >> 16) & 0x1)
> + p_attr->support_backbone = true;
> + if ((val >> 22) & 0x1)
> + p_attr->support_vcore_backbone = true;
> + if ((val >> 28) & 0x1)
> + p_attr->support_vcpu_backbone = true;
> +
> + /* waiting for completion of bus transaction */
> + if (p_attr->support_backbone) {
> + dev_dbg(dev, "%s: backbone supported\n", __func__);
> +
> + if (p_attr->support_vcore_backbone) {
> + if (p_attr->support_vcpu_backbone) {
> + /* step1 : disable request */
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0xFF);
> +
> + /* step2 : waiting for completion of bus transaction */
> + ret = wave5_wait_vcpu_bus_busy(vpu_dev,
> + W5_BACKBONE_BUS_STATUS_VCPU);
> + if (ret) {
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00);
> + return ret;
> + }
> + }
> + /* step1 : disable request */
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x7);
> +
> + /* step2 : waiting for completion of bus transaction */
> + if (wave5_wait_bus_busy(vpu_dev, W5_BACKBONE_BUS_STATUS_VCORE0)) {
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00);
> + return -EBUSY;
> + }
> + } else {
> + /* step1 : disable request */
> + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x7);
> +
> + /* step2 : waiting for completion of bus transaction */
> + if (wave5_wait_bus_busy(vpu_dev, W5_COMBINED_BACKBONE_BUS_STATUS)) {
> + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00);
> + return -EBUSY;
> + }
> + }
> + } else {
> + dev_dbg(dev, "%s: backbone NOT supported\n", __func__);
> + /* step1 : disable request */
> + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x100);
> +
> + /* step2 : waiting for completion of bus transaction */
> + ret = wave5_wait_bus_busy(vpu_dev, W5_GDI_BUS_STATUS);
> + if (ret) {
> + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00);
> + return ret;
> + }
> + }
> +
> + switch (reset_mode) {
> + case SW_RESET_ON_BOOT:
> + case SW_RESET_FORCE:
> + case SW_RESET_SAFETY:
> + val = W5_RST_BLOCK_ALL;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (val) {
> + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, val);
> +
> + ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_RESET_STATUS);
> + if (ret) {
> + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0);
> + return ret;
> + }
> + vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0);
> + }
> + /* step3 : must clear GDI_BUS_CTRL after done SW_RESET */
> + if (p_attr->support_backbone) {
> + if (p_attr->support_vcore_backbone) {
> + if (p_attr->support_vcpu_backbone)
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00);
> + wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00);
> + } else {
> + wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00);
> + }
> + } else {
> + wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00);
> + }
> + if (reset_mode == SW_RESET_SAFETY || reset_mode == SW_RESET_FORCE)
> + ret = wave5_vpu_sleep_wake(dev, false, NULL, 0);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res)
> +{
> + return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res);
> +}
> +
> +int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> +
> + p_dec_info->stream_endflag = eos ? 1 : 0;
> + vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
> + vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);
> +
> + return send_firmware_command(inst, W5_UPDATE_BS, true, NULL, NULL);
> +}
> +
> +int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret;
> +
> + vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, BIT(index));
> + vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, 0);
> + ret = wave5_send_query(inst, UPDATE_DISP_FLAG);
> +
> + if (ret) {
> + if (ret == -EIO) {
> + u32 reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
> +
> + PRINT_REG_ERR(inst->dev, reg_val);
> + }
> + return ret;
> + }
> +
> + p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC);
> +
> + return 0;
> +}
> +
> +int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index)
> +{
> + int ret;
> +
> + vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, 0);
> + vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, BIT(index));
> + ret = wave5_send_query(inst, UPDATE_DISP_FLAG);
> +
> + if (ret) {
> + if (ret == -EIO) {
> + u32 reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
> +
> + PRINT_REG_ERR(inst->dev, reg_val);

This is already done in wave5_vpu_firmware_command_queue_error_check() you can remove it.
And in above function.

> + }
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags)
> +{
> + u32 interrupt_reason;
> +
> + interrupt_reason = vpu_read_reg(inst->dev, W5_VPU_VINT_REASON_USR);
> + interrupt_reason &= ~flags;
> + vpu_write_reg(inst->dev, W5_VPU_VINT_REASON_USR, interrupt_reason);
> +
> + return 0;
> +}
> +
> +dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst)
> +{
> + int ret;
> +
> + ret = wave5_send_query(inst, GET_BS_RD_PTR);
> +
> + if (ret)
> + return inst->codec_info->dec_info.stream_rd_ptr;
> +
> + return vpu_read_reg(inst->dev, W5_RET_QUERY_DEC_BS_RD_PTR);
> +}
> +
> +int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr)
> +{
> + int ret;
> +
> + vpu_write_reg(inst->dev, W5_RET_QUERY_DEC_SET_BS_RD_PTR, addr);
> +
> + ret = wave5_send_query(inst, SET_BS_RD_PTR);
> +
> + return ret;
> +}
> +
> +/************************************************************************/
> +/* ENCODER functions */
> +/************************************************************************/
> +
> +int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst,
> + struct enc_open_param *open_param)
> +{
> + int ret;
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + u32 reg_val;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> + dma_addr_t buffer_addr;
> + size_t buffer_size;
> +
> + p_enc_info->cycle_per_tick = 256;
> + if (vpu_dev->sram_buf.size) {
> + p_enc_info->sec_axi_info.use_enc_rdo_enable = 1;
> + p_enc_info->sec_axi_info.use_enc_lf_enable = 1;
> + }
> +
> + p_enc_info->vb_work.size = WAVE521ENC_WORKBUF_SIZE;
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &p_enc_info->vb_work);
> + if (ret) {
> + memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work));
> + return ret;
> + }
> +
> + wave5_vdi_clear_memory(vpu_dev, &p_enc_info->vb_work);
> +
> + vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_enc_info->vb_work.daddr);
> + vpu_write_reg(inst->dev, W5_WORK_SIZE, p_enc_info->vb_work.size);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
> + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
> +
> + reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN;
> + vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val);
> + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
> + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1));
> +
> + /* This register must be reset explicitly */
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_VCORE_INFO, 1);
> +
> + ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
> + if (ret)
> + goto free_vb_work;
> +
> + buffer_addr = open_param->bitstream_buffer;
> + buffer_size = open_param->bitstream_buffer_size;
> + p_enc_info->stream_rd_ptr = buffer_addr;
> + p_enc_info->stream_wr_ptr = buffer_addr;
> + p_enc_info->line_buf_int_en = open_param->line_buf_int_en;
> + p_enc_info->stream_buf_start_addr = buffer_addr;
> + p_enc_info->stream_buf_size = buffer_size;
> + p_enc_info->stream_buf_end_addr = buffer_addr + buffer_size;
> + p_enc_info->stride = 0;
> + p_enc_info->initial_info_obtained = false;
> + p_enc_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER);
> +
> + return 0;
> +free_vb_work:
> + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work);

Could
memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work));
be useful here ?

> + return ret;
> +}
> +
> +static void wave5_set_enc_crop_info(u32 codec, struct enc_wave_param *param, int rot_mode,
> + int src_width, int src_height)
> +{
> + int aligned_width = (codec == W_HEVC_ENC) ? ALIGN(src_width, 32) : ALIGN(src_width, 16);
> + int aligned_height = (codec == W_HEVC_ENC) ? ALIGN(src_height, 32) : ALIGN(src_height, 16);
> + int pad_right, pad_bot;
> + int crop_right, crop_left, crop_top, crop_bot;
> + int prp_mode = rot_mode >> 1; /* remove prp_enable bit */
> +
> + if (codec == W_HEVC_ENC &&
> + (!rot_mode || prp_mode == 14)) /* prp_mode 14 : hor_mir && ver_mir && rot_180 */
> + return;
> +
> + pad_right = aligned_width - src_width;
> + pad_bot = aligned_height - src_height;
> +
> + if (param->conf_win_right > 0)
> + crop_right = param->conf_win_right + pad_right;
> + else
> + crop_right = pad_right;
> +
> + if (param->conf_win_bot > 0)
> + crop_bot = param->conf_win_bot + pad_bot;
> + else
> + crop_bot = pad_bot;
> +
> + crop_top = param->conf_win_top;
> + crop_left = param->conf_win_left;
> +
> + param->conf_win_top = crop_top;
> + param->conf_win_left = crop_left;
> + param->conf_win_bot = crop_bot;
> + param->conf_win_right = crop_right;
> +
> + switch (prp_mode) {
> + case 1:
> + case 15:
> + param->conf_win_top = crop_right;
> + param->conf_win_left = crop_top;
> + param->conf_win_bot = crop_left;
> + param->conf_win_right = crop_bot;
> + break;
> + case 2:
> + case 12:
> + param->conf_win_top = crop_bot;
> + param->conf_win_left = crop_right;
> + param->conf_win_bot = crop_top;
> + param->conf_win_right = crop_left;
> + break;
> + case 3:
> + case 13:
> + param->conf_win_top = crop_left;
> + param->conf_win_left = crop_bot;
> + param->conf_win_bot = crop_right;
> + param->conf_win_right = crop_top;
> + break;
> + case 4:
> + case 10:
> + param->conf_win_top = crop_bot;
> + param->conf_win_bot = crop_top;
> + break;
> + case 8:
> + case 6:
> + param->conf_win_left = crop_right;
> + param->conf_win_right = crop_left;
> + break;
> + case 5:
> + case 11:
> + param->conf_win_top = crop_left;
> + param->conf_win_left = crop_top;
> + param->conf_win_bot = crop_right;
> + param->conf_win_right = crop_bot;
> + break;
> + case 7:
> + case 9:
> + param->conf_win_top = crop_right;
> + param->conf_win_left = crop_bot;
> + param->conf_win_bot = crop_left;
> + param->conf_win_right = crop_top;
> + break;
> + default:
> + WARN(1, "Invalid prp_mode: %d, must be in range of 1 - 15\n", prp_mode);
> + }
> +}
> +
> +int wave5_vpu_enc_init_seq(struct vpu_instance *inst)
> +{
> + u32 reg_val = 0, rot_mir_mode, fixed_cu_size_mode = 0x7;
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + struct enc_open_param *p_open_param = &p_enc_info->open_param;
> + struct enc_wave_param *p_param = &p_open_param->wave_param;
> +
> + /*
> + * OPT_COMMON:
> + * the last SET_PARAM command should be called with OPT_COMMON
> + */
> + rot_mir_mode = 0;
> + if (p_enc_info->rotation_enable) {
> + switch (p_enc_info->rotation_angle) {
> + case 0:
> + rot_mir_mode |= NONE_ROTATE;
> + break;
> + case 90:
> + rot_mir_mode |= ROT_CLOCKWISE_90;
> + break;
> + case 180:
> + rot_mir_mode |= ROT_CLOCKWISE_180;
> + break;
> + case 270:
> + rot_mir_mode |= ROT_CLOCKWISE_270;
> + break;
> + }
> + }
> +
> + if (p_enc_info->mirror_enable) {
> + switch (p_enc_info->mirror_direction) {
> + case MIRDIR_NONE:
> + rot_mir_mode |= NONE_ROTATE;
> + break;
> + case MIRDIR_VER:
> + rot_mir_mode |= MIR_VER_FLIP;
> + break;
> + case MIRDIR_HOR:
> + rot_mir_mode |= MIR_HOR_FLIP;
> + break;
> + case MIRDIR_HOR_VER:
> + rot_mir_mode |= MIR_HOR_VER_FLIP;
> + break;
> + }
> + }
> +
> + wave5_set_enc_crop_info(inst->std, p_param, rot_mir_mode, p_open_param->pic_width,
> + p_open_param->pic_height);
> +
> + /* SET_PARAM + COMMON */
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SET_PARAM_OPTION, OPT_COMMON);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SRC_SIZE, p_open_param->pic_height << 16
> + | p_open_param->pic_width);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN, VDI_LITTLE_ENDIAN);
> +
> + reg_val = p_param->profile |
> + (p_param->level << 3) |
> + (p_param->internal_bit_depth << 14);
> + if (inst->std == W_HEVC_ENC)
> + reg_val |= (p_param->tier << 12) |
> + (p_param->tmvp_enable << 23) |
> + (p_param->sao_enable << 24) |
> + (p_param->skip_intra_trans << 25) |
> + (p_param->strong_intra_smooth_enable << 27) |
> + (p_param->en_still_picture << 30);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val);
> +
> + reg_val = (p_param->lossless_enable) |
> + (p_param->const_intra_pred_flag << 1) |
> + (p_param->lf_cross_slice_boundary_enable << 2) |
> + (p_param->wpp_enable << 4) |
> + (p_param->disable_deblk << 5) |
> + ((p_param->beta_offset_div2 & 0xF) << 6) |
> + ((p_param->tc_offset_div2 & 0xF) << 10) |
> + ((p_param->chroma_cb_qp_offset & 0x1F) << 14) |
> + ((p_param->chroma_cr_qp_offset & 0x1F) << 19) |
> + (p_param->transform8x8_enable << 29) |
> + (p_param->entropy_coding_mode << 30);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_PPS_PARAM, reg_val);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_GOP_PARAM, p_param->gop_preset_idx);
> +
> + if (inst->std == W_AVC_ENC)
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, p_param->intra_qp |
> + ((p_param->intra_period & 0x7ff) << 6) |
> + ((p_param->avc_idr_period & 0x7ff) << 17));
> + else if (inst->std == W_HEVC_ENC)
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM,
> + p_param->decoding_refresh_type | (p_param->intra_qp << 3) |
> + (p_param->intra_period << 16));
> +
> + reg_val = (p_param->rdo_skip << 2) |
> + (p_param->lambda_scaling_enable << 3) |
> + (fixed_cu_size_mode << 5) |
> + (p_param->intra_nx_n_enable << 8) |
> + (p_param->max_num_merge << 18);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RDO_PARAM, reg_val);
> +
> + if (inst->std == W_AVC_ENC)
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH,
> + p_param->intra_mb_refresh_arg << 16 | p_param->intra_mb_refresh_mode);
> + else if (inst->std == W_HEVC_ENC)
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH,
> + p_param->intra_refresh_arg << 16 | p_param->intra_refresh_mode);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_FRAME_RATE, p_open_param->frame_rate_info);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_TARGET_RATE, p_open_param->bit_rate);
> +
> + reg_val = p_open_param->rc_enable |
> + (p_param->hvs_qp_enable << 2) |
> + (p_param->hvs_qp_scale << 4) |
> + ((p_param->initial_rc_qp & 0x3F) << 14) |
> + (p_open_param->vbv_buffer_size << 20);
> + if (inst->std == W_AVC_ENC)
> + reg_val |= (p_param->mb_level_rc_enable << 1);
> + else if (inst->std == W_HEVC_ENC)
> + reg_val |= (p_param->cu_level_rc_enable << 1);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_PARAM, 0);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM,
> + p_param->rc_weight_buf << 8 | p_param->rc_weight_param);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_MIN_MAX_QP, p_param->min_qp_i |
> + (p_param->max_qp_i << 6) | (p_param->hvs_max_delta_qp << 12));
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP, p_param->min_qp_p |
> + (p_param->max_qp_p << 6) | (p_param->min_qp_b << 12) |
> + (p_param->max_qp_b << 18));
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT,
> + p_param->conf_win_bot << 16 | p_param->conf_win_top);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT,
> + p_param->conf_win_right << 16 | p_param->conf_win_left);
> +
> + if (inst->std == W_AVC_ENC)
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE,
> + p_param->avc_slice_arg << 16 | p_param->avc_slice_mode);
> + else if (inst->std == W_HEVC_ENC)
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE,
> + p_param->independ_slice_mode_arg << 16 |
> + p_param->independ_slice_mode);
> +
> + if (inst->std == W_HEVC_ENC) {
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_DEPENDENT_SLICE,
> + p_param->depend_slice_mode_arg << 16 | p_param->depend_slice_mode);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_PARAM, 0);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_WEIGHT,
> + p_param->nr_intra_weight_y |
> + (p_param->nr_intra_weight_cb << 5) |
> + (p_param->nr_intra_weight_cr << 10) |
> + (p_param->nr_inter_weight_y << 15) |
> + (p_param->nr_inter_weight_cb << 20) |
> + (p_param->nr_inter_weight_cr << 25));
> + }
> +
> + return send_firmware_command(inst, W5_ENC_SET_PARAM, true, NULL, NULL);
> +}
> +
> +int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info)
> +{
> + int ret;
> + u32 reg_val;
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> +
> + /* send QUERY cmd */
> + ret = wave5_send_query(inst, GET_RESULT);
> + if (ret) {
> + if (ret == -EIO) {
> + reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);

Again you can remove it ;-)

> + PRINT_REG_ERR(inst->dev, reg_val);
> + }
> + return ret;
> + }
> +
> + dev_dbg(inst->dev->dev, "%s: init seq\n", __func__);
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
> +
> + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff;
> + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> +
> + if (vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS) != 1) {
> + info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO);
> + ret = -EIO;
> + } else {
> + info->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO);
> + }
> +
> + info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_ENC_NUM_REQUIRED_FB);
> + info->min_src_frame_count = vpu_read_reg(inst->dev, W5_RET_ENC_MIN_SRC_BUF_NUM);
> + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
> + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
> + p_enc_info->vlc_buf_size = info->vlc_buf_size;
> + p_enc_info->param_buf_size = info->param_buf_size;
> +
> + return ret;
> +}
> +
> +static u32 calculate_luma_stride(u32 width, u32 bit_depth)
> +{
> + return ALIGN(ALIGN(width, 16) * ((bit_depth > 8) ? 5 : 4), 32);
> +}
> +
> +static u32 calculate_chroma_stride(u32 width, u32 bit_depth)
> +{
> + return ALIGN(ALIGN(width / 2, 16) * ((bit_depth > 8) ? 5 : 4), 32);
> +}
> +
> +int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst,
> + struct frame_buffer *fb_arr, enum tiled_map_type map_type,
> + unsigned int count)
> +{
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> + int ret = 0;
> + u32 stride;
> + u32 start_no, end_no;
> + size_t remain, idx, j, i, cnt_8_chunk;
> + u32 reg_val = 0, pic_size = 0, mv_col_size, fbc_y_tbl_size, fbc_c_tbl_size;
> + u32 sub_sampled_size = 0;
> + u32 luma_stride, chroma_stride;
> + u32 buf_height = 0, buf_width = 0;
> + u32 bit_depth;
> + bool avc_encoding = (inst->std == W_AVC_ENC);
> + struct vpu_buf vb_mv = {0};
> + struct vpu_buf vb_fbc_y_tbl = {0};
> + struct vpu_buf vb_fbc_c_tbl = {0};
> + struct vpu_buf vb_sub_sam_buf = {0};
> + struct vpu_buf vb_task = {0};
> + struct enc_open_param *p_open_param;
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> +
> + p_open_param = &p_enc_info->open_param;
> + mv_col_size = 0;
> + fbc_y_tbl_size = 0;
> + fbc_c_tbl_size = 0;
> + stride = p_enc_info->stride;
> + bit_depth = p_open_param->wave_param.internal_bit_depth;
> +
> + if (avc_encoding) {
> + buf_width = ALIGN(p_open_param->pic_width, 16);
> + buf_height = ALIGN(p_open_param->pic_height, 16);
> +
> + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
> + !(p_enc_info->rotation_angle == 180 &&
> + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
> + buf_width = ALIGN(p_open_param->pic_width, 16);
> + buf_height = ALIGN(p_open_param->pic_height, 16);
> + }
> +
> + if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) {
> + buf_width = ALIGN(p_open_param->pic_height, 16);
> + buf_height = ALIGN(p_open_param->pic_width, 16);
> + }
> + } else {
> + buf_width = ALIGN(p_open_param->pic_width, 8);
> + buf_height = ALIGN(p_open_param->pic_height, 8);
> +
> + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
> + !(p_enc_info->rotation_angle == 180 &&
> + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
> + buf_width = ALIGN(p_open_param->pic_width, 32);
> + buf_height = ALIGN(p_open_param->pic_height, 32);
> + }
> +
> + if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) {
> + buf_width = ALIGN(p_open_param->pic_height, 32);
> + buf_height = ALIGN(p_open_param->pic_width, 32);
> + }
> + }
> +
> + pic_size = (buf_width << 16) | buf_height;
> +
> + if (avc_encoding) {
> + mv_col_size = WAVE5_ENC_AVC_BUF_SIZE(buf_width, buf_height);
> + vb_mv.daddr = 0;
> + vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
> + } else {
> + mv_col_size = WAVE5_ENC_HEVC_BUF_SIZE(buf_width, buf_height);
> + mv_col_size = ALIGN(mv_col_size, 16);
> + vb_mv.daddr = 0;
> + vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
> + }
> +
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_mv);
> + if (ret)
> + return ret;
> +
> + p_enc_info->vb_mv = vb_mv;
> +
> + fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(buf_width, buf_height), 16);
> + fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(buf_width, buf_height), 16);
> +
> + vb_fbc_y_tbl.daddr = 0;
> + vb_fbc_y_tbl.size = ALIGN(fbc_y_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_y_tbl);
> + if (ret)
> + goto free_vb_fbc_y_tbl;
> +
> + p_enc_info->vb_fbc_y_tbl = vb_fbc_y_tbl;
> +
> + vb_fbc_c_tbl.daddr = 0;
> + vb_fbc_c_tbl.size = ALIGN(fbc_c_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_c_tbl);
> + if (ret)
> + goto free_vb_fbc_c_tbl;
> +
> + p_enc_info->vb_fbc_c_tbl = vb_fbc_c_tbl;
> +
> + if (avc_encoding)
> + sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE_AVC(buf_width, buf_height);
> + else
> + sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE(buf_width, buf_height);
> + vb_sub_sam_buf.size = ALIGN(sub_sampled_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
> + vb_sub_sam_buf.daddr = 0;
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_sub_sam_buf);
> + if (ret)
> + goto free_vb_sam_buf;
> +
> + p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf;
> +
> + vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) +
> + (p_enc_info->param_buf_size * COMMAND_QUEUE_DEPTH);
> + vb_task.daddr = 0;
> + if (p_enc_info->vb_task.size == 0) {
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task);
> + if (ret)
> + goto free_vb_task;
> +
> + p_enc_info->vb_task = vb_task;
> +
> + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
> + p_enc_info->vb_task.daddr);
> + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_task.size);
> + }
> +
> + /* set sub-sampled buffer base addr */
> + vpu_write_reg(inst->dev, W5_ADDR_SUB_SAMPLED_FB_BASE, vb_sub_sam_buf.daddr);
> + /* set sub-sampled buffer size for one frame */
> + vpu_write_reg(inst->dev, W5_SUB_SAMPLED_ONE_FB_SIZE, sub_sampled_size);
> +
> + vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size);
> +
> + /* set stride of luma/chroma for compressed buffer */
> + if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
> + !(p_enc_info->rotation_angle == 180 &&
> + p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
> + luma_stride = calculate_luma_stride(buf_width, bit_depth);
> + chroma_stride = calculate_chroma_stride(buf_width / 2, bit_depth);
> + } else {
> + luma_stride = calculate_luma_stride(p_open_param->pic_width, bit_depth);
> + chroma_stride = calculate_chroma_stride(p_open_param->pic_width / 2, bit_depth);
> + }
> +
> + vpu_write_reg(inst->dev, W5_FBC_STRIDE, luma_stride << 16 | chroma_stride);
> + vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, stride);
> +
> + remain = count;
> + cnt_8_chunk = DIV_ROUND_UP(count, 8);
> + idx = 0;
> + for (j = 0; j < cnt_8_chunk; j++) {
> + reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3);
> + vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val);
> + start_no = j * 8;
> + end_no = start_no + ((remain >= 8) ? 8 : remain) - 1;
> +
> + vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no);
> +
> + for (i = 0; i < 8 && i < remain; i++) {
> + vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), fb_arr[i +
> + start_no].buf_y);
> + vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4),
> + fb_arr[i + start_no].buf_cb);
> + /* luma FBC offset table */
> + vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4),
> + vb_fbc_y_tbl.daddr + idx * fbc_y_tbl_size);
> + /* chroma FBC offset table */
> + vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4),
> + vb_fbc_c_tbl.daddr + idx * fbc_c_tbl_size);
> +
> + vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2),
> + vb_mv.daddr + idx * mv_col_size);
> + idx++;
> + }
> + remain -= i;
> +
> + ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL);
> + if (ret)
> + goto free_vb_mem;
> + }
> +
> + ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
> + if (ret)
> + goto free_vb_mem;
> +
> + return ret;
> +
> +free_vb_mem:
> + wave5_vdi_free_dma_memory(vpu_dev, &vb_task);
> +free_vb_task:
> + wave5_vdi_free_dma_memory(vpu_dev, &vb_sub_sam_buf);
> +free_vb_sam_buf:
> + wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_c_tbl);
> +free_vb_fbc_c_tbl:
> + wave5_vdi_free_dma_memory(vpu_dev, &vb_fbc_y_tbl);
> +free_vb_fbc_y_tbl:
> + wave5_vdi_free_dma_memory(vpu_dev, &vb_mv);
> + return ret;
> +}
> +
> +int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res)
> +{
> + u32 src_frame_format;
> + u32 reg_val = 0;
> + u32 src_stride_c = 0;
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + struct frame_buffer *p_src_frame = option->source_frame;
> + struct enc_open_param *p_open_param = &p_enc_info->open_param;
> + bool justified = WTL_RIGHT_JUSTIFIED;
> + u32 format_no = WTL_PIXEL_8BIT;
> + int ret;
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_BS_START_ADDR, option->pic_stream_buffer_addr);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_BS_SIZE, option->pic_stream_buffer_size);
> + p_enc_info->stream_buf_start_addr = option->pic_stream_buffer_addr;
> + p_enc_info->stream_buf_size = option->pic_stream_buffer_size;
> + p_enc_info->stream_buf_end_addr =
> + option->pic_stream_buffer_addr + option->pic_stream_buffer_size;
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_AXI_SEL, DEFAULT_SRC_AXI);
> + /* secondary AXI */
> + reg_val = (p_enc_info->sec_axi_info.use_enc_rdo_enable << 11) |
> + (p_enc_info->sec_axi_info.use_enc_lf_enable << 15);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_USE_SEC_AXI, reg_val);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_REPORT_PARAM, 0);
> +
> + /*
> + * CODEOPT_ENC_VCL is used to implicitly encode header/headers to generate bitstream.
> + * (use ENC_PUT_VIDEO_HEADER for give_command to encode only a header)
> + */
> + if (option->code_option.implicit_header_encode)
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION,
> + CODEOPT_ENC_HEADER_IMPLICIT | CODEOPT_ENC_VCL |
> + (option->code_option.encode_aud << 5) |
> + (option->code_option.encode_eos << 6) |
> + (option->code_option.encode_eob << 7));
> + else
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_CODE_OPTION,
> + option->code_option.implicit_header_encode |
> + (option->code_option.encode_vcl << 1) |
> + (option->code_option.encode_vps << 2) |
> + (option->code_option.encode_sps << 3) |
> + (option->code_option.encode_pps << 4) |
> + (option->code_option.encode_aud << 5) |
> + (option->code_option.encode_eos << 6) |
> + (option->code_option.encode_eob << 7));
> +
> + if (option->src_end_flag)
> + /* no more source images. */
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, 0xFFFFFFFF);
> + else
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_PIC_IDX, option->src_idx);
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_Y, p_src_frame->buf_y);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_U, p_src_frame->buf_cb);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_ADDR_V, p_src_frame->buf_cr);
> +
> + switch (p_open_param->src_format) {
> + case FORMAT_420:
> + case FORMAT_422:
> + case FORMAT_YUYV:
> + case FORMAT_YVYU:
> + case FORMAT_UYVY:
> + case FORMAT_VYUY:
> + justified = WTL_LEFT_JUSTIFIED;
> + format_no = WTL_PIXEL_8BIT;
> + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
> + (p_src_frame->stride / 2);
> + src_stride_c = (p_open_param->src_format == FORMAT_422) ? src_stride_c * 2 :
> + src_stride_c;
> + break;
> + case FORMAT_420_P10_16BIT_MSB:
> + case FORMAT_422_P10_16BIT_MSB:
> + case FORMAT_YUYV_P10_16BIT_MSB:
> + case FORMAT_YVYU_P10_16BIT_MSB:
> + case FORMAT_UYVY_P10_16BIT_MSB:
> + case FORMAT_VYUY_P10_16BIT_MSB:
> + justified = WTL_RIGHT_JUSTIFIED;
> + format_no = WTL_PIXEL_16BIT;
> + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
> + (p_src_frame->stride / 2);
> + src_stride_c = (p_open_param->src_format ==
> + FORMAT_422_P10_16BIT_MSB) ? src_stride_c * 2 : src_stride_c;
> + break;
> + case FORMAT_420_P10_16BIT_LSB:
> + case FORMAT_422_P10_16BIT_LSB:
> + case FORMAT_YUYV_P10_16BIT_LSB:
> + case FORMAT_YVYU_P10_16BIT_LSB:
> + case FORMAT_UYVY_P10_16BIT_LSB:
> + case FORMAT_VYUY_P10_16BIT_LSB:
> + justified = WTL_LEFT_JUSTIFIED;
> + format_no = WTL_PIXEL_16BIT;
> + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
> + (p_src_frame->stride / 2);
> + src_stride_c = (p_open_param->src_format ==
> + FORMAT_422_P10_16BIT_LSB) ? src_stride_c * 2 : src_stride_c;
> + break;
> + case FORMAT_420_P10_32BIT_MSB:
> + case FORMAT_422_P10_32BIT_MSB:
> + case FORMAT_YUYV_P10_32BIT_MSB:
> + case FORMAT_YVYU_P10_32BIT_MSB:
> + case FORMAT_UYVY_P10_32BIT_MSB:
> + case FORMAT_VYUY_P10_32BIT_MSB:
> + justified = WTL_RIGHT_JUSTIFIED;
> + format_no = WTL_PIXEL_32BIT;
> + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
> + ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave);
> + src_stride_c = (p_open_param->src_format ==
> + FORMAT_422_P10_32BIT_MSB) ? src_stride_c * 2 : src_stride_c;
> + break;
> + case FORMAT_420_P10_32BIT_LSB:
> + case FORMAT_422_P10_32BIT_LSB:
> + case FORMAT_YUYV_P10_32BIT_LSB:
> + case FORMAT_YVYU_P10_32BIT_LSB:
> + case FORMAT_UYVY_P10_32BIT_LSB:
> + case FORMAT_VYUY_P10_32BIT_LSB:
> + justified = WTL_LEFT_JUSTIFIED;
> + format_no = WTL_PIXEL_32BIT;
> + src_stride_c = inst->cbcr_interleave ? p_src_frame->stride :
> + ALIGN(p_src_frame->stride / 2, 16) * BIT(inst->cbcr_interleave);
> + src_stride_c = (p_open_param->src_format ==
> + FORMAT_422_P10_32BIT_LSB) ? src_stride_c * 2 : src_stride_c;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + src_frame_format = (inst->cbcr_interleave << 1) | (inst->nv21);
> + switch (p_open_param->packed_format) {
> + case PACKED_YUYV:
> + src_frame_format = 4;
> + break;
> + case PACKED_YVYU:
> + src_frame_format = 5;
> + break;
> + case PACKED_UYVY:
> + src_frame_format = 6;
> + break;
> + case PACKED_VYUY:
> + src_frame_format = 7;
> + break;
> + default:
> + break;
> + }
> +
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_STRIDE,
> + (p_src_frame->stride << 16) | src_stride_c);
> + vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_FORMAT, src_frame_format |
> + (format_no << 3) | (justified << 5) | (PIC_SRC_ENDIANNESS_BIG_ENDIAN << 6));
> +
> + ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, &reg_val, fail_res);
> + if (ret == -ETIMEDOUT)
> + return ret;
> +
> + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff;
> + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> +
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result)
> +{
> + int ret;
> + u32 encoding_success;
> + u32 reg_val;
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + ret = wave5_send_query(inst, GET_RESULT);
> + if (ret) {
> + if (ret == -EIO) {
> + reg_val = vpu_read_reg(inst->dev, W5_RET_FAIL_REASON);
> + PRINT_REG_ERR(inst->dev, reg_val);

You can remove it.
I stop mention it from now, you know how to fix it.

> + }
> + return ret;
> + }
> + dev_dbg(inst->dev->dev, "%s: enc pic complete\n", __func__);
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);
> +
> + p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff;
> + p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);
> +
> + encoding_success = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS);
> + if (!encoding_success) {
> + result->error_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO);
> + return -EIO;
> + }
> +
> + result->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO);
> +
> + reg_val = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_TYPE);
> + result->pic_type = reg_val & 0xFFFF;
> +
> + result->enc_vcl_nut = vpu_read_reg(inst->dev, W5_RET_ENC_VCL_NUT);
> + /*
> + * To get the reconstructed frame use the following index on
> + * inst->frame_buf
> + */
> + result->recon_frame_index = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_IDX);
> + result->enc_pic_byte = vpu_read_reg(inst->dev, W5_RET_ENC_PIC_BYTE);
> + result->enc_src_idx = vpu_read_reg(inst->dev, W5_RET_ENC_USED_SRC_IDX);
> + p_enc_info->stream_wr_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_WR_PTR);
> + p_enc_info->stream_rd_ptr = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR);
> +
> + result->bitstream_buffer = vpu_read_reg(inst->dev, W5_RET_ENC_RD_PTR);
> + result->rd_ptr = p_enc_info->stream_rd_ptr;
> + result->wr_ptr = p_enc_info->stream_wr_ptr;
> +
> + /*result for header only(no vcl) encoding */
> + if (result->recon_frame_index == RECON_IDX_FLAG_HEADER_ONLY)
> + result->bitstream_size = result->enc_pic_byte;
> + else if (result->recon_frame_index < 0)
> + result->bitstream_size = 0;
> + else
> + result->bitstream_size = result->enc_pic_byte;
> +
> + result->enc_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_ENC_HOST_CMD_TICK);
> + result->enc_encode_end_tick = vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_END_TICK);
> +
> + if (!p_enc_info->first_cycle_check) {
> + result->frame_cycle = (result->enc_encode_end_tick - result->enc_host_cmd_tick) *
> + p_enc_info->cycle_per_tick;
> + p_enc_info->first_cycle_check = true;
> + } else {
> + result->frame_cycle =
> + (result->enc_encode_end_tick - vpu_dev->last_performance_cycles) *
> + p_enc_info->cycle_per_tick;
> + if (vpu_dev->last_performance_cycles < result->enc_host_cmd_tick)
> + result->frame_cycle = (result->enc_encode_end_tick -
> + result->enc_host_cmd_tick) * p_enc_info->cycle_per_tick;
> + }
> + vpu_dev->last_performance_cycles = result->enc_encode_end_tick;
> +
> + return 0;
> +}
> +
> +int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res)
> +{
> + return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res);
> +}
> +
> +static bool wave5_vpu_enc_check_common_param_valid(struct vpu_instance *inst,
> + struct enc_open_param *open_param)
> +{
> + bool low_delay = true;
> + struct enc_wave_param *param = &open_param->wave_param;
> + struct vpu_device *vpu_dev = inst->dev;
> + struct device *dev = vpu_dev->dev;
> + u32 num_ctu_row = (open_param->pic_height + 64 - 1) / 64;
> + u32 num_ctu_col = (open_param->pic_width + 64 - 1) / 64;
> + u32 ctu_sz = num_ctu_col * num_ctu_row;
> +
> + if (inst->std == W_HEVC_ENC && low_delay &&
> + param->decoding_refresh_type == DEC_REFRESH_TYPE_CRA) {
> + dev_warn(dev,
> + "dec_refresh_type(CRA) shouldn't be used together with low delay GOP\n");
> + dev_warn(dev, "Suggested configuration parameter: decoding refresh type (IDR)\n");
> + param->decoding_refresh_type = 2;
> + }
> +
> + if (param->wpp_enable && param->independ_slice_mode) {
> + unsigned int num_ctb_in_width = ALIGN(open_param->pic_width, 64) >> 6;
> +
> + if (param->independ_slice_mode_arg % num_ctb_in_width) {
> + dev_err(dev, "independ_slice_mode_arg %u must be a multiple of %u\n",
> + param->independ_slice_mode_arg, num_ctb_in_width);
> + return false;
> + }
> + }
> +
> + /* multi-slice & wpp */
> + if (param->wpp_enable && param->depend_slice_mode) {
> + dev_err(dev, "wpp_enable && depend_slice_mode cannot be used simultaneously\n");
> + return false;
> + }
> +
> + if (!param->independ_slice_mode && param->depend_slice_mode) {
> + dev_err(dev, "depend_slice_mode requires independ_slice_mode\n");
> + return false;
> + } else if (param->independ_slice_mode &&
> + param->depend_slice_mode == DEPEND_SLICE_MODE_RECOMMENDED &&
> + param->independ_slice_mode_arg < param->depend_slice_mode_arg) {
> + dev_err(dev, "independ_slice_mode_arg: %u must be smaller than %u\n",
> + param->independ_slice_mode_arg, param->depend_slice_mode_arg);
> + return false;
> + }
> +
> + if (param->independ_slice_mode && param->independ_slice_mode_arg > 65535) {
> + dev_err(dev, "independ_slice_mode_arg: %u must be smaller than 65535\n",
> + param->independ_slice_mode_arg);
> + return false;
> + }
> +
> + if (param->depend_slice_mode && param->depend_slice_mode_arg > 65535) {
> + dev_err(dev, "depend_slice_mode_arg: %u must be smaller than 65535\n",
> + param->depend_slice_mode_arg);
> + return false;
> + }
> +
> + if (param->conf_win_top % 2) {
> + dev_err(dev, "conf_win_top: %u, must be a multiple of 2\n", param->conf_win_top);
> + return false;
> + }
> +
> + if (param->conf_win_bot % 2) {
> + dev_err(dev, "conf_win_bot: %u, must be a multiple of 2\n", param->conf_win_bot);
> + return false;
> + }
> +
> + if (param->conf_win_left % 2) {
> + dev_err(dev, "conf_win_left: %u, must be a multiple of 2\n", param->conf_win_left);
> + return false;
> + }
> +
> + if (param->conf_win_right % 2) {
> + dev_err(dev, "conf_win_right: %u, Must be a multiple of 2\n",
> + param->conf_win_right);
> + return false;
> + }
> +
> + if (param->lossless_enable && open_param->rc_enable) {
> + dev_err(dev, "option rate_control cannot be used with lossless_coding\n");
> + return false;
> + }
> +
> + if (param->lossless_enable && !param->skip_intra_trans) {
> + dev_err(dev, "option intra_trans_skip must be enabled with lossless_coding\n");
> + return false;
> + }
> +
> + /* intra refresh */
> + if (param->intra_refresh_mode && param->intra_refresh_arg == 0) {
> + dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u must be > 0\n",
> + param->intra_refresh_mode, param->intra_refresh_arg);
> + return false;
> + }
> + switch (param->intra_refresh_mode) {
> + case REFRESH_MODE_CTU_ROWS:
> + if (param->intra_mb_refresh_arg > num_ctu_row)
> + goto invalid_refresh_argument;
> + break;
> + case REFRESH_MODE_CTU_COLUMNS:
> + if (param->intra_refresh_arg > num_ctu_col)
> + goto invalid_refresh_argument;
> + break;
> + case REFRESH_MODE_CTU_STEP_SIZE:
> + if (param->intra_refresh_arg > ctu_sz)
> + goto invalid_refresh_argument;
> + break;
> + case REFRESH_MODE_CTUS:
> + if (param->intra_refresh_arg > ctu_sz)
> + goto invalid_refresh_argument;
> + if (param->lossless_enable) {
> + dev_err(dev, "mode: %u cannot be used lossless_enable",
> + param->intra_refresh_mode);
> + return false;
> + }
> + };
> + return true;
> +
> +invalid_refresh_argument:
> + dev_err(dev, "Invalid refresh argument, mode: %u, refresh: %u > W(%u)xH(%u)\n",
> + param->intra_refresh_mode, param->intra_refresh_arg,
> + num_ctu_row, num_ctu_col);
> + return false;
> +}
> +
> +static bool wave5_vpu_enc_check_param_valid(struct vpu_device *vpu_dev,
> + struct enc_open_param *open_param)
> +{
> + struct enc_wave_param *param = &open_param->wave_param;
> +
> + if (open_param->rc_enable) {
> + if (param->min_qp_i > param->max_qp_i || param->min_qp_p > param->max_qp_p ||
> + param->min_qp_b > param->max_qp_b) {
> + dev_err(vpu_dev->dev, "Configuration failed because min_qp is greater than max_qp\n");
> + dev_err(vpu_dev->dev, "Suggested configuration parameters: min_qp = max_qp\n");
> + return false;
> + }
> +
> + if (open_param->bit_rate <= (int)open_param->frame_rate_info) {
> + dev_err(vpu_dev->dev,
> + "enc_bit_rate: %u must be greater than the frame_rate: %u\n",
> + open_param->bit_rate, (int)open_param->frame_rate_info);
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> +int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param)
> +{
> + u32 pic_width;
> + u32 pic_height;
> + s32 product_id = inst->dev->product;
> + struct vpu_attr *p_attr = &inst->dev->attr;
> + struct enc_wave_param *param;
> +
> + if (!open_param)
> + return -EINVAL;
> +
> + param = &open_param->wave_param;
> + pic_width = open_param->pic_width;
> + pic_height = open_param->pic_height;
> +
> + if (inst->id >= MAX_NUM_INSTANCE) {
> + dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n",
> + inst->id, MAX_NUM_INSTANCE);
> + return -EOPNOTSUPP;
> + }
> +
> + if (inst->std != W_HEVC_ENC &&
> + !(inst->std == W_AVC_ENC && product_id == PRODUCT_ID_521)) {
> + dev_err(inst->dev->dev, "Unsupported encoder-codec & product combination\n");
> + return -EOPNOTSUPP;
> + }
> +
> + if (param->internal_bit_depth == 10) {
> + if (inst->std == W_HEVC_ENC && !p_attr->support_hevc10bit_enc) {
> + dev_err(inst->dev->dev,
> + "Flag support_hevc10bit_enc must be set to encode 10bit HEVC\n");
> + return -EOPNOTSUPP;
> + } else if (inst->std == W_AVC_ENC && !p_attr->support_avc10bit_enc) {
> + dev_err(inst->dev->dev,
> + "Flag support_avc10bit_enc must be set to encode 10bit AVC\n");
> + return -EOPNOTSUPP;
> + }
> + }
> +
> + if (!open_param->frame_rate_info) {
> + dev_err(inst->dev->dev, "No frame rate information.\n");
> + return -EINVAL;
> + }
> +
> + if (open_param->bit_rate > MAX_BIT_RATE) {
> + dev_err(inst->dev->dev, "Invalid encoding bit-rate: %u (valid: 0-%u)\n",
> + open_param->bit_rate, MAX_BIT_RATE);
> + return -EINVAL;
> + }
> +
> + if (pic_width < W5_MIN_ENC_PIC_WIDTH || pic_width > W5_MAX_ENC_PIC_WIDTH ||
> + pic_height < W5_MIN_ENC_PIC_HEIGHT || pic_height > W5_MAX_ENC_PIC_HEIGHT) {
> + dev_err(inst->dev->dev, "Invalid encoding dimension: %ux%u\n",
> + pic_width, pic_height);
> + return -EINVAL;
> + }
> +
> + if (param->profile) {
> + if (inst->std == W_HEVC_ENC) {
> + if ((param->profile != HEVC_PROFILE_MAIN ||
> + (param->profile == HEVC_PROFILE_MAIN &&
> + param->internal_bit_depth > 8)) &&
> + (param->profile != HEVC_PROFILE_MAIN10 ||
> + (param->profile == HEVC_PROFILE_MAIN10 &&
> + param->internal_bit_depth < 10)) &&
> + param->profile != HEVC_PROFILE_STILLPICTURE) {
> + dev_err(inst->dev->dev,
> + "Invalid HEVC encoding profile: %u (bit-depth: %u)\n",
> + param->profile, param->internal_bit_depth);
> + return -EINVAL;
> + }
> + } else if (inst->std == W_AVC_ENC) {
> + if ((param->internal_bit_depth > 8 &&
> + param->profile != H264_PROFILE_HIGH10)) {
> + dev_err(inst->dev->dev,
> + "Invalid AVC encoding profile: %u (bit-depth: %u)\n",
> + param->profile, param->internal_bit_depth);
> + return -EINVAL;
> + }
> + }
> + }
> +
> + if (param->decoding_refresh_type > DEC_REFRESH_TYPE_IDR) {
> + dev_err(inst->dev->dev, "Invalid decoding refresh type: %u (valid: 0-2)\n",
> + param->decoding_refresh_type);
> + return -EINVAL;
> + }
> +
> + if (param->intra_refresh_mode > REFRESH_MODE_CTUS) {
> + dev_err(inst->dev->dev, "Invalid intra refresh mode: %d (valid: 0-4)\n",
> + param->intra_refresh_mode);
> + return -EINVAL;
> + }
> +
> + if (inst->std == W_HEVC_ENC && param->independ_slice_mode &&
> + param->depend_slice_mode > DEPEND_SLICE_MODE_BOOST) {
> + dev_err(inst->dev->dev,
> + "Can't combine slice modes: independent and fast dependent for HEVC\n");
> + return -EINVAL;
> + }
> +
> + if (!param->disable_deblk) {
> + if (param->beta_offset_div2 < -6 || param->beta_offset_div2 > 6) {
> + dev_err(inst->dev->dev, "Invalid beta offset: %d (valid: -6-6)\n",
> + param->beta_offset_div2);
> + return -EINVAL;
> + }
> +
> + if (param->tc_offset_div2 < -6 || param->tc_offset_div2 > 6) {
> + dev_err(inst->dev->dev, "Invalid tc offset: %d (valid: -6-6)\n",
> + param->tc_offset_div2);
> + return -EINVAL;
> + }
> + }
> +
> + if (param->intra_qp > MAX_INTRA_QP) {
> + dev_err(inst->dev->dev,
> + "Invalid intra quantization parameter: %u (valid: 0-%u)\n",
> + param->intra_qp, MAX_INTRA_QP);
> + return -EINVAL;
> + }
> +
> + if (open_param->rc_enable) {
> + if (param->min_qp_i > MAX_INTRA_QP || param->max_qp_i > MAX_INTRA_QP ||
> + param->min_qp_p > MAX_INTRA_QP || param->max_qp_p > MAX_INTRA_QP ||
> + param->min_qp_b > MAX_INTRA_QP || param->max_qp_b > MAX_INTRA_QP) {
> + dev_err(inst->dev->dev,
> + "Invalid quantization parameter min/max values: "
> + "I: %u-%u, P: %u-%u, B: %u-%u (valid for each: 0-%u)\n",
> + param->min_qp_i, param->max_qp_i, param->min_qp_p, param->max_qp_p,
> + param->min_qp_b, param->max_qp_b, MAX_INTRA_QP);
> + return -EINVAL;
> + }
> +
> + if (param->hvs_qp_enable && param->hvs_max_delta_qp > MAX_HVS_MAX_DELTA_QP) {
> + dev_err(inst->dev->dev,
> + "Invalid HVS max delta quantization parameter: %u (valid: 0-%u)\n",
> + param->hvs_max_delta_qp, MAX_HVS_MAX_DELTA_QP);
> + return -EINVAL;
> + }
> +
> + if (open_param->vbv_buffer_size < MIN_VBV_BUFFER_SIZE ||
> + open_param->vbv_buffer_size > MAX_VBV_BUFFER_SIZE) {
> + dev_err(inst->dev->dev, "VBV buffer size: %u (valid: %u-%u)\n",
> + open_param->vbv_buffer_size, MIN_VBV_BUFFER_SIZE,
> + MAX_VBV_BUFFER_SIZE);
> + return -EINVAL;
> + }
> + }
> +
> + if (!wave5_vpu_enc_check_common_param_valid(inst, open_param))
> + return -EINVAL;
> +
> + if (!wave5_vpu_enc_check_param_valid(inst->dev, open_param))
> + return -EINVAL;
> +
> + if (param->chroma_cb_qp_offset < -12 || param->chroma_cb_qp_offset > 12) {
> + dev_err(inst->dev->dev,
> + "Invalid chroma Cb quantization parameter offset: %d (valid: -12-12)\n",
> + param->chroma_cb_qp_offset);
> + return -EINVAL;
> + }
> +
> + if (param->chroma_cr_qp_offset < -12 || param->chroma_cr_qp_offset > 12) {
> + dev_err(inst->dev->dev,
> + "Invalid chroma Cr quantization parameter offset: %d (valid: -12-12)\n",
> + param->chroma_cr_qp_offset);
> + return -EINVAL;
> + }
> +
> + if (param->intra_refresh_mode == REFRESH_MODE_CTU_STEP_SIZE && !param->intra_refresh_arg) {
> + dev_err(inst->dev->dev,
> + "Intra refresh mode CTU step-size requires an argument\n");
> + return -EINVAL;
> + }
> +
> + if (inst->std == W_HEVC_ENC) {
> + if (param->nr_intra_weight_y > MAX_INTRA_WEIGHT ||
> + param->nr_intra_weight_cb > MAX_INTRA_WEIGHT ||
> + param->nr_intra_weight_cr > MAX_INTRA_WEIGHT) {
> + dev_err(inst->dev->dev,
> + "Invalid intra weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n",
> + param->nr_intra_weight_y, param->nr_intra_weight_cb,
> + param->nr_intra_weight_cr, MAX_INTRA_WEIGHT);
> + return -EINVAL;
> + }
> +
> + if (param->nr_inter_weight_y > MAX_INTER_WEIGHT ||
> + param->nr_inter_weight_cb > MAX_INTER_WEIGHT ||
> + param->nr_inter_weight_cr > MAX_INTER_WEIGHT) {
> + dev_err(inst->dev->dev,
> + "Invalid inter weight Y(%u) Cb(%u) Cr(%u) (valid: %u)\n",
> + param->nr_inter_weight_y, param->nr_inter_weight_cb,
> + param->nr_inter_weight_cr, MAX_INTER_WEIGHT);
> + return -EINVAL;
> + }
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h
> new file mode 100644
> index 000000000000..8bcc1e64094b
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h
> @@ -0,0 +1,732 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - wave5 register definitions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#ifndef __WAVE5_REGISTER_DEFINE_H__
> +#define __WAVE5_REGISTER_DEFINE_H__
> +
> +enum W5_VPU_COMMAND {
> + W5_INIT_VPU = 0x0001,
> + W5_WAKEUP_VPU = 0x0002,
> + W5_SLEEP_VPU = 0x0004,
> + W5_CREATE_INSTANCE = 0x0008, /* queuing command */
> + W5_FLUSH_INSTANCE = 0x0010,
> + W5_DESTROY_INSTANCE = 0x0020, /* queuing command */
> + W5_INIT_SEQ = 0x0040, /* queuing command */
> + W5_SET_FB = 0x0080,
> + W5_DEC_ENC_PIC = 0x0100, /* queuing command */
> + W5_ENC_SET_PARAM = 0x0200, /* queuing command */
> + W5_QUERY = 0x4000,
> + W5_UPDATE_BS = 0x8000,
> + W5_MAX_VPU_COMD = 0x10000,
> +};
> +
> +enum query_opt {
> + GET_VPU_INFO = 0,
> + SET_WRITE_PROT = 1,
> + GET_RESULT = 2,
> + UPDATE_DISP_FLAG = 3,
> + GET_BW_REPORT = 4,
> + GET_BS_RD_PTR = 5, /* for decoder */
> + GET_BS_WR_PTR = 6, /* for encoder */
> + GET_SRC_BUF_FLAG = 7, /* for encoder */
> + SET_BS_RD_PTR = 8, /* for decoder */
> + GET_DEBUG_INFO = 0x61,
> +};
> +
> +#define W5_REG_BASE 0x00000000
> +#define W5_CMD_REG_BASE 0x00000100
> +#define W5_CMD_REG_END 0x00000200
> +
> +/*
> + * COMMON
> + *
> + * ----
> + *
> + * Power on configuration
> + * PO_DEBUG_MODE [0] 1 - power on with debug mode
> + * USE_PO_CONF [3] 1 - use power-on-configuration
> + */
> +#define W5_PO_CONF (W5_REG_BASE + 0x0000)
> +#define W5_VCPU_CUR_PC (W5_REG_BASE + 0x0004)
> +#define W5_VCPU_CUR_LR (W5_REG_BASE + 0x0008)
> +#define W5_VPU_PDBG_STEP_MASK_V (W5_REG_BASE + 0x000C)
> +#define W5_VPU_PDBG_CTRL (W5_REG_BASE + 0x0010) /* v_cpu debugger ctrl register */
> +#define W5_VPU_PDBG_IDX_REG (W5_REG_BASE + 0x0014) /* v_cpu debugger index register */
> +#define W5_VPU_PDBG_WDATA_REG (W5_REG_BASE + 0x0018) /* v_cpu debugger write data reg */
> +#define W5_VPU_PDBG_RDATA_REG (W5_REG_BASE + 0x001C) /* v_cpu debugger read data reg */
> +
> +#define W5_VPU_FIO_CTRL_ADDR (W5_REG_BASE + 0x0020)
> +#define W5_VPU_FIO_DATA (W5_REG_BASE + 0x0024)
> +#define W5_VPU_VINT_REASON_USR (W5_REG_BASE + 0x0030)
> +#define W5_VPU_VINT_REASON_CLR (W5_REG_BASE + 0x0034)
> +#define W5_VPU_HOST_INT_REQ (W5_REG_BASE + 0x0038)
> +#define W5_VPU_VINT_CLEAR (W5_REG_BASE + 0x003C)
> +#define W5_VPU_HINT_CLEAR (W5_REG_BASE + 0x0040)
> +#define W5_VPU_VPU_INT_STS (W5_REG_BASE + 0x0044)
> +#define W5_VPU_VINT_ENABLE (W5_REG_BASE + 0x0048)
> +#define W5_VPU_VINT_REASON (W5_REG_BASE + 0x004C)
> +#define W5_VPU_RESET_REQ (W5_REG_BASE + 0x0050)
> +#define W5_RST_BLOCK_CCLK(_core) BIT((_core))
> +#define W5_RST_BLOCK_CCLK_ALL (0xff)
> +#define W5_RST_BLOCK_BCLK(_core) (0x100 << (_core))
> +#define W5_RST_BLOCK_BCLK_ALL (0xff00)
> +#define W5_RST_BLOCK_ACLK(_core) (0x10000 << (_core))
> +#define W5_RST_BLOCK_ACLK_ALL (0xff0000)
> +#define W5_RST_BLOCK_VCPU_ALL (0x3f000000)
> +#define W5_RST_BLOCK_ALL (0x3fffffff)
> +#define W5_VPU_RESET_STATUS (W5_REG_BASE + 0x0054)
> +
> +#define W5_VCPU_RESTART (W5_REG_BASE + 0x0058)
> +#define W5_VPU_CLK_MASK (W5_REG_BASE + 0x005C)
> +
> +/* REMAP_CTRL
> + * PAGE SIZE: [8:0] 0x001 - 4K
> + * 0x002 - 8K
> + * 0x004 - 16K
> + * ...
> + * 0x100 - 1M
> + * REGION ATTR1 [10] 0 - normal
> + * 1 - make bus error for the region
> + * REGION ATTR2 [11] 0 - normal
> + * 1 - bypass region
> + * REMAP INDEX [15:12] - 0 ~ 3
> + * ENDIAN [19:16] - NOTE: Currently not supported in this driver
> + * AXI-ID [23:20] - upper AXI-ID
> + * BUS_ERROR [29] 0 - bypass
> + * 1 - make BUS_ERROR for unmapped region
> + * BYPASS_ALL [30] 1 - bypass all
> + * ENABLE [31] 1 - update control register[30:16]
> + */
> +#define W5_VPU_REMAP_CTRL (W5_REG_BASE + 0x0060)
> +#define W5_VPU_REMAP_VADDR (W5_REG_BASE + 0x0064)
> +#define W5_VPU_REMAP_PADDR (W5_REG_BASE + 0x0068)
> +#define W5_VPU_REMAP_CORE_START (W5_REG_BASE + 0x006C)
> +#define W5_VPU_BUSY_STATUS (W5_REG_BASE + 0x0070)
> +#define W5_VPU_HALT_STATUS (W5_REG_BASE + 0x0074)
> +#define W5_VPU_VCPU_STATUS (W5_REG_BASE + 0x0078)
> +#define W5_VPU_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0094)
> +/*
> + * assign vpu_config0 = {conf_map_converter_reg, // [31]
> + * conf_map_converter_sig, // [30]
> + * 8'd0, // [29:22]
> + * conf_std_switch_en, // [21]
> + * conf_bg_detect, // [20]
> + * conf_3dnr_en, // [19]
> + * conf_one_axi_en, // [18]
> + * conf_sec_axi_en, // [17]
> + * conf_bus_info, // [16]
> + * conf_afbc_en, // [15]
> + * conf_afbc_version_id, // [14:12]
> + * conf_fbc_en, // [11]
> + * conf_fbc_version_id, // [10:08]
> + * conf_scaler_en, // [07]
> + * conf_scaler_version_id, // [06:04]
> + * conf_bwb_en, // [03]
> + * 3'd0}; // [02:00]
> + */
> +#define W5_VPU_RET_VPU_CONFIG0 (W5_REG_BASE + 0x0098)
> +/*
> + * assign vpu_config1 = {4'd0, // [31:28]
> + * conf_perf_timer_en, // [27]
> + * conf_multi_core_en, // [26]
> + * conf_gcu_en, // [25]
> + * conf_cu_report, // [24]
> + * 4'd0, // [23:20]
> + * conf_vcore_id_3, // [19]
> + * conf_vcore_id_2, // [18]
> + * conf_vcore_id_1, // [17]
> + * conf_vcore_id_0, // [16]
> + * conf_bwb_opt, // [15]
> + * 7'd0, // [14:08]
> + * conf_cod_std_en_reserved_7, // [7]
> + * conf_cod_std_en_reserved_6, // [6]
> + * conf_cod_std_en_reserved_5, // [5]
> + * conf_cod_std_en_reserved_4, // [4]
> + * conf_cod_std_en_reserved_3, // [3]
> + * conf_cod_std_en_reserved_2, // [2]
> + * conf_cod_std_en_vp9, // [1]
> + * conf_cod_std_en_hevc}; // [0]
> + * }
> + */
> +#define W5_VPU_RET_VPU_CONFIG1 (W5_REG_BASE + 0x009C)
> +
> +#define W5_VPU_DBG_REG0 (W5_REG_BASE + 0x00f0)
> +#define W5_VPU_DBG_REG1 (W5_REG_BASE + 0x00f4)
> +#define W5_VPU_DBG_REG2 (W5_REG_BASE + 0x00f8)
> +#define W5_VPU_DBG_REG3 (W5_REG_BASE + 0x00fc)
> +
> +/************************************************************************/
> +/* PRODUCT INFORMATION */
> +/************************************************************************/
> +#define W5_PRODUCT_NAME (W5_REG_BASE + 0x1040)
> +#define W5_PRODUCT_NUMBER (W5_REG_BASE + 0x1044)
> +
> +/************************************************************************/
> +/* DECODER/ENCODER COMMON */
> +/************************************************************************/
> +#define W5_COMMAND (W5_REG_BASE + 0x0100)
> +#define W5_COMMAND_OPTION (W5_REG_BASE + 0x0104)
> +#define W5_QUERY_OPTION (W5_REG_BASE + 0x0104)
> +#define W5_RET_SUCCESS (W5_REG_BASE + 0x0108)
> +#define W5_RET_FAIL_REASON (W5_REG_BASE + 0x010C)
> +#define W5_RET_QUEUE_FAIL_REASON (W5_REG_BASE + 0x0110)
> +#define W5_CMD_INSTANCE_INFO (W5_REG_BASE + 0x0110)
> +
> +#define W5_RET_QUEUE_STATUS (W5_REG_BASE + 0x01E0)
> +#define W5_RET_BS_EMPTY_INST (W5_REG_BASE + 0x01E4)
> +#define W5_RET_QUEUE_CMD_DONE_INST (W5_REG_BASE + 0x01E8)
> +#define W5_RET_STAGE0_INSTANCE_INFO (W5_REG_BASE + 0x01EC)
> +#define W5_RET_STAGE1_INSTANCE_INFO (W5_REG_BASE + 0x01F0)
> +#define W5_RET_STAGE2_INSTANCE_INFO (W5_REG_BASE + 0x01F4)
> +
> +#define W5_RET_SEQ_DONE_INSTANCE_INFO (W5_REG_BASE + 0x01FC)
> +
> +#define W5_BS_OPTION (W5_REG_BASE + 0x0120)
> +
> +/* return info when QUERY (GET_RESULT) for en/decoder */
> +#define W5_RET_VLC_BUF_SIZE (W5_REG_BASE + 0x01B0)
> +/* return info when QUERY (GET_RESULT) for en/decoder */
> +#define W5_RET_PARAM_BUF_SIZE (W5_REG_BASE + 0x01B4)
> +
> +/* set when SET_FB for en/decoder */
> +#define W5_CMD_SET_FB_ADDR_TASK_BUF (W5_REG_BASE + 0x01D4)
> +#define W5_CMD_SET_FB_TASK_BUF_SIZE (W5_REG_BASE + 0x01D8)
> +/************************************************************************/
> +/* INIT_VPU - COMMON */
> +/************************************************************************/
> +/* note: W5_ADDR_CODE_BASE should be aligned to 4KB */
> +#define W5_ADDR_CODE_BASE (W5_REG_BASE + 0x0110)
> +#define W5_CODE_SIZE (W5_REG_BASE + 0x0114)
> +#define W5_CODE_PARAM (W5_REG_BASE + 0x0118)
> +#define W5_ADDR_TEMP_BASE (W5_REG_BASE + 0x011C)
> +#define W5_TEMP_SIZE (W5_REG_BASE + 0x0120)
> +#define W5_HW_OPTION (W5_REG_BASE + 0x012C)
> +#define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180)
> +
> +/************************************************************************/
> +/* CREATE_INSTANCE - COMMON */
> +/************************************************************************/
> +#define W5_ADDR_WORK_BASE (W5_REG_BASE + 0x0114)
> +#define W5_WORK_SIZE (W5_REG_BASE + 0x0118)
> +#define W5_CMD_DEC_BS_START_ADDR (W5_REG_BASE + 0x011C)
> +#define W5_CMD_DEC_BS_SIZE (W5_REG_BASE + 0x0120)
> +#define W5_CMD_BS_PARAM (W5_REG_BASE + 0x0124)
> +#define W5_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0130)
> +#define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134)
> +#define W5_CMD_EXT_ADDR (W5_REG_BASE + 0x0138)
> +#define W5_CMD_NUM_CQ_DEPTH_M1 (W5_REG_BASE + 0x013C)
> +#define W5_CMD_ERR_CONCEAL (W5_REG_BASE + 0x0140)
> +
> +/************************************************************************/
> +/* DECODER - INIT_SEQ */
> +/************************************************************************/
> +#define W5_BS_RD_PTR (W5_REG_BASE + 0x0118)
> +#define W5_BS_WR_PTR (W5_REG_BASE + 0x011C)
> +/************************************************************************/
> +/* SET_FRAME_BUF */
> +/************************************************************************/
> +/* SET_FB_OPTION 0x00 REGISTER FRAMEBUFFERS
> + * 0x01 UPDATE FRAMEBUFFER, just one framebuffer(linear, fbc and mvcol)
> + */
> +#define W5_SFB_OPTION (W5_REG_BASE + 0x0104)
> +#define W5_COMMON_PIC_INFO (W5_REG_BASE + 0x0118)
> +#define W5_PIC_SIZE (W5_REG_BASE + 0x011C)
> +#define W5_SET_FB_NUM (W5_REG_BASE + 0x0120)
> +#define W5_EXTRA_PIC_INFO (W5_REG_BASE + 0x0124)
> +
> +#define W5_ADDR_LUMA_BASE0 (W5_REG_BASE + 0x0134)
> +#define W5_ADDR_CB_BASE0 (W5_REG_BASE + 0x0138)
> +#define W5_ADDR_CR_BASE0 (W5_REG_BASE + 0x013C)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET0 (W5_REG_BASE + 0x013C)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET0 (W5_REG_BASE + 0x0140)
> +#define W5_ADDR_LUMA_BASE1 (W5_REG_BASE + 0x0144)
> +#define W5_ADDR_CB_ADDR1 (W5_REG_BASE + 0x0148)
> +#define W5_ADDR_CR_ADDR1 (W5_REG_BASE + 0x014C)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET1 (W5_REG_BASE + 0x014C)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET1 (W5_REG_BASE + 0x0150)
> +#define W5_ADDR_LUMA_BASE2 (W5_REG_BASE + 0x0154)
> +#define W5_ADDR_CB_ADDR2 (W5_REG_BASE + 0x0158)
> +#define W5_ADDR_CR_ADDR2 (W5_REG_BASE + 0x015C)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET2 (W5_REG_BASE + 0x015C)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET2 (W5_REG_BASE + 0x0160)
> +#define W5_ADDR_LUMA_BASE3 (W5_REG_BASE + 0x0164)
> +#define W5_ADDR_CB_ADDR3 (W5_REG_BASE + 0x0168)
> +#define W5_ADDR_CR_ADDR3 (W5_REG_BASE + 0x016C)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET3 (W5_REG_BASE + 0x016C)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET3 (W5_REG_BASE + 0x0170)
> +#define W5_ADDR_LUMA_BASE4 (W5_REG_BASE + 0x0174)
> +#define W5_ADDR_CB_ADDR4 (W5_REG_BASE + 0x0178)
> +#define W5_ADDR_CR_ADDR4 (W5_REG_BASE + 0x017C)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET4 (W5_REG_BASE + 0x017C)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET4 (W5_REG_BASE + 0x0180)
> +#define W5_ADDR_LUMA_BASE5 (W5_REG_BASE + 0x0184)
> +#define W5_ADDR_CB_ADDR5 (W5_REG_BASE + 0x0188)
> +#define W5_ADDR_CR_ADDR5 (W5_REG_BASE + 0x018C)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET5 (W5_REG_BASE + 0x018C)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET5 (W5_REG_BASE + 0x0190)
> +#define W5_ADDR_LUMA_BASE6 (W5_REG_BASE + 0x0194)
> +#define W5_ADDR_CB_ADDR6 (W5_REG_BASE + 0x0198)
> +#define W5_ADDR_CR_ADDR6 (W5_REG_BASE + 0x019C)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET6 (W5_REG_BASE + 0x019C)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET6 (W5_REG_BASE + 0x01A0)
> +#define W5_ADDR_LUMA_BASE7 (W5_REG_BASE + 0x01A4)
> +#define W5_ADDR_CB_ADDR7 (W5_REG_BASE + 0x01A8)
> +#define W5_ADDR_CR_ADDR7 (W5_REG_BASE + 0x01AC)
> +/* compression offset table for luma */
> +#define W5_ADDR_FBC_Y_OFFSET7 (W5_REG_BASE + 0x01AC)
> +/* compression offset table for chroma */
> +#define W5_ADDR_FBC_C_OFFSET7 (W5_REG_BASE + 0x01B0)
> +#define W5_ADDR_MV_COL0 (W5_REG_BASE + 0x01B4)
> +#define W5_ADDR_MV_COL1 (W5_REG_BASE + 0x01B8)
> +#define W5_ADDR_MV_COL2 (W5_REG_BASE + 0x01BC)
> +#define W5_ADDR_MV_COL3 (W5_REG_BASE + 0x01C0)
> +#define W5_ADDR_MV_COL4 (W5_REG_BASE + 0x01C4)
> +#define W5_ADDR_MV_COL5 (W5_REG_BASE + 0x01C8)
> +#define W5_ADDR_MV_COL6 (W5_REG_BASE + 0x01CC)
> +#define W5_ADDR_MV_COL7 (W5_REG_BASE + 0x01D0)
> +
> +/* UPDATE_FB */
> +/* CMD_SET_FB_STRIDE [15:0] - FBC framebuffer stride
> + * [31:15] - linear framebuffer stride
> + */
> +#define W5_CMD_SET_FB_STRIDE (W5_REG_BASE + 0x0118)
> +#define W5_CMD_SET_FB_INDEX (W5_REG_BASE + 0x0120)
> +#define W5_ADDR_LUMA_BASE (W5_REG_BASE + 0x0134)
> +#define W5_ADDR_CB_BASE (W5_REG_BASE + 0x0138)
> +#define W5_ADDR_CR_BASE (W5_REG_BASE + 0x013C)
> +#define W5_ADDR_MV_COL (W5_REG_BASE + 0x0140)
> +#define W5_ADDR_FBC_Y_BASE (W5_REG_BASE + 0x0144)
> +#define W5_ADDR_FBC_C_BASE (W5_REG_BASE + 0x0148)
> +#define W5_ADDR_FBC_Y_OFFSET (W5_REG_BASE + 0x014C)
> +#define W5_ADDR_FBC_C_OFFSET (W5_REG_BASE + 0x0150)
> +
> +/************************************************************************/
> +/* DECODER - DEC_PIC */
> +/************************************************************************/
> +#define W5_CMD_DEC_VCORE_INFO (W5_REG_BASE + 0x0194)
> +/* sequence change enable mask register
> + * CMD_SEQ_CHANGE_ENABLE_FLAG [5] profile_idc
> + * [16] pic_width/height_in_luma_sample
> + * [19] sps_max_dec_pic_buffering, max_num_reorder, max_latency_increase
> + */
> +#define W5_CMD_SEQ_CHANGE_ENABLE_FLAG (W5_REG_BASE + 0x0128)
> +#define W5_CMD_DEC_USER_MASK (W5_REG_BASE + 0x012C)
> +#define W5_CMD_DEC_TEMPORAL_ID_PLUS1 (W5_REG_BASE + 0x0130)
> +#define W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1 (W5_REG_BASE + 0x0134)
> +#define W5_USE_SEC_AXI (W5_REG_BASE + 0x0150)
> +
> +/************************************************************************/
> +/* DECODER - QUERY : GET_VPU_INFO */
> +/************************************************************************/
> +#define W5_RET_FW_VERSION (W5_REG_BASE + 0x0118)
> +#define W5_RET_PRODUCT_NAME (W5_REG_BASE + 0x011C)
> +#define W5_RET_PRODUCT_VERSION (W5_REG_BASE + 0x0120)
> +#define W5_RET_STD_DEF0 (W5_REG_BASE + 0x0124)
> +#define W5_RET_STD_DEF1 (W5_REG_BASE + 0x0128)
> +#define W5_RET_CONF_FEATURE (W5_REG_BASE + 0x012C)
> +#define W5_RET_CONF_DATE (W5_REG_BASE + 0x0130)
> +#define W5_RET_CONF_REVISION (W5_REG_BASE + 0x0134)
> +#define W5_RET_CONF_TYPE (W5_REG_BASE + 0x0138)
> +#define W5_RET_PRODUCT_ID (W5_REG_BASE + 0x013C)
> +#define W5_RET_CUSTOMER_ID (W5_REG_BASE + 0x0140)
> +
> +/************************************************************************/
> +/* DECODER - QUERY : GET_RESULT */
> +/************************************************************************/
> +#define W5_CMD_DEC_ADDR_REPORT_BASE (W5_REG_BASE + 0x0114)
> +#define W5_CMD_DEC_REPORT_SIZE (W5_REG_BASE + 0x0118)
> +#define W5_CMD_DEC_REPORT_PARAM (W5_REG_BASE + 0x011C)
> +
> +#define W5_RET_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C)
> +#define W5_RET_DEC_SEQ_PARAM (W5_REG_BASE + 0x0120)
> +#define W5_RET_DEC_COLOR_SAMPLE_INFO (W5_REG_BASE + 0x0124)
> +#define W5_RET_DEC_ASPECT_RATIO (W5_REG_BASE + 0x0128)
> +#define W5_RET_DEC_BIT_RATE (W5_REG_BASE + 0x012C)
> +#define W5_RET_DEC_FRAME_RATE_NR (W5_REG_BASE + 0x0130)
> +#define W5_RET_DEC_FRAME_RATE_DR (W5_REG_BASE + 0x0134)
> +#define W5_RET_DEC_NUM_REQUIRED_FB (W5_REG_BASE + 0x0138)
> +#define W5_RET_DEC_NUM_REORDER_DELAY (W5_REG_BASE + 0x013C)
> +#define W5_RET_DEC_SUB_LAYER_INFO (W5_REG_BASE + 0x0140)
> +#define W5_RET_DEC_NOTIFICATION (W5_REG_BASE + 0x0144)
> +/*
> + * USER_DATA_FLAGS for HEVC/H264 only.
> + * Bits:
> + * [1] - User data buffer full boolean
> + * [2] - VUI parameter flag
> + * [4] - Pic_timing SEI flag
> + * [5] - 1st user_data_registed_itu_t_t35 prefix SEI flag
> + * [6] - user_data_unregistered prefix SEI flag
> + * [7] - 1st user_data_registed_itu_t_t35 suffix SEI flag
> + * [8] - user_data_unregistered suffix SEI flag
> + * [10]- mastering_display_color_volume prefix SEI flag
> + * [11]- chroma_resampling_display_color_volume prefix SEI flag
> + * [12]- knee_function_info SEI flag
> + * [13]- tone_mapping_info prefix SEI flag
> + * [14]- film_grain_characteristics_info prefix SEI flag
> + * [15]- content_light_level_info prefix SEI flag
> + * [16]- color_remapping_info prefix SEI flag
> + * [28]- 2nd user_data_registed_itu_t_t35 prefix SEI flag
> + * [29]- 3rd user_data_registed_itu_t_t35 prefix SEI flag
> + * [30]- 2nd user_data_registed_itu_t_t35 suffix SEI flag
> + * [31]- 3rd user_data_registed_itu_t_t35 suffix SEI flag
> + */
> +#define W5_RET_DEC_USERDATA_IDC (W5_REG_BASE + 0x0148)
> +#define W5_RET_DEC_PIC_SIZE (W5_REG_BASE + 0x014C)
> +#define W5_RET_DEC_CROP_TOP_BOTTOM (W5_REG_BASE + 0x0150)
> +#define W5_RET_DEC_CROP_LEFT_RIGHT (W5_REG_BASE + 0x0154)
> +/*
> + * #define W5_RET_DEC_AU_START_POS (W5_REG_BASE + 0x0158)
> + * => Access unit (AU) Bitstream start position
> + * #define W5_RET_DEC_AU_END_POS (W5_REG_BASE + 0x015C)
> + * => Access unit (AU) Bitstream end position
> + */
> +
> +/*
> + * Decoded picture type:
> + * reg_val & 0x7 => picture type
> + * (reg_val >> 4) & 0x3f => VCL NAL unit type
> + * (reg_val >> 31) & 0x1 => output_flag
> + * 16 << ((reg_val >> 10) & 0x3) => ctu_size
> + */
> +#define W5_RET_DEC_PIC_TYPE (W5_REG_BASE + 0x0160)
> +#define W5_RET_DEC_PIC_POC (W5_REG_BASE + 0x0164)
> +/*
> + * #define W5_RET_DEC_RECOVERY_POINT (W5_REG_BASE + 0x0168)
> + * => HEVC recovery point
> + * reg_val & 0xff => number of signed recovery picture order counts
> + * (reg_val >> 16) & 0x1 => exact match flag
> + * (reg_val >> 17) & 0x1 => broken link flag
> + * (reg_val >> 18) & 0x1 => exist flag
> + */
> +#define W5_RET_DEC_DEBUG_INDEX (W5_REG_BASE + 0x016C)
> +#define W5_RET_DEC_DECODED_INDEX (W5_REG_BASE + 0x0170)
> +#define W5_RET_DEC_DISPLAY_INDEX (W5_REG_BASE + 0x0174)
> +/*
> + * #define W5_RET_DEC_REALLOC_INDEX (W5_REG_BASE + 0x0178)
> + * => display picture index in decoded picture buffer
> + * reg_val & 0xf => display picture index for FBC buffer (by reordering)
> + */
> +#define W5_RET_DEC_DISP_IDC (W5_REG_BASE + 0x017C)
> +/*
> + * #define W5_RET_DEC_ERR_CTB_NUM (W5_REG_BASE + 0x0180)
> + * => Number of error CTUs
> + * reg_val >> 16 => erroneous CTUs in bitstream
> + * reg_val & 0xffff => total CTUs in bitstream
> + *
> + * #define W5_RET_DEC_PIC_PARAM (W5_REG_BASE + 0x01A0)
> + * => Bitstream sequence/picture parameter information (AV1 only)
> + * reg_val & 0x1 => intrabc tool enable
> + * (reg_val >> 1) & 0x1 => screen content tools enable
> + */
> +#define W5_RET_DEC_HOST_CMD_TICK (W5_REG_BASE + 0x01B8)
> +/*
> + * #define W5_RET_DEC_SEEK_START_TICK (W5_REG_BASE + 0x01BC)
> + * #define W5_RET_DEC_SEEK_END_TICK (W5_REG_BASE + 0x01C0)
> + * => Start and end ticks for seeking slices of the picture
> + * #define W5_RET_DEC_PARSING_START_TICK (W5_REG_BASE + 0x01C4)
> + * #define W5_RET_DEC_PARSING_END_TICK (W5_REG_BASE + 0x01C8)
> + * => Start and end ticks for parsing slices of the picture
> + * #define W5_RET_DEC_DECODING_START_TICK (W5_REG_BASE + 0x01CC)
> + * => Start tick for decoding slices of the picture
> + */
> +#define W5_RET_DEC_DECODING_ENC_TICK (W5_REG_BASE + 0x01D0)
> +#define W5_RET_DEC_WARN_INFO (W5_REG_BASE + 0x01D4)
> +#define W5_RET_DEC_ERR_INFO (W5_REG_BASE + 0x01D8)
> +#define W5_RET_DEC_DECODING_SUCCESS (W5_REG_BASE + 0x01DC)
> +
> +/************************************************************************/
> +/* DECODER - FLUSH_INSTANCE */
> +/************************************************************************/
> +#define W5_CMD_FLUSH_INST_OPT (W5_REG_BASE + 0x104)
> +
> +/************************************************************************/
> +/* DECODER - QUERY : UPDATE_DISP_FLAG */
> +/************************************************************************/
> +#define W5_CMD_DEC_SET_DISP_IDC (W5_REG_BASE + 0x0118)
> +#define W5_CMD_DEC_CLR_DISP_IDC (W5_REG_BASE + 0x011C)
> +
> +/************************************************************************/
> +/* DECODER - QUERY : SET_BS_RD_PTR */
> +/************************************************************************/
> +#define W5_RET_QUERY_DEC_SET_BS_RD_PTR (W5_REG_BASE + 0x011C)
> +
> +/************************************************************************/
> +/* DECODER - QUERY : GET_BS_RD_PTR */
> +/************************************************************************/
> +#define W5_RET_QUERY_DEC_BS_RD_PTR (W5_REG_BASE + 0x011C)
> +
> +/************************************************************************/
> +/* QUERY : GET_DEBUG_INFO */
> +/************************************************************************/
> +#define W5_RET_QUERY_DEBUG_PRI_REASON (W5_REG_BASE + 0x114)
> +
> +/************************************************************************/
> +/* GDI register for debugging */
> +/************************************************************************/
> +#define W5_GDI_BASE 0x8800
> +#define W5_GDI_BUS_CTRL (W5_GDI_BASE + 0x0F0)
> +#define W5_GDI_BUS_STATUS (W5_GDI_BASE + 0x0F4)
> +
> +#define W5_BACKBONE_BASE_VCPU 0xFE00
> +#define W5_BACKBONE_BUS_CTRL_VCPU (W5_BACKBONE_BASE_VCPU + 0x010)
> +#define W5_BACKBONE_BUS_STATUS_VCPU (W5_BACKBONE_BASE_VCPU + 0x014)
> +#define W5_BACKBONE_PROG_AXI_ID (W5_BACKBONE_BASE_VCPU + 0x00C)
> +
> +#define W5_BACKBONE_PROC_EXT_ADDR (W5_BACKBONE_BASE_VCPU + 0x0C0)
> +#define W5_BACKBONE_AXI_PARAM (W5_BACKBONE_BASE_VCPU + 0x0E0)
> +
> +#define W5_BACKBONE_BASE_VCORE0 0x8E00
> +#define W5_BACKBONE_BUS_CTRL_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x010)
> +#define W5_BACKBONE_BUS_STATUS_VCORE0 (W5_BACKBONE_BASE_VCORE0 + 0x014)
> +
> +#define W5_BACKBONE_BASE_VCORE1 0x9E00 /* for dual-core product */
> +#define W5_BACKBONE_BUS_CTRL_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x010)
> +#define W5_BACKBONE_BUS_STATUS_VCORE1 (W5_BACKBONE_BASE_VCORE1 + 0x014)
> +
> +#define W5_COMBINED_BACKBONE_BASE 0xFE00
> +#define W5_COMBINED_BACKBONE_BUS_CTRL (W5_COMBINED_BACKBONE_BASE + 0x010)
> +#define W5_COMBINED_BACKBONE_BUS_STATUS (W5_COMBINED_BACKBONE_BASE + 0x014)
> +
> +/************************************************************************/
> +/* */
> +/* for ENCODER */
> +/* */
> +/************************************************************************/
> +#define W5_RET_STAGE3_INSTANCE_INFO (W5_REG_BASE + 0x1F8)
> +/************************************************************************/
> +/* ENCODER - CREATE_INSTANCE */
> +/************************************************************************/
> +/* 0x114 ~ 0x124 : defined above (CREATE_INSTANCE COMMON) */
> +#define W5_CMD_ENC_VCORE_INFO (W5_REG_BASE + 0x0194)
> +#define W5_CMD_ENC_SRC_OPTIONS (W5_REG_BASE + 0x0128)
> +
> +/************************************************************************/
> +/* ENCODER - SET_FB */
> +/************************************************************************/
> +#define W5_FBC_STRIDE (W5_REG_BASE + 0x128)
> +#define W5_ADDR_SUB_SAMPLED_FB_BASE (W5_REG_BASE + 0x12C)
> +#define W5_SUB_SAMPLED_ONE_FB_SIZE (W5_REG_BASE + 0x130)
> +
> +/************************************************************************/
> +/* ENCODER - ENC_SET_PARAM (COMMON & CHANGE_PARAM) */
> +/************************************************************************/
> +#define W5_CMD_ENC_SEQ_SET_PARAM_OPTION (W5_REG_BASE + 0x104)
> +#define W5_CMD_ENC_SEQ_SET_PARAM_ENABLE (W5_REG_BASE + 0x118)
> +#define W5_CMD_ENC_SEQ_SRC_SIZE (W5_REG_BASE + 0x11C)
> +#define W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN (W5_REG_BASE + 0x120)
> +#define W5_CMD_ENC_SEQ_SPS_PARAM (W5_REG_BASE + 0x124)
> +#define W5_CMD_ENC_SEQ_PPS_PARAM (W5_REG_BASE + 0x128)
> +#define W5_CMD_ENC_SEQ_GOP_PARAM (W5_REG_BASE + 0x12C)
> +#define W5_CMD_ENC_SEQ_INTRA_PARAM (W5_REG_BASE + 0x130)
> +#define W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT (W5_REG_BASE + 0x134)
> +#define W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT (W5_REG_BASE + 0x138)
> +#define W5_CMD_ENC_SEQ_RDO_PARAM (W5_REG_BASE + 0x13C)
> +#define W5_CMD_ENC_SEQ_INDEPENDENT_SLICE (W5_REG_BASE + 0x140)
> +#define W5_CMD_ENC_SEQ_DEPENDENT_SLICE (W5_REG_BASE + 0x144)
> +#define W5_CMD_ENC_SEQ_INTRA_REFRESH (W5_REG_BASE + 0x148)
> +#define W5_CMD_ENC_SEQ_INPUT_SRC_PARAM (W5_REG_BASE + 0x14C)
> +
> +#define W5_CMD_ENC_SEQ_RC_FRAME_RATE (W5_REG_BASE + 0x150)
> +#define W5_CMD_ENC_SEQ_RC_TARGET_RATE (W5_REG_BASE + 0x154)
> +#define W5_CMD_ENC_SEQ_RC_PARAM (W5_REG_BASE + 0x158)
> +#define W5_CMD_ENC_SEQ_RC_MIN_MAX_QP (W5_REG_BASE + 0x15C)
> +#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_0_3 (W5_REG_BASE + 0x160)
> +#define W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7 (W5_REG_BASE + 0x164)
> +#define W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP (W5_REG_BASE + 0x168)
> +#define W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM (W5_REG_BASE + 0x16C)
> +
> +#define W5_CMD_ENC_SEQ_ROT_PARAM (W5_REG_BASE + 0x170)
> +#define W5_CMD_ENC_SEQ_NUM_UNITS_IN_TICK (W5_REG_BASE + 0x174)
> +#define W5_CMD_ENC_SEQ_TIME_SCALE (W5_REG_BASE + 0x178)
> +#define W5_CMD_ENC_SEQ_NUM_TICKS_POC_DIFF_ONE (W5_REG_BASE + 0x17C)
> +
> +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU04 (W5_REG_BASE + 0x184)
> +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU08 (W5_REG_BASE + 0x188)
> +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU16 (W5_REG_BASE + 0x18C)
> +#define W5_CMD_ENC_SEQ_CUSTOM_MD_PU32 (W5_REG_BASE + 0x190)
> +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU08 (W5_REG_BASE + 0x194)
> +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU16 (W5_REG_BASE + 0x198)
> +#define W5_CMD_ENC_SEQ_CUSTOM_MD_CU32 (W5_REG_BASE + 0x19C)
> +#define W5_CMD_ENC_SEQ_NR_PARAM (W5_REG_BASE + 0x1A0)
> +#define W5_CMD_ENC_SEQ_NR_WEIGHT (W5_REG_BASE + 0x1A4)
> +#define W5_CMD_ENC_SEQ_BG_PARAM (W5_REG_BASE + 0x1A8)
> +#define W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR (W5_REG_BASE + 0x1AC)
> +#define W5_CMD_ENC_SEQ_USER_SCALING_LIST_ADDR (W5_REG_BASE + 0x1B0)
> +#define W5_CMD_ENC_SEQ_VUI_HRD_PARAM (W5_REG_BASE + 0x180)
> +#define W5_CMD_ENC_SEQ_VUI_RBSP_ADDR (W5_REG_BASE + 0x1B8)
> +#define W5_CMD_ENC_SEQ_HRD_RBSP_ADDR (W5_REG_BASE + 0x1BC)
> +
> +/************************************************************************/
> +/* ENCODER - ENC_SET_PARAM (CUSTOM_GOP) */
> +/************************************************************************/
> +#define W5_CMD_ENC_CUSTOM_GOP_PARAM (W5_REG_BASE + 0x11C)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_0 (W5_REG_BASE + 0x120)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_1 (W5_REG_BASE + 0x124)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_2 (W5_REG_BASE + 0x128)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_3 (W5_REG_BASE + 0x12C)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_4 (W5_REG_BASE + 0x130)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_5 (W5_REG_BASE + 0x134)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_6 (W5_REG_BASE + 0x138)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_7 (W5_REG_BASE + 0x13C)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_8 (W5_REG_BASE + 0x140)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_9 (W5_REG_BASE + 0x144)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_10 (W5_REG_BASE + 0x148)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_11 (W5_REG_BASE + 0x14C)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_12 (W5_REG_BASE + 0x150)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_13 (W5_REG_BASE + 0x154)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_14 (W5_REG_BASE + 0x158)
> +#define W5_CMD_ENC_CUSTOM_GOP_PIC_PARAM_15 (W5_REG_BASE + 0x15C)
> +
> +/************************************************************************/
> +/* ENCODER - ENC_PIC */
> +/************************************************************************/
> +#define W5_CMD_ENC_BS_START_ADDR (W5_REG_BASE + 0x118)
> +#define W5_CMD_ENC_BS_SIZE (W5_REG_BASE + 0x11C)
> +#define W5_CMD_ENC_PIC_USE_SEC_AXI (W5_REG_BASE + 0x124)
> +#define W5_CMD_ENC_PIC_REPORT_PARAM (W5_REG_BASE + 0x128)
> +
> +#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_PARAM (W5_REG_BASE + 0x138)
> +#define W5_CMD_ENC_PIC_CUSTOM_MAP_OPTION_ADDR (W5_REG_BASE + 0x13C)
> +#define W5_CMD_ENC_PIC_SRC_PIC_IDX (W5_REG_BASE + 0x144)
> +#define W5_CMD_ENC_PIC_SRC_ADDR_Y (W5_REG_BASE + 0x148)
> +#define W5_CMD_ENC_PIC_SRC_ADDR_U (W5_REG_BASE + 0x14C)
> +#define W5_CMD_ENC_PIC_SRC_ADDR_V (W5_REG_BASE + 0x150)
> +#define W5_CMD_ENC_PIC_SRC_STRIDE (W5_REG_BASE + 0x154)
> +#define W5_CMD_ENC_PIC_SRC_FORMAT (W5_REG_BASE + 0x158)
> +#define W5_CMD_ENC_PIC_SRC_AXI_SEL (W5_REG_BASE + 0x160)
> +#define W5_CMD_ENC_PIC_CODE_OPTION (W5_REG_BASE + 0x164)
> +#define W5_CMD_ENC_PIC_PIC_PARAM (W5_REG_BASE + 0x168)
> +#define W5_CMD_ENC_PIC_LONGTERM_PIC (W5_REG_BASE + 0x16C)
> +#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_Y (W5_REG_BASE + 0x170)
> +#define W5_CMD_ENC_PIC_WP_PIXEL_SIGMA_C (W5_REG_BASE + 0x174)
> +#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_Y (W5_REG_BASE + 0x178)
> +#define W5_CMD_ENC_PIC_WP_PIXEL_MEAN_C (W5_REG_BASE + 0x17C)
> +#define W5_CMD_ENC_PIC_CF50_Y_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x190)
> +#define W5_CMD_ENC_PIC_CF50_CB_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x194)
> +#define W5_CMD_ENC_PIC_CF50_CR_OFFSET_TABLE_ADDR (W5_REG_BASE + 0x198)
> +#define W5_CMD_ENC_PIC_PREFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x180)
> +#define W5_CMD_ENC_PIC_PREFIX_SEI_INFO (W5_REG_BASE + 0x184)
> +#define W5_CMD_ENC_PIC_SUFFIX_SEI_NAL_ADDR (W5_REG_BASE + 0x188)
> +#define W5_CMD_ENC_PIC_SUFFIX_SEI_INFO (W5_REG_BASE + 0x18c)
> +
> +/************************************************************************/
> +/* ENCODER - QUERY (GET_RESULT) */
> +/************************************************************************/
> +#define W5_RET_ENC_NUM_REQUIRED_FB (W5_REG_BASE + 0x11C)
> +#define W5_RET_ENC_MIN_SRC_BUF_NUM (W5_REG_BASE + 0x120)
> +#define W5_RET_ENC_PIC_TYPE (W5_REG_BASE + 0x124)
> +/*
> + * #define W5_RET_ENC_PIC_POC (W5_REG_BASE + 0x128)
> + * => picture order count value of current encoded picture
> + */
> +#define W5_RET_ENC_PIC_IDX (W5_REG_BASE + 0x12C)
> +/*
> + * #define W5_RET_ENC_PIC_SLICE_NUM (W5_REG_BASE + 0x130)
> + * reg_val & 0xffff = total independent slice segment number (16 bits)
> + * (reg_val >> 16) & 0xffff = total dependent slice segment number (16 bits)
> + *
> + * #define W5_RET_ENC_PIC_SKIP (W5_REG_BASE + 0x134)
> + * reg_val & 0xfe = picture skip flag (7 bits)
> + *
> + * #define W5_RET_ENC_PIC_NUM_INTRA (W5_REG_BASE + 0x138)
> + * => number of intra blocks in 8x8 (32 bits)
> + *
> + * #define W5_RET_ENC_PIC_NUM_MERGE (W5_REG_BASE + 0x13C)
> + * => number of merge blocks in 8x8 (32 bits)
> + *
> + * #define W5_RET_ENC_PIC_NUM_SKIP (W5_REG_BASE + 0x144)
> + * => number of skip blocks in 8x8 (32 bits)
> + *
> + * #define W5_RET_ENC_PIC_AVG_CTU_QP (W5_REG_BASE + 0x148)
> + * => Average CTU QP value (32 bits)
> + */
> +#define W5_RET_ENC_PIC_BYTE (W5_REG_BASE + 0x14C)
> +/*
> + * #define W5_RET_ENC_GOP_PIC_IDX (W5_REG_BASE + 0x150)
> + * => picture index in group of pictures
> + */
> +#define W5_RET_ENC_USED_SRC_IDX (W5_REG_BASE + 0x154)
> +/*
> + * #define W5_RET_ENC_PIC_NUM (W5_REG_BASE + 0x158)
> + * => encoded picture number
> + */
> +#define W5_RET_ENC_VCL_NUT (W5_REG_BASE + 0x15C)
> +/*
> + * Only for H264:
> + * #define W5_RET_ENC_PIC_DIST_LOW (W5_REG_BASE + 0x164)
> + * => lower 32 bits of the sum of squared difference between source Y picture
> + * and reconstructed Y picture
> + * #define W5_RET_ENC_PIC_DIST_HIGH (W5_REG_BASE + 0x168)
> + * => upper 32 bits of the sum of squared difference between source Y picture
> + * and reconstructed Y picture
> + */
> +#define W5_RET_ENC_PIC_MAX_LATENCY_PICS (W5_REG_BASE + 0x16C)
> +
> +#define W5_RET_ENC_HOST_CMD_TICK (W5_REG_BASE + 0x1B8)
> +/*
> + * #define W5_RET_ENC_PREPARE_START_TICK (W5_REG_BASE + 0x1BC)
> + * #define W5_RET_ENC_PREPARE_END_TICK (W5_REG_BASE + 0x1C0)
> + * => Start and end ticks for preparing slices of the picture
> + * #define W5_RET_ENC_PROCESSING_START_TICK (W5_REG_BASE + 0x1C4)
> + * #define W5_RET_ENC_PROCESSING_END_TICK (W5_REG_BASE + 0x1C8)
> + * => Start and end ticks for processing slices of the picture
> + * #define W5_RET_ENC_ENCODING_START_TICK (W5_REG_BASE + 0x1CC)
> + * => Start tick for encoding slices of the picture
> + */
> +#define W5_RET_ENC_ENCODING_END_TICK (W5_REG_BASE + 0x1D0)
> +
> +#define W5_RET_ENC_WARN_INFO (W5_REG_BASE + 0x1D4)
> +#define W5_RET_ENC_ERR_INFO (W5_REG_BASE + 0x1D8)
> +#define W5_RET_ENC_ENCODING_SUCCESS (W5_REG_BASE + 0x1DC)
> +
> +/************************************************************************/
> +/* ENCODER - QUERY (GET_BS_WR_PTR) */
> +/************************************************************************/
> +#define W5_RET_ENC_RD_PTR (W5_REG_BASE + 0x114)
> +#define W5_RET_ENC_WR_PTR (W5_REG_BASE + 0x118)
> +#define W5_CMD_ENC_REASON_SEL (W5_REG_BASE + 0x11C)
> +
> +/************************************************************************/
> +/* ENCODER - QUERY (GET_BW_REPORT) */
> +/************************************************************************/
> +#define RET_QUERY_BW_PRP_AXI_READ (W5_REG_BASE + 0x118)
> +#define RET_QUERY_BW_PRP_AXI_WRITE (W5_REG_BASE + 0x11C)
> +#define RET_QUERY_BW_FBD_Y_AXI_READ (W5_REG_BASE + 0x120)
> +#define RET_QUERY_BW_FBC_Y_AXI_WRITE (W5_REG_BASE + 0x124)
> +#define RET_QUERY_BW_FBD_C_AXI_READ (W5_REG_BASE + 0x128)
> +#define RET_QUERY_BW_FBC_C_AXI_WRITE (W5_REG_BASE + 0x12C)
> +#define RET_QUERY_BW_PRI_AXI_READ (W5_REG_BASE + 0x130)
> +#define RET_QUERY_BW_PRI_AXI_WRITE (W5_REG_BASE + 0x134)
> +#define RET_QUERY_BW_SEC_AXI_READ (W5_REG_BASE + 0x138)
> +#define RET_QUERY_BW_SEC_AXI_WRITE (W5_REG_BASE + 0x13C)
> +#define RET_QUERY_BW_PROC_AXI_READ (W5_REG_BASE + 0x140)
> +#define RET_QUERY_BW_PROC_AXI_WRITE (W5_REG_BASE + 0x144)
> +#define RET_QUERY_BW_BWB_AXI_WRITE (W5_REG_BASE + 0x148)
> +#define W5_CMD_BW_OPTION (W5_REG_BASE + 0x14C)
> +
> +/************************************************************************/
> +/* ENCODER - QUERY (GET_SRC_FLAG) */
> +/************************************************************************/
> +#define W5_RET_RELEASED_SRC_INSTANCE (W5_REG_BASE + 0x1EC)
> +
> +#define W5_ENC_PIC_SUB_FRAME_SYNC_IF (W5_REG_BASE + 0x0300)
> +
> +#endif /* __WAVE5_REGISTER_DEFINE_H__ */
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
> new file mode 100644
> index 000000000000..871090fa3407
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
> @@ -0,0 +1,208 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - low level access functions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#include <linux/bug.h>
> +#include "wave5-vdi.h"
> +#include "wave5-vpu.h"
> +#include "wave5-regdefine.h"
> +#include <linux/delay.h>
> +
> +static int wave5_vdi_allocate_common_memory(struct device *dev)
> +{
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + if (!vpu_dev->common_mem.vaddr) {
> + int ret;
> +
> + vpu_dev->common_mem.size = SIZE_COMMON;
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem);
> + if (ret) {
> + dev_err(dev, "unable to allocate common buffer\n");
> + return ret;
> + }
> + }
> +
> + dev_dbg(dev, "[VDI] common_mem: daddr=%pad size=%zu vaddr=0x%p\n",
> + &vpu_dev->common_mem.daddr, vpu_dev->common_mem.size, vpu_dev->common_mem.vaddr);
> +
> + return 0;
> +}
> +
> +int wave5_vdi_init(struct device *dev)
> +{
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> + int ret;
> +
> + ret = wave5_vdi_allocate_common_memory(dev);
> + if (ret < 0) {
> + dev_err(dev, "[VDI] failed to get vpu common buffer from driver\n");
> + return ret;
> + }
> +
> + if (!PRODUCT_CODE_W_SERIES(vpu_dev->product_code)) {
> + WARN_ONCE(1, "unsupported product code: 0x%x\n", vpu_dev->product_code);
> + return -EOPNOTSUPP;
> + }
> +
> + /* if BIT processor is not running. */
> + if (wave5_vdi_read_register(vpu_dev, W5_VCPU_CUR_PC) == 0) {
> + int i;
> +
> + for (i = 0; i < 64; i++)
> + wave5_vdi_write_register(vpu_dev, (i * 4) + 0x100, 0x0);
> + }
> +
> + dev_dbg(dev, "[VDI] driver initialized successfully\n");
> +
> + return 0;
> +}
> +
> +int wave5_vdi_release(struct device *dev)
> +{
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + vpu_dev->vdb_register = NULL;
> + wave5_vdi_free_dma_memory(vpu_dev, &vpu_dev->common_mem);
> +
> + return 0;
> +}
> +
> +void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data)
> +{
> + writel(data, vpu_dev->vdb_register + addr);
> +}
> +
> +unsigned int wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr)
> +{
> + return readl(vpu_dev->vdb_register + addr);
> +}
> +
> +int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
> +{
> + if (!vb || !vb->vaddr) {
> + dev_err(vpu_dev->dev, "%s: unable to clear unmapped buffer\n", __func__);
> + return -EINVAL;
> + }
> +
> + memset(vb->vaddr, 0, vb->size);
> + return vb->size;
> +}
> +
> +int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset,
> + u8 *data, size_t len)
> +{
> + if (!vb || !vb->vaddr) {
> + dev_err(vpu_dev->dev, "%s: unable to write to unmapped buffer\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (offset > vb->size || len > vb->size || offset + len > vb->size) {
> + dev_err(vpu_dev->dev, "%s: buffer too small\n", __func__);
> + return -ENOSPC;
> + }
> +
> + memcpy(vb->vaddr + offset, data, len);
> +
> + return len;
> +}
> +
> +int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
> +{
> + void *vaddr;
> + dma_addr_t daddr;
> +
> + if (!vb->size) {
> + dev_err(vpu_dev->dev, "%s: requested size==0\n", __func__);
> + return -EINVAL;
> + }
> +
> + vaddr = dma_alloc_coherent(vpu_dev->dev, vb->size, &daddr, GFP_KERNEL);
> + if (!vaddr)
> + return -ENOMEM;
> + vb->vaddr = vaddr;
> + vb->daddr = daddr;
> +
> + return 0;
> +}
> +
> +int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb)
> +{
> + if (vb->size == 0)
> + return -EINVAL;
> +
> + if (!vb->vaddr)
> + dev_err(vpu_dev->dev, "%s: requested free of unmapped buffer\n", __func__);
> + else
> + dma_free_coherent(vpu_dev->dev, vb->size, vb->vaddr, vb->daddr);
> +
> + memset(vb, 0, sizeof(*vb));
> +
> + return 0;
> +}
> +
> +int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count,
> + size_t size)
> +{
> + struct vpu_buf vb_buf;
> + int i, ret = 0;
> +
> + vb_buf.size = size;
> +
> + for (i = 0; i < count; i++) {
> + if (array[i].size == size)
> + continue;
> +
> + if (array[i].size != 0)
> + wave5_vdi_free_dma_memory(vpu_dev, &array[i]);
> +
> + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_buf);
> + if (ret)
> + return -ENOMEM;
> + array[i] = vb_buf;
> + }
> +
> + for (i = count; i < MAX_REG_FRAME; i++) {
> + ret = wave5_vdi_free_dma_memory(vpu_dev, &array[i]);

Do not check the returned value here because if one fail you will never
free the next indices.

> + if (ret)
> + break;
> + }
> +
> + return 0;
> +}
> +
> +void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev)
> +{
> + struct vpu_buf *vb = &vpu_dev->sram_buf;
> +
> + if (!vpu_dev->sram_pool || !vpu_dev->sram_size)
> + return;
> +
> + if (!vb->vaddr) {
> + vb->size = vpu_dev->sram_size;
> + vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size,
> + &vb->daddr);
> + if (!vb->vaddr)
> + vb->size = 0;
> + }
> +
> + dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n",
> + __func__, &vb->daddr, vb->size, vb->vaddr);
> +}
> +
> +void wave5_vdi_free_sram(struct vpu_device *vpu_dev)
> +{
> + struct vpu_buf *vb = &vpu_dev->sram_buf;
> +
> + if (!vb->size || !vb->vaddr)
> + return;
> +
> + if (vb->vaddr)
> + gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr,
> + vb->size);
> +
> + memset(vb, 0, sizeof(*vb));
> +}
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.h b/drivers/media/platform/chips-media/wave5/wave5-vdi.h
> new file mode 100644
> index 000000000000..6a5a48cc6a35
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.h
> @@ -0,0 +1,35 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - low level access functions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#ifndef _VDI_H_
> +#define _VDI_H_
> +
> +#include "wave5-vpuconfig.h"
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/device.h>
> +
> +/************************************************************************/
> +/* COMMON REGISTERS */
> +/************************************************************************/
> +#define VPU_PRODUCT_CODE_REGISTER 0x1044
> +
> +/* system register write */
> +#define vpu_write_reg(VPU_INST, ADDR, DATA) wave5_vdi_write_register(VPU_INST, ADDR, DATA)
> +/* system register read */
> +#define vpu_read_reg(CORE, ADDR) wave5_vdi_read_register(CORE, ADDR)
> +
> +struct vpu_buf {
> + size_t size;
> + dma_addr_t daddr;
> + void *vaddr;
> +};
> +
> +int wave5_vdi_init(struct device *dev);
> +int wave5_vdi_release(struct device *dev); //this function may be called only at system off.
> +
> +#endif //#ifndef _VDI_H_
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
> new file mode 100644
> index 000000000000..6b2552eeb3c9
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
> @@ -0,0 +1,958 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
> +/*
> + * Wave5 series multi-standard codec IP - helper functions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#include <linux/bug.h>
> +#include "wave5-vpuapi.h"
> +#include "wave5-regdefine.h"
> +#include "wave5.h"
> +
> +#define DECODE_ALL_TEMPORAL_LAYERS 0
> +#define DECODE_ALL_SPATIAL_LAYERS 0
> +
> +static int wave5_initialize_vpu(struct device *dev, u8 *code, size_t size)
> +{
> + int ret;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + if (wave5_vpu_is_init(vpu_dev)) {
> + wave5_vpu_re_init(dev, (void *)code, size);
> + ret = -EBUSY;
> + goto err_out;
> + }
> +
> + ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT);
> + if (ret)
> + goto err_out;
> +
> + ret = wave5_vpu_init(dev, (void *)code, size);
> +
> +err_out:
> + mutex_unlock(&vpu_dev->hw_lock);
> + return ret;
> +}
> +
> +int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size)
> +{
> + if (!bitcode || size == 0)
> + return -EINVAL;
> +
> + return wave5_initialize_vpu(dev, bitcode, size);
> +}
> +
> +int wave5_vpu_flush_instance(struct vpu_instance *inst)
> +{
> + int ret = 0;
> + int retry = 0;
> +
> + ret = mutex_lock_interruptible(&inst->dev->hw_lock);
> + if (ret)
> + return ret;
> + do {
> + /*
> + * Repeat the FLUSH command until the firmware reports that the
> + * VPU isn't running anymore
> + */
> + ret = wave5_vpu_hw_flush_instance(inst);
> + if (ret < 0 && ret != -EBUSY) {
> + dev_warn(inst->dev->dev, "Flush of %s instance with id: %d fail: %d\n",
> + inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id,
> + ret);
> + mutex_unlock(&inst->dev->hw_lock);
> + return ret;
> + }
> + if (ret == -EBUSY && retry++ >= MAX_FIRMWARE_CALL_RETRY) {
> + dev_warn(inst->dev->dev, "Flush of %s instance with id: %d timed out!\n",
> + inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id);
> + mutex_unlock(&inst->dev->hw_lock);
> + return -ETIMEDOUT;
> + }
> + } while (ret != 0);
> + mutex_unlock(&inst->dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id)
> +{
> + int ret;
> + struct vpu_device *vpu_dev = dev_get_drvdata(dev);
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + if (!wave5_vpu_is_init(vpu_dev)) {
> + ret = -EINVAL;
> + goto err_out;
> + }
> +
> + if (product_id)
> + *product_id = vpu_dev->product;
> + ret = wave5_vpu_get_version(vpu_dev, revision);
> +
> +err_out:
> + mutex_unlock(&vpu_dev->hw_lock);
> + return ret;
> +}
> +
> +static int wave5_check_dec_open_param(struct vpu_instance *inst, struct dec_open_param *param)
> +{
> + if (inst->id >= MAX_NUM_INSTANCE) {
> + dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n",
> + inst->id, MAX_NUM_INSTANCE);
> + return -EOPNOTSUPP;
> + }
> +
> + if (param->bitstream_buffer % 8) {
> + dev_err(inst->dev->dev,
> + "Bitstream buffer must be aligned to a multiple of 8\n");
> + return -EINVAL;
> + }
> +
> + if (param->bitstream_buffer_size % 1024 ||
> + param->bitstream_buffer_size < MIN_BITSTREAM_BUFFER_SIZE) {
> + dev_err(inst->dev->dev,
> + "Bitstream buffer size must be aligned to a multiple of 1024 and have a minimum size of %d\n",
> + MIN_BITSTREAM_BUFFER_SIZE);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param)
> +{
> + struct dec_info *p_dec_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> + dma_addr_t buffer_addr;
> + size_t buffer_size;
> +
> + ret = wave5_check_dec_open_param(inst, open_param);
> + if (ret)
> + return ret;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + if (!wave5_vpu_is_init(vpu_dev)) {
> + mutex_unlock(&vpu_dev->hw_lock);
> + return -ENODEV;
> + }
> +
> + p_dec_info = &inst->codec_info->dec_info;
> + memcpy(&p_dec_info->open_param, open_param, sizeof(struct dec_open_param));
> +
> + buffer_addr = open_param->bitstream_buffer;
> + buffer_size = open_param->bitstream_buffer_size;
> + p_dec_info->stream_wr_ptr = buffer_addr;
> + p_dec_info->stream_rd_ptr = buffer_addr;
> + p_dec_info->stream_buf_start_addr = buffer_addr;
> + p_dec_info->stream_buf_size = buffer_size;
> + p_dec_info->stream_buf_end_addr = buffer_addr + buffer_size;
> + p_dec_info->reorder_enable = TRUE;
> + p_dec_info->temp_id_select_mode = TEMPORAL_ID_MODE_ABSOLUTE;
> + p_dec_info->target_temp_id = DECODE_ALL_TEMPORAL_LAYERS;
> + p_dec_info->target_spatial_id = DECODE_ALL_SPATIAL_LAYERS;
> +
> + ret = wave5_vpu_build_up_dec_param(inst, open_param);
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +static int reset_auxiliary_buffers(struct vpu_instance *inst, unsigned int index)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> +
> + if (index >= MAX_REG_FRAME)
> + return -EINVAL;
> +
> + if (p_dec_info->vb_mv[index].size == 0 && p_dec_info->vb_fbc_y_tbl[index].size == 0 &&
> + p_dec_info->vb_fbc_c_tbl[index].size == 0)
> + return -EINVAL;
> +
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[index]);
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[index]);
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[index]);
> +
> + return 0;
> +}
> +
> +int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret;
> + int retry = 0;
> + struct vpu_device *vpu_dev = inst->dev;
> + int i;
> +
> + *fail_res = 0;
> + if (!inst->codec_info)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + do {
> + ret = wave5_vpu_dec_finish_seq(inst, fail_res);
> + if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
> + dev_warn(inst->dev->dev, "dec_finish_seq timed out\n");
> + goto unlock_and_return;
> + }
> +
> + if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
> + retry++ >= MAX_FIRMWARE_CALL_RETRY) {
> + ret = -ETIMEDOUT;
> + goto unlock_and_return;
> + }
> + } while (ret != 0);
> +
> + dev_dbg(inst->dev->dev, "%s: dec_finish_seq complete\n", __func__);
> +
> + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
> +
> + for (i = 0 ; i < MAX_REG_FRAME; i++) {
> + ret = reset_auxiliary_buffers(inst, i);
> + if (ret)
> + break;
> + }
> +
> + wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task);
> +
> +unlock_and_return:
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst)
> +{
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_dec_init_seq(inst);
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_dec_get_seq_info(inst, info);
> + if (!ret)
> + p_dec_info->initial_info_obtained = true;
> +
> + info->rd_ptr = wave5_dec_get_rd_ptr(inst);
> + info->wr_ptr = p_dec_info->stream_wr_ptr;
> +
> + p_dec_info->initial_info = *info;
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs,
> + int num_of_display_fbs, int stride, int height)
> +{
> + struct dec_info *p_dec_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> + struct frame_buffer *fb;
> +
> + if (num_of_decoding_fbs >= WAVE5_MAX_FBS || num_of_display_fbs >= WAVE5_MAX_FBS)
> + return -EINVAL;
> +
> + p_dec_info = &inst->codec_info->dec_info;
> + p_dec_info->num_of_decoding_fbs = num_of_decoding_fbs;
> + p_dec_info->num_of_display_fbs = num_of_display_fbs;
> + p_dec_info->stride = stride;
> +
> + if (!p_dec_info->initial_info_obtained)
> + return -EINVAL;
> +
> + if (stride < p_dec_info->initial_info.pic_width || (stride % 8 != 0) ||
> + height < p_dec_info->initial_info.pic_height)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + fb = inst->frame_buf;
> + ret = wave5_vpu_dec_register_framebuffer(inst, &fb[p_dec_info->num_of_decoding_fbs],
> + LINEAR_FRAME_MAP, p_dec_info->num_of_display_fbs);
> + if (ret)
> + goto err_out;
> +
> + ret = wave5_vpu_dec_register_framebuffer(inst, &fb[0], COMPRESSED_FRAME_MAP,
> + p_dec_info->num_of_decoding_fbs);
> +
> +err_out:
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr,
> + dma_addr_t *pwr_ptr, size_t *size)
> +{
> + struct dec_info *p_dec_info;
> + dma_addr_t rd_ptr;
> + dma_addr_t wr_ptr;
> + int room;
> + struct vpu_device *vpu_dev = inst->dev;
> + int ret;
> +
> + p_dec_info = &inst->codec_info->dec_info;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> + rd_ptr = wave5_dec_get_rd_ptr(inst);
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + wr_ptr = p_dec_info->stream_wr_ptr;
> +
> + if (wr_ptr < rd_ptr)
> + room = rd_ptr - wr_ptr;
> + else
> + room = (p_dec_info->stream_buf_end_addr - wr_ptr) +
> + (rd_ptr - p_dec_info->stream_buf_start_addr);
> + room--;
> +
> + if (prd_ptr)
> + *prd_ptr = rd_ptr;
> + if (pwr_ptr)
> + *pwr_ptr = wr_ptr;
> + if (size)
> + *size = room;
> +
> + return 0;
> +}
> +
> +int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size)
> +{
> + struct dec_info *p_dec_info;
> + dma_addr_t wr_ptr;
> + dma_addr_t rd_ptr;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + if (!inst->codec_info)
> + return -EINVAL;
> +
> + p_dec_info = &inst->codec_info->dec_info;
> + wr_ptr = p_dec_info->stream_wr_ptr;
> + rd_ptr = p_dec_info->stream_rd_ptr;
> +
> + if (size > 0) {
> + if (wr_ptr < rd_ptr && rd_ptr <= wr_ptr + size)
> + return -EINVAL;
> +
> + wr_ptr += size;
> +
> + if (wr_ptr > p_dec_info->stream_buf_end_addr) {
> + u32 room = wr_ptr - p_dec_info->stream_buf_end_addr;
> +
> + wr_ptr = p_dec_info->stream_buf_start_addr;
> + wr_ptr += room;
> + } else if (wr_ptr == p_dec_info->stream_buf_end_addr) {
> + wr_ptr = p_dec_info->stream_buf_start_addr;
> + }
> +
> + p_dec_info->stream_wr_ptr = wr_ptr;
> + p_dec_info->stream_rd_ptr = rd_ptr;
> + }
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> + ret = wave5_vpu_dec_set_bitstream_flag(inst, (size == 0));
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + if (p_dec_info->stride == 0) /* this means frame buffers have not been registered. */
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_decode(inst, res_fail);
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + ret = wave5_dec_set_rd_ptr(inst, addr);
> +
> + p_dec_info->stream_rd_ptr = addr;
> + if (update_wr_ptr)
> + p_dec_info->stream_wr_ptr = addr;
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst)
> +{
> + int ret;
> + dma_addr_t rd_ptr;
> +
> + ret = mutex_lock_interruptible(&inst->dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + rd_ptr = wave5_dec_get_rd_ptr(inst);
> +
> + mutex_unlock(&inst->dev->hw_lock);
> +
> + return rd_ptr;
> +}
> +
> +int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info)
> +{
> + struct dec_info *p_dec_info;
> + int ret;
> + struct vpu_rect rect_info;
> + u32 val;
> + u32 decoded_index;
> + u32 disp_idx;
> + u32 max_dec_index;
> + struct vpu_device *vpu_dev = inst->dev;
> + struct dec_output_info *disp_info;
> +
> + if (!info)
> + return -EINVAL;
> +
> + p_dec_info = &inst->codec_info->dec_info;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + memset(info, 0, sizeof(*info));
> +
> + ret = wave5_vpu_dec_get_result(inst, info);
> + if (ret) {
> + info->rd_ptr = p_dec_info->stream_rd_ptr;
> + info->wr_ptr = p_dec_info->stream_wr_ptr;
> + goto err_out;
> + }
> +
> + decoded_index = info->index_frame_decoded;
> +
> + /* calculate display frame region */
> + val = 0;
> + rect_info.left = 0;
> + rect_info.right = 0;
> + rect_info.top = 0;
> + rect_info.bottom = 0;
> +
> + if (decoded_index < WAVE5_MAX_FBS) {
> + if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC)
> + rect_info = p_dec_info->initial_info.pic_crop_rect;
> +
> + if (inst->std == W_HEVC_DEC)
> + p_dec_info->dec_out_info[decoded_index].decoded_poc = info->decoded_poc;
> +
> + p_dec_info->dec_out_info[decoded_index].rc_decoded = rect_info;
> + }
> + info->rc_decoded = rect_info;
> +
> + disp_idx = info->index_frame_display;
> + disp_info = &p_dec_info->dec_out_info[disp_idx];
> + if (info->index_frame_display >= 0 && info->index_frame_display < WAVE5_MAX_FBS) {
> + if (info->index_frame_display != info->index_frame_decoded) {
> + /*
> + * when index_frame_decoded < 0, and index_frame_display >= 0
> + * info->dec_pic_width and info->dec_pic_height are still valid
> + * but those of p_dec_info->dec_out_info[disp_idx] are invalid in VP9
> + */
> + info->disp_pic_width = disp_info->dec_pic_width;
> + info->disp_pic_height = disp_info->dec_pic_height;
> + } else {
> + info->disp_pic_width = info->dec_pic_width;
> + info->disp_pic_height = info->dec_pic_height;
> + }
> +
> + info->rc_display = disp_info->rc_decoded;
> +
> + } else {
> + info->rc_display.left = 0;
> + info->rc_display.right = 0;
> + info->rc_display.top = 0;
> + info->rc_display.bottom = 0;
> + info->disp_pic_width = 0;
> + info->disp_pic_height = 0;
> + }
> +
> + p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst);
> + p_dec_info->frame_display_flag = vpu_read_reg(vpu_dev, W5_RET_DEC_DISP_IDC);
> +
> + val = p_dec_info->num_of_decoding_fbs; //fb_offset
> +
> + max_dec_index = (p_dec_info->num_of_decoding_fbs > p_dec_info->num_of_display_fbs) ?
> + p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs;
> +
> + if (info->index_frame_display >= 0 &&
> + info->index_frame_display < (int)max_dec_index)
> + info->disp_frame = inst->frame_buf[val + info->index_frame_display];
> +
> + info->rd_ptr = p_dec_info->stream_rd_ptr;
> + info->wr_ptr = p_dec_info->stream_wr_ptr;
> + info->frame_display_flag = p_dec_info->frame_display_flag;
> +
> + info->sequence_no = p_dec_info->initial_info.sequence_no;
> + if (decoded_index < WAVE5_MAX_FBS)
> + p_dec_info->dec_out_info[decoded_index] = *info;
> +
> + if (disp_idx < WAVE5_MAX_FBS)
> + info->disp_frame.sequence_no = info->sequence_no;
> +
> + if (info->sequence_changed) {
> + memcpy((void *)&p_dec_info->initial_info, (void *)&p_dec_info->new_seq_info,
> + sizeof(struct dec_initial_info));
> + p_dec_info->initial_info.sequence_no++;
> + }
> +
> +err_out:
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret = 0;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + if (index >= p_dec_info->num_of_display_fbs)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> + ret = wave5_dec_clr_disp_flag(inst, index);
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret = 0;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + if (index >= p_dec_info->num_of_display_fbs)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> + ret = wave5_dec_set_disp_flag(inst, index);
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index)
> +{
> + if (index >= MAX_REG_FRAME)
> + return -EINVAL;
> +
> + if (inst->frame_vbuf[index].size == 0)
> + return -EINVAL;
> +
> + wave5_vdi_free_dma_memory(inst->dev, &inst->frame_vbuf[index]);
> +
> + return 0;
> +}
> +
> +int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter)
> +{
> + struct dec_info *p_dec_info = &inst->codec_info->dec_info;
> + int ret = 0;
> +
> + switch (cmd) {
> + case DEC_GET_QUEUE_STATUS: {
> + struct queue_status_info *queue_info = parameter;
> +
> + queue_info->instance_queue_count = p_dec_info->instance_queue_count;
> + queue_info->report_queue_count = p_dec_info->report_queue_count;
> + break;
> + }
> + case DEC_RESET_FRAMEBUF_INFO: {
> + int i;
> +
> + for (i = 0; i < MAX_REG_FRAME; i++) {
> + ret = wave5_vpu_dec_reset_framebuffer(inst, i);
> + if (ret)
> + break;
> + }
> +
> + for (i = 0; i < MAX_REG_FRAME; i++) {
> + ret = reset_auxiliary_buffers(inst, i);
> + if (ret)
> + break;
> + }
> +
> + wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
> + break;
> + }
> + case DEC_GET_SEQ_INFO: {
> + struct dec_initial_info *seq_info = parameter;
> +
> + *seq_info = p_dec_info->initial_info;
> + break;
> + }
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param)
> +{
> + struct enc_info *p_enc_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + ret = wave5_vpu_enc_check_open_param(inst, open_param);
> + if (ret)
> + return ret;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + if (!wave5_vpu_is_init(vpu_dev)) {
> + mutex_unlock(&vpu_dev->hw_lock);
> + return -ENODEV;
> + }
> +
> + p_enc_info = &inst->codec_info->enc_info;
> + p_enc_info->open_param = *open_param;
> +
> + ret = wave5_vpu_build_up_enc_param(vpu_dev->dev, inst, open_param);
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
> +{
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + int ret;
> + int retry = 0;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + *fail_res = 0;
> + if (!inst->codec_info)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + do {
> + ret = wave5_vpu_enc_finish_seq(inst, fail_res);
> + if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
> + dev_warn(inst->dev->dev, "enc_finish_seq timed out\n");
> + mutex_unlock(&vpu_dev->hw_lock);
> + return ret;
> + }
> +
> + if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
> + retry++ >= MAX_FIRMWARE_CALL_RETRY) {
> + mutex_unlock(&vpu_dev->hw_lock);
> + return -ETIMEDOUT;
> + }
> + } while (ret != 0);
> +
> + dev_dbg(inst->dev->dev, "%s: enc_finish_seq complete\n", __func__);
> +
> + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work);
> +
> + if (inst->std == W_HEVC_ENC || inst->std == W_AVC_ENC) {
> + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_sub_sam_buf);
> + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_mv);
> + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_y_tbl);
> + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_c_tbl);
> + }
> +
> + wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task);
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return 0;
> +}
> +
> +int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num,
> + unsigned int stride, int height,
> + enum tiled_map_type map_type)
> +{
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> + unsigned int size_luma, size_chroma;
> + int i;
> +
> + if (p_enc_info->stride)
> + return -EINVAL;
> +
> + if (!p_enc_info->initial_info_obtained)
> + return -EINVAL;
> +
> + if (num < p_enc_info->initial_info.min_frame_buffer_count)
> + return -EINVAL;
> +
> + if (stride == 0 || stride % 8 != 0)
> + return -EINVAL;
> +
> + if (height <= 0)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + p_enc_info->num_frame_buffers = num;
> + p_enc_info->stride = stride;
> +
> + size_luma = stride * height;
> + size_chroma = ALIGN(stride / 2, 16) * height;
> +
> + for (i = 0; i < num; i++) {
> + if (!inst->frame_buf[i].update_fb_info)
> + continue;
> +
> + inst->frame_buf[i].update_fb_info = false;
> + inst->frame_buf[i].stride = stride;
> + inst->frame_buf[i].height = height;
> + inst->frame_buf[i].map_type = COMPRESSED_FRAME_MAP;
> + inst->frame_buf[i].buf_y_size = size_luma;
> + inst->frame_buf[i].buf_cb = inst->frame_buf[i].buf_y + size_luma;
> + inst->frame_buf[i].buf_cb_size = size_chroma;
> + inst->frame_buf[i].buf_cr_size = 0;
> + }
> +
> + ret = wave5_vpu_enc_register_framebuffer(inst->dev->dev, inst, &inst->frame_buf[0],
> + COMPRESSED_FRAME_MAP,
> + p_enc_info->num_frame_buffers);
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +static int wave5_check_enc_param(struct vpu_instance *inst, struct enc_param *param)
> +{
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> +
> + if (!param)
> + return -EINVAL;
> +
> + if (!param->source_frame)
> + return -EINVAL;
> +
> + if (p_enc_info->open_param.bit_rate == 0 && inst->std == W_HEVC_ENC) {
> + if (param->pic_stream_buffer_addr % 16 || param->pic_stream_buffer_size == 0)
> + return -EINVAL;
> + }
> + if (param->pic_stream_buffer_addr % 8 || param->pic_stream_buffer_size == 0)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param, u32 *fail_res)
> +{
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + *fail_res = 0;
> +
> + if (p_enc_info->stride == 0) /* this means frame buffers have not been registered. */
> + return -EINVAL;
> +
> + ret = wave5_check_enc_param(inst, param);
> + if (ret)
> + return ret;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + p_enc_info->pts_map[param->src_idx] = param->pts;
> +
> + ret = wave5_vpu_encode(inst, param, fail_res);
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info)
> +{
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_enc_get_result(inst, info);
> + if (ret) {
> + info->pts = 0;
> + goto unlock;
> + }
> +
> + if (info->recon_frame_index >= 0)
> + info->pts = p_enc_info->pts_map[info->enc_src_idx];
> +
> +unlock:
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter)
> +{
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> +
> + switch (cmd) {
> + case ENABLE_ROTATION:
> + p_enc_info->rotation_enable = true;
> + break;
> + case ENABLE_MIRRORING:
> + p_enc_info->mirror_enable = true;
> + break;
> + case SET_MIRROR_DIRECTION: {
> + enum mirror_direction mir_dir;
> +
> + mir_dir = *(enum mirror_direction *)parameter;
> + if (mir_dir != MIRDIR_NONE && mir_dir != MIRDIR_HOR &&
> + mir_dir != MIRDIR_VER && mir_dir != MIRDIR_HOR_VER)
> + return -EINVAL;
> + p_enc_info->mirror_direction = mir_dir;
> + break;
> + }
> + case SET_ROTATION_ANGLE: {
> + int angle;
> +
> + angle = *(int *)parameter;
> + if (angle && angle != 90 && angle != 180 && angle != 270)
> + return -EINVAL;
> + if (p_enc_info->initial_info_obtained && (angle == 90 || angle == 270))
> + return -EINVAL;
> + p_enc_info->rotation_angle = angle;
> + break;
> + }
> + case ENC_GET_QUEUE_STATUS: {
> + struct queue_status_info *queue_info = parameter;
> +
> + queue_info->instance_queue_count = p_enc_info->instance_queue_count;
> + queue_info->report_queue_count = p_enc_info->report_queue_count;
> + break;
> + }
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst)
> +{
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_enc_init_seq(inst);
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return ret;
> +}
> +
> +int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info)
> +{
> + struct enc_info *p_enc_info = &inst->codec_info->enc_info;
> + int ret;
> + struct vpu_device *vpu_dev = inst->dev;
> +
> + if (!info)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
> + if (ret)
> + return ret;
> +
> + ret = wave5_vpu_enc_get_seq_info(inst, info);
> + if (ret) {
> + p_enc_info->initial_info_obtained = false;
> + mutex_unlock(&vpu_dev->hw_lock);
> + return ret;
> + }
> +
> + p_enc_info->initial_info_obtained = true;
> + p_enc_info->initial_info = *info;
> +
> + mutex_unlock(&vpu_dev->hw_lock);
> +
> + return 0;
> +}
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
> new file mode 100644
> index 000000000000..d33769ca9c2f
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
> @@ -0,0 +1,874 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - helper definitions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#ifndef VPUAPI_H_INCLUDED
> +#define VPUAPI_H_INCLUDED
> +
> +#include <linux/kfifo.h>
> +#include <linux/idr.h>
> +#include <linux/genalloc.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-ctrls.h>
> +#include "wave5-vpuerror.h"
> +#include "wave5-vpuconfig.h"
> +#include "wave5-vdi.h"
> +
> +enum product_id {
> + PRODUCT_ID_521,
> + PRODUCT_ID_511,
> + PRODUCT_ID_517,
> + PRODUCT_ID_NONE,
> +};
> +
> +struct vpu_attr;
> +
> +enum vpu_instance_type {
> + VPU_INST_TYPE_DEC = 0,
> + VPU_INST_TYPE_ENC = 1
> +};
> +
> +enum vpu_instance_state {
> + VPU_INST_STATE_NONE = 0,
> + VPU_INST_STATE_OPEN = 1,
> + VPU_INST_STATE_INIT_SEQ = 2,
> + VPU_INST_STATE_PIC_RUN = 3,
> + VPU_INST_STATE_STOP = 4
> +};
> +
> +#define WAVE5_MAX_FBS 32
> +
> +#define MAX_REG_FRAME (WAVE5_MAX_FBS * 2)
> +
> +#define WAVE5_DEC_HEVC_BUF_SIZE(_w, _h) (DIV_ROUND_UP(_w, 64) * DIV_ROUND_UP(_h, 64) * 256 + 64)
> +#define WAVE5_DEC_AVC_BUF_SIZE(_w, _h) ((((ALIGN(_w, 256) / 16) * (ALIGN(_h, 16) / 16)) + 16) * 80)
> +
> +#define WAVE5_FBC_LUMA_TABLE_SIZE(_w, _h) (ALIGN(_h, 64) * ALIGN(_w, 256) / 32)
> +#define WAVE5_FBC_CHROMA_TABLE_SIZE(_w, _h) (ALIGN((_h), 64) * ALIGN((_w) / 2, 256) / 32)
> +#define WAVE5_ENC_AVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) * ALIGN(_h, 64) / 32)
> +#define WAVE5_ENC_HEVC_BUF_SIZE(_w, _h) (ALIGN(_w, 64) / 64 * ALIGN(_h, 64) / 64 * 128)
> +
> +/*
> + * common struct and definition
> + */
> +enum cod_std {
> + STD_AVC = 0,
> + STD_HEVC = 12,
> + STD_MAX
> +};
> +
> +enum wave_std {
> + W_HEVC_DEC = 0x00,
> + W_HEVC_ENC = 0x01,
> + W_AVC_DEC = 0x02,
> + W_AVC_ENC = 0x03,
> + STD_UNKNOWN = 0xFF
> +};
> +
> +enum set_param_option {
> + OPT_COMMON = 0, /* SET_PARAM command option for encoding sequence */
> + OPT_CUSTOM_GOP = 1, /* SET_PARAM command option for setting custom GOP */
> + OPT_CUSTOM_HEADER = 2, /* SET_PARAM command option for setting custom VPS/SPS/PPS */
> + OPT_VUI = 3, /* SET_PARAM command option for encoding VUI */
> + OPT_CHANGE_PARAM = 0x10,
> +};
> +
> +/************************************************************************/
> +/* PROFILE & LEVEL */
> +/************************************************************************/
> +/* HEVC */
> +#define HEVC_PROFILE_MAIN 1
> +#define HEVC_PROFILE_MAIN10 2
> +#define HEVC_PROFILE_STILLPICTURE 3
> +#define HEVC_PROFILE_MAIN10_STILLPICTURE 2
> +
> +/* H.264 profile for encoder*/
> +#define H264_PROFILE_BP 1
> +#define H264_PROFILE_MP 2
> +#define H264_PROFILE_EXTENDED 3
> +#define H264_PROFILE_HP 4
> +#define H264_PROFILE_HIGH10 5
> +#define H264_PROFILE_HIGH422 6
> +#define H264_PROFILE_HIGH444 7
> +
> +/************************************************************************/
> +/* error codes */
> +/************************************************************************/
> +
> +/************************************************************************/
> +/* utility macros */
> +/************************************************************************/
> +
> +/* Initialize sequence firmware command mode */
> +#define INIT_SEQ_NORMAL 1
> +
> +/* Decode firmware command mode */
> +#define DEC_PIC_NORMAL 0
> +
> +/* bit_alloc_mode */
> +#define BIT_ALLOC_MODE_FIXED_RATIO 2
> +
> +/* bit_rate */
> +#define MAX_BIT_RATE 700000000
> +
> +/* decoding_refresh_type */
> +#define DEC_REFRESH_TYPE_NON_IRAP 0
> +#define DEC_REFRESH_TYPE_CRA 1
> +#define DEC_REFRESH_TYPE_IDR 2
> +
> +/* depend_slice_mode */
> +#define DEPEND_SLICE_MODE_RECOMMENDED 1
> +#define DEPEND_SLICE_MODE_BOOST 2
> +#define DEPEND_SLICE_MODE_FAST 3
> +
> +/* hvs_max_delta_qp */
> +#define MAX_HVS_MAX_DELTA_QP 51
> +
> +/* intra_refresh_mode */
> +#define REFRESH_MODE_CTU_ROWS 1
> +#define REFRESH_MODE_CTU_COLUMNS 2
> +#define REFRESH_MODE_CTU_STEP_SIZE 3
> +#define REFRESH_MODE_CTUS 4
> +
> +/* intra_mb_refresh_mode */
> +#define REFRESH_MB_MODE_NONE 0
> +#define REFRESH_MB_MODE_CTU_ROWS 1
> +#define REFRESH_MB_MODE_CTU_COLUMNS 2
> +#define REFRESH_MB_MODE_CTU_STEP_SIZE 3
> +
> +/* intra_qp */
> +#define MAX_INTRA_QP 63
> +
> +/* nr_inter_weight_* */
> +#define MAX_INTER_WEIGHT 31
> +
> +/* nr_intra_weight_* */
> +#define MAX_INTRA_WEIGHT 31
> +
> +/* nr_noise_sigma_* */
> +#define MAX_NOISE_SIGMA 255
> +
> +/* bitstream_buffer_size */
> +#define MIN_BITSTREAM_BUFFER_SIZE 1024
> +#define MIN_BITSTREAM_BUFFER_SIZE_WAVE521 (1024 * 64)
> +
> +/* vbv_buffer_size */
> +#define MIN_VBV_BUFFER_SIZE 10
> +#define MAX_VBV_BUFFER_SIZE 3000
> +
> +#define BUFFER_MARGIN 4096
> +
> +#define MAX_FIRMWARE_CALL_RETRY 10
> +
> +#define VDI_LITTLE_ENDIAN 0x0
> +
> +/*
> + * Parameters of DEC_SET_SEQ_CHANGE_MASK
> + */
> +#define SEQ_CHANGE_ENABLE_PROFILE BIT(5)
> +#define SEQ_CHANGE_ENABLE_SIZE BIT(16)
> +#define SEQ_CHANGE_ENABLE_BITDEPTH BIT(18)
> +#define SEQ_CHANGE_ENABLE_DPB_COUNT BIT(19)
> +#define SEQ_CHANGE_ENABLE_ASPECT_RATIO BIT(21)
> +#define SEQ_CHANGE_ENABLE_VIDEO_SIGNAL BIT(23)
> +#define SEQ_CHANGE_ENABLE_VUI_TIMING_INFO BIT(29)
> +
> +#define SEQ_CHANGE_ENABLE_ALL_HEVC (SEQ_CHANGE_ENABLE_PROFILE | \
> + SEQ_CHANGE_ENABLE_SIZE | \
> + SEQ_CHANGE_ENABLE_BITDEPTH | \
> + SEQ_CHANGE_ENABLE_DPB_COUNT)
> +
> +#define SEQ_CHANGE_ENABLE_ALL_AVC (SEQ_CHANGE_ENABLE_SIZE | \
> + SEQ_CHANGE_ENABLE_BITDEPTH | \
> + SEQ_CHANGE_ENABLE_DPB_COUNT | \
> + SEQ_CHANGE_ENABLE_ASPECT_RATIO | \
> + SEQ_CHANGE_ENABLE_VIDEO_SIGNAL | \
> + SEQ_CHANGE_ENABLE_VUI_TIMING_INFO)
> +
> +#define DISPLAY_IDX_FLAG_SEQ_END -1
> +#define DISPLAY_IDX_FLAG_NO_FB -3
> +#define DECODED_IDX_FLAG_NO_FB -1
> +#define DECODED_IDX_FLAG_SKIP -2
> +
> +#define RECON_IDX_FLAG_ENC_END -1
> +#define RECON_IDX_FLAG_ENC_DELAY -2
> +#define RECON_IDX_FLAG_HEADER_ONLY -3
> +#define RECON_IDX_FLAG_CHANGE_PARAM -4
> +
> +enum codec_command {
> + ENABLE_ROTATION,
> + ENABLE_MIRRORING,
> + SET_MIRROR_DIRECTION,
> + SET_ROTATION_ANGLE,
> + DEC_GET_QUEUE_STATUS,
> + ENC_GET_QUEUE_STATUS,
> + DEC_RESET_FRAMEBUF_INFO,
> + DEC_GET_SEQ_INFO,
> +};
> +
> +enum mirror_direction {
> + MIRDIR_NONE, /* no mirroring */
> + MIRDIR_VER, /* vertical mirroring */
> + MIRDIR_HOR, /* horizontal mirroring */
> + MIRDIR_HOR_VER /* horizontal and vertical mirroring */
> +};
> +
> +enum frame_buffer_format {
> + FORMAT_ERR = -1,
> + FORMAT_420 = 0, /* 8bit */
> + FORMAT_422, /* 8bit */
> + FORMAT_224, /* 8bit */
> + FORMAT_444, /* 8bit */
> + FORMAT_400, /* 8bit */
> +
> + /* little endian perspective */
> + /* | addr 0 | addr 1 | */
> + FORMAT_420_P10_16BIT_MSB = 5, /* lsb |000000xx|xxxxxxxx | msb */
> + FORMAT_420_P10_16BIT_LSB, /* lsb |xxxxxxx |xx000000 | msb */
> + FORMAT_420_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */
> + FORMAT_420_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */
> +
> + /* 4:2:2 packed format */
> + /* little endian perspective */
> + /* | addr 0 | addr 1 | */
> + FORMAT_422_P10_16BIT_MSB, /* lsb |000000xx |xxxxxxxx | msb */
> + FORMAT_422_P10_16BIT_LSB, /* lsb |xxxxxxxx |xx000000 | msb */
> + FORMAT_422_P10_32BIT_MSB, /* lsb |00xxxxxxxxxxxxxxxxxxxxxxxxxxx| msb */
> + FORMAT_422_P10_32BIT_LSB, /* lsb |xxxxxxxxxxxxxxxxxxxxxxxxxxx00| msb */
> +
> + FORMAT_YUYV, /* 8bit packed format : Y0U0Y1V0 Y2U1Y3V1 ... */
> + FORMAT_YUYV_P10_16BIT_MSB,
> + FORMAT_YUYV_P10_16BIT_LSB,
> + FORMAT_YUYV_P10_32BIT_MSB,
> + FORMAT_YUYV_P10_32BIT_LSB,
> +
> + FORMAT_YVYU, /* 8bit packed format : Y0V0Y1U0 Y2V1Y3U1 ... */
> + FORMAT_YVYU_P10_16BIT_MSB,
> + FORMAT_YVYU_P10_16BIT_LSB,
> + FORMAT_YVYU_P10_32BIT_MSB,
> + FORMAT_YVYU_P10_32BIT_LSB,
> +
> + FORMAT_UYVY, /* 8bit packed format : U0Y0V0Y1 U1Y2V1Y3 ... */
> + FORMAT_UYVY_P10_16BIT_MSB,
> + FORMAT_UYVY_P10_16BIT_LSB,
> + FORMAT_UYVY_P10_32BIT_MSB,
> + FORMAT_UYVY_P10_32BIT_LSB,
> +
> + FORMAT_VYUY, /* 8bit packed format : V0Y0U0Y1 V1Y2U1Y3 ... */
> + FORMAT_VYUY_P10_16BIT_MSB,
> + FORMAT_VYUY_P10_16BIT_LSB,
> + FORMAT_VYUY_P10_32BIT_MSB,
> + FORMAT_VYUY_P10_32BIT_LSB,
> +
> + FORMAT_MAX,
> +};
> +
> +enum packed_format_num {
> + NOT_PACKED = 0,
> + PACKED_YUYV,
> + PACKED_YVYU,
> + PACKED_UYVY,
> + PACKED_VYUY,
> +};
> +
> +enum wave5_interrupt_bit {
> + INT_WAVE5_INIT_VPU = 0,
> + INT_WAVE5_WAKEUP_VPU = 1,
> + INT_WAVE5_SLEEP_VPU = 2,
> + INT_WAVE5_CREATE_INSTANCE = 3,
> + INT_WAVE5_FLUSH_INSTANCE = 4,
> + INT_WAVE5_DESTROY_INSTANCE = 5,
> + INT_WAVE5_INIT_SEQ = 6,
> + INT_WAVE5_SET_FRAMEBUF = 7,
> + INT_WAVE5_DEC_PIC = 8,
> + INT_WAVE5_ENC_PIC = 8,
> + INT_WAVE5_ENC_SET_PARAM = 9,
> + INT_WAVE5_DEC_QUERY = 14,
> + INT_WAVE5_BSBUF_EMPTY = 15,
> + INT_WAVE5_BSBUF_FULL = 15,
> +};
> +
> +enum pic_type {
> + PIC_TYPE_I = 0,
> + PIC_TYPE_P = 1,
> + PIC_TYPE_B = 2,
> + PIC_TYPE_IDR = 5, /* H.264/H.265 IDR (Instantaneous Decoder Refresh) picture */
> + PIC_TYPE_MAX /* no meaning */
> +};
> +
> +enum sw_reset_mode {
> + SW_RESET_SAFETY,
> + SW_RESET_FORCE,
> + SW_RESET_ON_BOOT
> +};
> +
> +enum tiled_map_type {
> + LINEAR_FRAME_MAP = 0, /* linear frame map type */
> + COMPRESSED_FRAME_MAP = 17, /* compressed frame map type*/
> +};
> +
> +enum temporal_id_mode {
> + TEMPORAL_ID_MODE_ABSOLUTE,
> + TEMPORAL_ID_MODE_RELATIVE,
> +};
> +
> +struct vpu_attr {
> + u32 product_id;
> + char product_name[8]; /* product name in ascii code */
> + u32 product_version;
> + u32 fw_version;
> + u32 customer_id;
> + u32 support_decoders; /* bitmask: see <<vpuapi_h_cod_std>> */

what is <<vpuapi_h_cod_std>> ?

> + u32 support_encoders; /* bitmask: see <<vpuapi_h_cod_std>> */
> + u32 support_backbone: 1;
> + u32 support_avc10bit_enc: 1;
> + u32 support_hevc10bit_enc: 1;
> + u32 support_vcore_backbone: 1;
> + u32 support_vcpu_backbone: 1;
> +};
> +
> +struct frame_buffer {
> + dma_addr_t buf_y;
> + dma_addr_t buf_cb;
> + dma_addr_t buf_cr;
> + unsigned int buf_y_size;
> + unsigned int buf_cb_size;
> + unsigned int buf_cr_size;
> + enum tiled_map_type map_type;
> + unsigned int stride; /* horizontal stride for the given frame buffer */
> + unsigned int width; /* width of the given frame buffer */
> + unsigned int height; /* height of the given frame buffer */
> + size_t size; /* size of the given frame buffer */
> + unsigned int sequence_no;
> + bool update_fb_info;
> +};
> +
> +struct vpu_rect {
> + unsigned int left; /* horizontal pixel offset from left edge */
> + unsigned int top; /* vertical pixel offset from top edge */
> + unsigned int right; /* horizontal pixel offset from right edge */
> + unsigned int bottom; /* vertical pixel offset from bottom edge */
> +};
> +
> +/*
> + * decode struct and definition
> + */
> +
> +struct dec_open_param {
> + dma_addr_t bitstream_buffer;
> + size_t bitstream_buffer_size;
> +};
> +
> +struct dec_initial_info {
> + u32 pic_width;
> + u32 pic_height;
> + struct vpu_rect pic_crop_rect;
> + u32 min_frame_buffer_count; /* between 1 to 16 */
> +
> + u32 profile;
> + u32 luma_bitdepth; /* bit-depth of the luma sample */
> + u32 chroma_bitdepth; /* bit-depth of the chroma sample */
> + u32 seq_init_err_reason;
> + dma_addr_t rd_ptr; /* read pointer of bitstream buffer */
> + dma_addr_t wr_ptr; /* write pointer of bitstream buffer */
> + u32 sequence_no;
> + u32 vlc_buf_size;
> + u32 param_buf_size;
> +};
> +
> +struct dec_output_info {
> + /**
> + * This is a frame buffer index for the picture to be displayed at the moment
> + * among frame buffers which are registered using vpu_dec_register_frame_buffer().
> + * Frame data that will be displayed is stored in the frame buffer with this index
> + * When there is no display delay, this index is always the equal to
> + * index_frame_decoded, however, if displaying is delayed (for display
> + * reordering in AVC or B-frames in VC1), this index might be different to
> + * index_frame_decoded. By checking this index, HOST applications can easily figure
> + * out whether sequence decoding has been finished or not.
> + *
> + * -3(0xFFFD) or -2(0xFFFE) : when a display output cannot be given due to picture
> + * reordering or skip option
> + * -1(0xFFFF) : when there is no more output for display at the end of sequence
> + * decoding
> + */
> + s32 index_frame_display;
> + /**
> + * This is the frame buffer index of the decoded picture among the frame buffers which were
> + * registered using vpu_dec_register_frame_buffer(). The currently decoded frame is stored
> + * into the frame buffer specified by this index.
> + *
> + * -2 : indicates that no decoded output is generated because decoder meets EOS
> + * (end of sequence) or skip
> + * -1 : indicates that the decoder fails to decode a picture because there is no available
> + * frame buffer
> + */
> + s32 index_frame_decoded;
> + s32 index_frame_decoded_for_tiled;
> + u32 nal_type;
> + unsigned int pic_type;
> + struct vpu_rect rc_display;
> + unsigned int disp_pic_width;
> + unsigned int disp_pic_height;
> + struct vpu_rect rc_decoded;
> + u32 dec_pic_width;
> + u32 dec_pic_height;
> + s32 decoded_poc;
> + int temporal_id; /* temporal ID of the picture */
> + dma_addr_t rd_ptr; /* stream buffer read pointer for the current decoder instance */
> + dma_addr_t wr_ptr; /* stream buffer write pointer for the current decoder instance */
> + struct frame_buffer disp_frame;
> + u32 frame_display_flag; /* it reports a frame buffer flag to be displayed */
> + /**
> + * this variable reports that sequence has been changed while H.264/AVC stream decoding.
> + * if it is 1, HOST application can get the new sequence information by calling
> + * vpu_dec_get_initial_info() or wave5_vpu_dec_issue_seq_init().
> + *
> + * for H.265/HEVC decoder, each bit has a different meaning as follows.
> + *
> + * sequence_changed[5] : it indicates that the profile_idc has been changed
> + * sequence_changed[16] : it indicates that the resolution has been changed
> + * sequence_changed[19] : it indicates that the required number of frame buffer has
> + * been changed.
> + */
> + unsigned int frame_cycle; /* reports the number of cycles for processing a frame */
> + u32 sequence_no;
> +
> + u32 dec_host_cmd_tick; /* tick of DEC_PIC command for the picture */
> + u32 dec_decode_end_tick; /* end tick of decoding slices of the picture */
> +
> + u32 sequence_changed;
> +};
> +
> +struct queue_status_info {
> + u32 instance_queue_count;
> + u32 report_queue_count;
> +};
> +
> +/*
> + * encode struct and definition
> + */
> +
> +#define MAX_NUM_TEMPORAL_LAYER 7
> +#define MAX_NUM_SPATIAL_LAYER 3
> +#define MAX_GOP_NUM 8
> +
> +struct custom_gop_pic_param {
> + u32 pic_type; /* picture type of nth picture in the custom GOP */
> + u32 poc_offset; /* POC of nth picture in the custom GOP */
> + u32 pic_qp; /* quantization parameter of nth picture in the custom GOP */
> + u32 use_multi_ref_p; /* use multiref pic for P picture. valid only if PIC_TYPE is P */
> + u32 ref_poc_l0; /* POC of reference L0 of nth picture in the custom GOP */
> + u32 ref_poc_l1; /* POC of reference L1 of nth picture in the custom GOP */
> + s32 temporal_id; /* temporal ID of nth picture in the custom GOP */
> +};
> +
> +struct enc_wave_param {
> + /*
> + * profile indicator (HEVC only)
> + *
> + * 0 : the firmware determines a profile according to the internal_bit_depth
> + * 1 : main profile
> + * 2 : main10 profile
> + * 3 : main still picture profile
> + * In the AVC encoder, a profile cannot be set by the host application.
> + * The firmware decides it based on internal_bit_depth.
> + * profile = HIGH (bitdepth 8) profile = HIGH10 (bitdepth 10)
> + */
> + u32 profile;
> + u32 level; /* level indicator (level * 10) */
> + u32 internal_bit_depth: 4; /* 8/10 */
> + u32 gop_preset_idx: 4; /* 0 - 9 */
> + u32 decoding_refresh_type: 2; /* 0=non-IRAP, 1=CRA, 2=IDR */
> + u32 intra_qp; /* quantization parameter of intra picture */
> + u32 intra_period; /* period of intra picture in GOP size */
> + u32 conf_win_top; /* top offset of conformance window */
> + u32 conf_win_bot; /* bottom offset of conformance window */
> + u32 conf_win_left; /* left offset of conformance window */
> + u32 conf_win_right; /* right offset of conformance window */
> + u32 intra_refresh_mode: 3;
> + /*
> + * Argument for intra_ctu_refresh_mode.
> + *
> + * Depending on intra_refresh_mode, it can mean one of the following:
> + * - intra_ctu_refresh_mode (1) -> number of consecutive CTU rows
> + * - intra_ctu_refresh_mode (2) -> the number of consecutive CTU columns
> + * - intra_ctu_refresh_mode (3) -> step size in CTU
> + * - intra_ctu_refresh_mode (4) -> number of intra ct_us to be encoded in a picture
> + */
> + u32 intra_refresh_arg;
> + /*
> + * 0 : custom setting
> + * 1 : recommended encoder parameters (slow encoding speed, highest picture quality)
> + * 2 : boost mode (normal encoding speed, moderate picture quality)
> + * 3 : fast mode (fast encoding speed, low picture quality)
> + */
> + u32 depend_slice_mode : 2;
> + u32 depend_slice_mode_arg;
> + u32 independ_slice_mode : 1; /* 0=no-multi-slice, 1=slice-in-ctu-number*/
> + u32 independ_slice_mode_arg;
> + u32 max_num_merge: 2;
> + s32 beta_offset_div2: 4; /* sets beta_offset_div2 for deblocking filter */
> + s32 tc_offset_div2: 4; /* sets tc_offset_div3 for deblocking filter */
> + u32 hvs_qp_scale: 4; /* QP scaling factor for CU QP adjust if hvs_qp_scale_enable is 1 */
> + u32 hvs_max_delta_qp; /* maximum delta QP for HVS */
> + s32 chroma_cb_qp_offset; /* the value of chroma(cb) QP offset */
> + s32 chroma_cr_qp_offset; /* the value of chroma(cr) QP offset */
> + s32 initial_rc_qp;
> + u32 nr_intra_weight_y;
> + u32 nr_intra_weight_cb; /* weight to cb noise level for intra picture (0 ~ 31) */
> + u32 nr_intra_weight_cr; /* weight to cr noise level for intra picture (0 ~ 31) */
> + u32 nr_inter_weight_y;
> + u32 nr_inter_weight_cb; /* weight to cb noise level for inter picture (0 ~ 31) */
> + u32 nr_inter_weight_cr; /* weight to cr noise level for inter picture (0 ~ 31) */
> + u32 min_qp_i; /* minimum QP of I picture for rate control */
> + u32 max_qp_i; /* maximum QP of I picture for rate control */
> + u32 min_qp_p; /* minimum QP of P picture for rate control */
> + u32 max_qp_p; /* maximum QP of P picture for rate control */
> + u32 min_qp_b; /* minimum QP of B picture for rate control */
> + u32 max_qp_b; /* maximum QP of B picture for rate control */
> + u32 avc_idr_period; /* period of IDR picture (0 ~ 1024). 0 - implies an infinite period */
> + u32 avc_slice_arg; /* the number of MB for a slice when avc_slice_mode is set with 1 */
> + u32 intra_mb_refresh_mode: 2; /* 0=none, 1=row, 2=column, 3=step-size-in-mb */
> + /**
> + * Argument for intra_mb_refresh_mode.
> + *
> + * intra_mb_refresh_mode (1) -> number of consecutive MB rows
> + * intra_mb_refresh_mode (2) ->the number of consecutive MB columns
> + * intra_mb_refresh_mode (3) -> step size in MB
> + */
> + u32 intra_mb_refresh_arg;
> + u32 rc_weight_param;
> + u32 rc_weight_buf;
> +
> + /* flags */
> + u32 en_still_picture: 1; /* still picture profile */
> + u32 tier: 1; /* 0=main, 1=high */
> + u32 avc_slice_mode: 1; /* 0=none, 1=slice-in-mb-number */
> + u32 entropy_coding_mode: 1; /* 0=CAVLC, 1=CABAC */
> + u32 lossless_enable: 1; /* enable lossless encoding */
> + u32 const_intra_pred_flag: 1; /* enable constrained intra prediction */
> + u32 tmvp_enable: 1; /* enable temporal motion vector prediction */
> + u32 wpp_enable: 1;
> + u32 disable_deblk: 1; /* disable in-loop deblocking filtering */
> + u32 lf_cross_slice_boundary_enable: 1;
> + u32 skip_intra_trans: 1;
> + u32 sao_enable: 1; /* enable SAO (sample adaptive offset) */
> + u32 intra_nx_n_enable: 1; /* enables intra nx_n p_us */
> + u32 cu_level_rc_enable: 1; /* enable CU level rate control */
> + u32 hvs_qp_enable: 1; /* enable CU QP adjustment for subjective quality enhancement */
> + u32 strong_intra_smooth_enable: 1; /* enable strong intra smoothing */
> + u32 rdo_skip: 1; /* skip RDO (rate distortion optimization) */
> + u32 lambda_scaling_enable: 1; /* enable lambda scaling using custom GOP */
> + u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */
> + u32 mb_level_rc_enable: 1; /* enable MB-level rate control */
> +};
> +
> +struct enc_open_param {
> + dma_addr_t bitstream_buffer;
> + unsigned int bitstream_buffer_size;
> + u32 pic_width; /* width of a picture to be encoded in unit of sample */
> + u32 pic_height; /* height of a picture to be encoded in unit of sample */
> + u32 frame_rate_info;/* desired fps */
> + u32 vbv_buffer_size;
> + u32 bit_rate; /* target bitrate in bps */
> + struct enc_wave_param wave_param;
> + enum packed_format_num packed_format; /* <<vpuapi_h_packed_format_num>> */
> + enum frame_buffer_format src_format;
> + bool line_buf_int_en;
> + u32 rc_enable : 1; /* rate control */
> +};
> +
> +struct enc_initial_info {
> + u32 min_frame_buffer_count; /* minimum number of frame buffers */
> + u32 min_src_frame_count; /* minimum number of source buffers */
> + u32 seq_init_err_reason;
> + u32 warn_info;
> + u32 vlc_buf_size; /* size of task buffer */
> + u32 param_buf_size; /* size of task buffer */
> +};
> +
> +/*
> + * Flags to encode NAL units explicitly
> + */
> +struct enc_code_opt {
> + u32 implicit_header_encode: 1;
> + u32 encode_vcl: 1;
> + u32 encode_vps: 1;
> + u32 encode_sps: 1;
> + u32 encode_pps: 1;
> + u32 encode_aud: 1;
> + u32 encode_eos: 1;
> + u32 encode_eob: 1;
> + u32 encode_vui: 1;
> +};
> +
> +struct enc_param {
> + struct frame_buffer *source_frame;
> + u32 pic_stream_buffer_addr;
> + u64 pic_stream_buffer_size;
> + u32 src_idx; /* source frame buffer index */
> + struct enc_code_opt code_option;
> + u64 pts; /* presentation timestamp (PTS) of the input source */
> + bool src_end_flag;
> +};
> +
> +struct enc_output_info {
> + u32 bitstream_buffer;
> + u32 bitstream_size; /* byte size of encoded bitstream */
> + u32 pic_type: 2; /* <<vpuapi_h_pic_type>> */
> + s32 recon_frame_index;
> + dma_addr_t rd_ptr;
> + dma_addr_t wr_ptr;
> + u32 enc_pic_byte; /* number of encoded picture bytes */
> + s32 enc_src_idx; /* source buffer index of the currently encoded picture */
> + u32 enc_vcl_nut;
> + u32 error_reason; /* error reason of the currently encoded picture */
> + u32 warn_info; /* warning information on the currently encoded picture */
> + unsigned int frame_cycle; /* param for reporting the cycle number of encoding one frame*/
> + u64 pts;
> + u32 enc_host_cmd_tick; /* tick of ENC_PIC command for the picture */
> + u32 enc_encode_end_tick; /* end tick of encoding slices of the picture */
> +};
> +
> +enum enc_pic_code_option {
> + CODEOPT_ENC_HEADER_IMPLICIT = BIT(0),
> + CODEOPT_ENC_VCL = BIT(1), /* flag to encode VCL nal unit explicitly */
> +};
> +
> +enum gop_preset_idx {
> + PRESET_IDX_CUSTOM_GOP = 0, /* user defined GOP structure */
> + PRESET_IDX_ALL_I = 1, /* all intra, gopsize = 1 */
> + PRESET_IDX_IPP = 2, /* consecutive P, cyclic gopsize = 1 */
> + PRESET_IDX_IBBB = 3, /* consecutive B, cyclic gopsize = 1 */
> + PRESET_IDX_IBPBP = 4, /* gopsize = 2 */
> + PRESET_IDX_IBBBP = 5, /* gopsize = 4 */
> + PRESET_IDX_IPPPP = 6, /* consecutive P, cyclic gopsize = 4 */
> + PRESET_IDX_IBBBB = 7, /* consecutive B, cyclic gopsize = 4 */
> + PRESET_IDX_RA_IB = 8, /* random access, cyclic gopsize = 8 */
> + PRESET_IDX_IPP_SINGLE = 9, /* consecutive P, cyclic gopsize = 1, with single ref */
> +};
> +
> +struct sec_axi_info {
> + u32 use_ip_enable;
> + u32 use_bit_enable;
> + u32 use_lf_row_enable: 1;
> + u32 use_enc_rdo_enable: 1;
> + u32 use_enc_lf_enable: 1;
> +};
> +
> +struct dec_info {
> + struct dec_open_param open_param;
> + struct dec_initial_info initial_info;
> + struct dec_initial_info new_seq_info; /* temporal new sequence information */
> + u32 stream_wr_ptr;
> + u32 stream_rd_ptr;
> + u32 frame_display_flag;
> + dma_addr_t stream_buf_start_addr;
> + dma_addr_t stream_buf_end_addr;
> + u32 stream_buf_size;
> + struct vpu_buf vb_mv[MAX_REG_FRAME];
> + struct vpu_buf vb_fbc_y_tbl[MAX_REG_FRAME];
> + struct vpu_buf vb_fbc_c_tbl[MAX_REG_FRAME];
> + unsigned int num_of_decoding_fbs: 7;
> + unsigned int num_of_display_fbs: 7;
> + unsigned int stride;
> + struct sec_axi_info sec_axi_info;
> + dma_addr_t user_data_buf_addr;
> + u32 user_data_enable;
> + u32 user_data_buf_size;
> + struct vpu_buf vb_work;
> + struct vpu_buf vb_task;
> + struct dec_output_info dec_out_info[WAVE5_MAX_FBS];
> + u32 seq_change_mask;
> + enum temporal_id_mode temp_id_select_mode;
> + u32 target_temp_id;
> + u32 target_spatial_id;
> + u32 instance_queue_count;
> + u32 report_queue_count;
> + u32 cycle_per_tick;
> + u32 product_code;
> + u32 vlc_buf_size;
> + u32 param_buf_size;
> + bool initial_info_obtained;
> + bool reorder_enable;
> + bool first_cycle_check;
> + u32 stream_endflag: 1;
> +};
> +
> +struct enc_info {
> + struct enc_open_param open_param;
> + struct enc_initial_info initial_info;
> + u32 stream_rd_ptr;
> + u32 stream_wr_ptr;
> + dma_addr_t stream_buf_start_addr;
> + dma_addr_t stream_buf_end_addr;
> + u32 stream_buf_size;
> + unsigned int num_frame_buffers;
> + unsigned int stride;
> + bool rotation_enable;
> + bool mirror_enable;
> + enum mirror_direction mirror_direction;
> + unsigned int rotation_angle;
> + bool initial_info_obtained;
> + struct sec_axi_info sec_axi_info;
> + bool line_buf_int_en;
> + struct vpu_buf vb_work;
> + struct vpu_buf vb_mv; /* col_mv buffer */
> + struct vpu_buf vb_fbc_y_tbl; /* FBC luma table buffer */
> + struct vpu_buf vb_fbc_c_tbl; /* FBC chroma table buffer */
> + struct vpu_buf vb_sub_sam_buf; /* sub-sampled buffer for ME */
> + struct vpu_buf vb_task;
> + u64 cur_pts; /* current timestamp in 90_k_hz */
> + u64 pts_map[32]; /* PTS mapped with source frame index */
> + u32 instance_queue_count;
> + u32 report_queue_count;
> + bool first_cycle_check;
> + u32 cycle_per_tick;
> + u32 product_code;
> + u32 vlc_buf_size;
> + u32 param_buf_size;
> +};
> +
> +struct vpu_device {
> + struct device *dev;
> + struct v4l2_device v4l2_dev;
> + struct v4l2_m2m_dev *v4l2_m2m_dec_dev;
> + struct v4l2_m2m_dev *v4l2_m2m_enc_dev;
> + struct list_head instances;
> + struct video_device *video_dev_dec;
> + struct video_device *video_dev_enc;
> + struct mutex dev_lock; /* lock for the src, dst v4l2 queues */
> + struct mutex hw_lock; /* lock hw configurations */
> + int irq;
> + enum product_id product;
> + struct vpu_attr attr;
> + struct vpu_buf common_mem;
> + u32 last_performance_cycles;
> + u32 sram_size;
> + struct gen_pool *sram_pool;
> + struct vpu_buf sram_buf;
> + void __iomem *vdb_register;
> + u32 product_code;
> + struct ida inst_ida;
> + struct clk_bulk_data *clks;
> + int num_clks;
> +};
> +
> +struct vpu_instance;
> +
> +struct vpu_instance_ops {
> + void (*finish_process)(struct vpu_instance *inst);
> +};
> +
> +struct vpu_instance {
> + struct list_head list;
> + struct v4l2_fh v4l2_fh;
> + struct v4l2_m2m_dev *v4l2_m2m_dev;
> + struct v4l2_ctrl_handler v4l2_ctrl_hdl;
> + struct vpu_device *dev;
> + struct kfifo irq_status;
> + struct completion irq_done;
> +
> + struct v4l2_pix_format_mplane src_fmt;
> + struct v4l2_pix_format_mplane dst_fmt;
> + enum v4l2_colorspace colorspace;
> + enum v4l2_xfer_func xfer_func;
> + enum v4l2_ycbcr_encoding ycbcr_enc;
> + enum v4l2_quantization quantization;
> + enum v4l2_hsv_encoding hsv_enc;
> +
> + enum vpu_instance_state state;
> + enum vpu_instance_type type;
> + const struct vpu_instance_ops *ops;
> + spinlock_t state_spinlock; /* This protects the instance state */
> +
> + enum wave_std std;
> + s32 id;
> + union {
> + struct enc_info enc_info;
> + struct dec_info dec_info;
> + } *codec_info;
> + struct frame_buffer frame_buf[MAX_REG_FRAME];
> + struct vpu_buf frame_vbuf[MAX_REG_FRAME];
> + u32 fbc_buf_count;
> + u32 dst_buf_count;
> + u32 queued_src_buf_num;
> + u32 queued_dst_buf_num;
> + struct list_head avail_src_bufs;
> + struct list_head avail_dst_bufs;
> + struct v4l2_rect conf_win;
> + u64 timestamp;
> + enum frame_buffer_format output_format;
> + bool cbcr_interleave;
> + bool nv21;
> + bool eos;
> + struct vpu_buf bitstream_vbuf;
> + dma_addr_t last_rd_ptr;
> + size_t remaining_consumed_bytes;
> + bool needs_reallocation;
> +
> + unsigned int min_src_buf_count;
> + unsigned int src_buf_count;
> + unsigned int rot_angle;
> + unsigned int mirror_direction;
> + unsigned int bit_depth;
> + unsigned int frame_rate;
> + unsigned int vbv_buf_size;
> + unsigned int rc_mode;
> + unsigned int rc_enable;
> + unsigned int bit_rate;
> + unsigned int encode_aud;
> + struct enc_wave_param enc_param;
> +};
> +
> +void wave5_vdi_write_register(struct vpu_device *vpu_dev, u32 addr, u32 data);
> +u32 wave5_vdi_read_register(struct vpu_device *vpu_dev, u32 addr);
> +int wave5_vdi_clear_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb);
> +int wave5_vdi_allocate_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb);
> +int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, unsigned int count,
> + size_t size);
> +int wave5_vdi_write_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb, size_t offset,
> + u8 *data, size_t len);
> +int wave5_vdi_free_dma_memory(struct vpu_device *vpu_dev, struct vpu_buf *vb);
> +void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev);
> +void wave5_vdi_free_sram(struct vpu_device *vpu_dev);
> +
> +int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size);
> +int wave5_vpu_flush_instance(struct vpu_instance *inst);
> +int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id);
> +int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param);
> +int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res);
> +int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst);
> +int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info);
> +int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs,
> + int num_of_display_fbs, int stride, int height);
> +int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail);
> +int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info);
> +int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr);
> +dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst);
> +int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index);
> +int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter);
> +int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr,
> + dma_addr_t *pwr_ptr, size_t *size);
> +int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size);
> +int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index);
> +int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index);
> +
> +int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param);
> +int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res);
> +int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst);
> +int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info);
> +int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num,
> + unsigned int stride, int height,
> + enum tiled_map_type map_type);
> +int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param,
> + u32 *fail_res);
> +int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info);
> +int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter);
> +
> +#endif
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
> new file mode 100644
> index 000000000000..6a78d602cf08
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
> @@ -0,0 +1,77 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - product config definitions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#ifndef _VPU_CONFIG_H_
> +#define _VPU_CONFIG_H_
> +
> +#define WAVE517_CODE 0x5170
> +#define WAVE537_CODE 0x5370
> +#define WAVE511_CODE 0x5110
> +#define WAVE521_CODE 0x5210
> +#define WAVE521C_CODE 0x521c
> +#define WAVE521C_DUAL_CODE 0x521d // wave521 dual core
> +#define WAVE521E1_CODE 0x5211
> +
> +#define PRODUCT_CODE_W_SERIES(x) ({ \
> + int c = x; \
> + ((c) == WAVE517_CODE || (c) == WAVE537_CODE || \
> + (c) == WAVE511_CODE || (c) == WAVE521_CODE || \
> + (c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \
> + (c) == WAVE521C_DUAL_CODE); \
> +})
> +
> +#define WAVE517_WORKBUF_SIZE (2 * 1024 * 1024)
> +#define WAVE521ENC_WORKBUF_SIZE (128 * 1024) //HEVC 128K, AVC 40K
> +#define WAVE521DEC_WORKBUF_SIZE (1784 * 1024)
> +
> +#define MAX_NUM_INSTANCE 32
> +
> +#define W5_MIN_ENC_PIC_WIDTH 256
> +#define W5_MIN_ENC_PIC_HEIGHT 128
> +#define W5_MAX_ENC_PIC_WIDTH 8192
> +#define W5_MAX_ENC_PIC_HEIGHT 8192
> +
> +// application specific configuration
> +#define VPU_ENC_TIMEOUT 60000
> +#define VPU_DEC_TIMEOUT 60000
> +
> +// for WAVE encoder
> +#define USE_SRC_PRP_AXI 0
> +#define USE_SRC_PRI_AXI 1
> +#define DEFAULT_SRC_AXI USE_SRC_PRP_AXI
> +
> +/************************************************************************/
> +/* VPU COMMON MEMORY */
> +/************************************************************************/
> +#define VLC_BUF_NUM (2)
> +
> +#define COMMAND_QUEUE_DEPTH (2)
> +
> +#define W5_REMAP_INDEX0 0
> +#define W5_REMAP_INDEX1 1
> +#define W5_REMAP_MAX_SIZE (1024 * 1024)
> +
> +#define WAVE5_MAX_CODE_BUF_SIZE (2 * 1024 * 1024)
> +#define WAVE5_TEMPBUF_OFFSET WAVE5_MAX_CODE_BUF_SIZE
> +#define WAVE5_TEMPBUF_SIZE (1024 * 1024)
> +
> +#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE)
> +
> +//=====4. VPU REPORT MEMORY ======================//
> +
> +#define WAVE5_UPPER_PROC_AXI_ID 0x0
> +
> +#define WAVE5_PROC_AXI_ID 0x0
> +#define WAVE5_PRP_AXI_ID 0x0
> +#define WAVE5_FBD_Y_AXI_ID 0x0
> +#define WAVE5_FBC_Y_AXI_ID 0x0
> +#define WAVE5_FBD_C_AXI_ID 0x0
> +#define WAVE5_FBC_C_AXI_ID 0x0
> +#define WAVE5_SEC_AXI_ID 0x0
> +#define WAVE5_PRI_AXI_ID 0x0
> +
> +#endif /* _VPU_CONFIG_H_ */
> diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h
> new file mode 100644
> index 000000000000..6d7aac0c593b
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuerror.h
> @@ -0,0 +1,292 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - error values
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#ifndef ERROR_CODE_H_INCLUDED
> +#define ERROR_CODE_H_INCLUDED
> +
> +/*
> + * WAVE5
> + */
> +
> +/************************************************************************/
> +/* WAVE5 COMMON SYSTEM ERROR (FAIL_REASON) */
> +/************************************************************************/
> +#define WAVE5_SYSERR_QUEUEING_FAIL 0x00000001
> +#define WAVE5_SYSERR_ACCESS_VIOLATION_HW 0x00000040
> +#define WAVE5_SYSERR_BUS_ERROR 0x00000200
> +#define WAVE5_SYSERR_DOUBLE_FAULT 0x00000400
> +#define WAVE5_SYSERR_RESULT_NOT_READY 0x00000800
> +#define WAVE5_SYSERR_VPU_STILL_RUNNING 0x00001000
> +#define WAVE5_SYSERR_UNKNOWN_CMD 0x00002000
> +#define WAVE5_SYSERR_UNKNOWN_CODEC_STD 0x00004000
> +#define WAVE5_SYSERR_UNKNOWN_QUERY_OPTION 0x00008000
> +#define WAVE5_SYSERR_VLC_BUF_FULL 0x00010000
> +#define WAVE5_SYSERR_WATCHDOG_TIMEOUT 0x00020000
> +#define WAVE5_SYSERR_VCPU_TIMEOUT 0x00080000
> +#define WAVE5_SYSERR_TEMP_SEC_BUF_OVERFLOW 0x00200000
> +#define WAVE5_SYSERR_NEED_MORE_TASK_BUF 0x00400000
> +#define WAVE5_SYSERR_PRESCAN_ERR 0x00800000
> +#define WAVE5_SYSERR_ENC_GBIN_OVERCONSUME 0x01000000
> +#define WAVE5_SYSERR_ENC_MAX_ZERO_DETECT 0x02000000
> +#define WAVE5_SYSERR_ENC_LVL_FIRST_ERROR 0x04000000
> +#define WAVE5_SYSERR_ENC_EG_RANGE_OVER 0x08000000
> +#define WAVE5_SYSERR_ENC_IRB_FRAME_DROP 0x10000000
> +#define WAVE5_SYSERR_INPLACE_V 0x20000000
> +#define WAVE5_SYSERR_FATAL_VPU_HANGUP 0xf0000000
> +
> +/************************************************************************/
> +/* WAVE5 COMMAND QUEUE ERROR (FAIL_REASON) */
> +/************************************************************************/
> +#define WAVE5_CMDQ_ERR_NOT_QUEABLE_CMD 0x00000001
> +#define WAVE5_CMDQ_ERR_SKIP_MODE_ENABLE 0x00000002
> +#define WAVE5_CMDQ_ERR_INST_FLUSHING 0x00000003
> +#define WAVE5_CMDQ_ERR_INST_INACTIVE 0x00000004
> +#define WAVE5_CMDQ_ERR_QUEUE_FAIL 0x00000005
> +#define WAVE5_CMDQ_ERR_CMD_BUF_FULL 0x00000006
> +
> +/************************************************************************/
> +/* WAVE5 ERROR ON DECODER (ERR_INFO) */
> +/************************************************************************/
> +// HEVC
> +#define HEVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000
> +#define HEVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001
> +#define HEVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002
> +#define HEVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003
> +#define HEVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004
> +#define HEVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005
> +#define HEVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006
> +#define HEVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007
> +#define HEVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008
> +#define HEVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009
> +#define HEVC_SPSERR_LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 0x0000100A
> +#define HEVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B
> +#define HEVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C
> +#define HEVC_SPSERR_SPS_MAX_LATENCY_INCREASE 0x0000100D
> +#define HEVC_SPSERR_LOG2_MIN_LUMA_CODING_BLOCK_SIZE_MINUS3 0x0000100E
> +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE 0x0000100F
> +#define HEVC_SPSERR_LOG2_MIN_TRANSFORM_BLOCK_SIZE_MINUS2 0x00001010
> +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_TRANSFORM_BLOCK_SIZE 0x00001011
> +#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTER 0x00001012
> +#define HEVC_SPSERR_MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA 0x00001013
> +#define HEVC_SPSERR_SCALING_LIST 0x00001014
> +#define HEVC_SPSERR_LOG2_DIFF_MIN_PCM_LUMA_CODING_BLOCK_SIZE_MINUS3 0x00001015
> +#define HEVC_SPSERR_LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE 0x00001016
> +#define HEVC_SPSERR_NUM_SHORT_TERM_REF_PIC_SETS 0x00001017
> +#define HEVC_SPSERR_NUM_LONG_TERM_REF_PICS_SPS 0x00001018
> +#define HEVC_SPSERR_GBU_PARSING_ERROR 0x00001019
> +#define HEVC_SPSERR_EXTENSION_FLAG 0x0000101A
> +#define HEVC_SPSERR_VUI_ERROR 0x0000101B
> +#define HEVC_SPSERR_ACTIVATE_SPS 0x0000101C
> +#define HEVC_SPSERR_PROFILE_SPACE 0x0000101D
> +#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000
> +#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001
> +#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002
> +#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003
> +#define HEVC_PPSERR_INIT_QP_MINUS26 0x00002004
> +#define HEVC_PPSERR_DIFF_CU_QP_DELTA_DEPTH 0x00002005
> +#define HEVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006
> +#define HEVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007
> +#define HEVC_PPSERR_NUM_TILE_COLUMNS_MINUS1 0x00002008
> +#define HEVC_PPSERR_NUM_TILE_ROWS_MINUS1 0x00002009
> +#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1 0x0000200A
> +#define HEVC_PPSERR_ROW_HEIGHT_MINUS1 0x0000200B
> +#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2 0x0000200C
> +#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2 0x0000200D
> +#define HEVC_PPSERR_SCALING_LIST 0x0000200E
> +#define HEVC_PPSERR_LOG2_PARALLEL_MERGE_LEVEL_MINUS2 0x0000200F
> +#define HEVC_PPSERR_NUM_TILE_COLUMNS_RANGE_OUT 0x00002010
> +#define HEVC_PPSERR_NUM_TILE_ROWS_RANGE_OUT 0x00002011
> +#define HEVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012
> +#define HEVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013
> +#define HEVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014
> +#define HEVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015
> +#define HEVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016
> +#define HEVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017
> +#define HEVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018
> +#define HEVC_PPSERR_COLUMN_WIDTH_MINUS1_RANGE_OUT 0x00002019
> +#define HEVC_PPSERR_ROW_HEIGHT_MINUS1_RANGE_OUT 0x00002020
> +#define HEVC_PPSERR_PPS_BETA_OFFSET_DIV2_RANGE_OUT 0x00002021
> +#define HEVC_PPSERR_PPS_TC_OFFSET_DIV2_RANGE_OUT 0x00002022
> +#define HEVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000
> +#define HEVC_SHERR_ACTIVATE_PPS 0x00003001
> +#define HEVC_SHERR_ACTIVATE_SPS 0x00003002
> +#define HEVC_SHERR_SLICE_TYPE 0x00003003
> +#define HEVC_SHERR_FIRST_SLICE_IS_DEPENDENT_SLICE 0x00003004
> +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_SPS_FLAG 0x00003005
> +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET 0x00003006
> +#define HEVC_SHERR_SHORT_TERM_REF_PIC_SET_IDX 0x00003007
> +#define HEVC_SHERR_NUM_LONG_TERM_SPS 0x00003008
> +#define HEVC_SHERR_NUM_LONG_TERM_PICS 0x00003009
> +#define HEVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A
> +#define HEVC_SHERR_DELTA_POC_MSB_CYCLE_LT 0x0000300B
> +#define HEVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C
> +#define HEVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D
> +#define HEVC_SHERR_COLLOCATED_REF_IDX 0x0000300E
> +#define HEVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F
> +#define HEVC_SHERR_FIVE_MINUS_MAX_NUM_MERGE_CAND 0x00003010
> +#define HEVC_SHERR_SLICE_QP_DELTA 0x00003011
> +#define HEVC_SHERR_SLICE_QP_DELTA_IS_OUT_OF_RANGE 0x00003012
> +#define HEVC_SHERR_SLICE_CB_QP_OFFSET 0x00003013
> +#define HEVC_SHERR_SLICE_CR_QP_OFFSET 0x00003014
> +#define HEVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015
> +#define HEVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016
> +#define HEVC_SHERR_NUM_ENTRY_POINT_OFFSETS 0x00003017
> +#define HEVC_SHERR_OFFSET_LEN_MINUS1 0x00003018
> +#define HEVC_SHERR_SLICE_SEGMENT_HEADER_EXTENSION_LENGTH 0x00003019
> +#define HEVC_SHERR_WRONG_POC_IN_STILL_PICTURE_PROFILE 0x0000301A
> +#define HEVC_SHERR_SLICE_TYPE_ERROR_IN_STILL_PICTURE_PROFILE 0x0000301B
> +#define HEVC_SHERR_PPS_ID_NOT_EQUAL_PREV_VALUE 0x0000301C
> +#define HEVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000
> +#define HEVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001
> +#define HEVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002
> +#define HEVC_SPECERR_OVER_BIT_DEPTH 0x00004003
> +#define HEVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004
> +#define HEVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005
> +#define HEVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000
> +#define HEVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001
> +#define HEVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002
> +#define HEVC_ETCERR_INPLACE_V 0x0000500F
> +
> +// AVC
> +#define AVC_SPSERR_SEQ_PARAMETER_SET_ID 0x00001000
> +#define AVC_SPSERR_CHROMA_FORMAT_IDC 0x00001001
> +#define AVC_SPSERR_PIC_WIDTH_IN_LUMA_SAMPLES 0x00001002
> +#define AVC_SPSERR_PIC_HEIGHT_IN_LUMA_SAMPLES 0x00001003
> +#define AVC_SPSERR_CONF_WIN_LEFT_OFFSET 0x00001004
> +#define AVC_SPSERR_CONF_WIN_RIGHT_OFFSET 0x00001005
> +#define AVC_SPSERR_CONF_WIN_TOP_OFFSET 0x00001006
> +#define AVC_SPSERR_CONF_WIN_BOTTOM_OFFSET 0x00001007
> +#define AVC_SPSERR_BIT_DEPTH_LUMA_MINUS8 0x00001008
> +#define AVC_SPSERR_BIT_DEPTH_CHROMA_MINUS8 0x00001009
> +#define AVC_SPSERR_SPS_MAX_DEC_PIC_BUFFERING 0x0000100B
> +#define AVC_SPSERR_SPS_MAX_NUM_REORDER_PICS 0x0000100C
> +#define AVC_SPSERR_SCALING_LIST 0x00001014
> +#define AVC_SPSERR_GBU_PARSING_ERROR 0x00001019
> +#define AVC_SPSERR_VUI_ERROR 0x0000101B
> +#define AVC_SPSERR_ACTIVATE_SPS 0x0000101C
> +#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID 0x00002000
> +#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID 0x00002001
> +#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1 0x00002002
> +#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1 0x00002003
> +#define AVC_PPSERR_INIT_QP_MINUS26 0x00002004
> +#define AVC_PPSERR_PPS_CB_QP_OFFSET 0x00002006
> +#define AVC_PPSERR_PPS_CR_QP_OFFSET 0x00002007
> +#define AVC_PPSERR_SCALING_LIST 0x0000200E
> +#define AVC_PPSERR_MORE_RBSP_DATA_ERROR 0x00002012
> +#define AVC_PPSERR_PPS_PIC_PARAMETER_SET_ID_RANGE_OUT 0x00002013
> +#define AVC_PPSERR_PPS_SEQ_PARAMETER_SET_ID_RANGE_OUT 0x00002014
> +#define AVC_PPSERR_NUM_REF_IDX_L0_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002015
> +#define AVC_PPSERR_NUM_REF_IDX_L1_DEFAULT_ACTIVE_MINUS1_RANGE_OUT 0x00002016
> +#define AVC_PPSERR_PPS_CB_QP_OFFSET_RANGE_OUT 0x00002017
> +#define AVC_PPSERR_PPS_CR_QP_OFFSET_RANGE_OUT 0x00002018
> +#define AVC_SHERR_SLICE_PIC_PARAMETER_SET_ID 0x00003000
> +#define AVC_SHERR_ACTIVATE_PPS 0x00003001
> +#define AVC_SHERR_ACTIVATE_SPS 0x00003002
> +#define AVC_SHERR_SLICE_TYPE 0x00003003
> +#define AVC_SHERR_FIRST_MB_IN_SLICE 0x00003004
> +#define AVC_SHERR_RPLM 0x00003006
> +#define AVC_SHERR_LT_IDX_SPS_IS_OUT_OF_RANGE 0x0000300A
> +#define AVC_SHERR_NUM_REF_IDX_L0_ACTIVE_MINUS1 0x0000300C
> +#define AVC_SHERR_NUM_REF_IDX_L1_ACTIVE_MINUS1 0x0000300D
> +#define AVC_SHERR_PRED_WEIGHT_TABLE 0x0000300F
> +#define AVC_SHERR_SLICE_QP_DELTA 0x00003011
> +#define AVC_SHERR_SLICE_BETA_OFFSET_DIV2 0x00003015
> +#define AVC_SHERR_SLICE_TC_OFFSET_DIV2 0x00003016
> +#define AVC_SHERR_DISABLE_DEBLOCK_FILTER_IDC 0x00003017
> +#define AVC_SPECERR_OVER_PICTURE_WIDTH_SIZE 0x00004000
> +#define AVC_SPECERR_OVER_PICTURE_HEIGHT_SIZE 0x00004001
> +#define AVC_SPECERR_OVER_CHROMA_FORMAT 0x00004002
> +#define AVC_SPECERR_OVER_BIT_DEPTH 0x00004003
> +#define AVC_SPECERR_OVER_BUFFER_OVER_FLOW 0x00004004
> +#define AVC_SPECERR_OVER_WRONG_BUFFER_ACCESS 0x00004005
> +#define AVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND 0x00005000
> +#define AVC_ETCERR_DEC_PIC_VCL_NOT_FOUND 0x00005001
> +#define AVC_ETCERR_NO_VALID_SLICE_IN_AU 0x00005002
> +#define AVC_ETCERR_ASO 0x00005004
> +#define AVC_ETCERR_FMO 0x00005005
> +#define AVC_ETCERR_INPLACE_V 0x0000500F
> +
> +/************************************************************************/
> +/* WAVE5 WARNING ON DECODER (WARN_INFO) */
> +/************************************************************************/
> +// HEVC
> +#define HEVC_SPSWARN_MAX_SUB_LAYERS_MINUS1 0x00000001
> +#define HEVC_SPSWARN_GENERAL_RESERVED_ZERO_44BITS 0x00000002
> +#define HEVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004
> +#define HEVC_SPSWARN_SUB_LAYER_RESERVED_ZERO_44BITS 0x00000008
> +#define HEVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010
> +#define HEVC_SPSWARN_SPS_MAX_DEC_PIC_BUFFERING_VALUE_OVER 0x00000020
> +#define HEVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040
> +#define HEVC_SPSWARN_ST_RPS_UE_ERROR 0x00000080
> +#define HEVC_SPSWARN_EXTENSION_FLAG 0x01000000
> +#define HEVC_SPSWARN_REPLACED_WITH_PREV_SPS 0x02000000
> +#define HEVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100
> +#define HEVC_PPSWARN_REPLACED_WITH_PREV_PPS 0x00000200
> +#define HEVC_SHWARN_FIRST_SLICE_SEGMENT_IN_PIC_FLAG 0x00001000
> +#define HEVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000
> +#define HEVC_SHWARN_PIC_OUTPUT_FLAG 0x00004000
> +#define HEVC_SHWARN_DUPLICATED_SLICE_SEGMENT 0x00008000
> +#define HEVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000
> +#define HEVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000
> +#define HEVC_ETCWARN_WRONG_TEMPORAL_ID 0x00040000
> +#define HEVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000
> +#define HEVC_SPECWARN_OVER_PROFILE 0x00100000
> +#define HEVC_SPECWARN_OVER_LEVEL 0x00200000
> +#define HEVC_PRESWARN_PARSING_ERR 0x04000000
> +#define HEVC_PRESWARN_MVD_OUT_OF_RANGE 0x08000000
> +#define HEVC_PRESWARN_CU_QP_DELTA_VAL_OUT_OF_RANGE 0x09000000
> +#define HEVC_PRESWARN_COEFF_LEVEL_REMAINING_OUT_OF_RANGE 0x0A000000
> +#define HEVC_PRESWARN_PCM_ERR 0x0B000000
> +#define HEVC_PRESWARN_OVERCONSUME 0x0C000000
> +#define HEVC_PRESWARN_END_OF_SUBSET_ONE_BIT_ERR 0x10000000
> +#define HEVC_PRESWARN_END_OF_SLICE_SEGMENT_FLAG 0x20000000
> +
> +// AVC
> +#define AVC_SPSWARN_RESERVED_ZERO_2BITS 0x00000004
> +#define AVC_SPSWARN_GENERAL_LEVEL_IDC 0x00000010
> +#define AVC_SPSWARN_RBSP_TRAILING_BITS 0x00000040
> +#define AVC_PPSWARN_RBSP_TRAILING_BITS 0x00000100
> +#define AVC_SHWARN_NO_OUTPUT_OF_PRIOR_PICS_FLAG 0x00002000
> +#define AVC_ETCWARN_INIT_SEQ_VCL_NOT_FOUND 0x00010000
> +#define AVC_ETCWARN_MISSING_REFERENCE_PICTURE 0x00020000
> +#define AVC_ETCWARN_ERROR_PICTURE_IS_REFERENCED 0x00080000
> +#define AVC_SPECWARN_OVER_PROFILE 0x00100000
> +#define AVC_SPECWARN_OVER_LEVEL 0x00200000
> +#define AVC_PRESWARN_MVD_RANGE_OUT 0x00400000
> +#define AVC_PRESWARN_MB_QPD_RANGE_OUT 0x00500000
> +#define AVC_PRESWARN_COEFF_RANGE_OUT 0x00600000
> +#define AVC_PRESWARN_MV_RANGE_OUT 0x00700000
> +#define AVC_PRESWARN_MB_SKIP_RUN_RANGE_OUT 0x00800000
> +#define AVC_PRESWARN_MB_TYPE_RANGE_OUT 0x00900000
> +#define AVC_PRESWARN_SUB_MB_TYPE_RANGE_OUT 0x00A00000
> +#define AVC_PRESWARN_CBP_RANGE_OUT 0x00B00000
> +#define AVC_PRESWARN_INTRA_CHROMA_PRED_MODE_RANGE_OUT 0x00C00000
> +#define AVC_PRESWARN_REF_IDX_RANGE_OUT 0x00D00000
> +#define AVC_PRESWARN_COEFF_TOKEN_RANGE_OUT 0x00E00000
> +#define AVC_PRESWARN_TOTAL_ZERO_RANGE_OUT 0x00F00000
> +#define AVC_PRESWARN_RUN_BEFORE_RANGE_OUT 0x01000000
> +#define AVC_PRESWARN_OVERCONSUME 0x01100000
> +#define AVC_PRESWARN_MISSING_SLICE 0x01200000
> +
> +/************************************************************************/
> +/* WAVE5 ERROR ON ENCODER (ERR_INFO) */
> +/************************************************************************/
> +
> +/************************************************************************/
> +/* WAVE5 WARNING ON ENCODER (WARN_INFO) */
> +/************************************************************************/
> +#define WAVE5_ETCWARN_FORCED_SPLIT_BY_CU8X8 0x000000001
> +
> +/************************************************************************/
> +/* WAVE5 debug info (PRI_REASON) */
> +/************************************************************************/
> +#define WAVE5_DEC_VCORE_VCE_HANGUP 0x0001
> +#define WAVE5_DEC_VCORE_UNDETECTED_SYNTAX_ERR 0x0002
> +#define WAVE5_DEC_VCORE_MIB_BUSY 0x0003
> +#define WAVE5_DEC_VCORE_VLC_BUSY 0x0004
> +
> +#endif /* ERROR_CODE_H_INCLUDED */
> diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h
> new file mode 100644
> index 000000000000..7144a090dc77
> --- /dev/null
> +++ b/drivers/media/platform/chips-media/wave5/wave5.h
> @@ -0,0 +1,116 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
> +/*
> + * Wave5 series multi-standard codec IP - wave5 backend definitions
> + *
> + * Copyright (C) 2021 CHIPS&MEDIA INC
> + */
> +
> +#ifndef __WAVE5_FUNCTION_H__
> +#define __WAVE5_FUNCTION_H__
> +
> +#define WAVE5_SUBSAMPLED_ONE_SIZE(_w, _h) (ALIGN((_w) / 4, 16) * ALIGN((_h) / 4, 8))
> +#define WAVE5_SUBSAMPLED_ONE_SIZE_AVC(_w, _h) (ALIGN((_w) / 4, 32) * ALIGN((_h) / 4, 4))
> +
> +/*
> + * Bitstream buffer option: Explicit End
> + * When set to 1 the VPU assumes that the bitstream has at least one frame and
> + * will read until the end of the bitstream buffer.
> + * When set to 0 the VPU will not read the last few bytes.
> + * This option can be set anytime but cannot be cleared during processing.
> + * It can be set to force finish decoding even though there is not enough
> + * bitstream data for a full frame.
> + */
> +#define BSOPTION_ENABLE_EXPLICIT_END BIT(0)
> +#define BSOPTION_HIGHLIGHT_STREAM_END BIT(1)
> +
> +/*
> + * Currently the driver only supports hardware with little endian but for source
> + * picture format, the bitstream and the report parameter the hardware works
> + * with the opposite endianness, thus hard-code big endian for the register
> + * writes
> + */
> +#define PIC_SRC_ENDIANNESS_BIG_ENDIAN 0xf
> +#define BITSTREAM_ENDIANNESS_BIG_ENDIAN 0xf
> +#define REPORT_PARAM_ENDIANNESS_BIG_ENDIAN 0xf
> +
> +#define WTL_RIGHT_JUSTIFIED 0
> +#define WTL_LEFT_JUSTIFIED 1
> +#define WTL_PIXEL_8BIT 0
> +#define WTL_PIXEL_16BIT 1
> +#define WTL_PIXEL_32BIT 2
> +
> +/* Mirror & rotation modes of the PRP (pre-processing) module */
> +#define NONE_ROTATE 0x0
> +#define ROT_CLOCKWISE_90 0x3
> +#define ROT_CLOCKWISE_180 0x5
> +#define ROT_CLOCKWISE_270 0x7
> +#define MIR_HOR_FLIP 0x11
> +#define MIR_VER_FLIP 0x9
> +#define MIR_HOR_VER_FLIP (MIR_HOR_FLIP | MIR_VER_FLIP)
> +
> +bool wave5_vpu_is_init(struct vpu_device *vpu_dev);
> +
> +unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev);
> +
> +void wave5_bit_issue_command(struct vpu_instance *inst, u32 cmd);
> +
> +int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision);
> +
> +int wave5_vpu_init(struct device *dev, u8 *fw, size_t size);
> +
> +int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode);
> +
> +int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, struct dec_open_param *param);
> +
> +int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos);
> +
> +int wave5_vpu_hw_flush_instance(struct vpu_instance *inst);
> +
> +int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst,
> + struct frame_buffer *fb_arr, enum tiled_map_type map_type,
> + unsigned int count);
> +
> +int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size);
> +
> +int wave5_vpu_dec_init_seq(struct vpu_instance *inst);
> +
> +int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info);
> +
> +int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res);
> +
> +int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result);
> +
> +int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res);
> +
> +int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index);
> +
> +int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index);
> +
> +int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags);
> +
> +dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst);
> +
> +int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr);
> +
> +/***< WAVE5 encoder >******/
> +
> +int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst,
> + struct enc_open_param *open_param);
> +
> +int wave5_vpu_enc_init_seq(struct vpu_instance *inst);
> +
> +int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info);
> +
> +int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst,
> + struct frame_buffer *fb_arr, enum tiled_map_type map_type,
> + unsigned int count);
> +
> +int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res);
> +
> +int wave5_vpu_enc_get_result(struct vpu_instance *inst, struct enc_output_info *result);
> +
> +int wave5_vpu_enc_finish_seq(struct vpu_instance *inst, u32 *fail_res);
> +
> +int wave5_vpu_enc_check_open_param(struct vpu_instance *inst, struct enc_open_param *open_param);
> +
> +#endif /* __WAVE5_FUNCTION_H__ */
>

2023-09-27 06:41:20

by Nicolas Dufresne

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

Le vendredi 22 septembre 2023 à 09:33 +0200, Hans Verkuil a écrit :
> On 21/09/2023 21:11, Nicolas Dufresne wrote:
> > Le mercredi 20 septembre 2023 à 17:13 +0200, Hans Verkuil a écrit :
> > > On 15/09/2023 23:11, Sebastian Fricke wrote:
> > > > From: Nas Chung <[email protected]>
> > > >
> > > > Add the decoder and encoder implementing the v4l2
> > > > API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
> > > >
> > > > Signed-off-by: Sebastian Fricke <[email protected]>
> > > > Signed-off-by: Nicolas Dufresne <[email protected]>
> > > > Signed-off-by: Robert Beckett <[email protected]>
> > > > Signed-off-by: Dafna Hirschfeld <[email protected]>
> > > > Signed-off-by: Nas Chung <[email protected]>
> > > > ---
> > > > drivers/media/platform/chips-media/Kconfig | 1 +
> > > > drivers/media/platform/chips-media/Makefile | 1 +
> > > > drivers/media/platform/chips-media/wave5/Kconfig | 12 +
> > > > drivers/media/platform/chips-media/wave5/Makefile | 10 +
> > > > .../platform/chips-media/wave5/wave5-helper.c | 196 ++
> > > > .../platform/chips-media/wave5/wave5-helper.h | 30 +
> > > > .../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
> > > > .../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
> > > > .../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
> > > > .../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
> > > > 10 files changed, 4454 insertions(+)
> > > >
>
> <snip>
>
> > > > +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
> > > > +{
> > > > + int ret;
> > > > +
> > > > + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
> > > > + if (ret) {
> > > > + dev_err(inst->dev->dev,
> > > > + "Setting EOS for the bitstream, fail: %d\n", ret);
> > >
> > > Is this an error due to a driver problem, or because a bad bitstream is
> > > fed from userspace? In the first case, dev_err would be right, in the
> > > second dev_dbg would be more appropriate. Bad userspace input should not
> > > spam the kernel log in general.
> >
> > Its the first. To set the EOS flag, a command is sent to the firmware. That
> > command may never return (timeout) or may report an error. For this specific
> > command, if that happens we are likely facing firmware of driver problem (or
> > both).
>
> OK, I'd add that as a comment here as this is unexpected behavior.
>
> >
> > >
> > > > + return ret;
> > > > + }
> > > > + return 0;
> > > > +}
>
> <snip>
>
> > > > +static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
> > > > + struct v4l2_create_buffers *create)
> > > > +{
> > > > + struct v4l2_format *f = &create->format;
> > > > +
> > > > + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > > > + return -ENOTTY;
> > >
> > > Huh? Why is this needed?
> >
> > Minimally a comment should be added. The why is that we support CREATE_BUF for
> > OUTPUT queue (bitstream) but not for CAPTURE queues. This is simply not
> > supported by Wave5 firmware. Do you have any suggestion how this asymmetry can
> > be implemented better ?
>
> Certainly not with ENOTTY: the ioctl exists, it is just not supported for
> CAPTURE queues.
>
> How about -EPERM? And document this error as well in the VIDIOC_CREATE_BUFS
> documentation. And you want a dev_dbg here too.

The suggestion cannot be used since there is documentation for that one already,
and it does not match "unsupported".

"Permission denied. Can be returned if the device needs write permission, or
some special capabilities is needed (e. g. root)"

What about using the most logical error code, which name is actually obvious,
like ENOTSUP ?

#define ENOTSUPP 524 /* Operation is not supported */

>
> So I would propose that EPERM is returned if CREATE_BUFS is only supported
> for for one of the two queues of an M2M device.

Note that userspace does not care of the difference between an ioctl not being
implemented at all or not being implement for one queue. GStreamer have been
testing with both queue type for couple of years now. Adding this distinction is
just leaking an implementation details to userspace. I'm fine to just do what
you'd like, just stating the obvious that while it may look logical inside the
kernel, its a bit of a non-sense for our users.

regards,
Nicolas

>
> >
> > >
> > > > +
> > > > + return v4l2_m2m_ioctl_create_bufs(file, priv, create);
> > > > +}
>
> <snip>
>
> > > > +static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
> > > > + unsigned int *num_planes, unsigned int sizes[],
> > > > + struct device *alloc_devs[])
> > > > +{
> > > > + struct vpu_instance *inst = vb2_get_drv_priv(q);
> > > > + struct v4l2_pix_format_mplane inst_format =
> > > > + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
> > > > + unsigned int i;
> > > > +
> > > > + dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
> > > > + *num_buffers, *num_planes, q->type);
> > > > +
> > > > + /* the CREATE_BUFS case */
> > > > + if (*num_planes) {
> > > > + if (inst_format.num_planes != *num_planes)
> > > > + return -EINVAL;
> > > > +
> > > > + for (i = 0; i < *num_planes; i++) {
> > > > + if (sizes[i] < inst_format.plane_fmt[i].sizeimage)
> > > > + return -EINVAL;
> > > > + }
> > > > + /* the REQBUFS case */
> > > > + } else {
> > > > + *num_planes = inst_format.num_planes;
> > > > +
> > > > + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> > > > + sizes[0] = inst_format.plane_fmt[0].sizeimage;
> > > > + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
> > > > + } else if (*num_planes == 1) {
> > > > + if (inst->output_format == FORMAT_422)
> > > > + sizes[0] = inst_format.width * inst_format.height * 2;
> > > > + else
> > > > + sizes[0] = inst_format.width * inst_format.height * 3 / 2;
> > > > + dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
> > > > + } else if (*num_planes == 2) {
> > > > + sizes[0] = inst_format.width * inst_format.height;
> > > > + if (inst->output_format == FORMAT_422)
> > > > + sizes[1] = inst_format.width * inst_format.height;
> > > > + else
> > > > + sizes[1] = inst_format.width * inst_format.height / 2;
> > > > + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n",
> > > > + __func__, sizes[0], sizes[1]);
> > > > + } else if (*num_planes == 3) {
> > > > + sizes[0] = inst_format.width * inst_format.height;
> > > > + if (inst->output_format == FORMAT_422) {
> > > > + sizes[1] = inst_format.width * inst_format.height / 2;
> > > > + sizes[2] = inst_format.width * inst_format.height / 2;
> > > > + } else {
> > > > + sizes[1] = inst_format.width * inst_format.height / 4;
> > > > + sizes[2] = inst_format.width * inst_format.height / 4;
> > > > + }
> > > > + dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n",
> > > > + __func__, sizes[0], sizes[1], sizes[2]);
> > > > + }
> > > > + }
> > > > +
> > > > + if (inst->state == VPU_INST_STATE_INIT_SEQ &&
> > > > + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> > > > + if (*num_buffers > inst->dst_buf_count &&
> > > > + *num_buffers < WAVE5_MAX_FBS)
> > > > + inst->dst_buf_count = *num_buffers;
> > >
> > > In the create_bufs case, *num_buffers is the number of buffers you are
> > > adding to the already existing buffers. Frankly, the logic here is
> > > dubious. I'm not sure what the intent is. Why do you need to keep track
> > > of the buf count at all when the vb2_queue already does that?
> >
> > This needs to be cleaned up. CREATE_BUFS case is not supported for capture, and
> > so there is no such weirdo case second calls for that queue at least. Meanwhile,
> > inst->dst_buf_count is used a MIN_BUFFERS_FOR_CAPTURE initially, and the number
> > of allocated buffer later. I think it would be better to simply store the min,
> > and use the queue to track the number of allocated buffers.
> >
> > In this diver, the reference frame are stored separately, and compressed. The
> > capture queue contains the display frame. There is a gap when comes time to
> > transfer timestamp, and for this reason we had to keep the two fbc_count equal.
> > We classified this as hardware issue and moved on.
> >
> > I think the dst_buf_count can be dropped now and the "*num_buffers > inst-
> > > dst_buf_count" not longer make any sense.
> >
> > >
> > > WAVE5_MAX_FBS == 32, which is VIDEO_MAX_FRAMES. You can just drop the check
> > > against WAVE5_MAX_FBS since the core ensures already it will never allocate
> > > more than VIDEO_MAX_FRAMES.
> > >
> > > I'm not sure why WAVE5_MAX_FBS is defined at all, when it is just equal to
> > > VIDEO_MAX_FRAMES. Perhaps it can be replaced everywhere with VIDEO_MAX_FRAMES?
> >
> > That is more challenging changes, since VIDEO_MAX_FRAMES is a software
> > limitation, but WAVE5_MAX_FBS is a hardware limitation. Buffer index only have 4
> > bits on this hardware. And the marking of frame being used for display is using
> > a 32bit flag. Considering there is effort to lift that software limitation, it
> > seems ill advised to completely stop ensuring this HW limit is respected.
>
> If there are only 4 bits for the buffer index, shouldn't WAVE5_MAX_FBS be 16? Or
> did you mean '5 bits'? Assuming that you meant '5 bits', then that makes
> WAVE5_MAX_FBS identical to VIDEO_MAX_FRAMES, but that is just luck, really.
>
> In any case, you should document at the place where WAVE5_MAX_FBS is defined that
> this is a hardware limitation, and not a random software limit.
>
> I also think that the DELETE_BUFS series should allow drivers to set max_num_buffers
> to a value less than 32 (currently the requirement is that it is at least 32).
>
> I saw a few more drivers that limit the number of buffers, usually based on the
> format (and so the buffer size) and some HW memory limitation. It might be interesting
> to allow drivers to change max_num_buffers on the fly (provided no buffers are
> allocated, of course). Limit checking is really something that vb2 should do, and
> not the driver.
>
> >
> > I'm open for suggestions.
> >
> > >
> > > > +
> > > > + *num_buffers = inst->dst_buf_count;
> > > > + }
> > > > +
> > > > + return 0;
> > > > +}
>
> Regards,
>
> Hans

2023-09-27 07:26:24

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

On 27/09/2023 01:29, Nicolas Dufresne wrote:
> Le vendredi 22 septembre 2023 à 09:33 +0200, Hans Verkuil a écrit :
>> On 21/09/2023 21:11, Nicolas Dufresne wrote:
>>> Le mercredi 20 septembre 2023 à 17:13 +0200, Hans Verkuil a écrit :
>>>> On 15/09/2023 23:11, Sebastian Fricke wrote:
>>>>> From: Nas Chung <[email protected]>
>>>>>
>>>>> Add the decoder and encoder implementing the v4l2
>>>>> API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
>>>>>
>>>>> Signed-off-by: Sebastian Fricke <[email protected]>
>>>>> Signed-off-by: Nicolas Dufresne <[email protected]>
>>>>> Signed-off-by: Robert Beckett <[email protected]>
>>>>> Signed-off-by: Dafna Hirschfeld <[email protected]>
>>>>> Signed-off-by: Nas Chung <[email protected]>
>>>>> ---
>>>>> drivers/media/platform/chips-media/Kconfig | 1 +
>>>>> drivers/media/platform/chips-media/Makefile | 1 +
>>>>> drivers/media/platform/chips-media/wave5/Kconfig | 12 +
>>>>> drivers/media/platform/chips-media/wave5/Makefile | 10 +
>>>>> .../platform/chips-media/wave5/wave5-helper.c | 196 ++
>>>>> .../platform/chips-media/wave5/wave5-helper.h | 30 +
>>>>> .../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
>>>>> .../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
>>>>> .../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
>>>>> .../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
>>>>> 10 files changed, 4454 insertions(+)
>>>>>
>>
>> <snip>
>>
>>>>> +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
>>>>> + if (ret) {
>>>>> + dev_err(inst->dev->dev,
>>>>> + "Setting EOS for the bitstream, fail: %d\n", ret);
>>>>
>>>> Is this an error due to a driver problem, or because a bad bitstream is
>>>> fed from userspace? In the first case, dev_err would be right, in the
>>>> second dev_dbg would be more appropriate. Bad userspace input should not
>>>> spam the kernel log in general.
>>>
>>> Its the first. To set the EOS flag, a command is sent to the firmware. That
>>> command may never return (timeout) or may report an error. For this specific
>>> command, if that happens we are likely facing firmware of driver problem (or
>>> both).
>>
>> OK, I'd add that as a comment here as this is unexpected behavior.
>>
>>>
>>>>
>>>>> + return ret;
>>>>> + }
>>>>> + return 0;
>>>>> +}
>>
>> <snip>
>>
>>>>> +static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
>>>>> + struct v4l2_create_buffers *create)
>>>>> +{
>>>>> + struct v4l2_format *f = &create->format;
>>>>> +
>>>>> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>>>> + return -ENOTTY;
>>>>
>>>> Huh? Why is this needed?
>>>
>>> Minimally a comment should be added. The why is that we support CREATE_BUF for
>>> OUTPUT queue (bitstream) but not for CAPTURE queues. This is simply not
>>> supported by Wave5 firmware. Do you have any suggestion how this asymmetry can
>>> be implemented better ?
>>
>> Certainly not with ENOTTY: the ioctl exists, it is just not supported for
>> CAPTURE queues.
>>
>> How about -EPERM? And document this error as well in the VIDIOC_CREATE_BUFS
>> documentation. And you want a dev_dbg here too.
>
> The suggestion cannot be used since there is documentation for that one already,
> and it does not match "unsupported".
>
> "Permission denied. Can be returned if the device needs write permission, or
> some special capabilities is needed (e. g. root)"
>
> What about using the most logical error code, which name is actually obvious,
> like ENOTSUP ?
>
> #define ENOTSUPP 524 /* Operation is not supported */
>

Let's go with EOPNOTSUPP. That seems to be the more commonly used error
code in drivers.

>>
>> So I would propose that EPERM is returned if CREATE_BUFS is only supported
>> for for one of the two queues of an M2M device.
>
> Note that userspace does not care of the difference between an ioctl not being
> implemented at all or not being implement for one queue. GStreamer have been
> testing with both queue type for couple of years now. Adding this distinction is
> just leaking an implementation details to userspace. I'm fine to just do what
> you'd like, just stating the obvious that while it may look logical inside the
> kernel, its a bit of a non-sense for our users.

I don't agree with that. If an ioctl returns ENOTTY, then userspace can be certain
that that ioctl is not implemented for the given file descriptor. That's not the case
here: it is implemented, the operation is just not supported for one of the queues.

Regards,

Hans

2023-10-02 23:51:43

by Deborah Brouwer

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

On Wed, Sep 27, 2023 at 09:19:46AM +0200, Hans Verkuil wrote:
> On 27/09/2023 01:29, Nicolas Dufresne wrote:
> > Le vendredi 22 septembre 2023 ? 09:33 +0200, Hans Verkuil a ?crit?:
> >> On 21/09/2023 21:11, Nicolas Dufresne wrote:
> >>> Le mercredi 20 septembre 2023 ? 17:13 +0200, Hans Verkuil a ?crit?:
> >>>> On 15/09/2023 23:11, Sebastian Fricke wrote:
> >>>>> From: Nas Chung <[email protected]>
> >>>>>
> >>>>> Add the decoder and encoder implementing the v4l2
> >>>>> API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
> >>>>>
> >>>>> Signed-off-by: Sebastian Fricke <[email protected]>
> >>>>> Signed-off-by: Nicolas Dufresne <[email protected]>
> >>>>> Signed-off-by: Robert Beckett <[email protected]>
> >>>>> Signed-off-by: Dafna Hirschfeld <[email protected]>
> >>>>> Signed-off-by: Nas Chung <[email protected]>
> >>>>> ---
> >>>>> drivers/media/platform/chips-media/Kconfig | 1 +
> >>>>> drivers/media/platform/chips-media/Makefile | 1 +
> >>>>> drivers/media/platform/chips-media/wave5/Kconfig | 12 +
> >>>>> drivers/media/platform/chips-media/wave5/Makefile | 10 +
> >>>>> .../platform/chips-media/wave5/wave5-helper.c | 196 ++
> >>>>> .../platform/chips-media/wave5/wave5-helper.h | 30 +
> >>>>> .../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
> >>>>> .../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
> >>>>> .../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
> >>>>> .../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
> >>>>> 10 files changed, 4454 insertions(+)
> >>>>>
> >>
> >> <snip>
> >>
> >>>>> +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
> >>>>> +{
> >>>>> + int ret;
> >>>>> +
> >>>>> + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
> >>>>> + if (ret) {
> >>>>> + dev_err(inst->dev->dev,
> >>>>> + "Setting EOS for the bitstream, fail: %d\n", ret);
> >>>>
> >>>> Is this an error due to a driver problem, or because a bad bitstream is
> >>>> fed from userspace? In the first case, dev_err would be right, in the
> >>>> second dev_dbg would be more appropriate. Bad userspace input should not
> >>>> spam the kernel log in general.
> >>>
> >>> Its the first. To set the EOS flag, a command is sent to the firmware. That
> >>> command may never return (timeout) or may report an error. For this specific
> >>> command, if that happens we are likely facing firmware of driver problem (or
> >>> both).
> >>
> >> OK, I'd add that as a comment here as this is unexpected behavior.
> >>
> >>>
> >>>>
> >>>>> + return ret;
> >>>>> + }
> >>>>> + return 0;
> >>>>> +}
> >>
> >> <snip>
> >>
> >>>>> +static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
> >>>>> + struct v4l2_create_buffers *create)
> >>>>> +{
> >>>>> + struct v4l2_format *f = &create->format;
> >>>>> +
> >>>>> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> >>>>> + return -ENOTTY;
> >>>>
> >>>> Huh? Why is this needed?
> >>>
> >>> Minimally a comment should be added. The why is that we support CREATE_BUF for
> >>> OUTPUT queue (bitstream) but not for CAPTURE queues. This is simply not
> >>> supported by Wave5 firmware. Do you have any suggestion how this asymmetry can
> >>> be implemented better ?
> >>
> >> Certainly not with ENOTTY: the ioctl exists, it is just not supported for
> >> CAPTURE queues.
> >>
> >> How about -EPERM? And document this error as well in the VIDIOC_CREATE_BUFS
> >> documentation. And you want a dev_dbg here too.
> >
> > The suggestion cannot be used since there is documentation for that one already,
> > and it does not match "unsupported".
> >
> > "Permission denied. Can be returned if the device needs write permission, or
> > some special capabilities is needed (e. g. root)"
> >
> > What about using the most logical error code, which name is actually obvious,
> > like ENOTSUP ?
> >
> > #define ENOTSUPP 524 /* Operation is not supported */
> >
>
> Let's go with EOPNOTSUPP. That seems to be the more commonly used error
> code in drivers.

Hi Hans,

Sorry to belabour this issue but when I change the return value
to EOPNOTSUPP, it now causes v4l2-compliance to fail because
v4l2-test-buffers.cpp expects ENOTTY if CREATE_BUFS is not supported.

We didn't get this warning before because there was a typo in the
buffer check and it was only checking for single-planar buffers.

How would you prefer to handle this? The options seem like
keep ENOTTY in this driver or
patch v4l2-compliance to warn if it also receives EOPNOTSUPP?

>
> >>
> >> So I would propose that EPERM is returned if CREATE_BUFS is only supported
> >> for for one of the two queues of an M2M device.
> >
> > Note that userspace does not care of the difference between an ioctl not being
> > implemented at all or not being implement for one queue. GStreamer have been
> > testing with both queue type for couple of years now. Adding this distinction is
> > just leaking an implementation details to userspace. I'm fine to just do what
> > you'd like, just stating the obvious that while it may look logical inside the
> > kernel, its a bit of a non-sense for our users.
>
> I don't agree with that. If an ioctl returns ENOTTY, then userspace can be certain
> that that ioctl is not implemented for the given file descriptor. That's not the case
> here: it is implemented, the operation is just not supported for one of the queues.
>
> Regards,
>
> Hans

2023-10-03 06:55:43

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v12 5/7] media: chips-media: wave5: Add the v4l2 layer

Hi Deb,

On 03/10/2023 01:51, Deborah Brouwer wrote:
> On Wed, Sep 27, 2023 at 09:19:46AM +0200, Hans Verkuil wrote:
>> On 27/09/2023 01:29, Nicolas Dufresne wrote:
>>> Le vendredi 22 septembre 2023 à 09:33 +0200, Hans Verkuil a écrit :
>>>> On 21/09/2023 21:11, Nicolas Dufresne wrote:
>>>>> Le mercredi 20 septembre 2023 à 17:13 +0200, Hans Verkuil a écrit :
>>>>>> On 15/09/2023 23:11, Sebastian Fricke wrote:
>>>>>>> From: Nas Chung <[email protected]>
>>>>>>>
>>>>>>> Add the decoder and encoder implementing the v4l2
>>>>>>> API. This patch also adds the Makefile and the VIDEO_WAVE_VPU config
>>>>>>>
>>>>>>> Signed-off-by: Sebastian Fricke <[email protected]>
>>>>>>> Signed-off-by: Nicolas Dufresne <[email protected]>
>>>>>>> Signed-off-by: Robert Beckett <[email protected]>
>>>>>>> Signed-off-by: Dafna Hirschfeld <[email protected]>
>>>>>>> Signed-off-by: Nas Chung <[email protected]>
>>>>>>> ---
>>>>>>> drivers/media/platform/chips-media/Kconfig | 1 +
>>>>>>> drivers/media/platform/chips-media/Makefile | 1 +
>>>>>>> drivers/media/platform/chips-media/wave5/Kconfig | 12 +
>>>>>>> drivers/media/platform/chips-media/wave5/Makefile | 10 +
>>>>>>> .../platform/chips-media/wave5/wave5-helper.c | 196 ++
>>>>>>> .../platform/chips-media/wave5/wave5-helper.h | 30 +
>>>>>>> .../platform/chips-media/wave5/wave5-vpu-dec.c | 1965 ++++++++++++++++++++
>>>>>>> .../platform/chips-media/wave5/wave5-vpu-enc.c | 1825 ++++++++++++++++++
>>>>>>> .../media/platform/chips-media/wave5/wave5-vpu.c | 331 ++++
>>>>>>> .../media/platform/chips-media/wave5/wave5-vpu.h | 83 +
>>>>>>> 10 files changed, 4454 insertions(+)
>>>>>>>
>>>>
>>>> <snip>
>>>>
>>>>>>> +static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + ret = wave5_vpu_dec_update_bitstream_buffer(inst, 0);
>>>>>>> + if (ret) {
>>>>>>> + dev_err(inst->dev->dev,
>>>>>>> + "Setting EOS for the bitstream, fail: %d\n", ret);
>>>>>>
>>>>>> Is this an error due to a driver problem, or because a bad bitstream is
>>>>>> fed from userspace? In the first case, dev_err would be right, in the
>>>>>> second dev_dbg would be more appropriate. Bad userspace input should not
>>>>>> spam the kernel log in general.
>>>>>
>>>>> Its the first. To set the EOS flag, a command is sent to the firmware. That
>>>>> command may never return (timeout) or may report an error. For this specific
>>>>> command, if that happens we are likely facing firmware of driver problem (or
>>>>> both).
>>>>
>>>> OK, I'd add that as a comment here as this is unexpected behavior.
>>>>
>>>>>
>>>>>>
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> + return 0;
>>>>>>> +}
>>>>
>>>> <snip>
>>>>
>>>>>>> +static int wave5_vpu_dec_create_bufs(struct file *file, void *priv,
>>>>>>> + struct v4l2_create_buffers *create)
>>>>>>> +{
>>>>>>> + struct v4l2_format *f = &create->format;
>>>>>>> +
>>>>>>> + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>>>>>> + return -ENOTTY;
>>>>>>
>>>>>> Huh? Why is this needed?
>>>>>
>>>>> Minimally a comment should be added. The why is that we support CREATE_BUF for
>>>>> OUTPUT queue (bitstream) but not for CAPTURE queues. This is simply not
>>>>> supported by Wave5 firmware. Do you have any suggestion how this asymmetry can
>>>>> be implemented better ?
>>>>
>>>> Certainly not with ENOTTY: the ioctl exists, it is just not supported for
>>>> CAPTURE queues.
>>>>
>>>> How about -EPERM? And document this error as well in the VIDIOC_CREATE_BUFS
>>>> documentation. And you want a dev_dbg here too.
>>>
>>> The suggestion cannot be used since there is documentation for that one already,
>>> and it does not match "unsupported".
>>>
>>> "Permission denied. Can be returned if the device needs write permission, or
>>> some special capabilities is needed (e. g. root)"
>>>
>>> What about using the most logical error code, which name is actually obvious,
>>> like ENOTSUP ?
>>>
>>> #define ENOTSUPP 524 /* Operation is not supported */
>>>
>>
>> Let's go with EOPNOTSUPP. That seems to be the more commonly used error
>> code in drivers.
>
> Hi Hans,
>
> Sorry to belabour this issue but when I change the return value
> to EOPNOTSUPP, it now causes v4l2-compliance to fail because
> v4l2-test-buffers.cpp expects ENOTTY if CREATE_BUFS is not supported.
>
> We didn't get this warning before because there was a typo in the
> buffer check and it was only checking for single-planar buffers.
>
> How would you prefer to handle this? The options seem like
> keep ENOTTY in this driver or
> patch v4l2-compliance to warn if it also receives EOPNOTSUPP?

You patch v4l2-compliance. It makes sense: we're making a uAPI modification,
so that implies changes to v4l2-compliance.

So v4l2-compliance needs to understand EOPNOTSUPP for CREATE_BUFS: if it is
returned it has to check that it is used correctly: so there has to be at
least one buffer type for which CREATE_BUFS actually works. In other words,
v4l2-compliance must check that EOPNOTSUPP isn't used as a replacement
for ENOTTY.

This can be done in testReqBufs().

Regards,

Hans