RE: [PATCH v7 3/9] media: chips-media: wave6: Add Wave6 VPU interface
From: Nas Chung <nas.chung@chipsnmedia.com>
Date: 2026-09-14 04:37:50
Also in:
linux-arm-kernel, linux-media, lkml
Hi, Frank.
-----Original Message----- From: Frank Li <redacted> Sent: Friday, September 11, 2026 4:51 AM To: Nas Chung <nas.chung@chipsnmedia.com> Cc: mchehab@kernel.org; hverkuil@xs4all.nl; robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org; shawnguo@kernel.org; s.hauer@pengutronix.de; linux-media@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-imx@nxp.com; linux-arm-kernel@lists.infradead.org; jackson.lee [off-list ref]; lafley.kim [off-list ref]; marek.vasut@mailbox.org; Ming Qian [off-list ref] Subject: Re: [PATCH v7 3/9] media: chips-media: wave6: Add Wave6 VPU interface On Fri, Sep 04, 2026 at 03:46:29PM +0900, Nas Chung wrote:quoted
Add an interface layer to manage hardware register configuration and communication with the Chips&Media Wave6 video codec IP. The interface provides low-level helper functions used by the Wave6 core driver to implement video encoding and decoding operations. It handles command submission to the firmware via MMIO registers, and waits for a response by polling the firmware busy flag. Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com> Tested-by: Ming Qian <redacted> Tested-by: Marek Vasut <redacted> --- MAINTAINERS | 1 + .../platform/chips-media/wave6/wave6-hw.c | 2030 +++++++++++++++++ .../platform/chips-media/wave6/wave6-hw.h | 56 + .../chips-media/wave6/wave6-regdefine.h | 649 ++++++ .../platform/chips-media/wave6/wave6-vdi.h | 92 + .../platform/chips-media/wave6/wave6-vpuapi.c | 336 +++ .../platform/chips-media/wave6/wave6-vpuapi.h | 1007 ++++++++ .../chips-media/wave6/wave6-vpuconfig.h | 72 + .../chips-media/wave6/wave6-vpuerror.h | 262 +++You need make this patch built-able. Add Makefile and Kconfig part.
OK. I'll move Makefile and Kconfig in v8.
quoted
9 files changed, 4505 insertions(+) create mode 100644 drivers/media/platform/chips-media/wave6/wave6-hw.c create mode 100644 drivers/media/platform/chips-media/wave6/wave6-hw.h create mode 100644 drivers/media/platform/chips-media/wave6/wave6-regdefine.hquoted
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vdi.h create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuapi.cquoted
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuapi.hquoted
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuconfig.hquoted
create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpuerror.hquoted
diff --git a/MAINTAINERS b/MAINTAINERS index 7387a11facbe..e29018c2546b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS@@ -29239,6 +29239,7 @@ M: Jackson Lee <jackson.lee@chipsnmedia.com> L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/nxp,imx95-vpu.yaml +F: drivers/media/platform/chips-media/wave6/ WHISKEYCOVE PMIC GPIO DRIVER M: Kuppuswamy Sathyanarayanan[off-list ref]quoted
diff --git a/drivers/media/platform/chips-media/wave6/wave6-hw.cb/drivers/media/platform/chips-media/wave6/wave6-hw.cquoted
new file mode 100644 index 000000000000..a91b582af023--- /dev/null +++ b/drivers/media/platform/chips-media/wave6/wave6-hw.c@@ -0,0 +1,2030 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave6 series multi-standard codec IP - wave6 backend interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + */ + +#include <linux/iopoll.h> +#include "wave6-vpu-core.h" +#include "wave6-hw.h" +#include "wave6-regdefine.h" +#include "wave6-trace.h"Add trace support later, add with patch6.
OK.
quoted
+ +void wave6_vpu_writel(struct vpu_core_device *core, u32 addr, u32 data) +{ + wave6_vdi_writel(core->reg_base, addr, data); + trace_wave6_vpu_writel(core->dev, addr, data);Add trace support at later patches.
OK.
quoted
+} + +u32 wave6_vpu_readl(struct vpu_core_device *core, u32 addr) +{ + u32 data; + + data = wave6_vdi_readl(core->reg_base, addr); + trace_wave6_vpu_readl(core->dev, addr, data); + + return data; +} + +static void wave6_print_reg_err(struct vpu_core_device *core, u32fail_reason)quoted
+{ + void *caller = __builtin_return_address(0); + struct device *dev = core->dev; + + switch (fail_reason) { + case WAVE6_SYSERR_QUEUEING_FAIL:you define it as bit mask, is it possible more than 1 bits to set?
No, the firmware reports one reason at a time in W6_RET_FAIL_REASON, so only one bit is ever set.
quoted
+ dev_dbg(dev, "%pS: queueing failure 0x%x\n", caller,fail_reason);quoted
+ break; + case WAVE6_SYSERR_RESULT_NOT_READY: + dev_err(dev, "%pS: result not ready 0x%x\n", caller,fail_reason);quoted
+ break; + case WAVE6_SYSERR_ACCESS_VIOLATION_HW: + dev_err(dev, "%pS: access violation 0x%x\n", caller,fail_reason);quoted
+ break; + case WAVE6_SYSERR_WATCHDOG_TIMEOUT: + dev_err(dev, "%pS: watchdog timeout 0x%x\n", caller,fail_reason);quoted
+ break; + case WAVE6_SYSERR_BUS_ERROR: + dev_err(dev, "%pS: bus error 0x%x\n", caller, fail_reason); + break; + case WAVE6_SYSERR_DOUBLE_FAULT: + dev_err(dev, "%pS: double fault 0x%x\n", caller, fail_reason); + break; + case WAVE6_SYSERR_VPU_STILL_RUNNING: + dev_err(dev, "%pS: still running 0x%x\n", caller,fail_reason);quoted
+ break; + default: + dev_err(dev, "%pS: failure: 0x%x\n", caller, fail_reason); + break; + } +} + +static void wave6_dec_set_display_buffer(struct vpu_instance *inst,struct frame_buffer fb)quoted
+ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int index; + + for (index = 0; index < WAVE6_MAX_FBS; index++) { + if (!p_dec_info->disp_buf[index].buf_y) { + p_dec_info->disp_buf[index] = fb; + p_dec_info->disp_buf[index].index = index;index is the same as nature array index p_dec_info->disp_buf, why need it.
Agreed. I'll remove the index field.
quoted
+ break;where update .buf_y?
p_dec_info->disp_buf[index] = fb; assigns the whole struct, buf_y included.
quoted
+ } + } +} + +static struct frame_buffer wave6_dec_get_display_buffer(structvpu_instance *inst,quoted
+ dma_addr_t addr) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int i; + struct frame_buffer fb; + + for (i = 0; i < WAVE6_MAX_FBS; i++) { + if (p_dec_info->disp_buf[i].buf_y == addr) + return p_dec_info->disp_buf[i]; + } + + memset(&fb, 0, sizeof(struct frame_buffer)); + fb.index = -1;suppose this error case, did caller tolerent fb.index -1, I have not see check this value.
In wave6_vpu_dec_get_output_info(), decoded_idx and disp_idx start at -1 and every dec_out_info[] access checks both bounds. I'll remove the index and handle this in wave6_vpu_dec_get_output_info().
quoted
+ + return fb; +} + +static void wave6_dec_remove_display_buffer(struct vpu_instance *inst, + dma_addr_t addr) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + int i; + + for (i = 0; i < WAVE6_MAX_FBS; i++) { + if (p_dec_info->disp_buf[i].buf_y == addr) { + memset(&p_dec_info->disp_buf[i], 0, sizeof(structframe_buffer)); you suppose need set p_dec_info->disp_buf[i].buf_y to 0?
Agreed. I'll drop the memset and just set buf_y to 0.
quoted
+ break; + } + } +} + +static enum chroma_format_idc get_chroma_format_idc(enumframe_buffer_format fmt)quoted
+{ + switch (fmt) { + case FORMAT_420: + case FORMAT_420_P10_16BIT_MSB: + case FORMAT_420_P10_16BIT_LSB: + case FORMAT_420_P10_32BIT_MSB: + case FORMAT_420_P10_32BIT_LSB: + return C_FMT_IDC_YUV420; + case FORMAT_422: + case FORMAT_422_P10_16BIT_MSB: + case FORMAT_422_P10_16BIT_LSB: + case FORMAT_422_P10_32BIT_MSB: + case FORMAT_422_P10_32BIT_LSB: + case FORMAT_YUYV: + case FORMAT_YVYU: + case FORMAT_UYVY: + case FORMAT_VYUY: + case FORMAT_YUYV_P10_16BIT_MSB: + case FORMAT_YVYU_P10_16BIT_MSB: + case FORMAT_UYVY_P10_16BIT_MSB: + case FORMAT_VYUY_P10_16BIT_MSB: + case FORMAT_YUYV_P10_16BIT_LSB: + case FORMAT_YVYU_P10_16BIT_LSB: + case FORMAT_UYVY_P10_16BIT_LSB: + case FORMAT_VYUY_P10_16BIT_LSB: + case FORMAT_YUYV_P10_32BIT_MSB: + case FORMAT_YVYU_P10_32BIT_MSB: + case FORMAT_UYVY_P10_32BIT_MSB: + case FORMAT_VYUY_P10_32BIT_MSB: + case FORMAT_YUYV_P10_32BIT_LSB: + case FORMAT_YVYU_P10_32BIT_LSB: + case FORMAT_UYVY_P10_32BIT_LSB: + case FORMAT_VYUY_P10_32BIT_LSB: + return C_FMT_IDC_YUV422; + case FORMAT_444: + case FORMAT_444_P10_16BIT_MSB: + case FORMAT_444_P10_16BIT_LSB: + case FORMAT_444_P10_32BIT_MSB: + case FORMAT_444_P10_32BIT_LSB: + return C_FMT_IDC_YUV444; + case FORMAT_400: + case FORMAT_400_P10_16BIT_MSB: + case FORMAT_400_P10_16BIT_LSB: + case FORMAT_400_P10_32BIT_MSB: + case FORMAT_400_P10_32BIT_LSB: + case FORMAT_YUV444_24BIT: + return C_FMT_IDC_YUV400; + case FORMAT_RGB_24BIT_PACKED: + case FORMAT_YUV444_24BIT_PACKED: + case FORMAT_RGB_32BIT_PACKED: + case FORMAT_RGB_P10_32BIT_PACKED: + case FORMAT_YUV444_32BIT_PACKED: + case FORMAT_YUV444_P10_32BIT_PACKED: + return C_FMT_IDC_RGB; + default: + return C_FMT_IDC_YUV400; + } +} + +static int wave6_wait_vpu_busy(struct vpu_core_device *core, unsignedint addr)quoted
+{ + u32 data; + + lockdep_assert_held(&core->hw_lock); + + return read_poll_timeout(vpu_read_reg, data, !data, + W6_VPU_POLL_DELAY_US, W6_VPU_POLL_TIMEOUT, + false, core, addr); +} + +void wave6_vpu_enable_interrupt(struct vpu_core_device *core) +{ + u32 data; + + lockdep_assert_held(&core->hw_lock); + + data = BIT(W6_INT_BIT_ENC_SET_PARAM); + data |= BIT(W6_INT_BIT_ENC_PIC); + data |= BIT(W6_INT_BIT_INIT_SEQ); + data |= BIT(W6_INT_BIT_DEC_PIC); + data |= BIT(W6_INT_BIT_BSBUF_ERROR); + data |= BIT(W6_INT_BIT_REQ_WORK_BUF); + vpu_write_reg(core, W6_VPU_VINT_ENABLE, data); +} + +bool wave6_vpu_is_init(struct vpu_core_device *core) +{ + lockdep_assert_held(&core->hw_lock); + + return vpu_read_reg(core, W6_VPU_VCPU_CUR_PC) != 0; +} + +static u32 wave6_vpu_get_product_id(struct vpu_core_device *core) +{ + u32 product_id = PRODUCT_ID_NONE; + u32 val; + + lockdep_assert_held(&core->hw_lock); + + val = vpu_read_reg(core, W6_VPU_RET_PRODUCT_CODE); + + switch (val) { + case WAVE617_CODE: + product_id = PRODUCT_ID_617;return PRODUCT_ID_617;quoted
+ break; + case WAVE627_CODE: + product_id = PRODUCT_ID_627; + break;return PRODUCT_ID_627;quoted
+ case WAVE633_CODE: + case WAVE637_CODE: + case WAVE663_CODE: + case WAVE677_CODE: + product_id = PRODUCT_ID_637; + break;return PRODUCT_ID_637;quoted
+ default: + dev_err(core->dev, "Invalid product (%x)\n", val); + break; + } + + return product_id;return PRODUCT_ID_NONE; needn't variable product_id
OK.
quoted
+} + +static void wave6_send_command(struct vpu_core_device *core, u32 id, u32std, u32 cmd)quoted
+{ + u32 reg_val; + + lockdep_assert_held(&core->hw_lock); + + if (cmd == W6_CMD_CREATE_INSTANCE) + reg_val = INSTANCE_INFO_CODEC_STD(std); + else + reg_val = INSTANCE_INFO_CODEC_STD(std) | INSTANCE_INFO_ID(id);reg_val = INSTANCE_INFO_CODEC_STD(std); if (cmd != W6_CMD_CREATE_INSTANCE) reg_val |= INSTANCE_INFO_ID(id);
OK.
quoted
+ vpu_write_reg(core, W6_CMD_INSTANCE_INFO, reg_val); + + vpu_write_reg(core, W6_VPU_BUSY_STATUS, BUSY_STATUS_SET); + vpu_write_reg(core, W6_COMMAND, cmd); + vpu_write_reg(core, W6_VPU_HOST_INT_REQ, HOST_INT_REQ_ON); + + trace_wave6_vpu_send_command(core, id, std, cmd); +} + +static int wave6_send_query(struct vpu_core_device *core, u32 id, u32std,quoted
+ enum wave6_query_option query_opt) +{ + int ret; + u32 reg_val; + + lockdep_assert_held(&core->hw_lock); + + vpu_write_reg(core, W6_QUERY_OPTION, query_opt); + wave6_send_command(core, id, std, W6_CMD_QUERY); + + ret = wave6_wait_vpu_busy(core, W6_VPU_BUSY_STATUS); + if (ret) { + dev_err(core->dev, "query timed out opt=0x%x\n", query_opt); + return ret; + } + + if (!vpu_read_reg(core, W6_RET_SUCCESS)) { + reg_val = vpu_read_reg(core, W6_RET_FAIL_REASON); + wave6_print_reg_err(core, reg_val);You can add such debug helper function later to reduce each patch's change.
OK. I'll split the debug part to other patch.
quoted
+ return -EIO; + } + + return 0; +}
...
quoted
+static void wave6_get_dec_seq_result(struct vpu_instance *inst, structdec_seq_info *info)quoted
+{ + u32 reg_val; + u32 profile, profile_comp; + + lockdep_assert_held(&inst->dev->hw_lock); + + info->rd_ptr = wave6_vpu_dec_get_rd_ptr(inst); + + reg_val = vpu_read_reg(inst->dev, W6_RET_DEC_PIC_SIZE); + info->pic_width = DEC_PIC_SIZE_WIDTH(reg_val); + info->pic_height = DEC_PIC_SIZE_HEIGHT(reg_val); + + info->min_frame_buffer_count = vpu_read_reg(inst->dev,W6_RET_DEC_NUM_REQUIRED_FBC_FB);quoted
+ info->frame_buf_delay = vpu_read_reg(inst->dev,W6_RET_DEC_NUM_REORDER_DELAY);quoted
+ info->req_mv_buffer_count = vpu_read_reg(inst->dev,W6_RET_DEC_NUM_REQUIRED_COL_BUF);quoted
+ + reg_val = vpu_read_reg(inst->dev, W6_RET_DEC_CROP_TOP_BOTTOM); + info->pic_crop_rect.top = DEC_CROP_TOP(reg_val); + info->pic_crop_rect.bottom = info->pic_height -DEC_CROP_BOTTOM(reg_val);quoted
+ + reg_val = vpu_read_reg(inst->dev, W6_RET_DEC_CROP_LEFT_RIGHT); + info->pic_crop_rect.left = DEC_CROP_LEFT(reg_val); + info->pic_crop_rect.right = info->pic_width -DEC_CROP_RIGHT(reg_val);quoted
+ + info->f_rate_numerator = vpu_read_reg(inst->dev,W6_RET_DEC_FRAME_RATE_NR);quoted
+ info->f_rate_denominator = vpu_read_reg(inst->dev,W6_RET_DEC_FRAME_RATE_DR);quoted
+ + reg_val = vpu_read_reg(inst->dev, W6_RET_DEC_COLOR_SAMPLE); + info->luma_bit_depth = DEC_COLOR_SAMPLE_L_BIT_DEPTH(reg_val); + info->chroma_bit_depth = DEC_COLOR_SAMPLE_C_BIT_DEPTH(reg_val); + info->c_fmt_idc = DEC_COLOR_SAMPLE_C_FMT_IDC(reg_val); + info->aspect_rate_info = DEC_COLOR_SAMPLE_ASPECT_RATIO_IDC(reg_val); + info->is_ext_sar = info->aspect_rate_info ==H264_VUI_SAR_IDC_EXTENDED;quoted
+ if (info->is_ext_sar) + info->aspect_rate_info = vpu_read_reg(inst->dev,W6_RET_DEC_ASPECT_RATIO);quoted
+ info->bitrate = vpu_read_reg(inst->dev, W6_RET_DEC_BIT_RATE); + + reg_val = vpu_read_reg(inst->dev, W6_RET_DEC_SEQ_PARAM); + info->level = DEC_SEQ_PARAM_LEVEL(reg_val); + info->tier = DEC_SEQ_PARAM_TIER(reg_val); + + profile = DEC_SEQ_PARAM_PROFILE(reg_val); + profile_comp = DEC_SEQ_PARAM_PROFILE_COMPATIBILITY(reg_val); + if (inst->std == W_HEVC_DEC) { + info->profile = profile; + if (!info->profile) { + if ((profile_comp & PROFILE_COMPATIBILITY_MAIN) && + (profile_comp & PROFILE_COMPATIBILITY_MAIN10)) + info->profile = HEVC_PROFILE_MAIN; + else if (profile_comp & PROFILE_COMPATIBILITY_MAIN10) + info->profile = HEVC_PROFILE_MAIN10; + else if (profile_comp &PROFILE_COMPATIBILITY_STILL_PICTURE)quoted
+ info->profile = HEVC_PROFILE_STILLPICTURE; + else + info->profile = HEVC_PROFILE_MAIN; + } + } else if (inst->std == W_AVC_DEC) { + if (profile == PROFILE_H264_BP) + info->profile = H264_PROFILE_BP; + else if (profile == PROFILE_H264_HP) + info->profile = H264_PROFILE_HP; + else if (profile == PROFILE_H264_MP) + info->profile = H264_PROFILE_MP; + else if (profile == PROFILE_H264_HIGH10) + info->profile = H264_PROFILE_HIGH10; + else if (profile == PROFILE_H264_EXTENDED) + info->profile = H264_PROFILE_EXTENDED; + else + info->profile = H264_PROFILE_BP;use small helper function for such convertion, you can use switch case for this maping or static const array.
OK. I'll address this in v8.
quoted
+ } + + reg_val = vpu_read_reg(inst->dev, W6_RET_DEC_COLOR_CONFIG); + if (reg_val) { + info->color.video_signal_type_present = true; + info->color.color_description_present =DEC_COLOR_CONFIG_COLOR_PRESENT(reg_val);quoted
+ info->color.color_primaries =DEC_COLOR_CONFIG_COLOR_PRIMARIES(reg_val);quoted
+ info->color.transfer_characteristics =DEC_COLOR_CONFIG_TRANS_CHAR(reg_val);quoted
+ info->color.matrix_coefficients =DEC_COLOR_CONFIG_MATRIX_COEFF(reg_val);quoted
+ info->color.color_range =DEC_COLOR_CONFIG_COLOR_RANGE(reg_val);quoted
+ } else { + info->color.video_signal_type_present = false; + } +} + +int wave6_vpu_dec_get_seq_info(struct vpu_instance *inst, structdec_seq_info *info)quoted
+{ + int ret; + + lockdep_assert_held(&inst->dev->hw_lock); + + ret = wave6_send_query(inst->dev, inst->id, inst->std,W6_QUERY_OPT_GET_RESULT);quoted
+ if (ret) + return ret; + + if (vpu_read_reg(inst->dev, W6_RET_DEC_DECODING_SUCCESS) != 1) { + info->err_reason = vpu_read_reg(inst->dev,W6_RET_DEC_ERR_INFO);quoted
+ ret = -EIO; + } else { + info->warn_info = vpu_read_reg(inst->dev,W6_RET_DEC_WARN_INFO);quoted
+ } + + wave6_get_dec_seq_result(inst, info); + + return ret; +} + +int wave6_vpu_dec_register_frame_buffer(struct vpu_instance *inst) +{ + struct dec_info *p_dec_info; + struct frame_buffer *fb_arr; + size_t fbc_remain, mv_remain, fbc_idx = 0, mv_idx = 0; + size_t i, k, group_num, fbc_count, mv_count; + dma_addr_t fbc_cr_tbl_addr; + u32 reg_val; + int ret; + + guard(mutex)(&inst->dev->hw_lock); + + p_dec_info = &inst->codec_info->dec_info; + + fb_arr = &inst->frame_buf[0]; + fbc_count = p_dec_info->seq_info.min_frame_buffer_count; + mv_count = p_dec_info->seq_info.req_mv_buffer_count; + + for (i = 0; i < fbc_count; i++) { + if (!inst->aux_vbuf[AUX_BUF_FBC_Y_TBL][i].daddr) + return -EINVAL; + if (!inst->aux_vbuf[AUX_BUF_FBC_C_TBL][i].daddr) + return -EINVAL; + } + for (i = 0; i < mv_count; i++) { + if (!inst->aux_vbuf[AUX_BUF_MV_COL][i].daddr) + return -EINVAL; + }use small helper function, like check_aux_vbuf().
OK.
quoted
+ + reg_val = SET_FB_PIC_SIZE_WIDTH(p_dec_info->seq_info.pic_width) | + SET_FB_PIC_SIZE_HEIGHT(p_dec_info->seq_info.pic_height); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_PIC_SIZE, reg_val); + reg_val = SET_FB_PIC_INFO_C_FMT_IDC(p_dec_info->seq_info.c_fmt_idc)|quoted
+ SET_FB_PIC_INFO_L_BIT_DEPTH(p_dec_info- seq_info.luma_bit_depth) | + SET_FB_PIC_INFO_C_BIT_DEPTH(p_dec_info- seq_info.chroma_bit_depth); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_PIC_INFO, reg_val); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_DEFAULT_CDF, 0); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_SEGMAP, 0); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_MV_COL_PRE_ENT, 0); + + fbc_remain = fbc_count; + mv_remain = mv_count; + group_num = DIV_ROUND_UP(max(fbc_count, mv_count), 16);Add empty line here.
OK.
quoted
+ for (i = 0; i < group_num; i++) { + bool first_group = i == 0; + bool last_group = i == group_num - 1; + u32 set_fbc_num = (fbc_remain >= 16) ? 16 : fbc_remain; + u32 set_mv_num = (mv_remain >= 16) ? 16 : mv_remain; + u32 fbc_start_no = i * 16; + u32 fbc_end_no = fbc_start_no + set_fbc_num - 1; + u32 mv_start_no = i * 16; + u32 mv_end_no = mv_start_no + set_mv_num - 1; + + reg_val = SET_FB_OPTION_ENDIAN(VDI_128BIT_BIG_ENDIAN); + if (first_group) + reg_val |= SET_FB_OPTION_START; + if (last_group) + reg_val |= SET_FB_OPTION_END; + vpu_write_reg(inst->dev, W6_CMD_SET_FB_OPTION, reg_val); + + reg_val = SET_FB_NUM_FBC_START_IDX(fbc_start_no) | + SET_FB_NUM_FBC_END_IDX(fbc_end_no) | + SET_FB_NUM_MV_COL_START_IDX(mv_start_no) | + SET_FB_NUM_MV_COL_END_IDX(mv_end_no); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_NUM, reg_val); + + for (k = 0; k < set_fbc_num; k++) { + vpu_write_reg(inst->dev, W6_CMD_SET_FB_FBC_Y(k), + fb_arr[fbc_idx].buf_y); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_FBC_C(k), + fb_arr[fbc_idx].buf_cb); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_FBC_CR(k), + fb_arr[fbc_idx].buf_cr); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_FBC_Y_OFFSET(k), + inst- aux_vbuf[AUX_BUF_FBC_Y_TBL][fbc_idx].daddr); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_FBC_C_OFFSET(k), + inst- aux_vbuf[AUX_BUF_FBC_C_TBL][fbc_idx].daddr); + fbc_cr_tbl_addr = inst- aux_vbuf[AUX_BUF_FBC_C_TBL][fbc_idx].daddr + + (inst- aux_vbuf[AUX_BUF_FBC_C_TBL][fbc_idx].size >> 1); + vpu_write_reg(inst->dev, W6_CMD_SET_FB_FBC_CR_OFFSET(k), + fbc_cr_tbl_addr);I am not sure how big set_fbc_num, I suggest change to use writel_relax(). because you wave6_wait_vpu_busy() to check status, extra memory barry in writel() is not neccessary.
OK, as I answered on your earlier mail. The barriered writel() stays in wave6_send_command().
quoted
+ fbc_idx++; + } + fbc_remain -= k; + + for (k = 0; k < set_mv_num; k++) { + vpu_write_reg(inst->dev, W6_CMD_SET_FB_MV_COL(k), + inst- aux_vbuf[AUX_BUF_MV_COL][mv_idx].daddr); + mv_idx++; + } + mv_remain -= k; + + wave6_send_command(inst->dev, inst->id, inst->std,W6_CMD_SET_FB);quoted
+ ret = wave6_wait_vpu_busy(inst->dev, W6_VPU_BUSY_STATUS); + if (ret) { + dev_err(inst->dev->dev, "%s: timeout\n", __func__); + return ret; + } + + if (!vpu_read_reg(inst->dev, W6_RET_SUCCESS)) + return -EIO; + } + + return 0; +} + +int wave6_vpu_dec_register_display_buffer(struct vpu_instance *inst,struct frame_buffer fb)quoted
+{ + int ret; + struct dec_info *p_dec_info; + u32 reg_val; + u32 c_fmt_idc, out_fmt, out_mode; + + guard(mutex)(&inst->dev->hw_lock); + + p_dec_info = &inst->codec_info->dec_info; + + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_SCL_PARAM, + inst->scaler_info.enable); + reg_val = SET_DISP_SCL_PIC_SIZE_WIDTH(inst->scaler_info.width) | + SET_DISP_SCL_PIC_SIZE_HEIGHT(inst->scaler_info.height); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_SCL_PIC_SIZE, reg_val); + reg_val = SET_DISP_PIC_SIZE_WIDTH(p_dec_info->seq_info.pic_width) | + SET_DISP_PIC_SIZE_HEIGHT(p_dec_info->seq_info.pic_height); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_PIC_SIZE, reg_val); + + c_fmt_idc = get_chroma_format_idc(p_dec_info->wtl_format); + switch (p_dec_info->wtl_format) { + case FORMAT_420_P10_16BIT_MSB: + case FORMAT_422_P10_16BIT_MSB: + case FORMAT_444_P10_16BIT_MSB: + case FORMAT_400_P10_16BIT_MSB: + out_mode = (WTL_RIGHT_JUSTIFIED << 2) | WTL_PIXEL_16BIT; + break; + case FORMAT_420_P10_16BIT_LSB: + case FORMAT_422_P10_16BIT_LSB: + case FORMAT_444_P10_16BIT_LSB: + case FORMAT_400_P10_16BIT_LSB: + out_mode = (WTL_LEFT_JUSTIFIED << 2) | WTL_PIXEL_16BIT; + break; + case FORMAT_420_P10_32BIT_MSB: + case FORMAT_422_P10_32BIT_MSB: + case FORMAT_444_P10_32BIT_MSB: + case FORMAT_400_P10_32BIT_MSB: + out_mode = (WTL_RIGHT_JUSTIFIED << 2) | WTL_PIXEL_32BIT; + break; + case FORMAT_420_P10_32BIT_LSB: + case FORMAT_422_P10_32BIT_LSB: + case FORMAT_444_P10_32BIT_LSB: + case FORMAT_400_P10_32BIT_LSB: + out_mode = (WTL_LEFT_JUSTIFIED << 2) | WTL_PIXEL_32BIT; + break; + default: + out_mode = (WTL_RIGHT_JUSTIFIED << 2) | WTL_PIXEL_8BIT; + break; + }move such long convert to small helper function
OK.
quoted
+ out_fmt = (inst->nv21 << 1) | inst->cbcr_interleave; + + reg_val = SET_DISP_COMMON_PIC_INFO_BWB_ON | + SET_DISP_COMMON_PIC_INFO_C_FMT_IDC(c_fmt_idc) | + SET_DISP_COMMON_PIC_INFO_PIXEL_ORDER(PIXEL_ORDER_INCREASING)|quoted
+ SET_DISP_COMMON_PIC_INFO_OUT_MODE(out_mode) | + SET_DISP_COMMON_PIC_INFO_OUT_FMT(out_fmt) | + SET_DISP_COMMON_PIC_INFO_STRIDE(fb.stride); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_COMMON_PIC_INFO,reg_val);quoted
+ reg_val = SET_DISP_OPTION_ENDIAN(VDI_128BIT_BIG_ENDIAN); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_OPTION, reg_val); + reg_val = SET_DISP_PIC_INFO_L_BIT_DEPTH(fb.luma_bit_depth) | + SET_DISP_PIC_INFO_C_BIT_DEPTH(fb.chroma_bit_depth) | + SET_DISP_PIC_INFO_C_FMT_IDC(fb.c_fmt_idc); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_PIC_INFO, reg_val); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_Y_BASE, fb.buf_y); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_CB_BASE, fb.buf_cb); + vpu_write_reg(inst->dev, W6_CMD_DEC_SET_DISP_CR_BASE, fb.buf_cr); + + wave6_send_command(inst->dev, inst->id, inst->std,W6_CMD_DEC_SET_DISP);quoted
+ ret = wave6_wait_vpu_busy(inst->dev, W6_VPU_BUSY_STATUS); + if (ret) { + dev_err(inst->dev->dev, "%s: timeout\n", __func__); + return ret; + } + + if (!vpu_read_reg(inst->dev, W6_RET_SUCCESS)) + return -EIO; + + wave6_dec_set_display_buffer(inst, fb); + + return 0; +}
...
quoted
+static int wave6_set_enc_crop_info(u32 codec, struct enc_codec_param*param,quoted
+ u32 rot_mir, int width, int height) +{ + int aligned_width = (codec == W_HEVC_ENC) ? ALIGN(width, 32) :ALIGN(width, 16);quoted
+ int aligned_height = (codec == W_HEVC_ENC) ? ALIGN(height, 32) :ALIGN(height, 16);quoted
+ int pad_right, pad_bot; + int crop_right, crop_left, crop_top, crop_bot; + + if (codec == W_HEVC_ENC && + (!rot_mir || + rot_mir == (SET_PARAM_ROT_MIRH | SET_PARAM_ROT_MIRV |SET_PARAM_ROT_180)))quoted
+ return 0; + + pad_right = aligned_width - width; + pad_bot = aligned_height - height; + + if (param->conf_win.right > 0) + crop_right = param->conf_win.right + pad_right; + else + crop_right = pad_right; + + if (param->conf_win.bottom > 0) + crop_bot = param->conf_win.bottom + 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.bottom = crop_bot; + param->conf_win.right = crop_right; + + if (rot_mir == SET_PARAM_ROT_90 || + rot_mir == (SET_PARAM_ROT_MIRH | SET_PARAM_ROT_MIRV |SET_PARAM_ROT_270)) {quoted
+ param->conf_win.top = crop_right; + param->conf_win.left = crop_top; + param->conf_win.bottom = crop_left; + param->conf_win.right = crop_bot; + } else if (rot_mir == SET_PARAM_ROT_180 || + rot_mir == (SET_PARAM_ROT_MIRH | SET_PARAM_ROT_MIRV)) { + param->conf_win.top = crop_bot; + param->conf_win.left = crop_right; + param->conf_win.bottom = crop_top; + param->conf_win.right = crop_left; + } else if (rot_mir == SET_PARAM_ROT_270 || + rot_mir == (SET_PARAM_ROT_MIRH | SET_PARAM_ROT_MIRV |SET_PARAM_ROT_90)) {quoted
+ param->conf_win.top = crop_left; + param->conf_win.left = crop_bot; + param->conf_win.bottom = crop_right; + param->conf_win.right = crop_top; + } else if (rot_mir == SET_PARAM_ROT_MIRV || + rot_mir == (SET_PARAM_ROT_MIRH | SET_PARAM_ROT_180)) { + param->conf_win.top = crop_bot; + param->conf_win.bottom = crop_top;put bottom/right here to make code consistent.
OK. I'll fix it.
quoted
+ } else if (rot_mir == SET_PARAM_ROT_MIRH || + rot_mir == (SET_PARAM_ROT_MIRV | SET_PARAM_ROT_180)) { + param->conf_win.left = crop_right; + param->conf_win.right = crop_left; + } else if (rot_mir == (SET_PARAM_ROT_MIRV | SET_PARAM_ROT_90) || + rot_mir == (SET_PARAM_ROT_MIRH | SET_PARAM_ROT_270)) { + param->conf_win.top = crop_left; + param->conf_win.left = crop_top; + param->conf_win.bottom = crop_right; + param->conf_win.right = crop_bot; + } else if (rot_mir == (SET_PARAM_ROT_MIRV | SET_PARAM_ROT_270) || + rot_mir == (SET_PARAM_ROT_MIRH | SET_PARAM_ROT_90)) { + param->conf_win.top = crop_right; + param->conf_win.left = crop_bot; + param->conf_win.bottom = crop_left; + param->conf_win.right = crop_top; + }else { param->conf_win.top = crop_top; param->conf_win.left = crop_left; param->conf_win.bottom = crop_bot; param->conf_win.right = crop_right; } Avoid overwrite it again at if-else branch.
OK. I'll fix it.
quoted
+ + return 0; +} + +static void wave6_update_enc_info(struct enc_info *p_enc_info) +{ + struct enc_open_param op = p_enc_info->open_param; + + p_enc_info->width = op.pic_width; + p_enc_info->height = op.pic_height; + p_enc_info->c_fmt_idc = get_chroma_format_idc(op.output_format); +} + +static void wave6_gen_set_param_reg_common(struct enc_info *p_enc_info,enum codec_std std,quoted
+ struct enc_cmd_set_param_reg *reg) +{ + struct enc_open_param *p_open_param = &p_enc_info->open_param; + struct enc_codec_param *p_param = &p_open_param->codec_param; + unsigned int i; + u32 rot_mir = 0; + + switch (p_enc_info->rot_angle) { + case ROT_0: + break; + case ROT_90: + rot_mir |= SET_PARAM_ROT_90; + break; + case ROT_180: + rot_mir |= SET_PARAM_ROT_180; + break; + case ROT_270: + rot_mir |= SET_PARAM_ROT_270; + break; + } + + switch (p_enc_info->mir_dir) { + case MIR_NONE: + break; + case MIR_VER: + rot_mir |= SET_PARAM_ROT_MIRV; + break; + case MIR_HOR: + rot_mir |= SET_PARAM_ROT_MIRH; + break; + case MIR_HOR_VER: + rot_mir |= SET_PARAM_ROT_MIRH | SET_PARAM_ROT_MIRV; + break; + }use small convert helper to make this function shorter
OK. I'll address this in v8. ...
quoted
+static void wave6_gen_enc_pic_reg(struct enc_info *p_enc_info, boolcbcr_interleave, bool nv21,quoted
+ struct enc_param *opt, structenc_cmd_enc_pic_reg *reg)quoted
+{ + struct enc_open_param open = p_enc_info->open_param; + struct enc_codec_param param = open.codec_param; + bool is_lsb = false; + bool is_10bit = false; + bool is_3p4b = false; + bool is_cr_first = nv21; + u32 c_fmt_idc = get_chroma_format_idc(open.src_format); + u32 stride_c = opt->source_frame->stride_c; + bool is_ayuv = false; + bool is_csc_format = false; + bool is_24bit = false; + bool is_packed = false; + bool is_packed_uv_first = false; + + if ((c_fmt_idc == C_FMT_IDC_YUV422 || c_fmt_idc == C_FMT_IDC_YUV444)&"ed
+ p_enc_info->c_fmt_idc == C_FMT_IDC_YUV420) + stride_c *= 2; + + switch (open.src_format) { + case FORMAT_420: + case FORMAT_422: + case FORMAT_444: + case FORMAT_400: + case FORMAT_YUYV: + case FORMAT_YVYU: + case FORMAT_UYVY: + case FORMAT_VYUY: + is_lsb = false; + is_3p4b = false; + break; + case FORMAT_420_P10_16BIT_MSB: + case FORMAT_422_P10_16BIT_MSB: + case FORMAT_444_P10_16BIT_MSB: + case FORMAT_400_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: + is_lsb = false; + is_10bit = true; + is_3p4b = false; + break; + case FORMAT_420_P10_16BIT_LSB: + case FORMAT_422_P10_16BIT_LSB: + case FORMAT_444_P10_16BIT_LSB: + case FORMAT_400_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: + is_lsb = true; + is_10bit = true; + is_3p4b = false; + break; + case FORMAT_420_P10_32BIT_MSB: + case FORMAT_422_P10_32BIT_MSB: + case FORMAT_444_P10_32BIT_MSB: + case FORMAT_400_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: + is_lsb = false; + is_10bit = true; + is_3p4b = true; + break; + case FORMAT_420_P10_32BIT_LSB: + case FORMAT_422_P10_32BIT_LSB: + case FORMAT_444_P10_32BIT_LSB: + case FORMAT_400_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: + is_lsb = true; + is_10bit = true; + is_3p4b = true; + break; + case FORMAT_RGB_32BIT_PACKED: + is_ayuv = false; + is_csc_format = true; + break; + case FORMAT_RGB_P10_32BIT_PACKED: + is_ayuv = false; + is_csc_format = true; + is_10bit = true; + break; + case FORMAT_YUV444_32BIT_PACKED: + is_ayuv = true; + is_csc_format = true; + break; + case FORMAT_YUV444_P10_32BIT_PACKED: + is_ayuv = true; + is_csc_format = true; + is_10bit = true; + break; + case FORMAT_RGB_24BIT_PACKED: + is_ayuv = false; + is_csc_format = true; + is_24bit = true; + break; + case FORMAT_YUV444_24BIT_PACKED: + is_ayuv = true; + is_csc_format = true; + is_24bit = true; + break; + case FORMAT_YUV444_24BIT: + is_ayuv = true; + break; + default: + break; + }move to small helper to this kinds of convert functions
OK. I'll address this in v8. ...
quoted
+static inline void wave6_vdi_writel(void __iomem *base, u32 addr, u32data)quoted
+{ + writel(data, base + addr);consider big amount reg read write, suggest use writel_relaxed() and readl_relaxed()
Same as above - I'll add a relaxed variant rather than change wave6_vdi_writel() itself, so the HOST_INT_REQ and the firmware start keep the barrier.
quoted
+} + +static inline unsigned int wave6_vdi_readl(void __iomem *base, u32 addr) +{ + return readl(base + addr); +}
...
quoted
+struct dec_open_param { + enum display_mode disp_mode; + u32 ext_addr_vcpu: 8; + bool is_secure_inst;you use bit field: here is_secure_inst: 1
OK. enc_open_param has the same pattern, I'll change both.
quoted
+ u32 inst_priority: 5; + struct instance_buffer inst_buffer; +};
...
quoted
+struct vpu_performance_info { + ktime_t ts_start; + ktime_t ts_first; + ktime_t ts_last; + s64 latency_first; + s64 latency_max; + s64 min_process_time; + s64 max_process_time; + u64 total_sw_time; + u64 total_hw_time; +};You can add performance_info later, like trace and dbg.
OK. I'll address this in v8. Thanks. Nas.