diff options
Diffstat (limited to 'drivers/media/platform')
105 files changed, 7957 insertions, 1975 deletions
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 85d518823159..32eef2fd1f2a 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -26,6 +26,7 @@ #include "vpu_cmds.h" #include "vpu_rpc.h" +#define VDEC_SLOT_CNT_DFT 32 #define VDEC_MIN_BUFFER_CAP 8 #define VDEC_MIN_BUFFER_OUT 8 @@ -41,6 +42,14 @@ struct vdec_fs_info { u32 tag; }; +struct vdec_frame_store_t { + struct vpu_vb2_buffer *curr; + struct vpu_vb2_buffer *pend; + dma_addr_t addr; + unsigned int state; + u32 tag; +}; + struct vdec_t { u32 seq_hdr_found; struct vpu_buffer udata; @@ -48,7 +57,8 @@ struct vdec_t { struct vpu_dec_codec_info codec_info; enum vpu_codec_state state; - struct vpu_vb2_buffer *slots[VB2_MAX_FRAME]; + struct vdec_frame_store_t *slots; + u32 slot_count; u32 req_frame_count; struct vdec_fs_info mbi; struct vdec_fs_info dcp; @@ -232,6 +242,35 @@ static int vdec_ctrl_init(struct vpu_inst *inst) V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE, 0, 1, 1, 0); + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_MAIN); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_6_2, + 0, + V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + ~((1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10)), + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, + 0, + V4L2_MPEG_VIDEO_HEVC_LEVEL_4); + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2); if (ctrl) @@ -258,6 +297,63 @@ static int vdec_ctrl_init(struct vpu_inst *inst) return 0; } +static void vdec_attach_frame_store(struct vpu_inst *inst, struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + struct vdec_t *vdec = inst->priv; + struct vdec_frame_store_t *new_slots = NULL; + dma_addr_t addr; + int i; + + addr = vpu_get_vb_phy_addr(vb, 0); + for (i = 0; i < vdec->slot_count; i++) { + if (addr == vdec->slots[i].addr) { + if (vdec->slots[i].curr && vdec->slots[i].curr != vpu_buf) { + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_CHANGED); + vdec->slots[i].pend = vpu_buf; + } else { + vpu_set_buffer_state(vbuf, vdec->slots[i].state); + } + vpu_buf->fs_id = i; + return; + } + } + + for (i = 0; i < vdec->slot_count; i++) { + if (!vdec->slots[i].addr) { + vdec->slots[i].addr = addr; + vpu_buf->fs_id = i; + return; + } + } + + new_slots = krealloc_array(vdec->slots, vdec->slot_count * 2, + sizeof(*vdec->slots), + GFP_KERNEL | __GFP_ZERO); + if (!new_slots) { + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_ERROR); + return; + } + + vdec->slots = new_slots; + vdec->slot_count *= 2; + + vdec->slots[i].addr = addr; + vpu_buf->fs_id = i; +} + +static void vdec_reset_frame_store(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; + + if (!vdec->slots || !vdec->slot_count) + return; + + vpu_trace(inst->dev, "inst[%d] reset slots\n", inst->id); + memset(vdec->slots, 0, sizeof(*vdec->slots) * vdec->slot_count); +} + static void vdec_handle_resolution_change(struct vpu_inst *inst) { struct vdec_t *vdec = inst->priv; @@ -714,11 +810,11 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg) struct vb2_v4l2_buffer *src_buf; int ret = 0; - if (!info || info->id >= ARRAY_SIZE(vdec->slots)) + if (!info || info->id >= vdec->slot_count) return -EINVAL; vpu_inst_lock(inst); - vpu_buf = vdec->slots[info->id]; + vpu_buf = vdec->slots[info->id].curr; if (!vpu_buf) { dev_err(inst->dev, "[%d] decoded invalid frame[%d]\n", inst->id, info->id); ret = -EINVAL; @@ -739,11 +835,13 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg) if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED) dev_info(inst->dev, "[%d] buf[%d] has been decoded\n", inst->id, info->id); vpu_set_buffer_state(vbuf, VPU_BUF_STATE_DECODED); + vdec->slots[info->id].state = VPU_BUF_STATE_DECODED; vdec->decoded_frame_count++; if (vdec->params.display_delay_enable) { struct vpu_format *cur_fmt; cur_fmt = vpu_get_format(inst, inst->cap_format.type); + vdec->slots[info->id].state = VPU_BUF_STATE_READY; vpu_set_buffer_state(vbuf, VPU_BUF_STATE_READY); for (int i = 0; i < vbuf->vb2_buf.num_planes; i++) vb2_set_plane_payload(&vbuf->vb2_buf, @@ -766,11 +864,11 @@ static struct vpu_vb2_buffer *vdec_find_buffer(struct vpu_inst *inst, u32 luma) struct vdec_t *vdec = inst->priv; int i; - for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) { - if (!vdec->slots[i]) + for (i = 0; i < vdec->slot_count; i++) { + if (!vdec->slots[i].curr) continue; - if (luma == vdec->slots[i]->luma) - return vdec->slots[i]; + if (luma == vdec->slots[i].addr) + return vdec->slots[i].curr; } return NULL; @@ -804,11 +902,11 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame) cur_fmt = vpu_get_format(inst, inst->cap_format.type); vbuf = &vpu_buf->m2m_buf.vb; - if (vbuf->vb2_buf.index != frame->id) - dev_err(inst->dev, "[%d] buffer id(%d, %d) mismatch\n", - inst->id, vbuf->vb2_buf.index, frame->id); + if (vpu_buf->fs_id != frame->id) + dev_err(inst->dev, "[%d] buffer id(%d(%d), %d) mismatch\n", + inst->id, vpu_buf->fs_id, vbuf->vb2_buf.index, frame->id); - if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_READY && vdec->params.display_delay_enable) + if (vdec->params.display_delay_enable) return; if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_DECODED) @@ -821,10 +919,11 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame) vbuf->sequence = vdec->sequence; dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp); - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); vpu_inst_lock(inst); + vdec->slots[vpu_buf->fs_id].state = VPU_BUF_STATE_READY; vdec->display_frame_count++; vpu_inst_unlock(inst); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); dev_dbg(inst->dev, "[%d] decoded : %d, display : %d, sequence : %d\n", inst->id, vdec->decoded_frame_count, vdec->display_frame_count, vdec->sequence); } @@ -1052,18 +1151,30 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb if (!vbuf) return -EINVAL; - if (vdec->slots[vbuf->vb2_buf.index]) { - dev_err(inst->dev, "[%d] repeat alloc fs %d\n", - inst->id, vbuf->vb2_buf.index); + vpu_buf = to_vpu_vb2_buffer(vbuf); + if (vpu_buf->fs_id < 0 || vpu_buf->fs_id >= vdec->slot_count) { + dev_err(inst->dev, "invalid fs %d for v4l2 buffer %d\n", + vpu_buf->fs_id, vbuf->vb2_buf.index); return -EINVAL; } + if (vdec->slots[vpu_buf->fs_id].curr) { + if (vdec->slots[vpu_buf->fs_id].curr != vpu_buf) { + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_CHANGED); + vdec->slots[vpu_buf->fs_id].pend = vpu_buf; + } else { + vpu_set_buffer_state(vbuf, vdec->slots[vpu_buf->fs_id].state); + } + dev_err(inst->dev, "[%d] repeat alloc fs %d (v4l2 index %d)\n", + inst->id, vpu_buf->fs_id, vbuf->vb2_buf.index); + return -EAGAIN; + } + dev_dbg(inst->dev, "[%d] state = %s, alloc fs %d, tag = 0x%x\n", inst->id, vpu_codec_state_name(inst->state), vbuf->vb2_buf.index, vdec->seq_tag); - vpu_buf = to_vpu_vb2_buffer(vbuf); memset(&info, 0, sizeof(info)); - info.id = vbuf->vb2_buf.index; + info.id = vpu_buf->fs_id; info.type = MEM_RES_FRAME; info.tag = vdec->seq_tag; info.luma_addr = vpu_get_vb_phy_addr(&vbuf->vb2_buf, 0); @@ -1078,12 +1189,13 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb if (ret) return ret; - vpu_buf->tag = info.tag; vpu_buf->luma = info.luma_addr; vpu_buf->chroma_u = info.chroma_addr; vpu_buf->chroma_v = 0; vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE); - vdec->slots[info.id] = vpu_buf; + vdec->slots[info.id].tag = info.tag; + vdec->slots[info.id].curr = vpu_buf; + vdec->slots[info.id].state = VPU_BUF_STATE_INUSE; vdec->req_frame_count--; return 0; @@ -1144,25 +1256,76 @@ static void vdec_recycle_buffer(struct vpu_inst *inst, struct vb2_v4l2_buffer *v v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf); } -static void vdec_clear_slots(struct vpu_inst *inst) +static void vdec_release_curr_frame_store(struct vpu_inst *inst, u32 id) { struct vdec_t *vdec = inst->priv; struct vpu_vb2_buffer *vpu_buf; struct vb2_v4l2_buffer *vbuf; + + if (id >= vdec->slot_count) + return; + if (!vdec->slots[id].curr) + return; + + vpu_buf = vdec->slots[id].curr; + vbuf = &vpu_buf->m2m_buf.vb; + + vdec_response_fs_release(inst, id, vdec->slots[id].tag); + if (vpu_buf->fs_id == id) { + if (vpu_buf->state != VPU_BUF_STATE_READY) + vdec_recycle_buffer(inst, vbuf); + vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + } + + vdec->slots[id].curr = NULL; + vdec->slots[id].state = VPU_BUF_STATE_IDLE; + + if (vdec->slots[id].pend) { + vpu_set_buffer_state(&vdec->slots[id].pend->m2m_buf.vb, VPU_BUF_STATE_IDLE); + vdec->slots[id].pend = NULL; + } +} + +static void vdec_clear_slots(struct vpu_inst *inst) +{ + struct vdec_t *vdec = inst->priv; int i; - for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) { - if (!vdec->slots[i]) + for (i = 0; i < vdec->slot_count; i++) { + if (!vdec->slots[i].curr) continue; - vpu_buf = vdec->slots[i]; - vbuf = &vpu_buf->m2m_buf.vb; - vpu_trace(inst->dev, "clear slot %d\n", i); - vdec_response_fs_release(inst, i, vpu_buf->tag); - vdec_recycle_buffer(inst, vbuf); - vdec->slots[i]->state = VPU_BUF_STATE_IDLE; - vdec->slots[i] = NULL; + vdec_release_curr_frame_store(inst, i); + } +} + +static void vdec_update_v4l2_ctrl(struct vpu_inst *inst, u32 id, u32 val) +{ + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&inst->ctrl_handler, id); + + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, val); +} + +static void vdec_update_v4l2_profile_level(struct vpu_inst *inst, struct vpu_dec_codec_info *hdr) +{ + switch (inst->out_format.pixfmt) { + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_H264_MVC: + vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + vpu_get_h264_v4l2_profile(hdr)); + vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_H264_LEVEL, + vpu_get_h264_v4l2_level(hdr)); + break; + case V4L2_PIX_FMT_HEVC: + vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + vpu_get_hevc_v4l2_profile(hdr)); + vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + vpu_get_hevc_v4l2_level(hdr)); + break; + default: + return; } } @@ -1189,6 +1352,7 @@ static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info vdec_init_crop(inst); vdec_init_mbi(inst); vdec_init_dcp(inst); + vdec_update_v4l2_profile_level(inst, hdr); if (!vdec->seq_hdr_found) { vdec->seq_tag = vdec->codec_info.tag; if (vdec->is_source_changed) { @@ -1263,39 +1427,29 @@ static void vdec_event_req_fs(struct vpu_inst *inst, struct vpu_fs_info *fs) static void vdec_evnet_rel_fs(struct vpu_inst *inst, struct vpu_fs_info *fs) { struct vdec_t *vdec = inst->priv; - struct vpu_vb2_buffer *vpu_buf; - struct vb2_v4l2_buffer *vbuf; - if (!fs || fs->id >= ARRAY_SIZE(vdec->slots)) + if (!fs || fs->id >= vdec->slot_count) return; if (fs->type != MEM_RES_FRAME) return; - if (fs->id >= vpu_get_num_buffers(inst, inst->cap_format.type)) { + if (fs->id >= vdec->slot_count) { dev_err(inst->dev, "[%d] invalid fs(%d) to release\n", inst->id, fs->id); return; } vpu_inst_lock(inst); - vpu_buf = vdec->slots[fs->id]; - vdec->slots[fs->id] = NULL; - - if (!vpu_buf) { + if (!vdec->slots[fs->id].curr) { dev_dbg(inst->dev, "[%d] fs[%d] has bee released\n", inst->id, fs->id); goto exit; } - vbuf = &vpu_buf->m2m_buf.vb; - if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED) { + if (vdec->slots[fs->id].state == VPU_BUF_STATE_DECODED) { dev_dbg(inst->dev, "[%d] frame skip\n", inst->id); vdec->sequence++; } - vdec_response_fs_release(inst, fs->id, vpu_buf->tag); - if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_READY) - vdec_recycle_buffer(inst, vbuf); - - vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + vdec_release_curr_frame_store(inst, fs->id); vpu_process_capture_buffer(inst); exit: @@ -1485,6 +1639,11 @@ static void vdec_cleanup(struct vpu_inst *inst) return; vdec = inst->priv; + if (vdec) { + kfree(vdec->slots); + vdec->slots = NULL; + vdec->slot_count = 0; + } vfree(vdec); inst->priv = NULL; vfree(inst); @@ -1606,11 +1765,43 @@ static int vdec_stop_session(struct vpu_inst *inst, u32 type) return 0; } -static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i) +static int vdec_get_slot_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i) { struct vdec_t *vdec = inst->priv; + struct vpu_vb2_buffer *vpu_buf; int num = -1; + vpu_inst_lock(inst); + if (i >= vdec->slot_count || !vdec->slots[i].addr) + goto exit; + + vpu_buf = vdec->slots[i].curr; + + num = scnprintf(str, size, "slot[%2d] :", i); + if (vpu_buf) { + num += scnprintf(str + num, size - num, " %2d", + vpu_buf->m2m_buf.vb.vb2_buf.index); + num += scnprintf(str + num, size - num, "; state = %d", vdec->slots[i].state); + } else { + num += scnprintf(str + num, size - num, " -1"); + } + + if (vdec->slots[i].pend) + num += scnprintf(str + num, size - num, "; %d", + vdec->slots[i].pend->m2m_buf.vb.vb2_buf.index); + + num += scnprintf(str + num, size - num, "\n"); +exit: + vpu_inst_unlock(inst); + + return num; +} + +static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i) +{ + struct vdec_t *vdec = inst->priv; + int num; + switch (i) { case 0: num = scnprintf(str, size, @@ -1664,6 +1855,7 @@ static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i vdec->codec_info.vui_present); break; default: + num = vdec_get_slot_debug_info(inst, str, size, i - 10); break; } @@ -1687,6 +1879,8 @@ static struct vpu_inst_ops vdec_inst_ops = { .get_debug_info = vdec_get_debug_info, .wait_prepare = vpu_inst_unlock, .wait_finish = vpu_inst_lock, + .attach_frame_store = vdec_attach_frame_store, + .reset_frame_store = vdec_reset_frame_store, }; static void vdec_init(struct file *file) @@ -1727,6 +1921,16 @@ static int vdec_open(struct file *file) return -ENOMEM; } + vdec->slots = kmalloc_array(VDEC_SLOT_CNT_DFT, + sizeof(*vdec->slots), + GFP_KERNEL | __GFP_ZERO); + if (!vdec->slots) { + vfree(vdec); + vfree(inst); + return -ENOMEM; + } + vdec->slot_count = VDEC_SLOT_CNT_DFT; + inst->ops = &vdec_inst_ops; inst->formats = vdec_formats; inst->type = VPU_CORE_TYPE_DEC; diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index 1451549c9dd2..cac0f1a64fea 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -222,6 +222,8 @@ struct vpu_inst_ops { int (*get_debug_info)(struct vpu_inst *inst, char *str, u32 size, u32 i); void (*wait_prepare)(struct vpu_inst *inst); void (*wait_finish)(struct vpu_inst *inst); + void (*attach_frame_store)(struct vpu_inst *inst, struct vb2_buffer *vb); + void (*reset_frame_store)(struct vpu_inst *inst); }; struct vpu_inst { @@ -295,7 +297,8 @@ enum { VPU_BUF_STATE_DECODED, VPU_BUF_STATE_READY, VPU_BUF_STATE_SKIP, - VPU_BUF_STATE_ERROR + VPU_BUF_STATE_ERROR, + VPU_BUF_STATE_CHANGED }; struct vpu_vb2_buffer { @@ -304,8 +307,8 @@ struct vpu_vb2_buffer { dma_addr_t chroma_u; dma_addr_t chroma_v; unsigned int state; - u32 tag; u32 average_qp; + s32 fs_id; }; void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val); diff --git a/drivers/media/platform/amphion/vpu_color.c b/drivers/media/platform/amphion/vpu_color.c index 4ae435cbc5cd..7c0ab8289a7b 100644 --- a/drivers/media/platform/amphion/vpu_color.c +++ b/drivers/media/platform/amphion/vpu_color.c @@ -108,76 +108,3 @@ u32 vpu_color_cvrt_full_range_i2v(u32 full_range) return V4L2_QUANTIZATION_LIM_RANGE; } - -int vpu_color_check_primaries(u32 primaries) -{ - return vpu_color_cvrt_primaries_v2i(primaries) ? 0 : -EINVAL; -} - -int vpu_color_check_transfers(u32 transfers) -{ - return vpu_color_cvrt_transfers_v2i(transfers) ? 0 : -EINVAL; -} - -int vpu_color_check_matrix(u32 matrix) -{ - return vpu_color_cvrt_matrix_v2i(matrix) ? 0 : -EINVAL; -} - -int vpu_color_check_full_range(u32 full_range) -{ - int ret = -EINVAL; - - switch (full_range) { - case V4L2_QUANTIZATION_FULL_RANGE: - case V4L2_QUANTIZATION_LIM_RANGE: - ret = 0; - break; - default: - break; - } - - return ret; -} - -int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range) -{ - u32 transfers; - u32 matrix; - u32 full_range; - - switch (primaries) { - case V4L2_COLORSPACE_REC709: - transfers = V4L2_XFER_FUNC_709; - matrix = V4L2_YCBCR_ENC_709; - break; - case V4L2_COLORSPACE_470_SYSTEM_M: - case V4L2_COLORSPACE_470_SYSTEM_BG: - case V4L2_COLORSPACE_SMPTE170M: - transfers = V4L2_XFER_FUNC_709; - matrix = V4L2_YCBCR_ENC_601; - break; - case V4L2_COLORSPACE_SMPTE240M: - transfers = V4L2_XFER_FUNC_SMPTE240M; - matrix = V4L2_YCBCR_ENC_SMPTE240M; - break; - case V4L2_COLORSPACE_BT2020: - transfers = V4L2_XFER_FUNC_709; - matrix = V4L2_YCBCR_ENC_BT2020; - break; - default: - transfers = V4L2_XFER_FUNC_DEFAULT; - matrix = V4L2_YCBCR_ENC_DEFAULT; - break; - } - full_range = V4L2_QUANTIZATION_LIM_RANGE; - - if (ptransfers) - *ptransfers = transfers; - if (pmatrix) - *pmatrix = matrix; - if (pfull_range) - *pfull_range = full_range; - - return 0; -} diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c index 940e5bda5fa3..497ae4e8a229 100644 --- a/drivers/media/platform/amphion/vpu_dbg.c +++ b/drivers/media/platform/amphion/vpu_dbg.c @@ -48,6 +48,7 @@ static char *vpu_stat_name[] = { [VPU_BUF_STATE_READY] = "ready", [VPU_BUF_STATE_SKIP] = "skip", [VPU_BUF_STATE_ERROR] = "error", + [VPU_BUF_STATE_CHANGED] = "changed", }; static inline const char *to_vpu_stat_name(int state) @@ -164,6 +165,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) for (i = 0; i < vb2_get_num_buffers(vq); i++) { struct vb2_buffer *vb; struct vb2_v4l2_buffer *vbuf; + struct vpu_vb2_buffer *vpu_buf; vb = vb2_get_buffer(vq, i); if (!vb) @@ -173,13 +175,24 @@ static int vpu_dbg_instance(struct seq_file *s, void *data) continue; vbuf = to_vb2_v4l2_buffer(vb); + vpu_buf = to_vpu_vb2_buffer(vbuf); num = scnprintf(str, sizeof(str), - "capture[%2d] state = %10s, %8s\n", + "capture[%2d] state = %10s, %8s", i, vb2_stat_name[vb->state], to_vpu_stat_name(vpu_get_buffer_state(vbuf))); if (seq_write(s, str, num)) return 0; + + if (vpu_buf->fs_id >= 0) { + num = scnprintf(str, sizeof(str), "; fs %d", vpu_buf->fs_id); + if (seq_write(s, str, num)) + return 0; + } + + num = scnprintf(str, sizeof(str), "\n"); + if (seq_write(s, str, num)) + return 0; } num = scnprintf(str, sizeof(str), "sequence = %d\n", inst->sequence); diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h index 428d988cf2f7..f56245ae2205 100644 --- a/drivers/media/platform/amphion/vpu_defs.h +++ b/drivers/media/platform/amphion/vpu_defs.h @@ -134,6 +134,7 @@ struct vpu_dec_codec_info { u32 decoded_height; struct v4l2_fract frame_rate; u32 dsp_asp_ratio; + u32 profile_idc; u32 level_idc; u32 bit_depth_luma; u32 bit_depth_chroma; @@ -147,6 +148,17 @@ struct vpu_dec_codec_info { u32 mbi_size; u32 dcp_size; u32 stride; + union { + struct { + u32 constraint_set5_flag : 1; + u32 constraint_set4_flag : 1; + u32 constraint_set3_flag : 1; + u32 constraint_set2_flag : 1; + u32 constraint_set1_flag : 1; + u32 constraint_set0_flag : 1; + }; + u32 constraint_set_flags; + }; }; struct vpu_dec_pic_info { diff --git a/drivers/media/platform/amphion/vpu_helpers.c b/drivers/media/platform/amphion/vpu_helpers.c index d12310af9ebc..886d5632388e 100644 --- a/drivers/media/platform/amphion/vpu_helpers.c +++ b/drivers/media/platform/amphion/vpu_helpers.c @@ -509,3 +509,126 @@ const char *vpu_codec_state_name(enum vpu_codec_state state) } return "<unknown>"; } + +struct codec_id_mapping { + u32 id; + u32 v4l2_id; +}; + +static struct codec_id_mapping h264_profiles[] = { + {66, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE}, + {77, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN}, + {88, V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED}, + {100, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH} +}; + +static struct codec_id_mapping h264_levels[] = { + {10, V4L2_MPEG_VIDEO_H264_LEVEL_1_0}, + {9, V4L2_MPEG_VIDEO_H264_LEVEL_1B}, + {11, V4L2_MPEG_VIDEO_H264_LEVEL_1_1}, + {12, V4L2_MPEG_VIDEO_H264_LEVEL_1_2}, + {13, V4L2_MPEG_VIDEO_H264_LEVEL_1_3}, + {20, V4L2_MPEG_VIDEO_H264_LEVEL_2_0}, + {21, V4L2_MPEG_VIDEO_H264_LEVEL_2_1}, + {22, V4L2_MPEG_VIDEO_H264_LEVEL_2_2}, + {30, V4L2_MPEG_VIDEO_H264_LEVEL_3_0}, + {31, V4L2_MPEG_VIDEO_H264_LEVEL_3_1}, + {32, V4L2_MPEG_VIDEO_H264_LEVEL_3_2}, + {40, V4L2_MPEG_VIDEO_H264_LEVEL_4_0}, + {41, V4L2_MPEG_VIDEO_H264_LEVEL_4_1}, + {42, V4L2_MPEG_VIDEO_H264_LEVEL_4_2}, + {50, V4L2_MPEG_VIDEO_H264_LEVEL_5_0}, + {51, V4L2_MPEG_VIDEO_H264_LEVEL_5_1}, + {52, V4L2_MPEG_VIDEO_H264_LEVEL_5_2}, + {60, V4L2_MPEG_VIDEO_H264_LEVEL_6_0}, + {61, V4L2_MPEG_VIDEO_H264_LEVEL_6_1}, + {62, V4L2_MPEG_VIDEO_H264_LEVEL_6_2} +}; + +static struct codec_id_mapping hevc_profiles[] = { + {1, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN}, + {2, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10} +}; + +static struct codec_id_mapping hevc_levels[] = { + {30, V4L2_MPEG_VIDEO_HEVC_LEVEL_1}, + {60, V4L2_MPEG_VIDEO_HEVC_LEVEL_2}, + {63, V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1}, + {90, V4L2_MPEG_VIDEO_HEVC_LEVEL_3}, + {93, V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1}, + {120, V4L2_MPEG_VIDEO_HEVC_LEVEL_4}, + {123, V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1}, + {150, V4L2_MPEG_VIDEO_HEVC_LEVEL_5}, + {153, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1}, + {156, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2}, + {180, V4L2_MPEG_VIDEO_HEVC_LEVEL_6}, + {183, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1}, + {186, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2} +}; + +static u32 vpu_find_v4l2_id(u32 id, struct codec_id_mapping *array, u32 array_sz) +{ + u32 i; + + if (!array || !array_sz) + return 0; + + for (i = 0; i < array_sz; i++) { + if (id == array[i].id) + return array[i].v4l2_id; + } + + return 0; +} + +u32 vpu_get_h264_v4l2_profile(struct vpu_dec_codec_info *hdr) +{ + if (!hdr) + return 0; + + /* + * In H.264 Document section A.2.1.1 Constrained Baseline profile + * Conformance of a bitstream to the Constrained Baseline profile is indicated by + * profile_idc being equal to 66 with constraint_set1_flag being equal to 1. + */ + if (hdr->profile_idc == 66 && hdr->constraint_set1_flag) + return V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE; + + return vpu_find_v4l2_id(hdr->profile_idc, h264_profiles, ARRAY_SIZE(h264_profiles)); +} + +u32 vpu_get_h264_v4l2_level(struct vpu_dec_codec_info *hdr) +{ + if (!hdr) + return 0; + + /* + * In H.264 Document section 7.4.2.1.1 Sequence parameter set data semantics + * If profile_idc is equal to 66, 77, or 88 and level_idc is equal to 11, + * constraint_set3_flag equal to 1 indicates that the coded video sequence + * obeys all constraints specified in Annex A for level 1b + * and constraint_set3_flag equal to 0 indicates that the coded video sequence + * obeys all constraints specified in Annex A for level 1.1. + */ + if (hdr->level_idc == 11 && hdr->constraint_set3_flag && + (hdr->profile_idc == 66 || hdr->profile_idc == 77 || hdr->profile_idc == 88)) + return V4L2_MPEG_VIDEO_H264_LEVEL_1B; + + return vpu_find_v4l2_id(hdr->level_idc, h264_levels, ARRAY_SIZE(h264_levels)); +} + +u32 vpu_get_hevc_v4l2_profile(struct vpu_dec_codec_info *hdr) +{ + if (!hdr) + return 0; + + return vpu_find_v4l2_id(hdr->profile_idc, hevc_profiles, ARRAY_SIZE(hevc_profiles)); +} + +u32 vpu_get_hevc_v4l2_level(struct vpu_dec_codec_info *hdr) +{ + if (!hdr) + return 0; + + return vpu_find_v4l2_id(hdr->level_idc, hevc_levels, ARRAY_SIZE(hevc_levels)); +} diff --git a/drivers/media/platform/amphion/vpu_helpers.h b/drivers/media/platform/amphion/vpu_helpers.h index 0eaddb07190d..76fba0d6090c 100644 --- a/drivers/media/platform/amphion/vpu_helpers.h +++ b/drivers/media/platform/amphion/vpu_helpers.h @@ -6,6 +6,8 @@ #ifndef _AMPHION_VPU_HELPERS_H #define _AMPHION_VPU_HELPERS_H +#include "vpu_defs.h" + struct vpu_pair { u32 src; u32 dst; @@ -54,10 +56,6 @@ static inline u8 vpu_helper_read_byte(struct vpu_buffer *stream_buffer, u32 pos) return pdata[pos % stream_buffer->length]; } -int vpu_color_check_primaries(u32 primaries); -int vpu_color_check_transfers(u32 transfers); -int vpu_color_check_matrix(u32 matrix); -int vpu_color_check_full_range(u32 full_range); u32 vpu_color_cvrt_primaries_v2i(u32 primaries); u32 vpu_color_cvrt_primaries_i2v(u32 primaries); u32 vpu_color_cvrt_transfers_v2i(u32 transfers); @@ -66,8 +64,12 @@ u32 vpu_color_cvrt_matrix_v2i(u32 matrix); u32 vpu_color_cvrt_matrix_i2v(u32 matrix); u32 vpu_color_cvrt_full_range_v2i(u32 full_range); u32 vpu_color_cvrt_full_range_i2v(u32 full_range); -int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range); int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src); int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst); + +u32 vpu_get_h264_v4l2_profile(struct vpu_dec_codec_info *hdr); +u32 vpu_get_h264_v4l2_level(struct vpu_dec_codec_info *hdr); +u32 vpu_get_hevc_v4l2_profile(struct vpu_dec_codec_info *hdr); +u32 vpu_get_hevc_v4l2_level(struct vpu_dec_codec_info *hdr); #endif diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index feca7d4220ed..ba688566dffd 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -908,7 +908,8 @@ static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt, info->frame_rate.numerator = 1000; info->frame_rate.denominator = pkt->data[8]; info->dsp_asp_ratio = pkt->data[9]; - info->level_idc = pkt->data[10]; + info->profile_idc = (pkt->data[10] >> 8) & 0xff; + info->level_idc = pkt->data[10] & 0xff; info->bit_depth_luma = pkt->data[13]; info->bit_depth_chroma = pkt->data[14]; info->chroma_fmt = pkt->data[15]; @@ -925,6 +926,8 @@ static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt, info->pixfmt = V4L2_PIX_FMT_NV12M_10BE_8L128; else info->pixfmt = V4L2_PIX_FMT_NV12M_8L128; + if (pkt->hdr.num > 28) + info->constraint_set_flags = pkt->data[28]; if (info->frame_rate.numerator && info->frame_rate.denominator) { unsigned long n, d; diff --git a/drivers/media/platform/amphion/vpu_mbox.c b/drivers/media/platform/amphion/vpu_mbox.c index c2963b8deb48..b2ac8de6a2d9 100644 --- a/drivers/media/platform/amphion/vpu_mbox.c +++ b/drivers/media/platform/amphion/vpu_mbox.c @@ -109,7 +109,3 @@ void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data) mbox_send_message(core->tx_data.ch, &data); mbox_send_message(core->tx_type.ch, &type); } - -void vpu_mbox_enable_rx(struct vpu_dev *dev) -{ -} diff --git a/drivers/media/platform/amphion/vpu_mbox.h b/drivers/media/platform/amphion/vpu_mbox.h index 79cfd874e92b..8b7aea4f606c 100644 --- a/drivers/media/platform/amphion/vpu_mbox.h +++ b/drivers/media/platform/amphion/vpu_mbox.h @@ -11,6 +11,5 @@ int vpu_mbox_request(struct vpu_core *core); void vpu_mbox_free(struct vpu_core *core); void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data); void vpu_mbox_send_type(struct vpu_core *core, u32 type); -void vpu_mbox_enable_rx(struct vpu_dev *dev); #endif diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index 45707931bc4f..74668fa362e2 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -501,14 +501,25 @@ static int vpu_vb2_queue_setup(struct vb2_queue *vq, call_void_vop(inst, release); } + if (V4L2_TYPE_IS_CAPTURE(vq->type)) + call_void_vop(inst, reset_frame_store); + return 0; } static int vpu_vb2_buf_init(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + vpu_buf->fs_id = -1; vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + + if (!inst->ops->attach_frame_store || V4L2_TYPE_IS_OUTPUT(vb->type)) + return 0; + + call_void_vop(inst, attach_frame_store, vb); return 0; } diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index cebcae196eec..7f1ce95cdc3f 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -57,6 +57,25 @@ #define CSI2RX_LANES_MAX 4 #define CSI2RX_STREAMS_MAX 4 +#define CSI2RX_ERROR_IRQS_REG 0x28 +#define CSI2RX_ERROR_IRQS_MASK_REG 0x2C + +#define CSI2RX_STREAM3_FIFO_OVERFLOW_IRQ BIT(19) +#define CSI2RX_STREAM2_FIFO_OVERFLOW_IRQ BIT(18) +#define CSI2RX_STREAM1_FIFO_OVERFLOW_IRQ BIT(17) +#define CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ BIT(16) +#define CSI2RX_FRONT_TRUNC_HDR_IRQ BIT(12) +#define CSI2RX_PROT_TRUNCATED_PACKET_IRQ BIT(11) +#define CSI2RX_FRONT_LP_NO_PAYLOAD_IRQ BIT(10) +#define CSI2RX_SP_INVALID_RCVD_IRQ BIT(9) +#define CSI2RX_DATA_ID_IRQ BIT(7) +#define CSI2RX_HEADER_CORRECTED_ECC_IRQ BIT(6) +#define CSI2RX_HEADER_ECC_IRQ BIT(5) +#define CSI2RX_PAYLOAD_CRC_IRQ BIT(4) + +#define CSI2RX_ECC_ERRORS GENMASK(7, 4) +#define CSI2RX_PACKET_ERRORS GENMASK(12, 9) + enum csi2rx_pads { CSI2RX_PAD_SINK, CSI2RX_PAD_SOURCE_STREAM0, @@ -71,9 +90,32 @@ struct csi2rx_fmt { u8 bpp; }; +struct csi2rx_event { + u32 mask; + const char *name; +}; + +static const struct csi2rx_event csi2rx_events[] = { + { CSI2RX_STREAM3_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 3 FIFO detected" }, + { CSI2RX_STREAM2_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 2 FIFO detected" }, + { CSI2RX_STREAM1_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 1 FIFO detected" }, + { CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 0 FIFO detected" }, + { CSI2RX_FRONT_TRUNC_HDR_IRQ, "A truncated header [short or long] has been received" }, + { CSI2RX_PROT_TRUNCATED_PACKET_IRQ, "A truncated long packet has been received" }, + { CSI2RX_FRONT_LP_NO_PAYLOAD_IRQ, "A truncated long packet has been received. No payload" }, + { CSI2RX_SP_INVALID_RCVD_IRQ, "A reserved or invalid short packet has been received" }, + { CSI2RX_DATA_ID_IRQ, "Data ID error in the header packet" }, + { CSI2RX_HEADER_CORRECTED_ECC_IRQ, "ECC error detected and corrected" }, + { CSI2RX_HEADER_ECC_IRQ, "Unrecoverable ECC error" }, + { CSI2RX_PAYLOAD_CRC_IRQ, "CRC error" }, +}; + +#define CSI2RX_NUM_EVENTS ARRAY_SIZE(csi2rx_events) + struct csi2rx_priv { struct device *dev; unsigned int count; + int error_irq; /* * Used to prevent race conditions between multiple, @@ -95,6 +137,7 @@ struct csi2rx_priv { u8 max_lanes; u8 max_streams; bool has_internal_dphy; + u32 events[CSI2RX_NUM_EVENTS]; struct v4l2_subdev subdev; struct v4l2_async_notifier notifier; @@ -124,6 +167,54 @@ static const struct csi2rx_fmt formats[] = { { .code = MEDIA_BUS_FMT_BGR888_1X24, .bpp = 24, }, }; +static void csi2rx_configure_error_irq_mask(void __iomem *base, + struct csi2rx_priv *csi2rx) +{ + u32 error_irq_mask = 0; + + error_irq_mask |= CSI2RX_ECC_ERRORS; + error_irq_mask |= CSI2RX_PACKET_ERRORS; + + /* + * Iterate through all source pads and check if they are linked + * to an active remote pad. If an active remote pad is found, + * calculate the corresponding bit position and set it in + * mask, enabling the stream overflow error in the mask. + */ + for (int i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { + struct media_pad *remote_pad; + + remote_pad = media_pad_remote_pad_first(&csi2rx->pads[i]); + if (remote_pad) { + int pad = i - CSI2RX_PAD_SOURCE_STREAM0; + u32 bit_mask = CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ << pad; + + error_irq_mask |= bit_mask; + } + } + + writel(error_irq_mask, base + CSI2RX_ERROR_IRQS_MASK_REG); +} + +static irqreturn_t csi2rx_irq_handler(int irq, void *dev_id) +{ + struct csi2rx_priv *csi2rx = dev_id; + int i; + u32 error_status, error_mask; + + error_status = readl(csi2rx->base + CSI2RX_ERROR_IRQS_REG); + error_mask = readl(csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG); + + for (i = 0; i < CSI2RX_NUM_EVENTS; i++) + if ((error_status & csi2rx_events[i].mask) && + (error_mask & csi2rx_events[i].mask)) + csi2rx->events[i]++; + + writel(error_status, csi2rx->base + CSI2RX_ERROR_IRQS_REG); + + return IRQ_HANDLED; +} + static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) { unsigned int i; @@ -220,6 +311,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) reset_control_deassert(csi2rx->p_rst); csi2rx_reset(csi2rx); + if (csi2rx->error_irq >= 0) + csi2rx_configure_error_irq_mask(csi2rx->base, csi2rx); + reg = csi2rx->num_lanes << 8; for (i = 0; i < csi2rx->num_lanes; i++) { reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]); @@ -332,6 +426,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) reset_control_assert(csi2rx->sys_rst); clk_disable_unprepare(csi2rx->sys_clk); + writel(0, csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG); + for (i = 0; i < csi2rx->max_streams; i++) { writel(CSI2RX_STREAM_CTRL_STOP, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); @@ -363,6 +459,21 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) } } +static int csi2rx_log_status(struct v4l2_subdev *sd) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(sd); + unsigned int i; + + for (i = 0; i < CSI2RX_NUM_EVENTS; i++) { + if (csi2rx->events[i]) + dev_info(csi2rx->dev, "%s events: %d\n", + csi2rx_events[i].name, + csi2rx->events[i]); + } + + return 0; +} + static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) { struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); @@ -468,7 +579,12 @@ static const struct v4l2_subdev_video_ops csi2rx_video_ops = { .s_stream = csi2rx_s_stream, }; +static const struct v4l2_subdev_core_ops csi2rx_core_ops = { + .log_status = csi2rx_log_status, +}; + static const struct v4l2_subdev_ops csi2rx_subdev_ops = { + .core = &csi2rx_core_ops, .video = &csi2rx_video_ops, .pad = &csi2rx_pad_ops, }; @@ -705,6 +821,21 @@ static int csi2rx_probe(struct platform_device *pdev) if (ret) goto err_cleanup; + csi2rx->error_irq = platform_get_irq_byname_optional(pdev, "error_irq"); + + if (csi2rx->error_irq < 0) { + dev_dbg(csi2rx->dev, "Optional interrupt not defined, proceeding without it\n"); + } else { + ret = devm_request_irq(csi2rx->dev, csi2rx->error_irq, + csi2rx_irq_handler, 0, + dev_name(&pdev->dev), csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Unable to request interrupt: %d\n", ret); + goto err_cleanup; + } + } + ret = v4l2_subdev_init_finalize(&csi2rx->subdev); if (ret) goto err_cleanup; diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 5c17bc58181e..8681dd193033 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -598,6 +598,27 @@ static void _bswap16(u16 *a) *a = ((*a & 0x00FF) << 8) | ((*a & 0xFF00) >> 8); } +static dma_addr_t mxc_jpeg_get_plane_dma_addr(struct vb2_buffer *buf, unsigned int plane_no) +{ + if (plane_no >= buf->num_planes) + return 0; + return vb2_dma_contig_plane_dma_addr(buf, plane_no) + buf->planes[plane_no].data_offset; +} + +static void *mxc_jpeg_get_plane_vaddr(struct vb2_buffer *buf, unsigned int plane_no) +{ + if (plane_no >= buf->num_planes) + return NULL; + return vb2_plane_vaddr(buf, plane_no) + buf->planes[plane_no].data_offset; +} + +static unsigned long mxc_jpeg_get_plane_payload(struct vb2_buffer *buf, unsigned int plane_no) +{ + if (plane_no >= buf->num_planes) + return 0; + return vb2_get_plane_payload(buf, plane_no) - buf->planes[plane_no].data_offset; +} + static void print_mxc_buf(struct mxc_jpeg_dev *jpeg, struct vb2_buffer *buf, unsigned long len) { @@ -610,11 +631,11 @@ static void print_mxc_buf(struct mxc_jpeg_dev *jpeg, struct vb2_buffer *buf, return; for (plane_no = 0; plane_no < buf->num_planes; plane_no++) { - payload = vb2_get_plane_payload(buf, plane_no); + payload = mxc_jpeg_get_plane_payload(buf, plane_no); if (len == 0) len = payload; - dma_addr = vb2_dma_contig_plane_dma_addr(buf, plane_no); - vaddr = vb2_plane_vaddr(buf, plane_no); + dma_addr = mxc_jpeg_get_plane_dma_addr(buf, plane_no); + vaddr = mxc_jpeg_get_plane_vaddr(buf, plane_no); v4l2_dbg(3, debug, &jpeg->v4l2_dev, "plane %d (vaddr=%p dma_addr=%x payload=%ld):", plane_no, vaddr, dma_addr, payload); @@ -712,16 +733,15 @@ static void mxc_jpeg_addrs(struct mxc_jpeg_desc *desc, struct mxc_jpeg_q_data *q_data; q_data = mxc_jpeg_get_q_data(ctx, raw_buf->type); - desc->buf_base0 = vb2_dma_contig_plane_dma_addr(raw_buf, 0); + desc->buf_base0 = mxc_jpeg_get_plane_dma_addr(raw_buf, 0); desc->buf_base1 = 0; if (img_fmt == STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV420)) { if (raw_buf->num_planes == 2) - desc->buf_base1 = vb2_dma_contig_plane_dma_addr(raw_buf, 1); + desc->buf_base1 = mxc_jpeg_get_plane_dma_addr(raw_buf, 1); else desc->buf_base1 = desc->buf_base0 + q_data->sizeimage[0]; } - desc->stm_bufbase = vb2_dma_contig_plane_dma_addr(jpeg_buf, 0) + - offset; + desc->stm_bufbase = mxc_jpeg_get_plane_dma_addr(jpeg_buf, 0) + offset; } static bool mxc_jpeg_is_extended_sequential(const struct mxc_jpeg_fmt *fmt) @@ -1029,8 +1049,8 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv) vb2_set_plane_payload(&dst_buf->vb2_buf, 1, payload); } dev_dbg(dev, "Decoding finished, payload size: %ld + %ld\n", - vb2_get_plane_payload(&dst_buf->vb2_buf, 0), - vb2_get_plane_payload(&dst_buf->vb2_buf, 1)); + mxc_jpeg_get_plane_payload(&dst_buf->vb2_buf, 0), + mxc_jpeg_get_plane_payload(&dst_buf->vb2_buf, 1)); } /* short preview of the results */ @@ -1889,8 +1909,8 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb) struct mxc_jpeg_sof *psof = NULL; struct mxc_jpeg_sos *psos = NULL; struct mxc_jpeg_src_buf *jpeg_src_buf = vb2_to_mxc_buf(vb); - u8 *src_addr = (u8 *)vb2_plane_vaddr(vb, 0); - u32 size = vb2_get_plane_payload(vb, 0); + u8 *src_addr = (u8 *)mxc_jpeg_get_plane_vaddr(vb, 0); + u32 size = mxc_jpeg_get_plane_payload(vb, 0); int ret; memset(&header, 0, sizeof(header)); @@ -2027,6 +2047,11 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb) i, vb2_plane_size(vb, i), sizeimage); return -EINVAL; } + if (!IS_ALIGNED(mxc_jpeg_get_plane_dma_addr(vb, i), MXC_JPEG_ADDR_ALIGNMENT)) { + dev_err(dev, "planes[%d] address is not %d aligned\n", + i, MXC_JPEG_ADDR_ALIGNMENT); + return -EINVAL; + } } if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { vb2_set_plane_payload(vb, 0, 0); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h index fdde45f7e163..44e46face6d1 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h @@ -30,6 +30,7 @@ #define MXC_JPEG_MAX_PLANES 2 #define MXC_JPEG_PATTERN_WIDTH 128 #define MXC_JPEG_PATTERN_HEIGHT 64 +#define MXC_JPEG_ADDR_ALIGNMENT 16 enum mxc_jpeg_enc_state { MXC_JPEG_ENCODING = 0, /* jpeg encode phase */ diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index d060eadebc7a..2beb5f43c2c0 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -28,6 +28,7 @@ #include <linux/reset.h> #include <linux/spinlock.h> +#include <media/mipi-csi2.h> #include <media/v4l2-common.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -229,25 +230,6 @@ #define DEFAULT_SCLK_CSIS_FREQ 166000000UL -/* MIPI CSI-2 Data Types */ -#define MIPI_CSI2_DATA_TYPE_YUV420_8 0x18 -#define MIPI_CSI2_DATA_TYPE_YUV420_10 0x19 -#define MIPI_CSI2_DATA_TYPE_LE_YUV420_8 0x1a -#define MIPI_CSI2_DATA_TYPE_CS_YUV420_8 0x1c -#define MIPI_CSI2_DATA_TYPE_CS_YUV420_10 0x1d -#define MIPI_CSI2_DATA_TYPE_YUV422_8 0x1e -#define MIPI_CSI2_DATA_TYPE_YUV422_10 0x1f -#define MIPI_CSI2_DATA_TYPE_RGB565 0x22 -#define MIPI_CSI2_DATA_TYPE_RGB666 0x23 -#define MIPI_CSI2_DATA_TYPE_RGB888 0x24 -#define MIPI_CSI2_DATA_TYPE_RAW6 0x28 -#define MIPI_CSI2_DATA_TYPE_RAW7 0x29 -#define MIPI_CSI2_DATA_TYPE_RAW8 0x2a -#define MIPI_CSI2_DATA_TYPE_RAW10 0x2b -#define MIPI_CSI2_DATA_TYPE_RAW12 0x2c -#define MIPI_CSI2_DATA_TYPE_RAW14 0x2d -#define MIPI_CSI2_DATA_TYPE_USER(x) (0x30 + (x)) - struct mipi_csis_event { bool debug; u32 mask; @@ -357,116 +339,116 @@ static const struct csis_pix_format mipi_csis_formats[] = { { .code = MEDIA_BUS_FMT_UYVY8_1X16, .output = MEDIA_BUS_FMT_UYVY8_1X16, - .data_type = MIPI_CSI2_DATA_TYPE_YUV422_8, + .data_type = MIPI_CSI2_DT_YUV422_8B, .width = 16, }, /* RGB formats. */ { .code = MEDIA_BUS_FMT_RGB565_1X16, .output = MEDIA_BUS_FMT_RGB565_1X16, - .data_type = MIPI_CSI2_DATA_TYPE_RGB565, + .data_type = MIPI_CSI2_DT_RGB565, .width = 16, }, { .code = MEDIA_BUS_FMT_BGR888_1X24, .output = MEDIA_BUS_FMT_RGB888_1X24, - .data_type = MIPI_CSI2_DATA_TYPE_RGB888, + .data_type = MIPI_CSI2_DT_RGB888, .width = 24, }, /* RAW (Bayer and greyscale) formats. */ { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .output = MEDIA_BUS_FMT_SBGGR8_1X8, - .data_type = MIPI_CSI2_DATA_TYPE_RAW8, + .data_type = MIPI_CSI2_DT_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .output = MEDIA_BUS_FMT_SGBRG8_1X8, - .data_type = MIPI_CSI2_DATA_TYPE_RAW8, + .data_type = MIPI_CSI2_DT_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .output = MEDIA_BUS_FMT_SGRBG8_1X8, - .data_type = MIPI_CSI2_DATA_TYPE_RAW8, + .data_type = MIPI_CSI2_DT_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .output = MEDIA_BUS_FMT_SRGGB8_1X8, - .data_type = MIPI_CSI2_DATA_TYPE_RAW8, + .data_type = MIPI_CSI2_DT_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_Y8_1X8, .output = MEDIA_BUS_FMT_Y8_1X8, - .data_type = MIPI_CSI2_DATA_TYPE_RAW8, + .data_type = MIPI_CSI2_DT_RAW8, .width = 8, }, { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .output = MEDIA_BUS_FMT_SBGGR10_1X10, - .data_type = MIPI_CSI2_DATA_TYPE_RAW10, + .data_type = MIPI_CSI2_DT_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .output = MEDIA_BUS_FMT_SGBRG10_1X10, - .data_type = MIPI_CSI2_DATA_TYPE_RAW10, + .data_type = MIPI_CSI2_DT_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .output = MEDIA_BUS_FMT_SGRBG10_1X10, - .data_type = MIPI_CSI2_DATA_TYPE_RAW10, + .data_type = MIPI_CSI2_DT_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .output = MEDIA_BUS_FMT_SRGGB10_1X10, - .data_type = MIPI_CSI2_DATA_TYPE_RAW10, + .data_type = MIPI_CSI2_DT_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_Y10_1X10, .output = MEDIA_BUS_FMT_Y10_1X10, - .data_type = MIPI_CSI2_DATA_TYPE_RAW10, + .data_type = MIPI_CSI2_DT_RAW10, .width = 10, }, { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .output = MEDIA_BUS_FMT_SBGGR12_1X12, - .data_type = MIPI_CSI2_DATA_TYPE_RAW12, + .data_type = MIPI_CSI2_DT_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .output = MEDIA_BUS_FMT_SGBRG12_1X12, - .data_type = MIPI_CSI2_DATA_TYPE_RAW12, + .data_type = MIPI_CSI2_DT_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .output = MEDIA_BUS_FMT_SGRBG12_1X12, - .data_type = MIPI_CSI2_DATA_TYPE_RAW12, + .data_type = MIPI_CSI2_DT_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .output = MEDIA_BUS_FMT_SRGGB12_1X12, - .data_type = MIPI_CSI2_DATA_TYPE_RAW12, + .data_type = MIPI_CSI2_DT_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_Y12_1X12, .output = MEDIA_BUS_FMT_Y12_1X12, - .data_type = MIPI_CSI2_DATA_TYPE_RAW12, + .data_type = MIPI_CSI2_DT_RAW12, .width = 12, }, { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .output = MEDIA_BUS_FMT_SBGGR14_1X14, - .data_type = MIPI_CSI2_DATA_TYPE_RAW14, + .data_type = MIPI_CSI2_DT_RAW14, .width = 14, }, { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .output = MEDIA_BUS_FMT_SGBRG14_1X14, - .data_type = MIPI_CSI2_DATA_TYPE_RAW14, + .data_type = MIPI_CSI2_DT_RAW14, .width = 14, }, { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .output = MEDIA_BUS_FMT_SGRBG14_1X14, - .data_type = MIPI_CSI2_DATA_TYPE_RAW14, + .data_type = MIPI_CSI2_DT_RAW14, .width = 14, }, { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .output = MEDIA_BUS_FMT_SRGGB14_1X14, - .data_type = MIPI_CSI2_DATA_TYPE_RAW14, + .data_type = MIPI_CSI2_DT_RAW14, .width = 14, }, /* JPEG */ @@ -494,7 +476,7 @@ static const struct csis_pix_format mipi_csis_formats[] = { * SoC that can support quad pixel mode, this will have to be * revisited. */ - .data_type = MIPI_CSI2_DATA_TYPE_RAW8, + .data_type = MIPI_CSI2_DT_RAW8, .width = 8, } }; @@ -583,7 +565,7 @@ static void __mipi_csis_set_format(struct mipi_csis_device *csis, * * TODO: Verify which other formats require DUAL (or QUAD) modes. */ - if (csis_fmt->data_type == MIPI_CSI2_DATA_TYPE_YUV422_8) + if (csis_fmt->data_type == MIPI_CSI2_DT_YUV422_8B) val |= MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL; val |= MIPI_CSIS_ISPCFG_FMT(csis_fmt->data_type); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index 1e79b1211b60..981648a03113 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -3,6 +3,7 @@ * Copyright 2019-2020 NXP */ +#include <linux/bits.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/errno.h> @@ -245,26 +246,41 @@ static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi) /* Panic will assert when the buffers are 50% full */ -/* For i.MX8QXP C0 and i.MX8MN ISI IER version */ +/* For i.MX8MN ISI IER version */ static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v1 = { - .oflw_y_buf_en = { .offset = 19, .mask = 0x80000 }, - .oflw_u_buf_en = { .offset = 21, .mask = 0x200000 }, - .oflw_v_buf_en = { .offset = 23, .mask = 0x800000 }, + .oflw_y_buf_en = { .mask = BIT(19) }, + .oflw_u_buf_en = { .mask = BIT(21) }, + .oflw_v_buf_en = { .mask = BIT(23) }, - .panic_y_buf_en = {.offset = 20, .mask = 0x100000 }, - .panic_u_buf_en = {.offset = 22, .mask = 0x400000 }, - .panic_v_buf_en = {.offset = 24, .mask = 0x1000000 }, + .panic_y_buf_en = { .mask = BIT(20) }, + .panic_u_buf_en = { .mask = BIT(22) }, + .panic_v_buf_en = { .mask = BIT(24) }, }; -/* For i.MX8MP ISI IER version */ +/* For i.MX8QXP C0 and i.MX8MP ISI IER version */ static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v2 = { - .oflw_y_buf_en = { .offset = 18, .mask = 0x40000 }, - .oflw_u_buf_en = { .offset = 20, .mask = 0x100000 }, - .oflw_v_buf_en = { .offset = 22, .mask = 0x400000 }, + .oflw_y_buf_en = { .mask = BIT(18) }, + .oflw_u_buf_en = { .mask = BIT(20) }, + .oflw_v_buf_en = { .mask = BIT(22) }, - .panic_y_buf_en = {.offset = 19, .mask = 0x80000 }, - .panic_u_buf_en = {.offset = 21, .mask = 0x200000 }, - .panic_v_buf_en = {.offset = 23, .mask = 0x800000 }, + .panic_y_buf_en = { .mask = BIT(19) }, + .panic_u_buf_en = { .mask = BIT(21) }, + .panic_v_buf_en = { .mask = BIT(23) }, +}; + +/* For i.MX8QM ISI IER version */ +static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_qm = { + .oflw_y_buf_en = { .mask = BIT(16) }, + .oflw_u_buf_en = { .mask = BIT(19) }, + .oflw_v_buf_en = { .mask = BIT(22) }, + + .excs_oflw_y_buf_en = { .mask = BIT(17) }, + .excs_oflw_u_buf_en = { .mask = BIT(20) }, + .excs_oflw_v_buf_en = { .mask = BIT(23) }, + + .panic_y_buf_en = { .mask = BIT(18) }, + .panic_u_buf_en = { .mask = BIT(21) }, + .panic_v_buf_en = { .mask = BIT(24) }, }; /* Panic will assert when the buffers are 50% full */ @@ -274,11 +290,6 @@ static const struct mxc_isi_set_thd mxc_imx8_isi_thd_v1 = { .panic_set_thd_v = { .mask = 0xf0000, .offset = 16, .threshold = 0x7 }, }; -static const struct clk_bulk_data mxc_imx8mn_clks[] = { - { .id = "axi" }, - { .id = "apb" }, -}; - static const struct mxc_isi_plat_data mxc_imx8mn_data = { .model = MXC_ISI_IMX8MN, .num_ports = 1, @@ -286,8 +297,6 @@ static const struct mxc_isi_plat_data mxc_imx8mn_data = { .reg_offset = 0, .ier_reg = &mxc_imx8_isi_ier_v1, .set_thd = &mxc_imx8_isi_thd_v1, - .clks = mxc_imx8mn_clks, - .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), .buf_active_reverse = false, .gasket_ops = &mxc_imx8_gasket_ops, .has_36bit_dma = false, @@ -300,8 +309,6 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = { .reg_offset = 0x2000, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, - .clks = mxc_imx8mn_clks, - .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), .buf_active_reverse = true, .gasket_ops = &mxc_imx8_gasket_ops, .has_36bit_dma = true, @@ -314,8 +321,6 @@ static const struct mxc_isi_plat_data mxc_imx8ulp_data = { .reg_offset = 0x0, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, - .clks = mxc_imx8mn_clks, - .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), .buf_active_reverse = true, .has_36bit_dma = false, }; @@ -327,13 +332,33 @@ static const struct mxc_isi_plat_data mxc_imx93_data = { .reg_offset = 0, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, - .clks = mxc_imx8mn_clks, - .num_clks = ARRAY_SIZE(mxc_imx8mn_clks), .buf_active_reverse = true, .gasket_ops = &mxc_imx93_gasket_ops, .has_36bit_dma = false, }; +static const struct mxc_isi_plat_data mxc_imx8qm_data = { + .model = MXC_ISI_IMX8QM, + .num_ports = 5, + .num_channels = 8, + .reg_offset = 0x10000, + .ier_reg = &mxc_imx8_isi_ier_qm, + .set_thd = &mxc_imx8_isi_thd_v1, + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + +static const struct mxc_isi_plat_data mxc_imx8qxp_data = { + .model = MXC_ISI_IMX8QXP, + .num_ports = 5, + .num_channels = 6, + .reg_offset = 0x10000, + .ier_reg = &mxc_imx8_isi_ier_v2, + .set_thd = &mxc_imx8_isi_thd_v1, + .buf_active_reverse = true, + .has_36bit_dma = false, +}; + /* ----------------------------------------------------------------------------- * Power management */ @@ -385,7 +410,7 @@ static int mxc_isi_runtime_suspend(struct device *dev) { struct mxc_isi_dev *isi = dev_get_drvdata(dev); - clk_bulk_disable_unprepare(isi->pdata->num_clks, isi->clks); + clk_bulk_disable_unprepare(isi->num_clks, isi->clks); return 0; } @@ -395,7 +420,7 @@ static int mxc_isi_runtime_resume(struct device *dev) struct mxc_isi_dev *isi = dev_get_drvdata(dev); int ret; - ret = clk_bulk_prepare_enable(isi->pdata->num_clks, isi->clks); + ret = clk_bulk_prepare_enable(isi->num_clks, isi->clks); if (ret) { dev_err(dev, "Failed to enable clocks (%d)\n", ret); return ret; @@ -413,27 +438,6 @@ static const struct dev_pm_ops mxc_isi_pm_ops = { * Probe, remove & driver */ -static int mxc_isi_clk_get(struct mxc_isi_dev *isi) -{ - unsigned int size = isi->pdata->num_clks - * sizeof(*isi->clks); - int ret; - - isi->clks = devm_kmemdup(isi->dev, isi->pdata->clks, size, GFP_KERNEL); - if (!isi->clks) - return -ENOMEM; - - ret = devm_clk_bulk_get(isi->dev, isi->pdata->num_clks, - isi->clks); - if (ret < 0) { - dev_err(isi->dev, "Failed to acquire clocks: %d\n", - ret); - return ret; - } - - return 0; -} - static int mxc_isi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -456,34 +460,25 @@ static int mxc_isi_probe(struct platform_device *pdev) if (!isi->pipes) return -ENOMEM; - ret = mxc_isi_clk_get(isi); - if (ret < 0) { - dev_err(dev, "Failed to get clocks\n"); - return ret; - } + isi->num_clks = devm_clk_bulk_get_all(dev, &isi->clks); + if (isi->num_clks < 0) + return dev_err_probe(dev, isi->num_clks, "Failed to get clocks\n"); isi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(isi->regs)) { - dev_err(dev, "Failed to get ISI register map\n"); - return PTR_ERR(isi->regs); - } + if (IS_ERR(isi->regs)) + return dev_err_probe(dev, PTR_ERR(isi->regs), + "Failed to get ISI register map\n"); if (isi->pdata->gasket_ops) { isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,blk-ctrl"); - if (IS_ERR(isi->gasket)) { - ret = PTR_ERR(isi->gasket); - dev_err(dev, "failed to get gasket: %d\n", ret); - return ret; - } + if (IS_ERR(isi->gasket)) + return dev_err_probe(dev, PTR_ERR(isi->gasket), + "failed to get gasket\n"); } dma_size = isi->pdata->has_36bit_dma ? 36 : 32; - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size)); - if (ret) { - dev_err(dev, "failed to set DMA mask\n"); - return ret; - } + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size)); pm_runtime_enable(dev); @@ -541,6 +536,8 @@ static void mxc_isi_remove(struct platform_device *pdev) static const struct of_device_id mxc_isi_of_match[] = { { .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data }, { .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data }, + { .compatible = "fsl,imx8qm-isi", .data = &mxc_imx8qm_data }, + { .compatible = "fsl,imx8qxp-isi", .data = &mxc_imx8qxp_data }, { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data }, { .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data }, { /* sentinel */ }, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 9c7fe9e5f941..206995bedca4 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -114,7 +114,6 @@ struct mxc_isi_buffer { }; struct mxc_isi_reg { - u32 offset; u32 mask; }; @@ -158,6 +157,8 @@ struct mxc_gasket_ops { enum model { MXC_ISI_IMX8MN, MXC_ISI_IMX8MP, + MXC_ISI_IMX8QM, + MXC_ISI_IMX8QXP, MXC_ISI_IMX8ULP, MXC_ISI_IMX93, }; @@ -170,8 +171,6 @@ struct mxc_isi_plat_data { const struct mxc_isi_ier_reg *ier_reg; const struct mxc_isi_set_thd *set_thd; const struct mxc_gasket_ops *gasket_ops; - const struct clk_bulk_data *clks; - unsigned int num_clks; bool buf_active_reverse; bool has_36bit_dma; }; @@ -283,6 +282,7 @@ struct mxc_isi_dev { void __iomem *regs; struct clk_bulk_data *clks; + int num_clks; struct regmap *gasket; struct mxc_isi_crossbar crossbar; diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 93a55c97cd17..ede6cc74c023 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -188,11 +188,12 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, * Create a 1:1 mapping between pixel link inputs and outputs to * pipelines by default. */ - routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL); + routing.num_routes = min(xbar->num_sinks - 1, xbar->num_sources); + routes = kcalloc(routing.num_routes, sizeof(*routes), GFP_KERNEL); if (!routes) return -ENOMEM; - for (i = 0; i < xbar->num_sources; ++i) { + for (i = 0; i < routing.num_routes; ++i) { struct v4l2_subdev_route *route = &routes[i]; route->sink_pad = i; @@ -200,7 +201,6 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd, route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE; } - routing.num_routes = xbar->num_sources; routing.routes = routes; ret = __mxc_isi_crossbar_set_routing(sd, state, &routing); @@ -352,9 +352,8 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd, sink_streams); if (ret) { dev_err(xbar->isi->dev, - "failed to %s streams 0x%llx on '%s':%u: %d\n", - "enable", sink_streams, remote_sd->name, - remote_pad, ret); + "failed to enable streams 0x%llx on '%s':%u: %d\n", + sink_streams, remote_sd->name, remote_pad, ret); mxc_isi_crossbar_gasket_disable(xbar, sink_pad); return ret; } @@ -392,9 +391,8 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, sink_streams); if (ret) dev_err(xbar->isi->dev, - "failed to %s streams 0x%llx on '%s':%u: %d\n", - "disable", sink_streams, remote_sd->name, - remote_pad, ret); + "failed to disable streams 0x%llx on '%s':%u: %d\n", + sink_streams, remote_sd->name, remote_pad, ret); mxc_isi_crossbar_gasket_disable(xbar, sink_pad); } @@ -453,7 +451,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi) * the memory input. */ xbar->num_sinks = isi->pdata->num_ports + 1; - xbar->num_sources = isi->pdata->num_ports; + xbar->num_sources = isi->pdata->num_channels; num_pads = xbar->num_sinks + xbar->num_sources; xbar->pads = kcalloc(num_pads, sizeof(*xbar->pads), GFP_KERNEL); diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index a8bcf60e2f37..3a4645f59a44 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -5,6 +5,7 @@ * Copyright (C) 2021 Purism SPC */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> @@ -62,6 +63,8 @@ #define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL 0x188 #define CSI2RX_CFG_DISABLE_PAYLOAD_1 0x130 +struct csi_state; + enum { ST_POWERED = 1, ST_STREAMING = 2, @@ -83,11 +86,11 @@ static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = { #define CSI2_NUM_CLKS ARRAY_SIZE(imx8mq_mipi_csi_clk_id) -#define GPR_CSI2_1_RX_ENABLE BIT(13) -#define GPR_CSI2_1_VID_INTFC_ENB BIT(12) -#define GPR_CSI2_1_HSEL BIT(10) -#define GPR_CSI2_1_CONT_CLK_MODE BIT(8) -#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2) +struct imx8mq_plat_data { + int (*enable)(struct csi_state *state, u32 hs_settle); + void (*disable)(struct csi_state *state); + bool use_reg_csr; +}; /* * The send level configures the number of entries that must accumulate in @@ -106,6 +109,7 @@ static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = { struct csi_state { struct device *dev; + const struct imx8mq_plat_data *pdata; void __iomem *regs; struct clk_bulk_data clks[CSI2_NUM_CLKS]; struct reset_control *rst; @@ -137,6 +141,123 @@ struct csi2_pix_format { u8 width; }; +/* ----------------------------------------------------------------------------- + * i.MX8MQ GPR + */ + +#define GPR_CSI2_1_RX_ENABLE BIT(13) +#define GPR_CSI2_1_VID_INTFC_ENB BIT(12) +#define GPR_CSI2_1_HSEL BIT(10) +#define GPR_CSI2_1_CONT_CLK_MODE BIT(8) +#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2) + +static int imx8mq_gpr_enable(struct csi_state *state, u32 hs_settle) +{ + regmap_update_bits(state->phy_gpr, + state->phy_gpr_reg, + 0x3fff, + GPR_CSI2_1_RX_ENABLE | + GPR_CSI2_1_VID_INTFC_ENB | + GPR_CSI2_1_HSEL | + GPR_CSI2_1_CONT_CLK_MODE | + GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle)); + + return 0; +} + +static const struct imx8mq_plat_data imx8mq_data = { + .enable = imx8mq_gpr_enable, +}; + +/* ----------------------------------------------------------------------------- + * i.MX8QXP + */ + +#define CSI2SS_PL_CLK_INTERVAL_US 100 +#define CSI2SS_PL_CLK_TIMEOUT_US 100000 + +#define CSI2SS_PLM_CTRL 0x0 +#define CSI2SS_PLM_CTRL_ENABLE_PL BIT(0) +#define CSI2SS_PLM_CTRL_VSYNC_OVERRIDE BIT(9) +#define CSI2SS_PLM_CTRL_HSYNC_OVERRIDE BIT(10) +#define CSI2SS_PLM_CTRL_VALID_OVERRIDE BIT(11) +#define CSI2SS_PLM_CTRL_POLARITY_HIGH BIT(12) +#define CSI2SS_PLM_CTRL_PL_CLK_RUN BIT(31) + +#define CSI2SS_PHY_CTRL 0x4 +#define CSI2SS_PHY_CTRL_RX_ENABLE BIT(0) +#define CSI2SS_PHY_CTRL_AUTO_PD_EN BIT(1) +#define CSI2SS_PHY_CTRL_DDRCLK_EN BIT(2) +#define CSI2SS_PHY_CTRL_CONT_CLK_MODE BIT(3) +#define CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK GENMASK(9, 4) +#define CSI2SS_PHY_CTRL_RTERM_SEL BIT(21) +#define CSI2SS_PHY_CTRL_PD BIT(22) + +#define CSI2SS_DATA_TYPE_DISABLE_BF 0x38 +#define CSI2SS_DATA_TYPE_DISABLE_BF_MASK GENMASK(23, 0) + +#define CSI2SS_CTRL_CLK_RESET 0x44 +#define CSI2SS_CTRL_CLK_RESET_EN BIT(0) + +static int imx8qxp_gpr_enable(struct csi_state *state, u32 hs_settle) +{ + int ret; + u32 val; + + /* Clear format */ + regmap_clear_bits(state->phy_gpr, CSI2SS_DATA_TYPE_DISABLE_BF, + CSI2SS_DATA_TYPE_DISABLE_BF_MASK); + + regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0); + + regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL, + FIELD_PREP(CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK, hs_settle) | + CSI2SS_PHY_CTRL_RX_ENABLE | CSI2SS_PHY_CTRL_DDRCLK_EN | + CSI2SS_PHY_CTRL_CONT_CLK_MODE | CSI2SS_PHY_CTRL_PD | + CSI2SS_PHY_CTRL_RTERM_SEL | CSI2SS_PHY_CTRL_AUTO_PD_EN); + + ret = regmap_read_poll_timeout(state->phy_gpr, CSI2SS_PLM_CTRL, + val, !(val & CSI2SS_PLM_CTRL_PL_CLK_RUN), + CSI2SS_PL_CLK_INTERVAL_US, + CSI2SS_PL_CLK_TIMEOUT_US); + + if (ret) { + dev_err(state->dev, "Timeout waiting for Pixel-Link clock\n"); + return ret; + } + + /* Enable Pixel link Master */ + regmap_set_bits(state->phy_gpr, CSI2SS_PLM_CTRL, + CSI2SS_PLM_CTRL_ENABLE_PL | CSI2SS_PLM_CTRL_VALID_OVERRIDE); + + /* PHY Enable */ + regmap_clear_bits(state->phy_gpr, CSI2SS_PHY_CTRL, + CSI2SS_PHY_CTRL_PD | CSI2SS_PLM_CTRL_POLARITY_HIGH); + + /* Release Reset */ + regmap_set_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET, CSI2SS_CTRL_CLK_RESET_EN); + + return ret; +} + +static void imx8qxp_gpr_disable(struct csi_state *state) +{ + /* Disable Pixel Link */ + regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0); + + /* Disable PHY */ + regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL, 0x0); + + regmap_clear_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET, + CSI2SS_CTRL_CLK_RESET_EN); +}; + +static const struct imx8mq_plat_data imx8qxp_data = { + .enable = imx8qxp_gpr_enable, + .disable = imx8qxp_gpr_disable, + .use_reg_csr = true, +}; + static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = { /* RAW (Bayer and greyscale) formats. */ { @@ -371,14 +492,9 @@ static int imx8mq_mipi_csi_start_stream(struct csi_state *state, if (ret) return ret; - regmap_update_bits(state->phy_gpr, - state->phy_gpr_reg, - 0x3fff, - GPR_CSI2_1_RX_ENABLE | - GPR_CSI2_1_VID_INTFC_ENB | - GPR_CSI2_1_HSEL | - GPR_CSI2_1_CONT_CLK_MODE | - GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle)); + ret = state->pdata->enable(state, hs_settle); + if (ret) + return ret; return 0; } @@ -386,6 +502,9 @@ static int imx8mq_mipi_csi_start_stream(struct csi_state *state, static void imx8mq_mipi_csi_stop_stream(struct csi_state *state) { imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf); + + if (state->pdata->disable) + state->pdata->disable(state); } /* ----------------------------------------------------------------------------- @@ -837,6 +956,25 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state) return PTR_ERR(state->rst); } + if (state->pdata->use_reg_csr) { + const struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + void __iomem *base; + + base = devm_platform_ioremap_resource(to_platform_device(dev), 1); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "Missing CSR register\n"); + + state->phy_gpr = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(state->phy_gpr)) + return dev_err_probe(dev, PTR_ERR(state->phy_gpr), + "Failed to init CSI MMIO regmap\n"); + return 0; + } + ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val, ARRAY_SIZE(out_val)); if (ret) { @@ -876,6 +1014,8 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev) state->dev = dev; + state->pdata = of_device_get_match_data(dev); + ret = imx8mq_mipi_csi_parse_dt(state); if (ret < 0) { dev_err(dev, "Failed to parse device tree: %d\n", ret); @@ -953,7 +1093,8 @@ static void imx8mq_mipi_csi_remove(struct platform_device *pdev) } static const struct of_device_id imx8mq_mipi_csi_of_match[] = { - { .compatible = "fsl,imx8mq-mipi-csi2", }, + { .compatible = "fsl,imx8mq-mipi-csi2", .data = &imx8mq_data }, + { .compatible = "fsl,imx8qxp-mipi-csi2", .data = &imx8qxp_data }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index f732a76de93e..88c0ba495c32 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -849,8 +849,7 @@ static int csiphy_init(struct csiphy_device *csiphy) regs->offset = 0x1000; break; default: - WARN(1, "unknown csiphy version\n"); - return -ENODEV; + break; } return 0; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index c622efcc92ff..2de97f58f9ae 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -103,11 +103,6 @@ const struct csiphy_formats csiphy_formats_8x96 = { .formats = formats_8x96 }; -const struct csiphy_formats csiphy_formats_sc7280 = { - .nformats = ARRAY_SIZE(formats_sdm845), - .formats = formats_sdm845 -}; - const struct csiphy_formats csiphy_formats_sdm845 = { .nformats = ARRAY_SIZE(formats_sdm845), .formats = formats_sdm845 diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index ab91273303b9..895f80003c44 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -126,7 +126,6 @@ void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); extern const struct csiphy_formats csiphy_formats_8x16; extern const struct csiphy_formats csiphy_formats_8x96; -extern const struct csiphy_formats csiphy_formats_sc7280; extern const struct csiphy_formats csiphy_formats_sdm845; extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index aa021fd5e123..8d05802d1735 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -225,6 +225,21 @@ static int video_check_format(struct camss_video *video) return 0; } +static int video_prepare_streaming(struct vb2_queue *q) +{ + struct camss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + int ret; + + ret = v4l2_pipeline_pm_get(&vdev->entity); + if (ret < 0) { + dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", + ret); + } + + return ret; +} + static int video_start_streaming(struct vb2_queue *q, unsigned int count) { struct camss_video *video = vb2_get_drv_priv(q); @@ -308,13 +323,23 @@ static void video_stop_streaming(struct vb2_queue *q) video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); } +static void video_unprepare_streaming(struct vb2_queue *q) +{ + struct camss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + + v4l2_pipeline_pm_put(&vdev->entity); +} + static const struct vb2_ops msm_video_vb2_q_ops = { .queue_setup = video_queue_setup, .buf_init = video_buf_init, .buf_prepare = video_buf_prepare, .buf_queue = video_buf_queue, + .prepare_streaming = video_prepare_streaming, .start_streaming = video_start_streaming, .stop_streaming = video_stop_streaming, + .unprepare_streaming = video_unprepare_streaming, }; /* ----------------------------------------------------------------------------- @@ -599,20 +624,10 @@ static int video_open(struct file *file) file->private_data = vfh; - ret = v4l2_pipeline_pm_get(&vdev->entity); - if (ret < 0) { - dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", - ret); - goto error_pm_use; - } - mutex_unlock(&video->lock); return 0; -error_pm_use: - v4l2_fh_release(file); - error_alloc: mutex_unlock(&video->lock); @@ -621,12 +636,8 @@ error_alloc: static int video_release(struct file *file) { - struct video_device *vdev = video_devdata(file); - vb2_fop_release(file); - v4l2_pipeline_pm_put(&vdev->entity); - file->private_data = NULL; return 0; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 06f42875702f..e08e70b93824 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1481,7 +1481,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .csiphy = { .id = 0, .hw_ops = &csiphy_ops_3ph_1_0, - .formats = &csiphy_formats_sc7280 + .formats = &csiphy_formats_sdm845, } }, /* CSIPHY1 */ @@ -1496,7 +1496,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .csiphy = { .id = 1, .hw_ops = &csiphy_ops_3ph_1_0, - .formats = &csiphy_formats_sc7280 + .formats = &csiphy_formats_sdm845, } }, /* CSIPHY2 */ @@ -1511,7 +1511,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .csiphy = { .id = 2, .hw_ops = &csiphy_ops_3ph_1_0, - .formats = &csiphy_formats_sc7280 + .formats = &csiphy_formats_sdm845, } }, /* CSIPHY3 */ @@ -1526,7 +1526,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .csiphy = { .id = 3, .hw_ops = &csiphy_ops_3ph_1_0, - .formats = &csiphy_formats_sc7280 + .formats = &csiphy_formats_sdm845, } }, /* CSIPHY4 */ @@ -1541,7 +1541,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { .csiphy = { .id = 4, .hw_ops = &csiphy_ops_3ph_1_0, - .formats = &csiphy_formats_sc7280 + .formats = &csiphy_formats_sdm845, } }, }; @@ -2486,8 +2486,8 @@ static const struct resources_icc icc_res_sm8550[] = { static const struct camss_subdev_resources csiphy_res_x1e80100[] = { /* CSIPHY0 */ { - .regulators = { "vdd-csiphy-0p8-supply", - "vdd-csiphy-1p2-supply" }, + .regulators = { "vdd-csiphy-0p8", + "vdd-csiphy-1p2" }, .clock = { "csiphy0", "csiphy0_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -2501,8 +2501,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY1 */ { - .regulators = { "vdd-csiphy-0p8-supply", - "vdd-csiphy-1p2-supply" }, + .regulators = { "vdd-csiphy-0p8", + "vdd-csiphy-1p2" }, .clock = { "csiphy1", "csiphy1_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -2516,8 +2516,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY2 */ { - .regulators = { "vdd-csiphy-0p8-supply", - "vdd-csiphy-1p2-supply" }, + .regulators = { "vdd-csiphy-0p8", + "vdd-csiphy-1p2" }, .clock = { "csiphy2", "csiphy2_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -2531,8 +2531,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY4 */ { - .regulators = { "vdd-csiphy-0p8-supply", - "vdd-csiphy-1p2-supply" }, + .regulators = { "vdd-csiphy-0p8", + "vdd-csiphy-1p2" }, .clock = { "csiphy4", "csiphy4_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -3386,43 +3386,39 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) struct camss *camss = container_of(async, struct camss, notifier); struct v4l2_device *v4l2_dev = &camss->v4l2_dev; struct v4l2_subdev *sd; - int ret; list_for_each_entry(sd, &v4l2_dev->subdevs, list) { - if (sd->host_priv) { - struct media_entity *sensor = &sd->entity; - struct csiphy_device *csiphy = - (struct csiphy_device *) sd->host_priv; - struct media_entity *input = &csiphy->subdev.entity; - unsigned int i; - - for (i = 0; i < sensor->num_pads; i++) { - if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) - break; - } - if (i == sensor->num_pads) { - dev_err(camss->dev, - "No source pad in external entity\n"); - return -EINVAL; - } + struct csiphy_device *csiphy = sd->host_priv; + struct media_entity *input, *sensor; + unsigned int i; + int ret; - ret = media_create_pad_link(sensor, i, - input, MSM_CSIPHY_PAD_SINK, - MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); - if (ret < 0) { - camss_link_err(camss, sensor->name, - input->name, - ret); - return ret; - } + if (!csiphy) + continue; + + input = &csiphy->subdev.entity; + sensor = &sd->entity; + + for (i = 0; i < sensor->num_pads; i++) { + if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) + break; + } + if (i == sensor->num_pads) { + dev_err(camss->dev, + "No source pad in external entity\n"); + return -EINVAL; } - } - ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); - if (ret < 0) - return ret; + ret = media_create_pad_link(sensor, i, input, + MSM_CSIPHY_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + camss_link_err(camss, sensor->name, input->name, ret); + return ret; + } + } - return media_device_register(&camss->media_dev); + return v4l2_device_register_subdev_nodes(&camss->v4l2_dev); } static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { @@ -3625,7 +3621,7 @@ static int camss_probe(struct platform_device *pdev) ret = v4l2_device_register(camss->dev, &camss->v4l2_dev); if (ret < 0) { dev_err(dev, "Failed to register V4L2 device: %d\n", ret); - goto err_genpd_cleanup; + goto err_media_device_cleanup; } v4l2_async_nf_init(&camss->notifier, &camss->v4l2_dev); @@ -3646,6 +3642,12 @@ static int camss_probe(struct platform_device *pdev) if (ret < 0) goto err_register_subdevs; + ret = media_device_register(&camss->media_dev); + if (ret < 0) { + dev_err(dev, "Failed to register media device: %d\n", ret); + goto err_register_subdevs; + } + if (num_subdevs) { camss->notifier.ops = &camss_subdev_notifier_ops; @@ -3654,32 +3656,29 @@ static int camss_probe(struct platform_device *pdev) dev_err(dev, "Failed to register async subdev nodes: %d\n", ret); - goto err_register_subdevs; + goto err_media_device_unregister; } } else { ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev); if (ret < 0) { dev_err(dev, "Failed to register subdev nodes: %d\n", ret); - goto err_register_subdevs; - } - - ret = media_device_register(&camss->media_dev); - if (ret < 0) { - dev_err(dev, "Failed to register media device: %d\n", - ret); - goto err_register_subdevs; + goto err_media_device_unregister; } } return 0; +err_media_device_unregister: + media_device_unregister(&camss->media_dev); err_register_subdevs: camss_unregister_entities(camss); err_v4l2_device_unregister: v4l2_device_unregister(&camss->v4l2_dev); v4l2_async_nf_cleanup(&camss->notifier); pm_runtime_disable(dev); +err_media_device_cleanup: + media_device_cleanup(&camss->media_dev); err_genpd_cleanup: camss_genpd_cleanup(camss); diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index e5c5a564fcb8..6425e4919e3b 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -205,6 +205,9 @@ static u32 iris_bitstream_buffer_size(struct iris_inst *inst) if (num_mbs > NUM_MBS_4K) { div_factor = 4; base_res_mbs = caps->max_mbpf; + } else { + if (inst->codec == V4L2_PIX_FMT_VP9) + div_factor = 1; } /* @@ -376,7 +379,7 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf return 0; } -int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane) +static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force) { const struct iris_platform_data *platform_data = inst->core->iris_platform_data; struct iris_buffer *buf, *next; @@ -396,6 +399,14 @@ int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane) for (i = 0; i < len; i++) { buffers = &inst->buffers[internal_buf_type[i]]; list_for_each_entry_safe(buf, next, &buffers->list, list) { + /* + * during stream on, skip destroying internal(DPB) buffer + * if firmware did not return it. + * during close, destroy all buffers irrespectively. + */ + if (!force && buf->attr & BUF_ATTR_QUEUED) + continue; + ret = iris_destroy_internal_buffer(inst, buf); if (ret) return ret; @@ -405,6 +416,16 @@ int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane) return 0; } +int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane) +{ + return iris_destroy_internal_buffers(inst, plane, true); +} + +int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane) +{ + return iris_destroy_internal_buffers(inst, plane, false); +} + static int iris_release_internal_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type) { @@ -593,10 +614,13 @@ int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf) vb2 = &vbuf->vb2_buf; - if (buf->flags & V4L2_BUF_FLAG_ERROR) + if (buf->flags & V4L2_BUF_FLAG_ERROR) { state = VB2_BUF_STATE_ERROR; - else - state = VB2_BUF_STATE_DONE; + vb2_set_plane_payload(vb2, 0, 0); + vb2->timestamp = 0; + v4l2_m2m_buf_done(vbuf, state); + return 0; + } vbuf->flags |= buf->flags; @@ -615,7 +639,10 @@ int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf) v4l2_event_queue_fh(&inst->fh, &ev); v4l2_m2m_mark_stopped(m2m_ctx); } + inst->last_buffer_dequeued = true; } + + state = VB2_BUF_STATE_DONE; vb2->timestamp = buf->timestamp; v4l2_m2m_buf_done(vbuf, state); diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h index c36b6347b077..00825ad2dc3a 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.h +++ b/drivers/media/platform/qcom/iris/iris_buffer.h @@ -106,7 +106,8 @@ void iris_get_internal_buffers(struct iris_inst *inst, u32 plane); int iris_create_internal_buffers(struct iris_inst *inst, u32 plane); int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane); int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer); -int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane); +int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane); +int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane); int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst); int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst); int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf); diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index b690578256d5..9136b723c0f2 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -17,12 +17,20 @@ static inline bool iris_valid_cap_id(enum platform_inst_fw_cap_type cap_id) static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) { switch (id) { - case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: - return DEBLOCK; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - return PROFILE; + return PROFILE_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + return PROFILE_HEVC; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + return PROFILE_VP9; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - return LEVEL; + return LEVEL_H264; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + return LEVEL_HEVC; + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: + return LEVEL_VP9; + case V4L2_CID_MPEG_VIDEO_HEVC_TIER: + return TIER; default: return INST_FW_CAP_MAX; } @@ -34,12 +42,20 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return 0; switch (cap_id) { - case DEBLOCK: - return V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER; - case PROFILE: + case PROFILE_H264: return V4L2_CID_MPEG_VIDEO_H264_PROFILE; - case LEVEL: + case PROFILE_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_PROFILE; + case PROFILE_VP9: + return V4L2_CID_MPEG_VIDEO_VP9_PROFILE; + case LEVEL_H264: return V4L2_CID_MPEG_VIDEO_H264_LEVEL; + case LEVEL_HEVC: + return V4L2_CID_MPEG_VIDEO_HEVC_LEVEL; + case LEVEL_VP9: + return V4L2_CID_MPEG_VIDEO_VP9_LEVEL; + case TIER: + return V4L2_CID_MPEG_VIDEO_HEVC_TIER; default: return 0; } @@ -84,8 +100,6 @@ int iris_ctrls_init(struct iris_inst *inst) if (iris_get_v4l2_id(cap[idx].cap_id)) num_ctrls++; } - if (!num_ctrls) - return -EINVAL; /* Adding 1 to num_ctrls to include V4L2_CID_MIN_BUFFERS_FOR_CAPTURE */ @@ -163,6 +177,7 @@ void iris_session_init_caps(struct iris_core *core) core->inst_fw_caps[cap_id].value = caps[i].value; core->inst_fw_caps[cap_id].flags = caps[i].flags; core->inst_fw_caps[cap_id].hfi_id = caps[i].hfi_id; + core->inst_fw_caps[cap_id].set = caps[i].set; } } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index b2c541367fc6..9e6aadb83783 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -140,6 +140,7 @@ struct hfi_subscription_params { u32 color_info; u32 profile; u32 level; + u32 tier; }; u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 64f887d9a17d..5fc30d54af4d 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -88,16 +88,29 @@ static int iris_hfi_gen1_sys_pc_prep(struct iris_core *core) static int iris_hfi_gen1_session_open(struct iris_inst *inst) { struct hfi_session_open_pkt packet; + u32 codec = 0; int ret; if (inst->state != IRIS_INST_DEINIT) return -EALREADY; + switch (inst->codec) { + case V4L2_PIX_FMT_H264: + codec = HFI_VIDEO_CODEC_H264; + break; + case V4L2_PIX_FMT_HEVC: + codec = HFI_VIDEO_CODEC_HEVC; + break; + case V4L2_PIX_FMT_VP9: + codec = HFI_VIDEO_CODEC_VP9; + break; + } + packet.shdr.hdr.size = sizeof(struct hfi_session_open_pkt); packet.shdr.hdr.pkt_type = HFI_CMD_SYS_SESSION_INIT; packet.shdr.session_id = inst->session_id; packet.session_domain = HFI_SESSION_TYPE_DEC; - packet.session_codec = HFI_VIDEO_CODEC_H264; + packet.session_codec = codec; reinit_completion(&inst->completion); @@ -208,8 +221,10 @@ static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane) flush_pkt.flush_type = flush_type; ret = iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size); - if (!ret) + if (!ret) { + inst->flush_responses_pending++; ret = iris_wait_for_session_response(inst, true); + } } return ret; @@ -386,6 +401,8 @@ static int iris_hfi_gen1_session_drain(struct iris_inst *inst, u32 plane) ip_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER; ip_pkt.shdr.session_id = inst->session_id; ip_pkt.flags = HFI_BUFFERFLAG_EOS; + if (inst->codec == V4L2_PIX_FMT_VP9) + ip_pkt.packet_buffer = 0xdeadb000; return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size); } @@ -490,14 +507,6 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm); break; } - case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: { - struct hfi_enable *en = prop_data; - u32 *in = pdata; - - en->enable = *in; - packet->shdr.hdr.size += sizeof(u32) + sizeof(*en); - break; - } default: return -EINVAL; } @@ -546,14 +555,15 @@ static int iris_hfi_gen1_set_resolution(struct iris_inst *inst) struct hfi_framesize fs; int ret; - fs.buffer_type = HFI_BUFFER_INPUT; - fs.width = inst->fmt_src->fmt.pix_mp.width; - fs.height = inst->fmt_src->fmt.pix_mp.height; - - ret = hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs)); - if (ret) - return ret; + if (!iris_drc_pending(inst)) { + fs.buffer_type = HFI_BUFFER_INPUT; + fs.width = inst->fmt_src->fmt.pix_mp.width; + fs.height = inst->fmt_src->fmt.pix_mp.height; + ret = hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs)); + if (ret) + return ret; + } fs.buffer_type = HFI_BUFFER_OUTPUT2; fs.width = inst->fmt_dst->fmt.pix_mp.width; fs.height = inst->fmt_dst->fmt.pix_mp.height; @@ -768,8 +778,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p iris_hfi_gen1_set_bufsize}, }; - config_params = core->iris_platform_data->input_config_params; - config_params_size = core->iris_platform_data->input_config_params_size; + config_params = core->iris_platform_data->input_config_params_default; + config_params_size = core->iris_platform_data->input_config_params_default_size; if (V4L2_TYPE_IS_OUTPUT(plane)) { for (i = 0; i < config_params_size; i++) { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 9f246816a286..d4d119ca98b0 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -13,6 +13,8 @@ #define HFI_SESSION_TYPE_DEC 2 #define HFI_VIDEO_CODEC_H264 0x00000002 +#define HFI_VIDEO_CODEC_HEVC 0x00002000 +#define HFI_VIDEO_CODEC_VP9 0x00004000 #define HFI_ERR_NONE 0x0 @@ -65,7 +67,6 @@ #define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS 0x202001 -#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER 0x1200001 #define HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS 0x120300e #define HFI_PROPERTY_CONFIG_VDEC_ENTROPY 0x1204004 @@ -117,6 +118,8 @@ #define HFI_FRAME_NOTCODED 0x7f002000 #define HFI_FRAME_YUV 0x7f004000 #define HFI_UNUSED_PICT 0x10000000 +#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008 +#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000 struct hfi_pkt_hdr { u32 size; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c index b72d503dd740..8d1ce8a19a45 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c @@ -200,14 +200,14 @@ static void iris_hfi_gen1_event_seq_changed(struct iris_inst *inst, iris_hfi_gen1_read_changed_params(inst, pkt); - if (inst->state != IRIS_INST_ERROR) { - reinit_completion(&inst->flush_completion); + if (inst->state != IRIS_INST_ERROR && !(inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)) { flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt); flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH; flush_pkt.shdr.session_id = inst->session_id; flush_pkt.flush_type = HFI_FLUSH_OUTPUT; - iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size); + if (!iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size)) + inst->flush_responses_pending++; } iris_vdec_src_change(inst); @@ -348,6 +348,10 @@ static void iris_hfi_gen1_session_etb_done(struct iris_inst *inst, void *packet) struct iris_buffer *buf = NULL; bool found = false; + /* EOS buffer sent via drain won't be in v4l2 buffer list */ + if (pkt->packet_buffer == 0xdeadb000) + return; + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) { buf = to_iris_buffer(&m2m_buffer->vb); if (buf->index == pkt->input_tag) { @@ -408,7 +412,9 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet) flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH; flush_pkt.shdr.session_id = inst->session_id; flush_pkt.flush_type = HFI_FLUSH_OUTPUT; - iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size); + if (!iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size)) + inst->flush_responses_pending++; + iris_inst_sub_state_change_drain_last(inst); return; @@ -455,7 +461,12 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet) timestamp_us = timestamp_hi; timestamp_us = (timestamp_us << 32) | timestamp_lo; } else { - flags |= V4L2_BUF_FLAG_LAST; + if (pkt->stream_id == 1 && !inst->last_buffer_dequeued) { + if (iris_drc_pending(inst)) { + flags |= V4L2_BUF_FLAG_LAST; + inst->last_buffer_dequeued = true; + } + } } buf->timestamp = timestamp_us; @@ -481,6 +492,12 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet) buf->attr |= BUF_ATTR_DEQUEUED; buf->attr |= BUF_ATTR_BUFFER_DONE; + if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT) + flags |= V4L2_BUF_FLAG_ERROR; + + if (hfi_flags & HFI_BUFFERFLAG_DROP_FRAME) + flags |= V4L2_BUF_FLAG_ERROR; + buf->flags |= flags; iris_vb2_buffer_done(inst, buf); @@ -558,7 +575,6 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response const struct iris_hfi_gen1_response_pkt_info *pkt_info; struct device *dev = core->dev; struct hfi_session_pkt *pkt; - struct completion *done; struct iris_inst *inst; bool found = false; u32 i; @@ -619,9 +635,12 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response if (shdr->error_type != HFI_ERR_NONE) iris_inst_change_state(inst, IRIS_INST_ERROR); - done = pkt_info->pkt == HFI_MSG_SESSION_FLUSH ? - &inst->flush_completion : &inst->completion; - complete(done); + if (pkt_info->pkt == HFI_MSG_SESSION_FLUSH) { + if (!(--inst->flush_responses_pending)) + complete(&inst->flush_completion); + } else { + complete(&inst->completion); + } } mutex_unlock(&inst->lock); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index a908b41e2868..7ca5ae13d62b 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -178,7 +178,7 @@ static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst) sizeof(u64)); } -static int iris_hfi_gen2_set_bit_dpeth(struct iris_inst *inst) +static int iris_hfi_gen2_set_bit_depth(struct iris_inst *inst) { struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); @@ -295,7 +295,19 @@ static int iris_hfi_gen2_set_profile(struct iris_inst *inst) { struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - u32 profile = inst->fw_caps[PROFILE].value; + u32 profile = 0; + + switch (inst->codec) { + case V4L2_PIX_FMT_HEVC: + profile = inst->fw_caps[PROFILE_HEVC].value; + break; + case V4L2_PIX_FMT_VP9: + profile = inst->fw_caps[PROFILE_VP9].value; + break; + case V4L2_PIX_FMT_H264: + profile = inst->fw_caps[PROFILE_H264].value; + break; + } inst_hfi_gen2->src_subcr_params.profile = profile; @@ -312,7 +324,19 @@ static int iris_hfi_gen2_set_level(struct iris_inst *inst) { struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - u32 level = inst->fw_caps[LEVEL].value; + u32 level = 0; + + switch (inst->codec) { + case V4L2_PIX_FMT_HEVC: + level = inst->fw_caps[LEVEL_HEVC].value; + break; + case V4L2_PIX_FMT_VP9: + level = inst->fw_caps[LEVEL_VP9].value; + break; + case V4L2_PIX_FMT_H264: + level = inst->fw_caps[LEVEL_H264].value; + break; + } inst_hfi_gen2->src_subcr_params.level = level; @@ -367,18 +391,35 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst) sizeof(u64)); } +static int iris_hfi_gen2_set_tier(struct iris_inst *inst) +{ + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + u32 tier = inst->fw_caps[TIER].value; + + inst_hfi_gen2->src_subcr_params.tier = tier; + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_TIER, + HFI_HOST_FLAGS_NONE, + port, + HFI_PAYLOAD_U32_ENUM, + &tier, + sizeof(u32)); +} + static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane) { struct iris_core *core = inst->core; - u32 config_params_size, i, j; - const u32 *config_params; + u32 config_params_size = 0, i, j; + const u32 *config_params = NULL; int ret; static const struct iris_hfi_prop_type_handle prop_type_handle_arr[] = { {HFI_PROP_BITSTREAM_RESOLUTION, iris_hfi_gen2_set_bitstream_resolution }, {HFI_PROP_CROP_OFFSETS, iris_hfi_gen2_set_crop_offsets }, {HFI_PROP_CODED_FRAMES, iris_hfi_gen2_set_coded_frames }, - {HFI_PROP_LUMA_CHROMA_BIT_DEPTH, iris_hfi_gen2_set_bit_dpeth }, + {HFI_PROP_LUMA_CHROMA_BIT_DEPTH, iris_hfi_gen2_set_bit_depth }, {HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, iris_hfi_gen2_set_min_output_count }, {HFI_PROP_PIC_ORDER_CNT_TYPE, iris_hfi_gen2_set_picture_order_count }, {HFI_PROP_SIGNAL_COLOR_INFO, iris_hfi_gen2_set_colorspace }, @@ -386,11 +427,27 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p {HFI_PROP_LEVEL, iris_hfi_gen2_set_level }, {HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat }, {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline }, + {HFI_PROP_TIER, iris_hfi_gen2_set_tier }, }; if (V4L2_TYPE_IS_OUTPUT(plane)) { - config_params = core->iris_platform_data->input_config_params; - config_params_size = core->iris_platform_data->input_config_params_size; + switch (inst->codec) { + case V4L2_PIX_FMT_H264: + config_params = core->iris_platform_data->input_config_params_default; + config_params_size = + core->iris_platform_data->input_config_params_default_size; + break; + case V4L2_PIX_FMT_HEVC: + config_params = core->iris_platform_data->input_config_params_hevc; + config_params_size = + core->iris_platform_data->input_config_params_hevc_size; + break; + case V4L2_PIX_FMT_VP9: + config_params = core->iris_platform_data->input_config_params_vp9; + config_params_size = + core->iris_platform_data->input_config_params_vp9_size; + break; + } } else { config_params = core->iris_platform_data->output_config_params; config_params_size = core->iris_platform_data->output_config_params_size; @@ -416,7 +473,19 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst) { struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); - u32 codec = HFI_CODEC_DECODE_AVC; + u32 codec = 0; + + switch (inst->codec) { + case V4L2_PIX_FMT_H264: + codec = HFI_CODEC_DECODE_AVC; + break; + case V4L2_PIX_FMT_HEVC: + codec = HFI_CODEC_DECODE_HEVC; + break; + case V4L2_PIX_FMT_VP9: + codec = HFI_CODEC_DECODE_VP9; + break; + } iris_hfi_gen2_packet_session_property(inst, HFI_PROP_CODEC, @@ -548,8 +617,8 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan struct hfi_subscription_params subsc_params; u32 prop_type, payload_size, payload_type; struct iris_core *core = inst->core; - const u32 *change_param; - u32 change_param_size; + const u32 *change_param = NULL; + u32 change_param_size = 0; u32 payload[32] = {0}; u32 hfi_port = 0, i; int ret; @@ -560,8 +629,23 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan return 0; } - change_param = core->iris_platform_data->input_config_params; - change_param_size = core->iris_platform_data->input_config_params_size; + switch (inst->codec) { + case V4L2_PIX_FMT_H264: + change_param = core->iris_platform_data->input_config_params_default; + change_param_size = + core->iris_platform_data->input_config_params_default_size; + break; + case V4L2_PIX_FMT_HEVC: + change_param = core->iris_platform_data->input_config_params_hevc; + change_param_size = + core->iris_platform_data->input_config_params_hevc_size; + break; + case V4L2_PIX_FMT_VP9: + change_param = core->iris_platform_data->input_config_params_vp9; + change_param_size = + core->iris_platform_data->input_config_params_vp9_size; + break; + } payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE; @@ -608,6 +692,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan payload_size = sizeof(u32); payload_type = HFI_PAYLOAD_U32; break; + case HFI_PROP_LUMA_CHROMA_BIT_DEPTH: + payload[0] = subsc_params.bit_depth; + payload_size = sizeof(u32); + payload_type = HFI_PAYLOAD_U32; + break; case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT: payload[0] = subsc_params.fw_min_count; payload_size = sizeof(u32); @@ -633,6 +722,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan payload_size = sizeof(u32); payload_type = HFI_PAYLOAD_U32; break; + case HFI_PROP_TIER: + payload[0] = subsc_params.tier; + payload_size = sizeof(u32); + payload_type = HFI_PAYLOAD_U32; + break; default: prop_type = 0; ret = -EINVAL; @@ -659,8 +753,8 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane) { struct iris_core *core = inst->core; - u32 subscribe_prop_size, i; - const u32 *subcribe_prop; + u32 subscribe_prop_size = 0, i; + const u32 *subcribe_prop = NULL; u32 payload[32] = {0}; payload[0] = HFI_MODE_PROPERTY; @@ -669,8 +763,23 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane) subscribe_prop_size = core->iris_platform_data->dec_input_prop_size; subcribe_prop = core->iris_platform_data->dec_input_prop; } else { - subscribe_prop_size = core->iris_platform_data->dec_output_prop_size; - subcribe_prop = core->iris_platform_data->dec_output_prop; + switch (inst->codec) { + case V4L2_PIX_FMT_H264: + subcribe_prop = core->iris_platform_data->dec_output_prop_avc; + subscribe_prop_size = + core->iris_platform_data->dec_output_prop_avc_size; + break; + case V4L2_PIX_FMT_HEVC: + subcribe_prop = core->iris_platform_data->dec_output_prop_hevc; + subscribe_prop_size = + core->iris_platform_data->dec_output_prop_hevc_size; + break; + case V4L2_PIX_FMT_VP9: + subcribe_prop = core->iris_platform_data->dec_output_prop_vp9; + subscribe_prop_size = + core->iris_platform_data->dec_output_prop_vp9_size; + break; + } } for (i = 0; i < subscribe_prop_size; i++) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index 806f8bb7f505..5f13dc11bea5 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -46,6 +46,7 @@ #define HFI_PROP_CROP_OFFSETS 0x03000105 #define HFI_PROP_PROFILE 0x03000107 #define HFI_PROP_LEVEL 0x03000108 +#define HFI_PROP_TIER 0x03000109 #define HFI_PROP_STAGE 0x0300010a #define HFI_PROP_PIPE 0x0300010b #define HFI_PROP_LUMA_CHROMA_BIT_DEPTH 0x0300010f @@ -104,6 +105,9 @@ enum hfi_color_format { enum hfi_codec_type { HFI_CODEC_DECODE_AVC = 1, HFI_CODEC_ENCODE_AVC = 2, + HFI_CODEC_DECODE_HEVC = 3, + HFI_CODEC_ENCODE_HEVC = 4, + HFI_CODEC_DECODE_VP9 = 5, }; enum hfi_picture_type { @@ -113,6 +117,7 @@ enum hfi_picture_type { HFI_PICTURE_I = 0x00000008, HFI_PICTURE_CRA = 0x00000010, HFI_PICTURE_BLA = 0x00000020, + HFI_PICTURE_NOSHOW = 0x00000040, }; enum hfi_buffer_type { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index b75a01641d5d..a8c30fc5c0d0 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -91,7 +91,9 @@ static int iris_hfi_gen2_get_driver_buffer_flags(struct iris_inst *inst, u32 hfi struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); u32 driver_flags = 0; - if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe) + if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_NOSHOW) + driver_flags |= V4L2_BUF_FLAG_ERROR; + else if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe) driver_flags |= V4L2_BUF_FLAG_KEYFRAME; else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_P) driver_flags |= V4L2_BUF_FLAG_PFRAME; @@ -265,7 +267,8 @@ static int iris_hfi_gen2_handle_system_error(struct iris_core *core, { struct iris_inst *instance; - dev_err(core->dev, "received system error of type %#x\n", pkt->type); + if (pkt) + dev_err(core->dev, "received system error of type %#x\n", pkt->type); core->state = IRIS_CORE_ERROR; @@ -377,6 +380,11 @@ static int iris_hfi_gen2_handle_output_buffer(struct iris_inst *inst, buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, hfi_buffer->flags); + if (!buf->data_size && inst->state == IRIS_INST_STREAMING && + !(hfi_buffer->flags & HFI_BUF_FW_FLAG_LAST)) { + buf->flags |= V4L2_BUF_FLAG_ERROR; + } + return 0; } @@ -563,9 +571,23 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) inst->crop.width = pixmp_ip->width - ((subsc_params.crop_offsets[1] >> 16) & 0xFFFF) - inst->crop.left; - inst->fw_caps[PROFILE].value = subsc_params.profile; - inst->fw_caps[LEVEL].value = subsc_params.level; + switch (inst->codec) { + case V4L2_PIX_FMT_HEVC: + inst->fw_caps[PROFILE_HEVC].value = subsc_params.profile; + inst->fw_caps[LEVEL_HEVC].value = subsc_params.level; + break; + case V4L2_PIX_FMT_VP9: + inst->fw_caps[PROFILE_VP9].value = subsc_params.profile; + inst->fw_caps[LEVEL_VP9].value = subsc_params.level; + break; + case V4L2_PIX_FMT_H264: + inst->fw_caps[PROFILE_H264].value = subsc_params.profile; + inst->fw_caps[LEVEL_H264].value = subsc_params.level; + break; + } + inst->fw_caps[POC].value = subsc_params.pic_order_cnt; + inst->fw_caps[TIER].value = subsc_params.tier; if (subsc_params.bit_depth != BIT_DEPTH_8 || !(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) { @@ -636,9 +658,6 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst, { struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); - if (pkt->port != HFI_PORT_BITSTREAM) - return 0; - if (pkt->flags & HFI_FW_FLAGS_INFORMATION) return 0; @@ -650,6 +669,9 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst, inst_hfi_gen2->src_subcr_params.crop_offsets[0] = pkt->payload[0]; inst_hfi_gen2->src_subcr_params.crop_offsets[1] = pkt->payload[1]; break; + case HFI_PROP_LUMA_CHROMA_BIT_DEPTH: + inst_hfi_gen2->src_subcr_params.bit_depth = pkt->payload[0]; + break; case HFI_PROP_CODED_FRAMES: inst_hfi_gen2->src_subcr_params.coded_frames = pkt->payload[0]; break; @@ -668,6 +690,9 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst, case HFI_PROP_LEVEL: inst_hfi_gen2->src_subcr_params.level = pkt->payload[0]; break; + case HFI_PROP_TIER: + inst_hfi_gen2->src_subcr_params.tier = pkt->payload[0]; + break; case HFI_PROP_PICTURE_TYPE: inst_hfi_gen2->hfi_frame_info.picture_type = pkt->payload[0]; break; @@ -791,8 +816,21 @@ static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst) full_range, video_format, video_signal_type_present_flag); - subsc_params->profile = inst->fw_caps[PROFILE].value; - subsc_params->level = inst->fw_caps[LEVEL].value; + switch (inst->codec) { + case V4L2_PIX_FMT_HEVC: + subsc_params->profile = inst->fw_caps[PROFILE_HEVC].value; + subsc_params->level = inst->fw_caps[LEVEL_HEVC].value; + break; + case V4L2_PIX_FMT_VP9: + subsc_params->profile = inst->fw_caps[PROFILE_VP9].value; + subsc_params->level = inst->fw_caps[LEVEL_VP9].value; + break; + case V4L2_PIX_FMT_H264: + subsc_params->profile = inst->fw_caps[PROFILE_H264].value; + subsc_params->level = inst->fw_caps[LEVEL_H264].value; + break; + } + subsc_params->pic_order_cnt = inst->fw_caps[POC].value; subsc_params->bit_depth = inst->fw_caps[BIT_DEPTH].value; if (inst->fw_caps[CODED_FRAMES].value == diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c b/drivers/media/platform/qcom/iris/iris_hfi_queue.c index fac7df0c4d1a..221dcd09e1e1 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_queue.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c @@ -113,7 +113,7 @@ int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_s { struct iris_iface_q_info *q_info = &core->command_queue; - if (core->state == IRIS_CORE_ERROR) + if (core->state == IRIS_CORE_ERROR || core->state == IRIS_CORE_DEINIT) return -EINVAL; if (!iris_hfi_queue_write(q_info, pkt, pkt_size)) { diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index caa3c6507006..0e1f5799b72d 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -27,6 +27,7 @@ * @crop: structure of crop info * @completion: structure of signal completions * @flush_completion: structure of signal completions for flush cmd + * @flush_responses_pending: counter to track number of pending flush responses * @fw_caps: array of supported instance firmware capabilities * @buffers: array of different iris buffers * @fw_min_count: minimnum count of buffers needed by fw @@ -42,6 +43,8 @@ * @sequence_out: a sequence counter for output queue * @tss: timestamp metadata * @metadata_idx: index for metadata buffer + * @codec: codec type + * @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver */ struct iris_inst { @@ -57,6 +60,7 @@ struct iris_inst { struct iris_hfi_rect_desc crop; struct completion completion; struct completion flush_completion; + u32 flush_responses_pending; struct platform_inst_fw_cap fw_caps[INST_FW_CAP_MAX]; struct iris_buffers buffers[BUF_TYPE_MAX]; u32 fw_min_count; @@ -72,6 +76,8 @@ struct iris_inst { u32 sequence_out; struct iris_ts_metadata tss[VIDEO_MAX_FRAME]; u32 metadata_idx; + u32 codec; + bool last_buffer_dequeued; }; #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index ac76d9e1ef9c..adafdce8a856 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -21,6 +21,7 @@ struct iris_inst; #define DEFAULT_MAX_HOST_BUF_COUNT 64 #define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256 #define DEFAULT_FPS 30 +#define NUM_MBS_8K ((8192 * 4352) / 256) enum stage_type { STAGE_1 = 1, @@ -80,8 +81,12 @@ struct platform_inst_caps { }; enum platform_inst_fw_cap_type { - PROFILE = 1, - LEVEL, + PROFILE_H264 = 1, + PROFILE_HEVC, + PROFILE_VP9, + LEVEL_H264, + LEVEL_HEVC, + LEVEL_VP9, INPUT_BUF_HOST_MAX_COUNT, STAGE, PIPE, @@ -89,7 +94,7 @@ enum platform_inst_fw_cap_type { CODED_FRAMES, BIT_DEPTH, RAP_FRAME, - DEBLOCK, + TIER, INST_FW_CAP_MAX, }; @@ -172,15 +177,24 @@ struct iris_platform_data { struct ubwc_config_data *ubwc_config; u32 num_vpp_pipe; u32 max_session_count; + /* max number of macroblocks per frame supported */ u32 max_core_mbpf; - const u32 *input_config_params; - unsigned int input_config_params_size; + const u32 *input_config_params_default; + unsigned int input_config_params_default_size; + const u32 *input_config_params_hevc; + unsigned int input_config_params_hevc_size; + const u32 *input_config_params_vp9; + unsigned int input_config_params_vp9_size; const u32 *output_config_params; unsigned int output_config_params_size; const u32 *dec_input_prop; unsigned int dec_input_prop_size; - const u32 *dec_output_prop; - unsigned int dec_output_prop_size; + const u32 *dec_output_prop_avc; + unsigned int dec_output_prop_avc_size; + const u32 *dec_output_prop_hevc; + unsigned int dec_output_prop_hevc_size; + const u32 *dec_output_prop_vp9; + unsigned int dec_output_prop_vp9_size; const u32 *dec_ip_int_buf_tbl; unsigned int dec_ip_int_buf_tbl_size; const u32 *dec_op_int_buf_tbl; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index 1e69ba15db0f..d3026b2bcb70 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -17,7 +17,7 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = { { - .cap_id = PROFILE, + .cap_id = PROFILE_H264, .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | @@ -31,7 +31,29 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = { .set = iris_set_u32_enum, }, { - .cap_id = LEVEL, + .cap_id = PROFILE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), + .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = PROFILE_VP9, + .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) | + BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2), + .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL_H264, .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2, .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | @@ -60,6 +82,60 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = { .set = iris_set_u32_enum, }, { + .cap_id = LEVEL_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2), + .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL_VP9, + .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0), + .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = TIER, + .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) | + BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH), + .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, + .hfi_id = HFI_PROP_TIER, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { .cap_id = INPUT_BUF_HOST_MAX_COUNT, .min = DEFAULT_MAX_HOST_BUF_COUNT, .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, @@ -181,9 +257,10 @@ static struct tz_cp_config tz_cp_config_sm8550 = { .cp_nonpixel_size = 0x24800000, }; -static const u32 sm8550_vdec_input_config_params[] = { +static const u32 sm8550_vdec_input_config_params_default[] = { HFI_PROP_BITSTREAM_RESOLUTION, HFI_PROP_CROP_OFFSETS, + HFI_PROP_LUMA_CHROMA_BIT_DEPTH, HFI_PROP_CODED_FRAMES, HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, HFI_PROP_PIC_ORDER_CNT_TYPE, @@ -192,6 +269,26 @@ static const u32 sm8550_vdec_input_config_params[] = { HFI_PROP_SIGNAL_COLOR_INFO, }; +static const u32 sm8550_vdec_input_config_param_hevc[] = { + HFI_PROP_BITSTREAM_RESOLUTION, + HFI_PROP_CROP_OFFSETS, + HFI_PROP_LUMA_CHROMA_BIT_DEPTH, + HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, + HFI_PROP_PROFILE, + HFI_PROP_LEVEL, + HFI_PROP_TIER, + HFI_PROP_SIGNAL_COLOR_INFO, +}; + +static const u32 sm8550_vdec_input_config_param_vp9[] = { + HFI_PROP_BITSTREAM_RESOLUTION, + HFI_PROP_CROP_OFFSETS, + HFI_PROP_LUMA_CHROMA_BIT_DEPTH, + HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, + HFI_PROP_PROFILE, + HFI_PROP_LEVEL, +}; + static const u32 sm8550_vdec_output_config_params[] = { HFI_PROP_COLOR_FORMAT, HFI_PROP_LINEAR_STRIDE_SCANLINE, @@ -201,11 +298,19 @@ static const u32 sm8550_vdec_subscribe_input_properties[] = { HFI_PROP_NO_OUTPUT, }; -static const u32 sm8550_vdec_subscribe_output_properties[] = { +static const u32 sm8550_vdec_subscribe_output_properties_avc[] = { HFI_PROP_PICTURE_TYPE, HFI_PROP_CABAC_SESSION, }; +static const u32 sm8550_vdec_subscribe_output_properties_hevc[] = { + HFI_PROP_PICTURE_TYPE, +}; + +static const u32 sm8550_vdec_subscribe_output_properties_vp9[] = { + HFI_PROP_PICTURE_TYPE, +}; + static const u32 sm8550_dec_ip_int_buf_tbl[] = { BUF_BIN, BUF_COMV, @@ -248,19 +353,34 @@ struct iris_platform_data sm8550_data = { .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, - .max_core_mbpf = ((8192 * 4352) / 256) * 2, - .input_config_params = - sm8550_vdec_input_config_params, - .input_config_params_size = - ARRAY_SIZE(sm8550_vdec_input_config_params), + .max_core_mbpf = NUM_MBS_8K * 2, + .input_config_params_default = + sm8550_vdec_input_config_params_default, + .input_config_params_default_size = + ARRAY_SIZE(sm8550_vdec_input_config_params_default), + .input_config_params_hevc = + sm8550_vdec_input_config_param_hevc, + .input_config_params_hevc_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), + .input_config_params_vp9 = + sm8550_vdec_input_config_param_vp9, + .input_config_params_vp9_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), .output_config_params = sm8550_vdec_output_config_params, .output_config_params_size = ARRAY_SIZE(sm8550_vdec_output_config_params), .dec_input_prop = sm8550_vdec_subscribe_input_properties, .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop = sm8550_vdec_subscribe_output_properties, - .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties), + .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, + .dec_output_prop_avc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), + .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, + .dec_output_prop_hevc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), + .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, + .dec_output_prop_vp9_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), @@ -308,19 +428,34 @@ struct iris_platform_data sm8650_data = { .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, - .max_core_mbpf = ((8192 * 4352) / 256) * 2, - .input_config_params = - sm8550_vdec_input_config_params, - .input_config_params_size = - ARRAY_SIZE(sm8550_vdec_input_config_params), + .max_core_mbpf = NUM_MBS_8K * 2, + .input_config_params_default = + sm8550_vdec_input_config_params_default, + .input_config_params_default_size = + ARRAY_SIZE(sm8550_vdec_input_config_params_default), + .input_config_params_hevc = + sm8550_vdec_input_config_param_hevc, + .input_config_params_hevc_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), + .input_config_params_vp9 = + sm8550_vdec_input_config_param_vp9, + .input_config_params_vp9_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), .output_config_params = sm8550_vdec_output_config_params, .output_config_params_size = ARRAY_SIZE(sm8550_vdec_output_config_params), .dec_input_prop = sm8550_vdec_subscribe_input_properties, .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop = sm8550_vdec_subscribe_output_properties, - .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties), + .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, + .dec_output_prop_avc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), + .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, + .dec_output_prop_hevc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), + .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, + .dec_output_prop_vp9_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), @@ -365,18 +500,33 @@ struct iris_platform_data qcs8300_data = { .num_vpp_pipe = 2, .max_session_count = 16, .max_core_mbpf = ((4096 * 2176) / 256) * 4, - .input_config_params = - sm8550_vdec_input_config_params, - .input_config_params_size = - ARRAY_SIZE(sm8550_vdec_input_config_params), + .input_config_params_default = + sm8550_vdec_input_config_params_default, + .input_config_params_default_size = + ARRAY_SIZE(sm8550_vdec_input_config_params_default), + .input_config_params_hevc = + sm8550_vdec_input_config_param_hevc, + .input_config_params_hevc_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), + .input_config_params_vp9 = + sm8550_vdec_input_config_param_vp9, + .input_config_params_vp9_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), .output_config_params = sm8550_vdec_output_config_params, .output_config_params_size = ARRAY_SIZE(sm8550_vdec_output_config_params), .dec_input_prop = sm8550_vdec_subscribe_input_properties, .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), - .dec_output_prop = sm8550_vdec_subscribe_output_properties, - .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties), + .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, + .dec_output_prop_avc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), + .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, + .dec_output_prop_hevc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), + .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, + .dec_output_prop_vp9_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h index f82355d72fcf..a8d66ed388a3 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h +++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h @@ -5,49 +5,125 @@ static struct platform_inst_fw_cap inst_fw_cap_qcs8300[] = { { - .cap_id = PROFILE, + .cap_id = PROFILE_H264, .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH), + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH), .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, .hfi_id = HFI_PROP_PROFILE, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, .set = iris_set_u32_enum, }, { - .cap_id = LEVEL, + .cap_id = PROFILE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), + .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = PROFILE_VP9, + .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) | + BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2), + .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL_H264, .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2, .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) | - BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2), + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2), .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1, .hfi_id = HFI_PROP_LEVEL, .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, .set = iris_set_u32_enum, }, { + .cap_id = LEVEL_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2), + .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL_VP9, + .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0), + .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = TIER, + .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) | + BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH), + .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, + .hfi_id = HFI_PROP_TIER, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { .cap_id = INPUT_BUF_HOST_MAX_COUNT, .min = DEFAULT_MAX_HOST_BUF_COUNT, .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c index 5c86fd7b7b6f..8d0816a67ae0 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c +++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c @@ -30,15 +30,6 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8250[] = { .hfi_id = HFI_PROPERTY_PARAM_WORK_MODE, .set = iris_set_stage, }, - { - .cap_id = DEBLOCK, - .min = 0, - .max = 1, - .step_or_mask = 1, - .value = 0, - .hfi_id = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER, - .set = iris_set_u32, - }, }; static struct platform_inst_caps platform_inst_cap_sm8250 = { @@ -136,10 +127,10 @@ struct iris_platform_data sm8250_data = { .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, - .max_core_mbpf = (8192 * 4352) / 256, - .input_config_params = + .max_core_mbpf = NUM_MBS_8K, + .input_config_params_default = sm8250_vdec_input_config_param_default, - .input_config_params_size = + .input_config_params_default_size = ARRAY_SIZE(sm8250_vdec_input_config_param_default), .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 9a7ce142f700..4e6e92357968 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -53,7 +53,7 @@ static int iris_init_power_domains(struct iris_core *core) struct dev_pm_domain_attach_data iris_opp_pd_data = { .pd_names = core->iris_platform_data->opp_pd_tbl, .num_pd_names = core->iris_platform_data->opp_pd_tbl_size, - .pd_flags = PD_FLAG_DEV_LINK_ON, + .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, }; ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl); diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c index 5976e926c83d..104e1687ad39 100644 --- a/drivers/media/platform/qcom/iris/iris_state.c +++ b/drivers/media/platform/qcom/iris/iris_state.c @@ -245,7 +245,7 @@ int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane) return iris_inst_change_sub_state(inst, 0, set_sub_state); } -static inline bool iris_drc_pending(struct iris_inst *inst) +bool iris_drc_pending(struct iris_inst *inst) { return inst->sub_state & IRIS_INST_SUB_DRC && inst->sub_state & IRIS_INST_SUB_DRC_LAST; diff --git a/drivers/media/platform/qcom/iris/iris_state.h b/drivers/media/platform/qcom/iris/iris_state.h index 78c61aac5e7e..e718386dbe04 100644 --- a/drivers/media/platform/qcom/iris/iris_state.h +++ b/drivers/media/platform/qcom/iris/iris_state.h @@ -140,5 +140,6 @@ int iris_inst_sub_state_change_drain_last(struct iris_inst *inst); int iris_inst_sub_state_change_drc_last(struct iris_inst *inst); int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane); bool iris_allow_cmd(struct iris_inst *inst, u32 cmd); +bool iris_drc_pending(struct iris_inst *inst); #endif diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c index cdf11feb590b..8b17c7c39487 100644 --- a/drivers/media/platform/qcom/iris/iris_vb2.c +++ b/drivers/media/platform/qcom/iris/iris_vb2.c @@ -259,13 +259,14 @@ int iris_vb2_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } - if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT)) - return -EINVAL; - if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT)) - return -EINVAL; - + if (!(inst->sub_state & IRIS_INST_SUB_DRC)) { + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT)) + return -EINVAL; + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT)) + return -EINVAL; + } return 0; } @@ -304,7 +305,7 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2) goto exit; } - if (V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) { + if (!inst->last_buffer_dequeued && V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) { if ((inst->sub_state & IRIS_INST_SUB_DRC && inst->sub_state & IRIS_INST_SUB_DRC_LAST) || (inst->sub_state & IRIS_INST_SUB_DRAIN && @@ -318,6 +319,7 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2) v4l2_event_queue_fh(&inst->fh, &eos); v4l2_m2m_mark_stopped(m2m_ctx); } + inst->last_buffer_dequeued = true; goto exit; } } diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 4143acedfc57..d670b51c5839 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -32,6 +32,7 @@ int iris_vdec_inst_init(struct iris_inst *inst) f->fmt.pix_mp.width = DEFAULT_WIDTH; f->fmt.pix_mp.height = DEFAULT_HEIGHT; f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264; + inst->codec = f->fmt.pix_mp.pixelformat; f->fmt.pix_mp.num_planes = 1; f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT); @@ -67,14 +68,67 @@ void iris_vdec_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } +static const struct iris_fmt iris_vdec_formats[] = { + [IRIS_FMT_H264] = { + .pixfmt = V4L2_PIX_FMT_H264, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_HEVC] = { + .pixfmt = V4L2_PIX_FMT_HEVC, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_VP9] = { + .pixfmt = V4L2_PIX_FMT_VP9, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + +static const struct iris_fmt * +find_format(struct iris_inst *inst, u32 pixfmt, u32 type) +{ + unsigned int size = ARRAY_SIZE(iris_vdec_formats); + const struct iris_fmt *fmt = iris_vdec_formats; + unsigned int i; + + for (i = 0; i < size; i++) { + if (fmt[i].pixfmt == pixfmt) + break; + } + + if (i == size || fmt[i].type != type) + return NULL; + + return &fmt[i]; +} + +static const struct iris_fmt * +find_format_by_index(struct iris_inst *inst, u32 index, u32 type) +{ + const struct iris_fmt *fmt = iris_vdec_formats; + unsigned int size = ARRAY_SIZE(iris_vdec_formats); + + if (index >= size || fmt[index].type != type) + return NULL; + + return &fmt[index]; +} + int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f) { + const struct iris_fmt *fmt; + switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - f->pixelformat = V4L2_PIX_FMT_H264; + fmt = find_format_by_index(inst, f->index, f->type); + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->pixfmt; f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (f->index) + return -EINVAL; f->pixelformat = V4L2_PIX_FMT_NV12; break; default: @@ -88,13 +142,15 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + const struct iris_fmt *fmt; struct v4l2_format *f_inst; struct vb2_queue *src_q; memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); + fmt = find_format(inst, pixmp->pixelformat, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264) { + if (!fmt) { f_inst = inst->fmt_src; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height; @@ -102,7 +158,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f) } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) { + if (!fmt) { f_inst = inst->fmt_dst; f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat; f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width; @@ -145,13 +201,14 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) switch (f->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264) + if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) return -EINVAL; fmt = inst->fmt_src; fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - - codec_align = DEFAULT_CODEC_ALIGNMENT; + fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat; + inst->codec = fmt->fmt.pix_mp.pixelformat; + codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align); fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align); fmt->fmt.pix_mp.num_planes = 1; @@ -171,6 +228,11 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) output_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; output_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; + /* Update capture format based on new ip w/h */ + output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128); + output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32); + inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT); + inst->crop.left = 0; inst->crop.top = 0; inst->crop.width = f->fmt.pix_mp.width; @@ -239,35 +301,6 @@ void iris_vdec_src_change(struct iris_inst *inst) v4l2_event_queue_fh(&inst->fh, &event); } -static int iris_vdec_get_num_queued_buffers(struct iris_inst *inst, - enum iris_buffer_type type) -{ - struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; - struct v4l2_m2m_buffer *buffer, *n; - struct iris_buffer *buf; - u32 count = 0; - - switch (type) { - case BUF_INPUT: - v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) { - buf = to_iris_buffer(&buffer->vb); - if (!(buf->attr & BUF_ATTR_QUEUED)) - continue; - count++; - } - return count; - case BUF_OUTPUT: - v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) { - buf = to_iris_buffer(&buffer->vb); - if (!(buf->attr & BUF_ATTR_QUEUED)) - continue; - count++; - } - return count; - default: - return count; - } -} static void iris_vdec_flush_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type type) @@ -316,7 +349,6 @@ int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane) { const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; enum iris_buffer_type buffer_type; - u32 count; int ret; switch (plane) { @@ -334,12 +366,6 @@ int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane) if (ret) goto error; - count = iris_vdec_get_num_queued_buffers(inst, buffer_type); - if (count) { - ret = -EINVAL; - goto error; - } - ret = iris_inst_state_change_streamoff(inst, plane); if (ret) goto error; @@ -408,7 +434,7 @@ int iris_vdec_streamon_input(struct iris_inst *inst) iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); if (ret) return ret; @@ -482,6 +508,8 @@ static int iris_vdec_process_streamon_output(struct iris_inst *inst) if (ret) return ret; + inst->last_buffer_dequeued = false; + return iris_inst_change_sub_state(inst, clear_sub_state, 0); } @@ -496,7 +524,7 @@ int iris_vdec_streamon_output(struct iris_inst *inst) iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); if (ret) return ret; diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h index b24932dc511a..cd7aab66dc7c 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.h +++ b/drivers/media/platform/qcom/iris/iris_vdec.h @@ -8,6 +8,17 @@ struct iris_inst; +enum iris_fmt_type { + IRIS_FMT_H264, + IRIS_FMT_HEVC, + IRIS_FMT_VP9, +}; + +struct iris_fmt { + u32 pixfmt; + u32 type; +}; + int iris_vdec_inst_init(struct iris_inst *inst); void iris_vdec_inst_deinit(struct iris_inst *inst); int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f); diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index ca0f4e310f77..c417e8c31f80 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -221,6 +221,33 @@ static void iris_session_close(struct iris_inst *inst) iris_wait_for_session_response(inst, false); } +static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 plane) +{ + const struct iris_platform_data *platform_data = inst->core->iris_platform_data; + struct iris_buffer *buf, *next; + struct iris_buffers *buffers; + const u32 *internal_buf_type; + u32 internal_buffer_count, i; + u32 count = 0; + + if (V4L2_TYPE_IS_OUTPUT(plane)) { + internal_buf_type = platform_data->dec_ip_int_buf_tbl; + internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size; + } else { + internal_buf_type = platform_data->dec_op_int_buf_tbl; + internal_buffer_count = platform_data->dec_op_int_buf_tbl_size; + } + + for (i = 0; i < internal_buffer_count; i++) { + buffers = &inst->buffers[internal_buf_type[i]]; + list_for_each_entry_safe(buf, next, &buffers->list, list) + count++; + if (count) + dev_err(inst->core->dev, "%d buffer of type %d not released", + count, internal_buf_type[i]); + } +} + int iris_close(struct file *filp) { struct iris_inst *inst = iris_get_inst(filp, NULL); @@ -233,8 +260,10 @@ int iris_close(struct file *filp) iris_session_close(inst); iris_inst_change_state(inst, IRIS_INST_DEINIT); iris_v4l2_fh_deinit(inst); - iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + iris_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + iris_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); iris_remove_session(inst); mutex_unlock(&inst->lock); mutex_destroy(&inst->ctx_q_lock); @@ -249,9 +278,6 @@ static int iris_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f) { struct iris_inst *inst = iris_get_inst(filp, NULL); - if (f->index) - return -EINVAL; - return iris_vdec_enum_fmt(inst, f); } diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index dce25e410d80..f92fd39fe310 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -31,6 +31,42 @@ static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_p return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes); } +static u32 size_h265d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + u32 product = frame_width * frame_height; + u32 size_yuv, size_bin_hdr, size_bin_res; + + size_yuv = (product <= BIN_BUFFER_THRESHOLD) ? + ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1); + size_bin_hdr = size_yuv * H265_CABAC_HDR_RATIO_HD_TOT; + size_bin_res = size_yuv * H265_CABAC_RES_RATIO_HD_TOT; + size_bin_hdr = ALIGN(size_bin_hdr / num_vpp_pipes, DMA_ALIGNMENT) * num_vpp_pipes; + size_bin_res = ALIGN(size_bin_res / num_vpp_pipes, DMA_ALIGNMENT) * num_vpp_pipes; + + return size_bin_hdr + size_bin_res; +} + +static u32 hfi_buffer_bin_vp9d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + u32 _size_yuv = ALIGN(frame_width, 16) * ALIGN(frame_height, 16) * 3 / 2; + u32 _size = ALIGN(((max_t(u32, _size_yuv, ((BIN_BUFFER_THRESHOLD * 3) >> 1)) * + VPX_DECODER_FRAME_BIN_HDR_BUDGET / VPX_DECODER_FRAME_BIN_DENOMINATOR * + VPX_DECODER_FRAME_CONCURENCY_LVL) / num_vpp_pipes), DMA_ALIGNMENT) + + ALIGN(((max_t(u32, _size_yuv, ((BIN_BUFFER_THRESHOLD * 3) >> 1)) * + VPX_DECODER_FRAME_BIN_RES_BUDGET / VPX_DECODER_FRAME_BIN_DENOMINATOR * + VPX_DECODER_FRAME_CONCURENCY_LVL) / num_vpp_pipes), DMA_ALIGNMENT); + + return _size * num_vpp_pipes; +} + +static u32 hfi_buffer_bin_h265d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + u32 n_aligned_w = ALIGN(frame_width, 16); + u32 n_aligned_h = ALIGN(frame_height, 16); + + return size_h265d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes); +} + static u32 hfi_buffer_comv_h264d(u32 frame_width, u32 frame_height, u32 _comv_bufcount) { u32 frame_height_in_mbs = DIV_ROUND_UP(frame_height, 16); @@ -55,6 +91,17 @@ static u32 hfi_buffer_comv_h264d(u32 frame_width, u32 frame_height, u32 _comv_bu return (size_colloc * (_comv_bufcount)) + 512; } +static u32 hfi_buffer_comv_h265d(u32 frame_width, u32 frame_height, u32 _comv_bufcount) +{ + u32 frame_height_in_mbs = (frame_height + 15) >> 4; + u32 frame_width_in_mbs = (frame_width + 15) >> 4; + u32 _size; + + _size = ALIGN(((frame_width_in_mbs * frame_height_in_mbs) << 8), 512); + + return (_size * (_comv_bufcount)) + 512; +} + static u32 size_h264d_bse_cmd_buf(u32 frame_height) { u32 height = ALIGN(frame_height, 32); @@ -63,6 +110,44 @@ static u32 size_h264d_bse_cmd_buf(u32 frame_height) SIZE_H264D_BSE_CMD_PER_BUF; } +static u32 size_h265d_bse_cmd_buf(u32 frame_width, u32 frame_height) +{ + u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) * + NUM_HW_PIC_BUF, DMA_ALIGNMENT); + _size = min_t(u32, _size, H265D_MAX_SLICE + 1); + _size = 2 * _size * SIZE_H265D_BSE_CMD_PER_BUF; + + return _size; +} + +static u32 hfi_buffer_persist_h265d(u32 rpu_enabled) +{ + return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 + + H265_NUM_FRM_INFO * H265_DISPLAY_BUF_SIZE + + H265_NUM_TILE * sizeof(u32) + + NUM_HW_PIC_BUF * SIZE_SEI_USERDATA + + rpu_enabled * NUM_HW_PIC_BUF * SIZE_DOLBY_RPU_METADATA), + DMA_ALIGNMENT); +} + +static inline +u32 hfi_iris3_vp9d_comv_size(void) +{ + return (((8192 + 63) >> 6) * ((4320 + 63) >> 6) * 8 * 8 * 2 * 8); +} + +static u32 hfi_buffer_persist_vp9d(void) +{ + return ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE, DMA_ALIGNMENT) + + ALIGN(hfi_iris3_vp9d_comv_size(), DMA_ALIGNMENT) + + ALIGN(MAX_SUPERFRAME_HEADER_LEN, DMA_ALIGNMENT) + + ALIGN(VP9_UDC_HEADER_BUF_SIZE, DMA_ALIGNMENT) + + ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE, DMA_ALIGNMENT) + + ALIGN(VP9_NUM_FRAME_INFO_BUF * VP9_FRAME_INFO_BUF_SIZE, DMA_ALIGNMENT) + + HDR10_HIST_EXTRADATA_SIZE; +} + static u32 size_h264d_vpp_cmd_buf(u32 frame_height) { u32 size, height = ALIGN(frame_height, 32); @@ -83,17 +168,45 @@ static u32 hfi_buffer_persist_h264d(void) static u32 hfi_buffer_non_comv_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) { - u32 size_bse, size_vpp, size; - - size_bse = size_h264d_bse_cmd_buf(frame_height); - size_vpp = size_h264d_vpp_cmd_buf(frame_height); - size = ALIGN(size_bse, DMA_ALIGNMENT) + + u32 size_bse = size_h264d_bse_cmd_buf(frame_height); + u32 size_vpp = size_h264d_vpp_cmd_buf(frame_height); + u32 size = ALIGN(size_bse, DMA_ALIGNMENT) + ALIGN(size_vpp, DMA_ALIGNMENT) + ALIGN(SIZE_HW_PIC(SIZE_H264D_HW_PIC_T), DMA_ALIGNMENT); return ALIGN(size, DMA_ALIGNMENT); } +static u32 size_h265d_vpp_cmd_buf(u32 frame_width, u32 frame_height) +{ + u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) * + NUM_HW_PIC_BUF, DMA_ALIGNMENT); + _size = min_t(u32, _size, H265D_MAX_SLICE + 1); + _size = ALIGN(_size, 4); + _size = 2 * _size * SIZE_H265D_VPP_CMD_PER_BUF; + if (_size > VPP_CMD_MAX_SIZE) + _size = VPP_CMD_MAX_SIZE; + + return _size; +} + +static u32 hfi_buffer_non_comv_h265d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + u32 _size_bse = size_h265d_bse_cmd_buf(frame_width, frame_height); + u32 _size_vpp = size_h265d_vpp_cmd_buf(frame_width, frame_height); + u32 _size = ALIGN(_size_bse, DMA_ALIGNMENT) + + ALIGN(_size_vpp, DMA_ALIGNMENT) + + ALIGN(NUM_HW_PIC_BUF * 20 * 22 * 4, DMA_ALIGNMENT) + + ALIGN(2 * sizeof(u16) * + (ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * + (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS), DMA_ALIGNMENT) + + ALIGN(SIZE_HW_PIC(SIZE_H265D_HW_PIC_T), DMA_ALIGNMENT) + + HDR10_HIST_EXTRADATA_SIZE; + + return ALIGN(_size, DMA_ALIGNMENT); +} + static u32 size_vpss_lb(u32 frame_width, u32 frame_height) { u32 opb_lb_wr_llb_y_buffer_size, opb_lb_wr_llb_uv_buffer_size; @@ -119,6 +232,203 @@ static u32 size_vpss_lb(u32 frame_width, u32 frame_height) opb_lb_wr_llb_y_buffer_size; } +static inline +u32 size_h265d_lb_fe_top_data(u32 frame_width, u32 frame_height) +{ + return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * + (ALIGN(frame_width, 64) + 8) * 2; +} + +static inline +u32 size_h265d_lb_fe_top_ctrl(u32 frame_width, u32 frame_height) +{ + return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * + (ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS); +} + +static inline +u32 size_h265d_lb_fe_left_ctrl(u32 frame_width, u32 frame_height) +{ + return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * + (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS); +} + +static inline +u32 size_h265d_lb_se_top_ctrl(u32 frame_width, u32 frame_height) +{ + return (LCU_MAX_SIZE_PELS / 8 * (128 / 8)) * ((frame_width + 15) >> 4); +} + +static inline +u32 size_h265d_lb_se_left_ctrl(u32 frame_width, u32 frame_height) +{ + return max_t(u32, ((frame_height + 16 - 1) / 8) * + MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE, + max_t(u32, ((frame_height + 32 - 1) / 8) * + MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE, + ((frame_height + 64 - 1) / 8) * + MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE)); +} + +static inline +u32 size_h265d_lb_pe_top_data(u32 frame_width, u32 frame_height) +{ + return MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * + (ALIGN(frame_width, LCU_MIN_SIZE_PELS) / LCU_MIN_SIZE_PELS); +} + +static inline +u32 size_h265d_lb_vsp_top(u32 frame_width, u32 frame_height) +{ + return ((frame_width + 63) >> 6) * 128; +} + +static inline +u32 size_h265d_lb_vsp_left(u32 frame_width, u32 frame_height) +{ + return ((frame_height + 63) >> 6) * 128; +} + +static inline +u32 size_h265d_lb_recon_dma_metadata_wr(u32 frame_width, u32 frame_height) +{ + return size_h264d_lb_recon_dma_metadata_wr(frame_height); +} + +static inline +u32 size_h265d_qp(u32 frame_width, u32 frame_height) +{ + return size_h264d_qp(frame_width, frame_height); +} + +static inline +u32 hfi_buffer_line_h265d(u32 frame_width, u32 frame_height, bool is_opb, u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0, _size; + + _size = ALIGN(size_h265d_lb_fe_top_data(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_h265d_lb_fe_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_h265d_lb_fe_left_ctrl(frame_width, frame_height), + DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_h265d_lb_se_left_ctrl(frame_width, frame_height), + DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_h265d_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_h265d_lb_pe_top_data(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_h265d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_h265d_lb_vsp_left(frame_width, frame_height), + DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_h265d_lb_recon_dma_metadata_wr(frame_width, frame_height), + DMA_ALIGNMENT) * 4 + + ALIGN(size_h265d_qp(frame_width, frame_height), DMA_ALIGNMENT); + if (is_opb) + vpss_lb_size = size_vpss_lb(frame_width, frame_height); + + return ALIGN((_size + vpss_lb_size), DMA_ALIGNMENT); +} + +static inline +u32 size_vpxd_lb_fe_left_ctrl(u32 frame_width, u32 frame_height) +{ + return max_t(u32, ((frame_height + 15) >> 4) * + MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE, + max_t(u32, ((frame_height + 31) >> 5) * + MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE, + ((frame_height + 63) >> 6) * + MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE)); +} + +static inline +u32 size_vpxd_lb_fe_top_ctrl(u32 frame_width, u32 frame_height) +{ + return ((ALIGN(frame_width, 64) + 8) * 10 * 2); +} + +static inline +u32 size_vpxd_lb_se_top_ctrl(u32 frame_width, u32 frame_height) +{ + return ((frame_width + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE; +} + +static inline +u32 size_vpxd_lb_se_left_ctrl(u32 frame_width, u32 frame_height) +{ + return max_t(u32, ((frame_height + 15) >> 4) * + MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE, + max_t(u32, ((frame_height + 31) >> 5) * + MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE, + ((frame_height + 63) >> 6) * + MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE)); +} + +static inline +u32 size_vpxd_lb_recon_dma_metadata_wr(u32 frame_width, u32 frame_height) +{ + return ALIGN((ALIGN(frame_height, 8) / (4 / 2)) * 64, + BUFFER_ALIGNMENT_32_BYTES); +} + +static inline __maybe_unused +u32 size_mp2d_lb_fe_top_data(u32 frame_width, u32 frame_height) +{ + return ((ALIGN(frame_width, 16) + 8) * 10 * 2); +} + +static inline +u32 size_vp9d_lb_fe_top_data(u32 frame_width, u32 frame_height) +{ + return (ALIGN(ALIGN(frame_width, 8), 64) + 8) * 10 * 2; +} + +static inline +u32 size_vp9d_lb_pe_top_data(u32 frame_width, u32 frame_height) +{ + return ((ALIGN(ALIGN(frame_width, 8), 64) >> 6) * 176); +} + +static inline +u32 size_vp9d_lb_vsp_top(u32 frame_width, u32 frame_height) +{ + return (((ALIGN(ALIGN(frame_width, 8), 64) >> 6) * 64 * 8) + 256); +} + +static inline +u32 size_vp9d_qp(u32 frame_width, u32 frame_height) +{ + return size_h264d_qp(frame_width, frame_height); +} + +static inline +u32 hfi_iris3_vp9d_lb_size(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + return ALIGN(size_vpxd_lb_fe_left_ctrl(frame_width, frame_height), DMA_ALIGNMENT) * + num_vpp_pipes + + ALIGN(size_vpxd_lb_se_left_ctrl(frame_width, frame_height), DMA_ALIGNMENT) * + num_vpp_pipes + + ALIGN(size_vp9d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_vpxd_lb_fe_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) + + 2 * ALIGN(size_vpxd_lb_recon_dma_metadata_wr(frame_width, frame_height), + DMA_ALIGNMENT) + + ALIGN(size_vpxd_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_vp9d_lb_pe_top_data(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_vp9d_lb_fe_top_data(frame_width, frame_height), DMA_ALIGNMENT) + + ALIGN(size_vp9d_qp(frame_width, frame_height), DMA_ALIGNMENT); +} + +static inline +u32 hfi_buffer_line_vp9d(u32 frame_width, u32 frame_height, u32 _yuv_bufcount_min, bool is_opb, + u32 num_vpp_pipes) +{ + u32 vpss_lb_size = 0; + u32 _lb_size; + + _lb_size = hfi_iris3_vp9d_lb_size(frame_width, frame_height, num_vpp_pipes); + + if (is_opb) + vpss_lb_size = size_vpss_lb(frame_width, frame_height); + + return _lb_size + vpss_lb_size + 4096; +} + static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height, bool is_opb, u32 num_vpp_pipes) { @@ -148,7 +458,14 @@ static u32 iris_vpu_dec_bin_size(struct iris_inst *inst) u32 height = f->fmt.pix_mp.height; u32 width = f->fmt.pix_mp.width; - return hfi_buffer_bin_h264d(width, height, num_vpp_pipes); + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_bin_h264d(width, height, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_bin_h265d(width, height, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_VP9) + return hfi_buffer_bin_vp9d(width, height, num_vpp_pipes); + + return 0; } static u32 iris_vpu_dec_comv_size(struct iris_inst *inst) @@ -158,12 +475,24 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst) u32 height = f->fmt.pix_mp.height; u32 width = f->fmt.pix_mp.width; - return hfi_buffer_comv_h264d(width, height, num_comv); + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_comv_h264d(width, height, num_comv); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_comv_h265d(width, height, num_comv); + + return 0; } static u32 iris_vpu_dec_persist_size(struct iris_inst *inst) { - return hfi_buffer_persist_h264d(); + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_persist_h264d(); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_persist_h265d(0); + else if (inst->codec == V4L2_PIX_FMT_VP9) + return hfi_buffer_persist_vp9d(); + + return 0; } static u32 iris_vpu_dec_dpb_size(struct iris_inst *inst) @@ -181,7 +510,12 @@ static u32 iris_vpu_dec_non_comv_size(struct iris_inst *inst) u32 height = f->fmt.pix_mp.height; u32 width = f->fmt.pix_mp.width; - return hfi_buffer_non_comv_h264d(width, height, num_vpp_pipes); + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_non_comv_h264d(width, height, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_non_comv_h265d(width, height, num_vpp_pipes); + + return 0; } static u32 iris_vpu_dec_line_size(struct iris_inst *inst) @@ -191,11 +525,20 @@ static u32 iris_vpu_dec_line_size(struct iris_inst *inst) u32 height = f->fmt.pix_mp.height; u32 width = f->fmt.pix_mp.width; bool is_opb = false; + u32 out_min_count = inst->buffers[BUF_OUTPUT].min_count; if (iris_split_mode_enabled(inst)) is_opb = true; - return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes); + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_line_h265d(width, height, is_opb, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_VP9) + return hfi_buffer_line_vp9d(width, height, out_min_count, is_opb, + num_vpp_pipes); + + return 0; } static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst) @@ -205,6 +548,24 @@ static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst) iris_vpu_dec_line_size(inst); } +static int output_min_count(struct iris_inst *inst) +{ + int output_min_count = 4; + + /* fw_min_count > 0 indicates reconfig event has already arrived */ + if (inst->fw_min_count) { + if (iris_split_mode_enabled(inst) && inst->codec == V4L2_PIX_FMT_VP9) + return min_t(u32, 4, inst->fw_min_count); + else + return inst->fw_min_count; + } + + if (inst->codec == V4L2_PIX_FMT_VP9) + output_min_count = 9; + + return output_min_count; +} + struct iris_vpu_buf_type_handle { enum iris_buffer_type type; u32 (*handle)(struct iris_inst *inst); @@ -238,6 +599,19 @@ int iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type) return size; } +static u32 internal_buffer_count(struct iris_inst *inst, + enum iris_buffer_type buffer_type) +{ + if (buffer_type == BUF_BIN || buffer_type == BUF_LINE || + buffer_type == BUF_PERSIST) { + return 1; + } else if (buffer_type == BUF_COMV || buffer_type == BUF_NON_COMV) { + if (inst->codec == V4L2_PIX_FMT_H264 || inst->codec == V4L2_PIX_FMT_HEVC) + return 1; + } + return 0; +} + static inline int iris_vpu_dpb_count(struct iris_inst *inst) { if (iris_split_mode_enabled(inst)) { @@ -254,12 +628,13 @@ int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type case BUF_INPUT: return MIN_BUFFERS; case BUF_OUTPUT: - return inst->fw_min_count; + return output_min_count(inst); case BUF_BIN: case BUF_COMV: case BUF_NON_COMV: case BUF_LINE: case BUF_PERSIST: + return internal_buffer_count(inst, buffer_type); case BUF_SCRATCH_1: return 1; /* internal buffer count needed by firmware is 1 */ case BUF_DPB: diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h index 62af6ea6ba1f..ee95fd20b794 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h @@ -13,6 +13,10 @@ struct iris_inst; #define DMA_ALIGNMENT 256 #define NUM_HW_PIC_BUF 32 +#define LCU_MAX_SIZE_PELS 64 +#define LCU_MIN_SIZE_PELS 16 +#define HDR10_HIST_EXTRADATA_SIZE (4 * 1024) + #define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf)) #define MAX_TILE_COLUMNS 32 @@ -28,11 +32,47 @@ struct iris_inst; #define SIZE_SLIST_BUF_H264 512 #define H264_DISPLAY_BUF_SIZE 3328 #define H264_NUM_FRM_INFO 66 - -#define SIZE_SEI_USERDATA 4096 - +#define H265_NUM_TILE_COL 32 +#define H265_NUM_TILE_ROW 128 +#define H265_NUM_TILE (H265_NUM_TILE_ROW * H265_NUM_TILE_COL + 1) +#define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32)) + +#define NUM_SLIST_BUF_H265 (80 + 20) +#define SIZE_SLIST_BUF_H265 (BIT(10)) +#define H265_DISPLAY_BUF_SIZE (3072) +#define H265_NUM_FRM_INFO (48) + +#define VP9_NUM_FRAME_INFO_BUF 32 +#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4) +#define VP9_PROB_TABLE_SIZE (3840) +#define VP9_FRAME_INFO_BUF_SIZE (6144) +#define BUFFER_ALIGNMENT_32_BYTES 32 +#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, BUFFER_ALIGNMENT_32_BYTES) +#define MAX_SUPERFRAME_HEADER_LEN (34) +#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE 64 +#define MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE 64 +#define MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE (128 / 8) +#define MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE (128 / 8) +#define VP9_UDC_HEADER_BUF_SIZE (3 * 128) + +#define SIZE_SEI_USERDATA 4096 +#define SIZE_DOLBY_RPU_METADATA (41 * 1024) #define H264_CABAC_HDR_RATIO_HD_TOT 1 #define H264_CABAC_RES_RATIO_HD_TOT 3 +#define H265D_MAX_SLICE 1200 +#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T +#define H265_CABAC_HDR_RATIO_HD_TOT 2 +#define H265_CABAC_RES_RATIO_HD_TOT 2 +#define SIZE_H265D_VPP_CMD_PER_BUF (256) + +#define VPX_DECODER_FRAME_CONCURENCY_LVL (2) +#define VPX_DECODER_FRAME_BIN_HDR_BUDGET 1 +#define VPX_DECODER_FRAME_BIN_RES_BUDGET 3 +#define VPX_DECODER_FRAME_BIN_DENOMINATOR 2 + +#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO (3 / 2) + #define SIZE_H264D_HW_PIC_T (BIT(11)) #define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64 diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index d305d74bb152..4c049c694d9c 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -424,13 +424,13 @@ static int venus_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&core->work, venus_sys_error_handler); init_waitqueue_head(&core->sys_err_done); - ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "venus", core); + ret = hfi_create(core, &venus_core_ops); if (ret) goto err_core_put; - ret = hfi_create(core, &venus_core_ops); + ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "venus", core); if (ret) goto err_core_put; @@ -709,11 +709,11 @@ static const struct venus_resources msm8996_res = { }; static const struct freq_tbl msm8998_freq_table[] = { - { 1944000, 465000000 }, /* 4k UHD @ 60 (decode only) */ - { 972000, 465000000 }, /* 4k UHD @ 30 */ - { 489600, 360000000 }, /* 1080p @ 60 */ - { 244800, 186000000 }, /* 1080p @ 30 */ - { 108000, 100000000 }, /* 720p @ 30 */ + { 1728000, 533000000 }, /* 4k UHD @ 60 (decode only) */ + { 1036800, 444000000 }, /* 2k @ 120 */ + { 829440, 355200000 }, /* 4k @ 44 */ + { 489600, 269330000 },/* 4k @ 30 */ + { 108000, 200000000 }, /* 1080p @ 60 */ }; static const struct reg_val msm8998_reg_preset[] = { diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index b412e0c5515a..5b1ba1c69adb 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -28,6 +28,8 @@ #define VIDC_RESETS_NUM_MAX 2 #define VIDC_MAX_HIER_CODING_LAYER 6 +#define VENUS_MAX_FPS 240 + extern int venus_fw_debug; struct freq_tbl { diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 0a041b4db9ef..cf0d97cbc463 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -33,8 +33,9 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, struct hfi_buffer_requirements *bufreq; struct hfi_extradata_input_crop *crop; struct hfi_dpb_counts *dpb_count; + u32 ptype, rem_bytes; + u32 size_read = 0; u8 *data_ptr; - u32 ptype; inst->error = HFI_ERR_NONE; @@ -44,86 +45,118 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, break; default: inst->error = HFI_ERR_SESSION_INVALID_PARAMETER; - goto done; + inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); + return; } event.event_type = pkt->event_data1; num_properties_changed = pkt->event_data2; - if (!num_properties_changed) { - inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; - goto done; - } + if (!num_properties_changed) + goto error; data_ptr = (u8 *)&pkt->ext_event_data[0]; + rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt); + do { + if (rem_bytes < sizeof(u32)) + goto error; ptype = *((u32 *)data_ptr); + + data_ptr += sizeof(u32); + rem_bytes -= sizeof(u32); + switch (ptype) { case HFI_PROPERTY_PARAM_FRAME_SIZE: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_framesize)) + goto error; + frame_sz = (struct hfi_framesize *)data_ptr; event.width = frame_sz->width; event.height = frame_sz->height; - data_ptr += sizeof(*frame_sz); + size_read = sizeof(struct hfi_framesize); break; case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_profile_level)) + goto error; + profile_level = (struct hfi_profile_level *)data_ptr; event.profile = profile_level->profile; event.level = profile_level->level; - data_ptr += sizeof(*profile_level); + size_read = sizeof(struct hfi_profile_level); break; case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_bit_depth)) + goto error; + pixel_depth = (struct hfi_bit_depth *)data_ptr; event.bit_depth = pixel_depth->bit_depth; - data_ptr += sizeof(*pixel_depth); + size_read = sizeof(struct hfi_bit_depth); break; case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_pic_struct)) + goto error; + pic_struct = (struct hfi_pic_struct *)data_ptr; event.pic_struct = pic_struct->progressive_only; - data_ptr += sizeof(*pic_struct); + size_read = sizeof(struct hfi_pic_struct); break; case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_colour_space)) + goto error; + colour_info = (struct hfi_colour_space *)data_ptr; event.colour_space = colour_info->colour_space; - data_ptr += sizeof(*colour_info); + size_read = sizeof(struct hfi_colour_space); break; case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(u32)) + goto error; + event.entropy_mode = *(u32 *)data_ptr; - data_ptr += sizeof(u32); + size_read = sizeof(u32); break; case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_buffer_requirements)) + goto error; + bufreq = (struct hfi_buffer_requirements *)data_ptr; event.buf_count = hfi_bufreq_get_count_min(bufreq, ver); - data_ptr += sizeof(*bufreq); + size_read = sizeof(struct hfi_buffer_requirements); break; case HFI_INDEX_EXTRADATA_INPUT_CROP: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_extradata_input_crop)) + goto error; + crop = (struct hfi_extradata_input_crop *)data_ptr; event.input_crop.left = crop->left; event.input_crop.top = crop->top; event.input_crop.width = crop->width; event.input_crop.height = crop->height; - data_ptr += sizeof(*crop); + size_read = sizeof(struct hfi_extradata_input_crop); break; case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS: - data_ptr += sizeof(u32); + if (rem_bytes < sizeof(struct hfi_dpb_counts)) + goto error; + dpb_count = (struct hfi_dpb_counts *)data_ptr; event.buf_count = dpb_count->fw_min_cnt; - data_ptr += sizeof(*dpb_count); + size_read = sizeof(struct hfi_dpb_counts); break; default: + size_read = 0; break; } + data_ptr += size_read; + rem_bytes -= size_read; num_properties_changed--; } while (num_properties_changed > 0); -done: + inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); + return; + +error: + inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event); } diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index b5f2ea879950..cec7f5964d3d 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -239,6 +239,7 @@ static int venus_write_queue(struct venus_hfi_device *hdev, static int venus_read_queue(struct venus_hfi_device *hdev, struct iface_queue *queue, void *pkt, u32 *tx_req) { + struct hfi_pkt_hdr *pkt_hdr = NULL; struct hfi_queue_header *qhdr; u32 dwords, new_rd_idx; u32 rd_idx, wr_idx, type, qsize; @@ -304,6 +305,9 @@ static int venus_read_queue(struct venus_hfi_device *hdev, memcpy(pkt, rd_ptr, len); memcpy(pkt + len, queue->qmem.kva, new_rd_idx << 2); } + pkt_hdr = (struct hfi_pkt_hdr *)(pkt); + if ((pkt_hdr->size >> 2) != dwords) + return -EINVAL; } else { /* bad packet received, dropping */ new_rd_idx = qhdr->write_idx; @@ -1678,6 +1682,7 @@ void venus_hfi_destroy(struct venus_core *core) venus_interface_queues_release(hdev); mutex_destroy(&hdev->lock); kfree(hdev); + disable_irq(core->irq); core->ops = NULL; } diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 409aa9bd0b5d..8dd5a9b0d060 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -41,16 +41,14 @@ static int core_clks_get(struct venus_core *core) static int core_clks_enable(struct venus_core *core) { const struct venus_resources *res = core->res; - const struct freq_tbl *freq_tbl = core->res->freq_tbl; - unsigned int freq_tbl_size = core->res->freq_tbl_size; - unsigned long freq; + struct device *dev = core->dev; + unsigned long freq = 0; + struct dev_pm_opp *opp; unsigned int i; int ret; - if (!freq_tbl) - return -EINVAL; - - freq = freq_tbl[freq_tbl_size - 1].freq; + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + dev_pm_opp_put(opp); for (i = 0; i < res->clks_num; i++) { if (IS_V6(core)) { @@ -636,7 +634,9 @@ static int decide_core(struct venus_inst *inst) u32 min_coreid, min_load, cur_inst_load; u32 min_lp_coreid, min_lp_load, cur_inst_lp_load; struct hfi_videocores_usage_type cu; - unsigned long max_freq; + unsigned long max_freq = ULONG_MAX; + struct device *dev = core->dev; + struct dev_pm_opp *opp; int ret = 0; if (legacy_binding) { @@ -659,7 +659,8 @@ static int decide_core(struct venus_inst *inst) cur_inst_lp_load *= inst->clk_data.low_power_freq; /*TODO : divide this inst->load by work_route */ - max_freq = core->res->freq_tbl[0].freq; + opp = dev_pm_opp_find_freq_floor(dev, &max_freq); + dev_pm_opp_put(opp); min_loaded_core(inst, &min_coreid, &min_load, false); min_loaded_core(inst, &min_lp_coreid, &min_lp_load, true); @@ -949,7 +950,10 @@ static int core_resets_get(struct venus_core *core) static int core_get_v4(struct venus_core *core) { struct device *dev = core->dev; + const struct freq_tbl *freq_tbl = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; const struct venus_resources *res = core->res; + unsigned int i; int ret; ret = core_clks_get(core); @@ -986,9 +990,17 @@ static int core_get_v4(struct venus_core *core) if (core->res->opp_pmdomain) { ret = devm_pm_opp_of_add_table(dev); - if (ret && ret != -ENODEV) { - dev_err(dev, "invalid OPP table in device tree\n"); - return ret; + if (ret) { + if (ret == -ENODEV) { + for (i = 0; i < num_rows; i++) { + ret = dev_pm_opp_add(dev, freq_tbl[i].freq, 0); + if (ret) + return ret; + } + } else { + dev_err(dev, "invalid OPP table in device tree\n"); + return ret; + } } } @@ -1078,11 +1090,11 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst, static int load_scale_v4(struct venus_inst *inst) { struct venus_core *core = inst->core; - const struct freq_tbl *table = core->res->freq_tbl; - unsigned int num_rows = core->res->freq_tbl_size; struct device *dev = core->dev; unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; + unsigned long max_freq = ULONG_MAX; unsigned long filled_len = 0; + struct dev_pm_opp *opp; int i, ret = 0; for (i = 0; i < inst->num_input_bufs; i++) @@ -1108,20 +1120,18 @@ static int load_scale_v4(struct venus_inst *inst) freq = max(freq_core1, freq_core2); - if (freq > table[0].freq) { - dev_dbg(dev, VDBGL "requested clock rate: %lu scaling clock rate : %lu\n", - freq, table[0].freq); + opp = dev_pm_opp_find_freq_floor(dev, &max_freq); + dev_pm_opp_put(opp); - freq = table[0].freq; + if (freq > max_freq) { + dev_dbg(dev, VDBGL "requested clock rate: %lu scaling clock rate : %lu\n", + freq, max_freq); + freq = max_freq; goto set_freq; } - for (i = num_rows - 1 ; i >= 0; i--) { - if (freq <= table[i].freq) { - freq = table[i].freq; - break; - } - } + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + dev_pm_opp_put(opp); set_freq: diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 99ce5fd41577..29b0d6a5303d 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -481,11 +481,9 @@ static int vdec_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; do_div(us_per_frame, timeperframe->denominator); - if (!us_per_frame) - return -EINVAL; - - fps = (u64)USEC_PER_SEC; - do_div(fps, us_per_frame); + us_per_frame = clamp(us_per_frame, 1, USEC_PER_SEC); + fps = USEC_PER_SEC / (u32)us_per_frame; + fps = min(VENUS_MAX_FPS, fps); inst->fps = fps; inst->timeperframe = *timeperframe; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index c7f8e37dba9b..c0a0ccdded80 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -411,11 +411,9 @@ static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; do_div(us_per_frame, timeperframe->denominator); - if (!us_per_frame) - return -EINVAL; - - fps = (u64)USEC_PER_SEC; - do_div(fps, us_per_frame); + us_per_frame = clamp(us_per_frame, 1, USEC_PER_SEC); + fps = USEC_PER_SEC / (u32)us_per_frame; + fps = min(VENUS_MAX_FPS, fps); inst->timeperframe = *timeperframe; inst->fps = fps; diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig index 46765a2e4c4d..a9e51fd94aad 100644 --- a/drivers/media/platform/raspberrypi/pisp_be/Kconfig +++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig @@ -3,6 +3,7 @@ config VIDEO_RASPBERRYPI_PISP_BE depends on V4L_PLATFORM_DRIVERS depends on VIDEO_DEV depends on ARCH_BCM2835 || COMPILE_TEST + depends on PM select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c index 7596ae1f7de6..b30891718d8d 100644 --- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c @@ -9,9 +9,11 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/lockdep.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/slab.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/videobuf2-dma-contig.h> @@ -161,8 +163,6 @@ struct pispbe_node { struct mutex node_lock; /* vb2_queue lock */ struct mutex queue_lock; - /* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */ - spinlock_t ready_lock; struct list_head ready_queue; struct vb2_queue queue; struct v4l2_format format; @@ -190,6 +190,8 @@ struct pispbe_hw_enables { /* Records a job configuration and memory addresses. */ struct pispbe_job_descriptor { + struct list_head queue; + struct pispbe_buffer *buffers[PISPBE_NUM_NODES]; dma_addr_t hw_dma_addrs[N_HW_ADDRESSES]; struct pisp_be_tiles_config *config; struct pispbe_hw_enables hw_enables; @@ -215,8 +217,10 @@ struct pispbe_dev { unsigned int sequence; u32 streaming_map; struct pispbe_job queued_job, running_job; - spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */ + /* protects "hw_busy" flag, streaming_map and job_queue */ + spinlock_t hw_lock; bool hw_busy; /* non-zero if a job is queued or is being started */ + struct list_head job_queue; int irq; u32 hw_version; u8 done, started; @@ -368,10 +372,7 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe, ret = pispbe_get_planes_addr(addrs, buf[MAIN_INPUT_NODE], &pispbe->node[MAIN_INPUT_NODE]); if (ret <= 0) { - /* - * This shouldn't happen; pispbe_schedule_internal should insist - * on an input. - */ + /* Shouldn't happen, we have validated an input is available. */ dev_warn(pispbe->dev, "ISP-BE missing input\n"); hw_en->bayer_enables = 0; hw_en->rgb_enables = 0; @@ -443,42 +444,48 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe, * For Output0, Output1, Tdn and Stitch, a buffer only needs to be * available if the blocks are enabled in the config. * - * Needs to be called with hw_lock held. + * If all the buffers required to form a job are available, append the + * job descriptor to the job queue to be later queued to the HW. * * Returns 0 if a job has been successfully prepared, < 0 otherwise. */ -static int pispbe_prepare_job(struct pispbe_dev *pispbe, - struct pispbe_job_descriptor *job) +static int pispbe_prepare_job(struct pispbe_dev *pispbe) { + struct pispbe_job_descriptor __free(kfree) *job = NULL; struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {}; + unsigned int streaming_map; unsigned int config_index; struct pispbe_node *node; - unsigned long flags; - lockdep_assert_held(&pispbe->hw_lock); + lockdep_assert_irqs_enabled(); - memset(job, 0, sizeof(struct pispbe_job_descriptor)); + scoped_guard(spinlock_irq, &pispbe->hw_lock) { + static const u32 mask = BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE); - if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) & - pispbe->streaming_map) != - (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE))) - return -ENODEV; + if ((pispbe->streaming_map & mask) != mask) + return -ENODEV; + + /* + * Take a copy of streaming_map: nodes activated after this + * point are ignored when preparing this job. + */ + streaming_map = pispbe->streaming_map; + } + + job = kzalloc(sizeof(*job), GFP_KERNEL); + if (!job) + return -ENOMEM; node = &pispbe->node[CONFIG_NODE]; - spin_lock_irqsave(&node->ready_lock, flags); buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue, struct pispbe_buffer, ready_list); - if (buf[CONFIG_NODE]) { - list_del(&buf[CONFIG_NODE]->ready_list); - pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE]; - } - spin_unlock_irqrestore(&node->ready_lock, flags); - - /* Exit early if no config buffer has been queued. */ if (!buf[CONFIG_NODE]) return -ENODEV; + list_del(&buf[CONFIG_NODE]->ready_list); + job->buffers[CONFIG_NODE] = buf[CONFIG_NODE]; + config_index = buf[CONFIG_NODE]->vb.vb2_buf.index; job->config = &pispbe->config[config_index]; job->tiles = pispbe->config_dma_addr + @@ -498,7 +505,7 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe, continue; buf[i] = NULL; - if (!(pispbe->streaming_map & BIT(i))) + if (!(streaming_map & BIT(i))) continue; if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) && @@ -525,25 +532,28 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe, node = &pispbe->node[i]; /* Pull a buffer from each V4L2 queue to form the queued job */ - spin_lock_irqsave(&node->ready_lock, flags); buf[i] = list_first_entry_or_null(&node->ready_queue, struct pispbe_buffer, ready_list); if (buf[i]) { list_del(&buf[i]->ready_list); - pispbe->queued_job.buf[i] = buf[i]; + job->buffers[i] = buf[i]; } - spin_unlock_irqrestore(&node->ready_lock, flags); if (!buf[i] && !ignore_buffers) goto err_return_buffers; } - pispbe->queued_job.valid = true; - /* Convert buffers to DMA addresses for the hardware */ pispbe_xlate_addrs(pispbe, job, buf); + scoped_guard(spinlock_irq, &pispbe->hw_lock) { + list_add_tail(&job->queue, &pispbe->job_queue); + } + + /* Set job to NULL to avoid automatic release due to __free(). */ + job = NULL; + return 0; err_return_buffers: @@ -554,33 +564,37 @@ err_return_buffers: continue; /* Return the buffer to the ready_list queue */ - spin_lock_irqsave(&n->ready_lock, flags); list_add(&buf[i]->ready_list, &n->ready_queue); - spin_unlock_irqrestore(&n->ready_lock, flags); } - memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job)); - return -ENODEV; } static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy) { - struct pispbe_job_descriptor job; - unsigned long flags; - int ret; + struct pispbe_job_descriptor *job; - spin_lock_irqsave(&pispbe->hw_lock, flags); + scoped_guard(spinlock_irqsave, &pispbe->hw_lock) { + if (clear_hw_busy) + pispbe->hw_busy = false; - if (clear_hw_busy) - pispbe->hw_busy = false; + if (pispbe->hw_busy) + return; - if (pispbe->hw_busy) - goto unlock_and_return; + job = list_first_entry_or_null(&pispbe->job_queue, + struct pispbe_job_descriptor, + queue); + if (!job) + return; - ret = pispbe_prepare_job(pispbe, &job); - if (ret) - goto unlock_and_return; + list_del(&job->queue); + + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) + pispbe->queued_job.buf[i] = job->buffers[i]; + pispbe->queued_job.valid = true; + + pispbe->hw_busy = true; + } /* * We can kick the job off without the hw_lock, as this can @@ -588,34 +602,8 @@ static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy) * only when the following job has been queued and an interrupt * is rised. */ - pispbe->hw_busy = true; - spin_unlock_irqrestore(&pispbe->hw_lock, flags); - - if (job.config->num_tiles <= 0 || - job.config->num_tiles > PISP_BACK_END_NUM_TILES || - !((job.hw_enables.bayer_enables | job.hw_enables.rgb_enables) & - PISP_BE_BAYER_ENABLE_INPUT)) { - /* - * Bad job. We can't let it proceed as it could lock up - * the hardware, or worse! - * - * For now, just force num_tiles to 0, which causes the - * H/W to do something bizarre but survivable. It - * increments (started,done) counters by more than 1, - * but we seem to survive... - */ - dev_dbg(pispbe->dev, "Bad job: invalid number of tiles: %u\n", - job.config->num_tiles); - job.config->num_tiles = 0; - } - - pispbe_queue_job(pispbe, &job); - - return; - -unlock_and_return: - /* No job has been queued, just release the lock and return. */ - spin_unlock_irqrestore(&pispbe->hw_lock, flags); + pispbe_queue_job(pispbe, job); + kfree(job); } static void pispbe_isr_jobdone(struct pispbe_dev *pispbe, @@ -706,6 +694,13 @@ static int pisp_be_validate_config(struct pispbe_dev *pispbe, return -EIO; } + if (config->num_tiles == 0 || + config->num_tiles > PISP_BACK_END_NUM_TILES) { + dev_dbg(dev, "%s: Invalid number of tiles: %d\n", __func__, + config->num_tiles); + return -EINVAL; + } + /* Ensure output config strides and buffer sizes match the V4L2 formats. */ fmt = &pispbe->node[TDN_OUTPUT_NODE].format; if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) { @@ -860,18 +855,16 @@ static void pispbe_node_buffer_queue(struct vb2_buffer *buf) container_of(vbuf, struct pispbe_buffer, vb); struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue); struct pispbe_dev *pispbe = node->pispbe; - unsigned long flags; dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); - spin_lock_irqsave(&node->ready_lock, flags); list_add_tail(&buffer->ready_list, &node->ready_queue); - spin_unlock_irqrestore(&node->ready_lock, flags); /* * Every time we add a buffer, check if there's now some work for the hw * to do. */ - pispbe_schedule(pispbe, false); + if (!pispbe_prepare_job(pispbe)) + pispbe_schedule(pispbe, false); } static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count) @@ -879,17 +872,16 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count) struct pispbe_node *node = vb2_get_drv_priv(q); struct pispbe_dev *pispbe = node->pispbe; struct pispbe_buffer *buf, *tmp; - unsigned long flags; int ret; ret = pm_runtime_resume_and_get(pispbe->dev); if (ret < 0) goto err_return_buffers; - spin_lock_irqsave(&pispbe->hw_lock, flags); - node->pispbe->streaming_map |= BIT(node->id); - node->pispbe->sequence = 0; - spin_unlock_irqrestore(&pispbe->hw_lock, flags); + scoped_guard(spinlock_irq, &pispbe->hw_lock) { + node->pispbe->streaming_map |= BIT(node->id); + node->pispbe->sequence = 0; + } dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n", __func__, NODE_NAME(node), count); @@ -897,17 +889,16 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count) node->pispbe->streaming_map); /* Maybe we're ready to run. */ - pispbe_schedule(pispbe, false); + if (!pispbe_prepare_job(pispbe)) + pispbe_schedule(pispbe, false); return 0; err_return_buffers: - spin_lock_irqsave(&pispbe->hw_lock, flags); list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) { list_del(&buf->ready_list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } - spin_unlock_irqrestore(&pispbe->hw_lock, flags); return ret; } @@ -916,8 +907,9 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q) { struct pispbe_node *node = vb2_get_drv_priv(q); struct pispbe_dev *pispbe = node->pispbe; + struct pispbe_job_descriptor *job, *temp; struct pispbe_buffer *buf; - unsigned long flags; + LIST_HEAD(tmp_list); /* * Now this is a bit awkward. In a simple M2M device we could just wait @@ -929,11 +921,7 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q) * This may return buffers out of order. */ dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); - spin_lock_irqsave(&pispbe->hw_lock, flags); do { - unsigned long flags1; - - spin_lock_irqsave(&node->ready_lock, flags1); buf = list_first_entry_or_null(&node->ready_queue, struct pispbe_buffer, ready_list); @@ -941,15 +929,26 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q) list_del(&buf->ready_list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } - spin_unlock_irqrestore(&node->ready_lock, flags1); } while (buf); - spin_unlock_irqrestore(&pispbe->hw_lock, flags); vb2_wait_for_all_buffers(&node->queue); - spin_lock_irqsave(&pispbe->hw_lock, flags); + spin_lock_irq(&pispbe->hw_lock); pispbe->streaming_map &= ~BIT(node->id); - spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + if (pispbe->streaming_map == 0) { + /* + * If all nodes have stopped streaming release all jobs + * without holding the lock. + */ + list_splice_init(&pispbe->job_queue, &tmp_list); + } + spin_unlock_irq(&pispbe->hw_lock); + + list_for_each_entry_safe(job, temp, &tmp_list, queue) { + list_del(&job->queue); + kfree(job); + } pm_runtime_mark_last_busy(pispbe->dev); pm_runtime_put_autosuspend(pispbe->dev); @@ -1114,10 +1113,12 @@ static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node) f->fmt.pix_mp.pixelformat = fmt->fourcc; f->fmt.pix_mp.num_planes = fmt->num_planes; f->fmt.pix_mp.field = V4L2_FIELD_NONE; - f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u), - PISP_BACK_END_MIN_TILE_WIDTH); - f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u), - PISP_BACK_END_MIN_TILE_HEIGHT); + f->fmt.pix_mp.width = clamp(f->fmt.pix_mp.width, + PISP_BACK_END_MIN_TILE_WIDTH, + PISP_BACK_END_MAX_TILE_WIDTH); + f->fmt.pix_mp.height = clamp(f->fmt.pix_mp.height, + PISP_BACK_END_MIN_TILE_HEIGHT, + PISP_BACK_END_MAX_TILE_HEIGHT); /* * Fill in the actual colour space when the requested one was @@ -1407,7 +1408,6 @@ static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id) mutex_init(&node->node_lock); mutex_init(&node->queue_lock); INIT_LIST_HEAD(&node->ready_queue); - spin_lock_init(&node->ready_lock); node->format.type = node->buf_type; pispbe_node_def_fmt(node); @@ -1691,6 +1691,8 @@ static int pispbe_probe(struct platform_device *pdev) if (!pispbe) return -ENOMEM; + INIT_LIST_HEAD(&pispbe->job_queue); + dev_set_drvdata(&pdev->dev, pispbe); pispbe->dev = &pdev->dev; platform_set_drvdata(pdev, pispbe); @@ -1726,7 +1728,7 @@ static int pispbe_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(pispbe->dev); pm_runtime_enable(pispbe->dev); - ret = pispbe_runtime_resume(pispbe->dev); + ret = pm_runtime_resume_and_get(pispbe->dev); if (ret) goto pm_runtime_disable_err; @@ -1748,7 +1750,7 @@ static int pispbe_probe(struct platform_device *pdev) disable_devs_err: pispbe_destroy_devices(pispbe); pm_runtime_suspend_err: - pispbe_runtime_suspend(pispbe->dev); + pm_runtime_put(pispbe->dev); pm_runtime_disable_err: pm_runtime_dont_use_autosuspend(pispbe->dev); pm_runtime_disable(pispbe->dev); @@ -1762,7 +1764,6 @@ static void pispbe_remove(struct platform_device *pdev) pispbe_destroy_devices(pispbe); - pispbe_runtime_suspend(pispbe->dev); pm_runtime_dont_use_autosuspend(pispbe->dev); pm_runtime_disable(pispbe->dev); } diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c index fcadb2143c88..62dca76b468d 100644 --- a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c @@ -1024,9 +1024,6 @@ static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name, node->buffer_queue.type); - if (vq->max_num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->max_num_buffers; - if (*nplanes) { if (sizes[0] < size) { cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size); @@ -1998,6 +1995,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id) q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->lock; q->min_queued_buffers = 1; + q->min_reqbufs_allocation = 3; q->dev = &cfe->pdev->dev; ret = vb2_queue_init(q); diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 9979de4f6ef1..d1b31ab8b8c4 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -172,20 +172,27 @@ struct rcar_csi2; #define V4H_PPI_RW_LPDCOCAL_TWAIT_CONFIG_REG 0x21c0a #define V4H_PPI_RW_LPDCOCAL_VT_CONFIG_REG 0x21c0c #define V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG 0x21c10 +#define V4H_PPI_RW_DDLCAL_CFG_n_REG(n) (0x21c40 + ((n) * 2)) /* n = 0 - 7 */ #define V4H_PPI_RW_COMMON_CFG_REG 0x21c6c #define V4H_PPI_RW_TERMCAL_CFG_0_REG 0x21c80 #define V4H_PPI_RW_OFFSETCAL_CFG_0_REG 0x21ca0 /* V4H CORE registers */ -#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(n) (0x22040 + ((n) * 2)) /* n = 0 - 15 */ -#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(n) (0x22440 + ((n) * 2)) /* n = 0 - 15 */ -#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(n) (0x22840 + ((n) * 2)) /* n = 0 - 15 */ -#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(n) (0x22c40 + ((n) * 2)) /* n = 0 - 15 */ -#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(n) (0x23040 + ((n) * 2)) /* n = 0 - 15 */ + +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, n) (0x22040 + ((l) * 0x400) + ((n) * 2)) +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_3_REG(l, n) (0x22060 + ((l) * 0x400) + ((n) * 2)) +#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_4_REG(l, n) (0x22080 + ((l) * 0x400) + ((n) * 2)) + #define V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(n) (0x23840 + ((n) * 2)) /* n = 0 - 11 */ #define V4H_CORE_DIG_RW_COMMON_REG(n) (0x23880 + ((n) * 2)) /* n = 0 - 15 */ #define V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(n) (0x239e0 + ((n) * 2)) /* n = 0 - 3 */ -#define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c +#define V4H_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM_REG 0x23fe0 + +#define V4H_CORE_DIG_DLANE_l_RW_CFG_n_REG(l, n) (0x26000 + ((l) * 0x400) + ((n) * 2)) +#define V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, n) (0x26080 + ((l) * 0x400) + ((n) * 2)) +#define V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, n) (0x26100 + ((l) * 0x400) + ((n) * 2)) +#define V4H_CORE_DIG_DLANE_CLK_RW_LP_n_REG(n) V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(4, (n)) +#define V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(n) V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(4, (n)) /* V4H C-PHY */ #define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */ @@ -197,6 +204,7 @@ struct rcar_csi2; #define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400 #define V4H_CORE_DIG_CLANE_1_RW_LP_0_REG 0x2a480 #define V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(n) (0x2a500 + ((n) * 2)) /* n = 0 - 6 */ +#define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c #define V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG 0x2a800 #define V4H_CORE_DIG_CLANE_2_RW_LP_0_REG 0x2a880 #define V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(n) (0x2a900 + ((n) * 2)) /* n = 0 - 6 */ @@ -954,6 +962,7 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, unsigned int lanes) { + struct media_pad *remote_pad; struct v4l2_subdev *source; s64 freq; u64 mbps; @@ -962,8 +971,9 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, return -ENODEV; source = priv->remote; + remote_pad = &source->entity.pads[priv->remote_pad]; - freq = v4l2_get_link_freq(source->ctrl_handler, bpp, 2 * lanes); + freq = v4l2_get_link_freq(remote_pad, bpp, 2 * lanes); if (freq < 0) { int ret = (int)freq; @@ -975,10 +985,6 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, mbps = div_u64(freq * 2, MEGA); - /* Adjust for C-PHY, divide by 2.8. */ - if (priv->cphy) - mbps = div_u64(mbps * 5, 14); - return mbps; } @@ -1203,9 +1209,14 @@ static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match) return -ETIMEDOUT; } -static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) +static const struct rcsi2_cphy_setting * +rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int mbps) { const struct rcsi2_cphy_setting *conf; + int msps; + + /* Adjust for C-PHY symbols, divide by 2.8. */ + msps = div_u64(mbps * 5, 14); for (conf = cphy_setting_table_r8a779g0; conf->msps != 0; conf++) { if (conf->msps > msps) @@ -1214,7 +1225,7 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) if (!conf->msps) { dev_err(priv->dev, "Unsupported PHY speed for msps setting (%u Msps)", msps); - return -ERANGE; + return NULL; } /* C-PHY specific */ @@ -1246,11 +1257,11 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(2), conf->rx2); rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(2), conf->rx2); - rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(2), 0x0001); - rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(2), 0); - rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(2), 0x0001); - rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(2), 0x0001); - rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(2), 0); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 2), 0x0001); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 2), 0); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 2), 0x0001); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(3, 2), 0x0001); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(4, 2), 0); rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(0), conf->trio0); rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(0), conf->trio0); @@ -1267,30 +1278,198 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) /* Configure data line order. */ rsci2_set_line_order(priv, priv->line_orders[0], V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG, - V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9)); + V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 9)); rsci2_set_line_order(priv, priv->line_orders[1], V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, - V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(9)); + V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 9)); rsci2_set_line_order(priv, priv->line_orders[2], V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG, - V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(9)); + V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 9)); /* TODO: This registers is not documented. */ rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000); - /* Leave Shutdown mode */ - rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0)); - rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0)); + return conf; +} - /* Wait for calibration */ - if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) { - dev_err(priv->dev, "PHY calibration failed\n"); - return -ETIMEDOUT; +struct rcsi2_d_phy_setting_v4h_lut_value { + unsigned int mbps; + unsigned char cfg_1; + unsigned char cfg_5_94; + unsigned char cfg_5_30; + unsigned char lane_ctrl_2_8; + unsigned char rw_hs_rx_3_83; + unsigned char rw_hs_rx_3_20; + unsigned char rw_hs_rx_6; + unsigned char rw_hs_rx_1; +}; + +static const struct rcsi2_d_phy_setting_v4h_lut_value * +rcsi2_d_phy_setting_v4h_lut_lookup(int mbps) +{ + static const struct rcsi2_d_phy_setting_v4h_lut_value values[] = { + { 4500, 0x3f, 0x07, 0x00, 0x01, 0x02, 0x01, 0x0d, 0x10 }, + { 4000, 0x47, 0x08, 0x01, 0x01, 0x05, 0x01, 0x0f, 0x0d }, + { 3600, 0x4f, 0x09, 0x01, 0x01, 0x06, 0x01, 0x10, 0x0b }, + { 3230, 0x57, 0x0a, 0x01, 0x01, 0x06, 0x01, 0x12, 0x09 }, + { 3000, 0x47, 0x08, 0x00, 0x00, 0x03, 0x01, 0x0f, 0x0c }, + { 2700, 0x4f, 0x09, 0x01, 0x00, 0x06, 0x01, 0x10, 0x0b }, + { 2455, 0x57, 0x0a, 0x01, 0x00, 0x06, 0x01, 0x12, 0x09 }, + { 2250, 0x5f, 0x0b, 0x01, 0x00, 0x08, 0x01, 0x13, 0x08 }, + { 2077, 0x67, 0x0c, 0x01, 0x00, 0x06, 0x02, 0x15, 0x0d }, + { 1929, 0x6f, 0x0d, 0x02, 0x00, 0x06, 0x02, 0x17, 0x0d }, + { 1800, 0x77, 0x0e, 0x02, 0x00, 0x06, 0x02, 0x18, 0x0d }, + { 1688, 0x7f, 0x0f, 0x02, 0x00, 0x08, 0x02, 0x1a, 0x0d }, + { 1588, 0x87, 0x10, 0x02, 0x00, 0x08, 0x02, 0x1b, 0x0d }, + { 1500, 0x8f, 0x11, 0x03, 0x00, 0x08, 0x02, 0x1d, 0x0c }, + }; + + for (unsigned int i = 0; i < ARRAY_SIZE(values); i++) + if (mbps >= values[i].mbps) + return &values[i]; + + return NULL; +} + +static int rcsi2_d_phy_setting_v4h(struct rcar_csi2 *priv, int mbps) +{ + const struct rcsi2_d_phy_setting_v4h_lut_value *lut = + rcsi2_d_phy_setting_v4h_lut_lookup(mbps); + u16 val; + + rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(7), 0x0000); + rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(7), mbps > 1500 ? 0x0028 : 0x0068); + rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(8), 0x0050); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(0), 0x0063); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(7), 0x1132); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(1), 0x1340); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(2), 0x4b13); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(4), 0x000a); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(6), 0x800a); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(7), 0x1109); + + if (mbps > 1500) { + val = DIV_ROUND_UP(5 * mbps, 64); + rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(3), val); + } + + if (lut) { + rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(1), + lut->cfg_1, 0x00ff); + rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(5), + lut->cfg_5_94 << 4, 0x03f0); + rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(5), + lut->cfg_5_30 << 0, 0x000f); + + for (unsigned int l = 0; l < 5; l++) + rcsi2_modify16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 8), + lut->lane_ctrl_2_8 << 12, 0x1000); + } + + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, 0), 0x463c); + + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 2), 0x0000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 2), 0x0000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 2), 0x0001); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(3, 2), 0x0000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(4, 2), 0x0000); + + rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(6), 0x0009); + + val = mbps > 1500 ? 0x0800 : 0x0802; + for (unsigned int l = 0; l < 5; l++) + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 12), val); + + val = mbps > 1500 ? 0x0000 : 0x0002; + for (unsigned int l = 0; l < 5; l++) + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 13), val); + + if (mbps >= 80) { + /* 2560: 6, 1280: 5, 640: 4, 320: 3, 160: 2, 80: 1 */ + val = ilog2(mbps / 80) + 1; + rcsi2_modify16(priv, + V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 9), + val << 5, 0xe0); + } + + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(0), 0x091c); + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(7), 0x3b06); + + val = DIV_ROUND_UP(1200, mbps) + 12; + for (unsigned int l = 0; l < 4; l++) + rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 0), val << 8, 0xf0); + + val = mbps > 1500 ? 0x0004 : 0x0008; + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_CFG_n_REG(l, 1), val); + + val = mbps > 2500 ? 0x669a : mbps > 1500 ? 0xe69a : 0xe69b; + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 2), val); + + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, 0), 0x163c); + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_LP_n_REG(0), 0x163c); + + if (lut) { + for (unsigned int l = 0; l < 4; l++) + rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 1), + lut->rw_hs_rx_1, 0xff); + } + + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 3), 0x9209); + + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 4), 0x0096); + + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 5), 0x0100); + + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 6), 0x2d02); + + for (unsigned int l = 0; l < 4; l++) + rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 7), 0x1b06); + + if (lut) { + /* + * Documentation LUT have two values but document writing both + * values in a single write. + */ + for (unsigned int l = 0; l < 4; l++) + rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 3), + lut->rw_hs_rx_3_83 << 3 | lut->rw_hs_rx_3_20, 0x1ff); + + for (unsigned int l = 0; l < 4; l++) + rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 6), + lut->rw_hs_rx_6 << 8, 0xff00); } - /* C-PHY setting - analog programing*/ - rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9), conf->lane29); - rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(7), conf->lane27); + static const u16 deskew_fine[] = { + 0x0404, 0x040c, 0x0414, 0x041c, 0x0423, 0x0429, 0x0430, 0x043a, + 0x0445, 0x044a, 0x0450, 0x045a, 0x0465, 0x0469, 0x0472, 0x047a, + 0x0485, 0x0489, 0x0490, 0x049a, 0x04a4, 0x04ac, 0x04b4, 0x04bc, + 0x04c4, 0x04cc, 0x04d4, 0x04dc, 0x04e4, 0x04ec, 0x04f4, 0x04fc, + 0x0504, 0x050c, 0x0514, 0x051c, 0x0523, 0x0529, 0x0530, 0x053a, + 0x0545, 0x054a, 0x0550, 0x055a, 0x0565, 0x0569, 0x0572, 0x057a, + 0x0585, 0x0589, 0x0590, 0x059a, 0x05a4, 0x05ac, 0x05b4, 0x05bc, + 0x05c4, 0x05cc, 0x05d4, 0x05dc, 0x05e4, 0x05ec, 0x05f4, 0x05fc, + 0x0604, 0x060c, 0x0614, 0x061c, 0x0623, 0x0629, 0x0632, 0x063a, + 0x0645, 0x064a, 0x0650, 0x065a, 0x0665, 0x0669, 0x0672, 0x067a, + 0x0685, 0x0689, 0x0690, 0x069a, 0x06a4, 0x06ac, 0x06b4, 0x06bc, + 0x06c4, 0x06cc, 0x06d4, 0x06dc, 0x06e4, 0x06ec, 0x06f4, 0x06fc, + 0x0704, 0x070c, 0x0714, 0x071c, 0x0723, 0x072a, 0x0730, 0x073a, + 0x0745, 0x074a, 0x0750, 0x075a, 0x0765, 0x0769, 0x0772, 0x077a, + 0x0785, 0x0789, 0x0790, 0x079a, 0x07a4, 0x07ac, 0x07b4, 0x07bc, + 0x07c4, 0x07cc, 0x07d4, 0x07dc, 0x07e4, 0x07ec, 0x07f4, 0x07fc, + }; + + for (unsigned int i = 0; i < ARRAY_SIZE(deskew_fine); i++) { + rcsi2_write16(priv, V4H_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM_REG, + deskew_fine[i]); + } return 0; } @@ -1298,10 +1477,11 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { + const struct rcsi2_cphy_setting *cphy = NULL; const struct rcar_csi2_format *format; const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; - int msps; + int mbps; int ret; /* Use the format on the sink pad to compute the receiver config. */ @@ -1314,28 +1494,40 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, if (ret) return ret; - msps = rcsi2_calc_mbps(priv, format->bpp, lanes); - if (msps < 0) - return msps; + mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); + if (mbps < 0) + return mbps; - /* Reset LINK and PHY*/ + /* T0: Reset LINK and PHY*/ rcsi2_write(priv, V4H_CSI2_RESETN_REG, 0); rcsi2_write(priv, V4H_DPHY_RSTZ_REG, 0); rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0); - /* PHY static setting */ - rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK); + /* T1: PHY static setting */ + rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK | + V4H_PHY_EN_ENABLE_0 | V4H_PHY_EN_ENABLE_1 | + V4H_PHY_EN_ENABLE_2 | V4H_PHY_EN_ENABLE_3); rcsi2_write(priv, V4H_FLDC_REG, 0); rcsi2_write(priv, V4H_FLDD_REG, 0); rcsi2_write(priv, V4H_IDIC_REG, 0); - rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_CPHY); + rcsi2_write(priv, V4H_PHY_MODE_REG, + priv->cphy ? V4H_PHY_MODE_CPHY : V4H_PHY_MODE_DPHY); rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1); - /* Reset CSI2 */ + rcsi2_write(priv, V4M_FRXM_REG, + V4M_FRXM_FORCERXMODE_0 | V4M_FRXM_FORCERXMODE_1 | + V4M_FRXM_FORCERXMODE_2 | V4M_FRXM_FORCERXMODE_3); + rcsi2_write(priv, V4M_OVR1_REG, + V4M_OVR1_FORCERXMODE_0 | V4M_OVR1_FORCERXMODE_1 | + V4M_OVR1_FORCERXMODE_2 | V4M_OVR1_FORCERXMODE_3); + + /* T2: Reset CSI2 */ rcsi2_write(priv, V4H_CSI2_RESETN_REG, BIT(0)); /* Registers static setting through APB */ /* Common setting */ + rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(10), 0x0030); + rcsi2_write16(priv, V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(2), 0x1444); rcsi2_write16(priv, V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(0), 0x1bfd); rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_STARTUP_1_1_REG, 0x0233); rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(6), 0x0027); @@ -1350,20 +1542,71 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG, 0x0105); rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x1000); rcsi2_write16(priv, V4H_PPI_RW_COMMON_CFG_REG, 0x0003); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(0), 0x0000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(1), 0x0400); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(3), 0x41f6); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(0), 0x0000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(3), 0x43f6); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x3000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(7), 0x0000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x7000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(7), 0x0000); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(5), 0x4000); + + /* T3: PHY settings */ + if (priv->cphy) { + cphy = rcsi2_c_phy_setting_v4h(priv, mbps); + if (!cphy) + return -ERANGE; + } else { + ret = rcsi2_d_phy_setting_v4h(priv, mbps); + if (ret) + return ret; + } - /* C-PHY settings */ - ret = rcsi2_c_phy_setting_v4h(priv, msps); - if (ret) - return ret; + /* T4: Leave Shutdown mode */ + rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0)); + rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0)); + + /* T5: Wait for calibration */ + if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) { + dev_err(priv->dev, "PHY calibration failed\n"); + return -ETIMEDOUT; + } + + /* T6: Analog programming */ + if (priv->cphy) { + for (unsigned int l = 0; l < 3; l++) { + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 9), + cphy->lane29); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 7), + cphy->lane27); + } + } else { + u16 val_2_9 = mbps > 2500 ? 0x14 : mbps > 1500 ? 0x04 : 0x00; + u16 val_2_15 = mbps > 1500 ? 0x03 : 0x00; + + for (unsigned int l = 0; l < 5; l++) { + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 9), + val_2_9); + rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 15), + val_2_15); + } + } + /* T7: Wait for stop state */ rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_STOPSTATE_0 | V4H_ST_PHYST_ST_STOPSTATE_1 | - V4H_ST_PHYST_ST_STOPSTATE_2); + V4H_ST_PHYST_ST_STOPSTATE_2 | + V4H_ST_PHYST_ST_STOPSTATE_3); + + /* T8: De-assert FRXM */ + rcsi2_write(priv, V4M_FRXM_REG, 0); return 0; } -static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int data_rate) +static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int mbps) { unsigned int timeout; int ret; @@ -2213,6 +2456,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a779g0 = { .start_receiver = rcsi2_start_receiver_v4h, .use_isp = true, .support_cphy = true, + .support_dphy = true, }; static const struct rcsi2_register_layout rcsi2_registers_v4m = { diff --git a/drivers/media/platform/renesas/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c index cee9bbce4e3a..f90c86bbce6e 100644 --- a/drivers/media/platform/renesas/rcar-fcp.c +++ b/drivers/media/platform/renesas/rcar-fcp.c @@ -9,6 +9,8 @@ #include <linux/device.h> #include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/iopoll.h> #include <linux/list.h> #include <linux/module.h> #include <linux/mod_devicetable.h> @@ -19,14 +21,25 @@ #include <media/rcar-fcp.h> +#define RCAR_FCP_REG_RST 0x0010 +#define RCAR_FCP_REG_RST_SOFTRST BIT(0) +#define RCAR_FCP_REG_STA 0x0018 +#define RCAR_FCP_REG_STA_ACT BIT(0) + struct rcar_fcp_device { struct list_head list; struct device *dev; + void __iomem *base; }; static LIST_HEAD(fcp_devices); static DEFINE_MUTEX(fcp_lock); +static inline void rcar_fcp_write(struct rcar_fcp_device *fcp, u32 reg, u32 val) +{ + iowrite32(val, fcp->base + reg); +} + /* ----------------------------------------------------------------------------- * Public API */ @@ -117,6 +130,25 @@ void rcar_fcp_disable(struct rcar_fcp_device *fcp) } EXPORT_SYMBOL_GPL(rcar_fcp_disable); +int rcar_fcp_soft_reset(struct rcar_fcp_device *fcp) +{ + u32 value; + int ret; + + if (!fcp) + return 0; + + rcar_fcp_write(fcp, RCAR_FCP_REG_RST, RCAR_FCP_REG_RST_SOFTRST); + ret = readl_poll_timeout(fcp->base + RCAR_FCP_REG_STA, + value, !(value & RCAR_FCP_REG_STA_ACT), + 1, 100); + if (ret) + dev_err(fcp->dev, "Failed to soft-reset\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(rcar_fcp_soft_reset); + /* ----------------------------------------------------------------------------- * Platform Driver */ @@ -131,6 +163,10 @@ static int rcar_fcp_probe(struct platform_device *pdev) fcp->dev = &pdev->dev; + fcp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(fcp->base)) + return PTR_ERR(fcp->base); + dma_set_max_seg_size(fcp->dev, UINT_MAX); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index 846ae7989b1d..f73729f59671 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -2,14 +2,14 @@ /* * Driver for Renesas R-Car VIN * + * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se> * Copyright (C) 2016 Renesas Electronics Corp. * Copyright (C) 2011-2013 Renesas Solutions Corp. * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> * Copyright (C) 2008 Magnus Damm - * - * Based on the soc-camera rcar_vin driver */ +#include <linux/idr.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> @@ -55,6 +55,7 @@ * be only one group for all instances. */ +static DEFINE_IDA(rvin_ida); static DEFINE_MUTEX(rvin_group_lock); static struct rvin_group *rvin_group_data; @@ -65,7 +66,7 @@ static void rvin_group_cleanup(struct rvin_group *group) } static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin, - int (*link_setup)(struct rvin_dev *), + int (*link_setup)(struct rvin_group *), const struct media_device_ops *ops) { struct media_device *mdev = &group->mdev; @@ -115,27 +116,12 @@ static void rvin_group_release(struct kref *kref) } static int rvin_group_get(struct rvin_dev *vin, - int (*link_setup)(struct rvin_dev *), + int (*link_setup)(struct rvin_group *), const struct media_device_ops *ops) { struct rvin_group *group; - u32 id; int ret; - /* Make sure VIN id is present and sane */ - ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id); - if (ret) { - vin_err(vin, "%pOF: No renesas,id property found\n", - vin->dev->of_node); - return -EINVAL; - } - - if (id >= RCAR_VIN_NUM) { - vin_err(vin, "%pOF: Invalid renesas,id '%u'\n", - vin->dev->of_node, id); - return -EINVAL; - } - /* Join or create a VIN group */ mutex_lock(&rvin_group_lock); if (rvin_group_data) { @@ -156,6 +142,7 @@ static int rvin_group_get(struct rvin_dev *vin, } kref_init(&group->refcount); + group->info = vin->info; rvin_group_data = group; } @@ -164,16 +151,15 @@ static int rvin_group_get(struct rvin_dev *vin, /* Add VIN to group */ mutex_lock(&group->lock); - if (group->vin[id]) { - vin_err(vin, "Duplicate renesas,id property value %u\n", id); + if (group->vin[vin->id]) { + vin_err(vin, "Duplicate renesas,id property value %u\n", vin->id); mutex_unlock(&group->lock); kref_put(&group->refcount, rvin_group_release); return -EINVAL; } - group->vin[id] = vin; + group->vin[vin->id] = vin; - vin->id = id; vin->group = group; vin->v4l2_dev.mdev = &group->mdev; @@ -213,7 +199,7 @@ static int rvin_group_entity_to_remote_id(struct rvin_group *group, sd = media_entity_to_v4l2_subdev(entity); - for (i = 0; i < RVIN_REMOTES_MAX; i++) + for (i = 0; i < ARRAY_SIZE(group->remotes); i++) if (group->remotes[i].subdev == sd) return i; @@ -246,7 +232,7 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) } } - return vin->group->link_setup(vin); + return vin->group->link_setup(vin->group); } static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, @@ -254,20 +240,32 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_async_connection *asc) { struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); - unsigned int i; + struct rvin_group *group = vin->group; - for (i = 0; i < RCAR_VIN_NUM; i++) - if (vin->group->vin[i]) - rvin_v4l2_unregister(vin->group->vin[i]); + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { + if (group->vin[i]) + rvin_v4l2_unregister(group->vin[i]); + } mutex_lock(&vin->group->lock); - for (i = 0; i < RVIN_CSI_MAX; i++) { - if (vin->group->remotes[i].asc != asc) + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { + if (!group->vin[i] || group->vin[i]->parallel.asc != asc) continue; - vin->group->remotes[i].subdev = NULL; + + group->vin[i]->parallel.subdev = NULL; + + vin_dbg(group->vin[i], "Unbind parallel subdev %s\n", + subdev->name); + } + + for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) { + if (group->remotes[i].asc != asc) + continue; + + group->remotes[i].subdev = NULL; + vin_dbg(vin, "Unbind %s from slot %u\n", subdev->name, i); - break; } mutex_unlock(&vin->group->lock); @@ -280,21 +278,38 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_connection *asc) { struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); - unsigned int i; + struct rvin_group *group = vin->group; - mutex_lock(&vin->group->lock); + guard(mutex)(&group->lock); - for (i = 0; i < RVIN_CSI_MAX; i++) { + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { + struct rvin_dev *pvin = group->vin[i]; + + if (!pvin || pvin->parallel.asc != asc) + continue; + + pvin->parallel.source_pad = 0; + for (unsigned int pad = 0; pad < subdev->entity.num_pads; pad++) + if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE) + pvin->parallel.source_pad = pad; + + pvin->parallel.subdev = subdev; + vin_dbg(pvin, "Bound subdev %s\n", subdev->name); + + return 0; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) { if (vin->group->remotes[i].asc != asc) continue; + vin->group->remotes[i].subdev = subdev; vin_dbg(vin, "Bound %s to slot %u\n", subdev->name, i); - break; - } - mutex_unlock(&vin->group->lock); + return 0; + } - return 0; + return -ENODEV; } static const struct v4l2_async_notifier_operations rvin_group_notify_ops = { @@ -343,12 +358,49 @@ out: return ret; } -static void rvin_group_notifier_cleanup(struct rvin_dev *vin) +static int rvin_parallel_parse_of(struct rvin_dev *vin) { - if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { - v4l2_async_nf_unregister(&vin->group->notifier); - v4l2_async_nf_cleanup(&vin->group->notifier); + struct fwnode_handle *fwnode __free(fwnode_handle) = NULL; + struct fwnode_handle *ep __free(fwnode_handle) = NULL; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_UNKNOWN, + }; + struct v4l2_async_connection *asc; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0); + if (!ep) + return 0; + + if (v4l2_fwnode_endpoint_parse(ep, &vep)) { + vin_err(vin, "Failed to parse %pOF\n", to_of_node(ep)); + return -EINVAL; } + + switch (vep.bus_type) { + case V4L2_MBUS_PARALLEL: + case V4L2_MBUS_BT656: + vin_dbg(vin, "Found %s media bus\n", + vep.bus_type == V4L2_MBUS_PARALLEL ? + "PARALLEL" : "BT656"); + vin->parallel.mbus_type = vep.bus_type; + vin->parallel.bus = vep.bus.parallel; + break; + default: + vin_err(vin, "Unknown media bus type\n"); + return -EINVAL; + } + + fwnode = fwnode_graph_get_remote_endpoint(ep); + asc = v4l2_async_nf_add_fwnode(&vin->group->notifier, fwnode, + struct v4l2_async_connection); + if (IS_ERR(asc)) + return PTR_ERR(asc); + + vin->parallel.asc = asc; + + vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode)); + + return 0; } static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port, @@ -385,6 +437,12 @@ static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port, if (!(vin_mask & BIT(i))) continue; + /* Parse local subdevice. */ + ret = rvin_parallel_parse_of(vin->group->vin[i]); + if (ret) + return ret; + + /* Parse shared subdevices. */ for (id = 0; id < max_id; id++) { if (vin->group->remotes[id].asc) continue; @@ -437,11 +495,11 @@ static void rvin_free_controls(struct rvin_dev *vin) vin->vdev.ctrl_handler = NULL; } -static int rvin_create_controls(struct rvin_dev *vin, struct v4l2_subdev *subdev) +static int rvin_create_controls(struct rvin_dev *vin) { int ret; - ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); + ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 1); if (ret < 0) return ret; @@ -455,287 +513,12 @@ static int rvin_create_controls(struct rvin_dev *vin, struct v4l2_subdev *subdev return ret; } - /* For the non-MC mode add controls from the subdevice. */ - if (subdev) { - ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, - subdev->ctrl_handler, NULL, true); - if (ret < 0) { - rvin_free_controls(vin); - return ret; - } - } - vin->vdev.ctrl_handler = &vin->ctrl_handler; return 0; } /* ----------------------------------------------------------------------------- - * Async notifier - */ - -static int rvin_find_pad(struct v4l2_subdev *sd, int direction) -{ - unsigned int pad; - - if (sd->entity.num_pads <= 1) - return 0; - - for (pad = 0; pad < sd->entity.num_pads; pad++) - if (sd->entity.pads[pad].flags & direction) - return pad; - - return -EINVAL; -} - -/* ----------------------------------------------------------------------------- - * Parallel async notifier - */ - -/* The vin lock should be held when calling the subdevice attach and detach */ -static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, - struct v4l2_subdev *subdev) -{ - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - /* Find source and sink pad of remote subdevice */ - ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); - if (ret < 0) - return ret; - vin->parallel.source_pad = ret; - - ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); - vin->parallel.sink_pad = ret < 0 ? 0 : ret; - - if (vin->info->use_mc) { - vin->parallel.subdev = subdev; - return 0; - } - - /* Find compatible subdevices mbus format */ - vin->mbus_code = 0; - code.index = 0; - code.pad = vin->parallel.source_pad; - while (!vin->mbus_code && - !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { - code.index++; - switch (code.code) { - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_UYVY10_2X10: - case MEDIA_BUS_FMT_RGB888_1X24: - vin->mbus_code = code.code; - vin_dbg(vin, "Found media bus format for %s: %d\n", - subdev->name, vin->mbus_code); - break; - default: - break; - } - } - - if (!vin->mbus_code) { - vin_err(vin, "Unsupported media bus format for %s\n", - subdev->name); - return -EINVAL; - } - - /* Read tvnorms */ - ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - /* Read standard */ - vin->std = V4L2_STD_UNKNOWN; - ret = v4l2_subdev_call(subdev, video, g_std, &vin->std); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - /* Add the controls */ - ret = rvin_create_controls(vin, subdev); - if (ret < 0) - return ret; - - vin->parallel.subdev = subdev; - - return 0; -} - -static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) -{ - rvin_v4l2_unregister(vin); - vin->parallel.subdev = NULL; - - if (!vin->info->use_mc) - rvin_free_controls(vin); -} - -static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) -{ - struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); - struct media_entity *source; - struct media_entity *sink; - int ret; - - ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); - if (ret < 0) { - vin_err(vin, "Failed to register subdev nodes\n"); - return ret; - } - - if (!video_is_registered(&vin->vdev)) { - ret = rvin_v4l2_register(vin); - if (ret < 0) - return ret; - } - - if (!vin->info->use_mc) - return 0; - - /* If we're running with media-controller, link the subdevs. */ - source = &vin->parallel.subdev->entity; - sink = &vin->vdev.entity; - - ret = media_create_pad_link(source, vin->parallel.source_pad, - sink, vin->parallel.sink_pad, 0); - if (ret) - vin_err(vin, "Error adding link from %s to %s: %d\n", - source->name, sink->name, ret); - - return ret; -} - -static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asc) -{ - struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); - - vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); - - mutex_lock(&vin->lock); - rvin_parallel_subdevice_detach(vin); - mutex_unlock(&vin->lock); -} - -static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asc) -{ - struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); - int ret; - - mutex_lock(&vin->lock); - ret = rvin_parallel_subdevice_attach(vin, subdev); - mutex_unlock(&vin->lock); - if (ret) - return ret; - - v4l2_set_subdev_hostdata(subdev, vin); - - vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", - subdev->name, vin->parallel.source_pad, - vin->parallel.sink_pad); - - return 0; -} - -static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { - .bound = rvin_parallel_notify_bound, - .unbind = rvin_parallel_notify_unbind, - .complete = rvin_parallel_notify_complete, -}; - -static int rvin_parallel_parse_of(struct rvin_dev *vin) -{ - struct fwnode_handle *ep, *fwnode; - struct v4l2_fwnode_endpoint vep = { - .bus_type = V4L2_MBUS_UNKNOWN, - }; - struct v4l2_async_connection *asc; - int ret; - - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0); - if (!ep) - return 0; - - fwnode = fwnode_graph_get_remote_endpoint(ep); - ret = v4l2_fwnode_endpoint_parse(ep, &vep); - fwnode_handle_put(ep); - if (ret) { - vin_err(vin, "Failed to parse %pOF\n", to_of_node(fwnode)); - ret = -EINVAL; - goto out; - } - - switch (vep.bus_type) { - case V4L2_MBUS_PARALLEL: - case V4L2_MBUS_BT656: - vin_dbg(vin, "Found %s media bus\n", - vep.bus_type == V4L2_MBUS_PARALLEL ? - "PARALLEL" : "BT656"); - vin->parallel.mbus_type = vep.bus_type; - vin->parallel.bus = vep.bus.parallel; - break; - default: - vin_err(vin, "Unknown media bus type\n"); - ret = -EINVAL; - goto out; - } - - asc = v4l2_async_nf_add_fwnode(&vin->notifier, fwnode, - struct v4l2_async_connection); - if (IS_ERR(asc)) { - ret = PTR_ERR(asc); - goto out; - } - - vin->parallel.asc = asc; - - vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode)); -out: - fwnode_handle_put(fwnode); - - return ret; -} - -static void rvin_parallel_cleanup(struct rvin_dev *vin) -{ - v4l2_async_nf_unregister(&vin->notifier); - v4l2_async_nf_cleanup(&vin->notifier); -} - -static int rvin_parallel_init(struct rvin_dev *vin) -{ - int ret; - - v4l2_async_nf_init(&vin->notifier, &vin->v4l2_dev); - - ret = rvin_parallel_parse_of(vin); - if (ret) - return ret; - - if (!vin->parallel.asc) - return -ENODEV; - - vin_dbg(vin, "Found parallel subdevice %pOF\n", - to_of_node(vin->parallel.asc->match.fwnode)); - - vin->notifier.ops = &rvin_parallel_notify_ops; - ret = v4l2_async_nf_register(&vin->notifier); - if (ret < 0) { - vin_err(vin, "Notifier registration failed\n"); - v4l2_async_nf_cleanup(&vin->notifier); - return ret; - } - - return 0; -} - -/* ----------------------------------------------------------------------------- * CSI-2 */ @@ -909,80 +692,91 @@ static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id, return 0; } -static int rvin_csi2_setup_links(struct rvin_dev *vin) +static int rvin_parallel_setup_links(struct rvin_group *group) +{ + u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; + + guard(mutex)(&group->lock); + + /* If the group also has links don't enable the link. */ + for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) { + if (group->remotes[i].subdev) { + flags = 0; + break; + } + } + + /* Create links. */ + for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) { + struct rvin_dev *vin = group->vin[i]; + struct media_entity *source; + struct media_entity *sink; + int ret; + + /* Nothing to do if there is no VIN or parallel subdev. */ + if (!vin || !vin->parallel.subdev) + continue; + + source = &vin->parallel.subdev->entity; + sink = &vin->vdev.entity; + + ret = media_create_pad_link(source, vin->parallel.source_pad, + sink, 0, flags); + if (ret) + return ret; + } + + return 0; +} + +static int rvin_csi2_setup_links(struct rvin_group *group) { const struct rvin_group_route *route; unsigned int id; - int ret = -EINVAL; + int ret; + + ret = rvin_parallel_setup_links(group); + if (ret) + return ret; /* Create all media device links between VINs and CSI-2's. */ - mutex_lock(&vin->group->lock); - for (route = vin->info->routes; route->chsel; route++) { + mutex_lock(&group->lock); + for (route = group->info->routes; route->chsel; route++) { /* Check that VIN' master is part of the group. */ - if (!vin->group->vin[route->master]) + if (!group->vin[route->master]) continue; /* Check that CSI-2 is part of the group. */ - if (!vin->group->remotes[route->csi].subdev) + if (!group->remotes[route->csi].subdev) continue; for (id = route->master; id < route->master + 4; id++) { /* Check that VIN is part of the group. */ - if (!vin->group->vin[id]) + if (!group->vin[id]) continue; - ret = rvin_csi2_create_link(vin->group, id, route); + ret = rvin_csi2_create_link(group, id, route); if (ret) goto out; } } out: - mutex_unlock(&vin->group->lock); + mutex_unlock(&group->lock); return ret; } -static void rvin_csi2_cleanup(struct rvin_dev *vin) -{ - rvin_parallel_cleanup(vin); - rvin_group_notifier_cleanup(vin); - rvin_group_put(vin); - rvin_free_controls(vin); -} - static int rvin_csi2_init(struct rvin_dev *vin) { int ret; - vin->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); - if (ret) - return ret; - - ret = rvin_create_controls(vin, NULL); - if (ret < 0) - return ret; - ret = rvin_group_get(vin, rvin_csi2_setup_links, &rvin_csi2_media_ops); if (ret) - goto err_controls; - - /* It's OK to not have a parallel subdevice. */ - ret = rvin_parallel_init(vin); - if (ret && ret != -ENODEV) - goto err_group; + return ret; ret = rvin_group_notifier_init(vin, 1, RVIN_CSI_MAX); if (ret) - goto err_parallel; - - return 0; -err_parallel: - rvin_parallel_cleanup(vin); -err_group: - rvin_group_put(vin); -err_controls: - rvin_free_controls(vin); + rvin_group_put(vin); return ret; } @@ -991,30 +785,31 @@ err_controls: * ISP */ -static int rvin_isp_setup_links(struct rvin_dev *vin) +static int rvin_isp_setup_links(struct rvin_group *group) { unsigned int i; int ret = -EINVAL; /* Create all media device links between VINs and ISP's. */ - mutex_lock(&vin->group->lock); + mutex_lock(&group->lock); for (i = 0; i < RCAR_VIN_NUM; i++) { struct media_pad *source_pad, *sink_pad; struct media_entity *source, *sink; unsigned int source_slot = i / 8; unsigned int source_idx = i % 8 + 1; + struct rvin_dev *vin = group->vin[i]; - if (!vin->group->vin[i]) + if (!vin) continue; /* Check that ISP is part of the group. */ - if (!vin->group->remotes[source_slot].subdev) + if (!group->remotes[source_slot].subdev) continue; - source = &vin->group->remotes[source_slot].subdev->entity; + source = &group->remotes[source_slot].subdev->entity; source_pad = &source->pads[source_idx]; - sink = &vin->group->vin[i]->vdev.entity; + sink = &vin->vdev.entity; sink_pad = &sink->pads[0]; /* Skip if link already exists. */ @@ -1030,44 +825,22 @@ static int rvin_isp_setup_links(struct rvin_dev *vin) break; } } - mutex_unlock(&vin->group->lock); + mutex_unlock(&group->lock); return ret; } -static void rvin_isp_cleanup(struct rvin_dev *vin) -{ - rvin_group_notifier_cleanup(vin); - rvin_group_put(vin); - rvin_free_controls(vin); -} - static int rvin_isp_init(struct rvin_dev *vin) { int ret; - vin->pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); - if (ret) - return ret; - - ret = rvin_create_controls(vin, NULL); - if (ret < 0) - return ret; - ret = rvin_group_get(vin, rvin_isp_setup_links, NULL); if (ret) - goto err_controls; + return ret; ret = rvin_group_notifier_init(vin, 2, RVIN_ISP_MAX); if (ret) - goto err_group; - - return 0; -err_group: - rvin_group_put(vin); -err_controls: - rvin_free_controls(vin); + rvin_group_put(vin); return ret; } @@ -1102,7 +875,7 @@ static int __maybe_unused rvin_resume(struct device *dev) * as we don't know if and in which order the master VINs will * be resumed. */ - if (vin->info->use_mc) { + if (vin->info->model == RCAR_GEN3) { unsigned int master_id = rvin_group_id_to_master(vin->id); struct rvin_dev *master = vin->group->vin[master_id]; int ret; @@ -1124,7 +897,6 @@ static int __maybe_unused rvin_resume(struct device *dev) static const struct rvin_info rcar_info_h1 = { .model = RCAR_H1, - .use_mc = false, .max_width = 2048, .max_height = 2048, .scaler = rvin_scaler_gen2, @@ -1132,7 +904,6 @@ static const struct rvin_info rcar_info_h1 = { static const struct rvin_info rcar_info_m1 = { .model = RCAR_M1, - .use_mc = false, .max_width = 2048, .max_height = 2048, .scaler = rvin_scaler_gen2, @@ -1140,7 +911,6 @@ static const struct rvin_info rcar_info_m1 = { static const struct rvin_info rcar_info_gen2 = { .model = RCAR_GEN2, - .use_mc = false, .max_width = 2048, .max_height = 2048, .scaler = rvin_scaler_gen2, @@ -1155,7 +925,6 @@ static const struct rvin_group_route rcar_info_r8a774e1_routes[] = { static const struct rvin_info rcar_info_r8a774e1 = { .model = RCAR_GEN3, - .use_mc = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a774e1_routes, @@ -1171,7 +940,6 @@ static const struct rvin_group_route rcar_info_r8a7795_routes[] = { static const struct rvin_info rcar_info_r8a7795 = { .model = RCAR_GEN3, - .use_mc = true, .nv12 = true, .max_width = 4096, .max_height = 4096, @@ -1189,7 +957,6 @@ static const struct rvin_group_route rcar_info_r8a7796_routes[] = { static const struct rvin_info rcar_info_r8a7796 = { .model = RCAR_GEN3, - .use_mc = true, .nv12 = true, .max_width = 4096, .max_height = 4096, @@ -1207,7 +974,6 @@ static const struct rvin_group_route rcar_info_r8a77965_routes[] = { static const struct rvin_info rcar_info_r8a77965 = { .model = RCAR_GEN3, - .use_mc = true, .nv12 = true, .max_width = 4096, .max_height = 4096, @@ -1222,7 +988,6 @@ static const struct rvin_group_route rcar_info_r8a77970_routes[] = { static const struct rvin_info rcar_info_r8a77970 = { .model = RCAR_GEN3, - .use_mc = true, .max_width = 4096, .max_height = 4096, .routes = rcar_info_r8a77970_routes, @@ -1236,7 +1001,6 @@ static const struct rvin_group_route rcar_info_r8a77980_routes[] = { static const struct rvin_info rcar_info_r8a77980 = { .model = RCAR_GEN3, - .use_mc = true, .nv12 = true, .max_width = 4096, .max_height = 4096, @@ -1250,7 +1014,6 @@ static const struct rvin_group_route rcar_info_r8a77990_routes[] = { static const struct rvin_info rcar_info_r8a77990 = { .model = RCAR_GEN3, - .use_mc = true, .nv12 = true, .max_width = 4096, .max_height = 4096, @@ -1264,7 +1027,6 @@ static const struct rvin_group_route rcar_info_r8a77995_routes[] = { static const struct rvin_info rcar_info_r8a77995 = { .model = RCAR_GEN3, - .use_mc = true, .nv12 = true, .max_width = 4096, .max_height = 4096, @@ -1274,7 +1036,6 @@ static const struct rvin_info rcar_info_r8a77995 = { static const struct rvin_info rcar_info_gen4 = { .model = RCAR_GEN4, - .use_mc = true, .use_isp = true, .nv12 = true, .raw10 = true, @@ -1361,6 +1122,56 @@ static const struct of_device_id rvin_of_id_table[] = { }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); +static int rvin_id_get(struct rvin_dev *vin) +{ + u32 oid; + int id; + + switch (vin->info->model) { + case RCAR_GEN3: + case RCAR_GEN4: + if (of_property_read_u32(vin->dev->of_node, "renesas,id", &oid)) { + vin_err(vin, "%pOF: No renesas,id property found\n", + vin->dev->of_node); + return -EINVAL; + } + + if (oid < 0 || oid >= RCAR_VIN_NUM) { + vin_err(vin, "%pOF: Invalid renesas,id '%u'\n", + vin->dev->of_node, oid); + return -EINVAL; + } + + vin->id = oid; + break; + default: + id = ida_alloc_range(&rvin_ida, 0, RCAR_VIN_NUM - 1, + GFP_KERNEL); + if (id < 0) { + vin_err(vin, "%pOF: Failed to allocate VIN group ID\n", + vin->dev->of_node); + return -EINVAL; + } + + vin->id = id; + break; + } + + return 0; +} + +static void rvin_id_put(struct rvin_dev *vin) +{ + switch (vin->info->model) { + case RCAR_GEN3: + case RCAR_GEN4: + break; + default: + ida_free(&rvin_ida, vin->id); + break; + } +} + static int rcar_vin_probe(struct platform_device *pdev) { struct rvin_dev *vin; @@ -1388,30 +1199,59 @@ static int rcar_vin_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vin); - if (vin->info->use_isp) { - ret = rvin_isp_init(vin); - } else if (vin->info->use_mc) { - ret = rvin_csi2_init(vin); + if (rvin_id_get(vin)) { + ret = -EINVAL; + goto err_dma; + } - if (vin->info->scaler && - rvin_group_id_to_master(vin->id) == vin->id) - vin->scaler = vin->info->scaler; - } else { - ret = rvin_parallel_init(vin); + vin->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); + if (ret) + goto err_id; + + ret = rvin_create_controls(vin); + if (ret < 0) + goto err_id; + + switch (vin->info->model) { + case RCAR_GEN3: + case RCAR_GEN4: + if (vin->info->use_isp) { + ret = rvin_isp_init(vin); + } else { + ret = rvin_csi2_init(vin); + + if (vin->info->scaler && + rvin_group_id_to_master(vin->id) == vin->id) + vin->scaler = vin->info->scaler; + } + break; + default: + ret = rvin_group_get(vin, rvin_parallel_setup_links, NULL); + if (!ret) + ret = rvin_group_notifier_init(vin, 0, 0); if (vin->info->scaler) vin->scaler = vin->info->scaler; + break; } - if (ret) { - rvin_dma_unregister(vin); - return ret; - } + if (ret) + goto err_ctrl; pm_suspend_ignore_children(&pdev->dev, true); pm_runtime_enable(&pdev->dev); return 0; + +err_ctrl: + rvin_free_controls(vin); +err_id: + rvin_id_put(vin); +err_dma: + rvin_dma_unregister(vin); + + return ret; } static void rcar_vin_remove(struct platform_device *pdev) @@ -1422,12 +1262,16 @@ static void rcar_vin_remove(struct platform_device *pdev) rvin_v4l2_unregister(vin); - if (vin->info->use_isp) - rvin_isp_cleanup(vin); - else if (vin->info->use_mc) - rvin_csi2_cleanup(vin); - else - rvin_parallel_cleanup(vin); + if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { + v4l2_async_nf_unregister(&vin->group->notifier); + v4l2_async_nf_cleanup(&vin->group->notifier); + } + + rvin_group_put(vin); + + rvin_free_controls(vin); + + rvin_id_put(vin); rvin_dma_unregister(vin); } diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index 5c08ee2c9807..b619d1436a41 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -2,18 +2,18 @@ /* * Driver for Renesas R-Car VIN * + * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se> * Copyright (C) 2016 Renesas Electronics Corp. * Copyright (C) 2011-2013 Renesas Solutions Corp. * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> * Copyright (C) 2008 Magnus Damm - * - * Based on the soc-camera rcar_vin driver */ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> +#include <media/v4l2-event.h> #include <media/videobuf2-dma-contig.h> #include "rcar-vin.h" @@ -115,11 +115,16 @@ #define VNFC_S_FRAME (1 << 0) /* Video n Interrupt Enable Register bits */ -#define VNIE_FIE (1 << 4) -#define VNIE_EFE (1 << 1) +#define VNIE_VFE BIT(17) +#define VNIE_VRE BIT(16) +#define VNIE_FIE BIT(4) +#define VNIE_EFE BIT(1) /* Video n Interrupt Status Register bits */ -#define VNINTS_FIS (1 << 4) +#define VNINTS_VFS BIT(17) +#define VNINTS_VRS BIT(16) +#define VNINTS_FIS BIT(4) +#define VNINTS_EFS BIT(1) /* Video n Data Mode Register bits */ #define VNDMR_A8BIT(n) (((n) & 0xff) << 24) @@ -555,17 +560,12 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) void rvin_scaler_gen2(struct rvin_dev *vin) { - unsigned int crop_height; u32 xs, ys; /* Set scaling coefficient */ - crop_height = vin->crop.height; - if (V4L2_FIELD_HAS_BOTH(vin->format.field)) - crop_height *= 2; - ys = 0; - if (crop_height != vin->compose.height) - ys = (4096 * crop_height) / vin->compose.height; + if (vin->crop.height != vin->compose.height) + ys = (4096 * vin->crop.height) / vin->compose.height; rvin_write(vin, ys, VNYS_REG); xs = 0; @@ -700,9 +700,6 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_FIELD_INTERLACED: /* Default to TB */ vnmc = VNMC_IM_FULL; - /* Use BT if video standard can be read and is 60 Hz format */ - if (!vin->info->use_mc && vin->std & V4L2_STD_525_60) - vnmc = VNMC_IM_FULL | VNMC_FOC; break; case V4L2_FIELD_INTERLACED_TB: vnmc = VNMC_IM_FULL; @@ -897,6 +894,8 @@ static int rvin_setup(struct rvin_dev *vin) /* Progressive or interlaced mode */ interrupts = progressive ? VNIE_FIE : VNIE_EFE; + /* Enable VSYNC Rising Edge Detection. */ + interrupts |= VNIE_VRE; /* Ack interrupts */ rvin_write(vin, interrupts, VNINTS_REG); @@ -912,21 +911,6 @@ static int rvin_setup(struct rvin_dev *vin) return 0; } -static void rvin_disable_interrupts(struct rvin_dev *vin) -{ - rvin_write(vin, 0, VNIE_REG); -} - -static u32 rvin_get_interrupt_status(struct rvin_dev *vin) -{ - return rvin_read(vin, VNINTS_REG); -} - -static void rvin_ack_interrupt(struct rvin_dev *vin) -{ - rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG); -} - static bool rvin_capture_active(struct rvin_dev *vin) { return rvin_read(vin, VNMS_REG) & VNMS_CA; @@ -1049,22 +1033,35 @@ static void rvin_capture_stop(struct rvin_dev *vin) static irqreturn_t rvin_irq(int irq, void *data) { struct rvin_dev *vin = data; - u32 int_status, vnms; + u32 capture, status, vnms; int slot; unsigned int handled = 0; unsigned long flags; spin_lock_irqsave(&vin->qlock, flags); - int_status = rvin_get_interrupt_status(vin); - if (!int_status) + status = rvin_read(vin, VNINTS_REG); + if (!status) goto done; - rvin_ack_interrupt(vin); + rvin_write(vin, status, VNINTS_REG); handled = 1; + /* Signal Start of Frame. */ + if (status & VNINTS_VRS) { + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = vin->sequence, + }; + + v4l2_event_queue(&vin->vdev, &event); + } + /* Nothing to do if nothing was captured. */ - if (!(int_status & VNINTS_FIS)) + capture = vin->format.field == V4L2_FIELD_NONE || + vin->format.field == V4L2_FIELD_ALTERNATE ? + VNINTS_FIS : VNINTS_EFS; + if (!(status & capture)) goto done; /* Nothing to do if not running. */ @@ -1297,14 +1294,6 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) struct media_pad *pad; int ret; - /* No media controller used, simply pass operation to subdevice. */ - if (!vin->info->use_mc) { - ret = v4l2_subdev_call(vin->parallel.subdev, video, s_stream, - on); - - return ret == -ENOIOCTLCMD ? 0 : ret; - } - pad = media_pad_remote_pad_first(&vin->pad); if (!pad) return -EPIPE; @@ -1417,7 +1406,7 @@ void rvin_stop_streaming(struct rvin_dev *vin) rvin_set_stream(vin, 0); /* disable interrupts */ - rvin_disable_interrupts(vin); + rvin_write(vin, 0, VNIE_REG); /* Return unprocessed buffers from hardware. */ for (unsigned int i = 0; i < HW_BUFFER_NUM; i++) { diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c index db091af57c19..62eddf3a35fc 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c @@ -2,12 +2,11 @@ /* * Driver for Renesas R-Car VIN * + * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se> * Copyright (C) 2016 Renesas Electronics Corp. * Copyright (C) 2011-2013 Renesas Solutions Corp. * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> * Copyright (C) 2008 Magnus Damm - * - * Based on the soc-camera rcar_vin driver */ #include <linux/pm_runtime.h> @@ -230,101 +229,6 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) * V4L2 */ -static int rvin_reset_format(struct rvin_dev *vin) -{ - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = vin->parallel.source_pad, - }; - int ret; - - ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt); - if (ret) - return ret; - - v4l2_fill_pix_format(&vin->format, &fmt.format); - - vin->crop.top = 0; - vin->crop.left = 0; - vin->crop.width = vin->format.width; - vin->crop.height = vin->format.height; - - /* Make use of the hardware interlacer by default. */ - if (vin->format.field == V4L2_FIELD_ALTERNATE) { - vin->format.field = V4L2_FIELD_INTERLACED; - vin->format.height *= 2; - } - - rvin_format_align(vin, &vin->format); - - vin->compose.top = 0; - vin->compose.left = 0; - vin->compose.width = vin->format.width; - vin->compose.height = vin->format.height; - - return 0; -} - -static int rvin_try_format(struct rvin_dev *vin, u32 which, - struct v4l2_pix_format *pix, - struct v4l2_rect *src_rect) -{ - struct v4l2_subdev *sd = vin_to_source(vin); - struct v4l2_subdev_state *sd_state; - static struct lock_class_key key; - struct v4l2_subdev_format format = { - .which = which, - .pad = vin->parallel.source_pad, - }; - enum v4l2_field field; - u32 width, height; - int ret; - - /* - * FIXME: Drop this call, drivers are not supposed to use - * __v4l2_subdev_state_alloc(). - */ - sd_state = __v4l2_subdev_state_alloc(sd, "rvin:state->lock", &key); - if (IS_ERR(sd_state)) - return PTR_ERR(sd_state); - - if (!rvin_format_from_pixel(vin, pix->pixelformat)) - pix->pixelformat = RVIN_DEFAULT_FORMAT; - - v4l2_fill_mbus_format(&format.format, pix, vin->mbus_code); - - /* Allow the video device to override field and to scale */ - field = pix->field; - width = pix->width; - height = pix->height; - - ret = v4l2_subdev_call(sd, pad, set_fmt, sd_state, &format); - if (ret < 0 && ret != -ENOIOCTLCMD) - goto done; - ret = 0; - - v4l2_fill_pix_format(pix, &format.format); - - if (src_rect) { - src_rect->top = 0; - src_rect->left = 0; - src_rect->width = pix->width; - src_rect->height = pix->height; - } - - if (field != V4L2_FIELD_ANY) - pix->field = field; - - pix->width = width; - pix->height = height; - - rvin_format_align(vin, pix); -done: - __v4l2_subdev_state_free(sd_state); - - return ret; -} - static int rvin_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -333,42 +237,6 @@ static int rvin_querycap(struct file *file, void *priv, return 0; } -static int rvin_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rvin_dev *vin = video_drvdata(file); - - return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL); -} - -static int rvin_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_rect fmt_rect, src_rect; - int ret; - - if (vb2_is_busy(&vin->queue)) - return -EBUSY; - - ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, - &src_rect); - if (ret) - return ret; - - vin->format = f->fmt.pix; - - fmt_rect.top = 0; - fmt_rect.left = 0; - fmt_rect.width = vin->format.width; - fmt_rect.height = vin->format.height; - - v4l2_rect_map_inside(&vin->crop, &src_rect); - v4l2_rect_map_inside(&vin->compose, &fmt_rect); - - return 0; -} - static int rvin_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -465,6 +333,7 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect) { + struct media_pad *pad = media_pad_remote_pad_first(&vin->pad); struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; @@ -472,18 +341,11 @@ static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect) unsigned int index; int ret; - if (vin->info->use_mc) { - struct media_pad *pad = media_pad_remote_pad_first(&vin->pad); - - if (!pad) - return -EINVAL; + if (!pad) + return -EINVAL; - sd = media_entity_to_v4l2_subdev(pad->entity); - index = pad->index; - } else { - sd = vin_to_source(vin); - index = vin->parallel.source_pad; - } + sd = media_entity_to_v4l2_subdev(pad->entity); + index = pad->index; fmt.pad = index; ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); @@ -623,284 +485,18 @@ static int rvin_s_selection(struct file *file, void *fh, return 0; } -static int rvin_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *parm) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - - return v4l2_g_parm_cap(&vin->vdev, sd, parm); -} - -static int rvin_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *parm) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - - return v4l2_s_parm_cap(&vin->vdev, sd, parm); -} - -static int rvin_g_pixelaspect(struct file *file, void *priv, - int type, struct v4l2_fract *f) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return v4l2_subdev_call(sd, video, g_pixelaspect, f); -} - -static int rvin_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - int ret; - - if (i->index != 0) - return -EINVAL; - - ret = v4l2_subdev_call(sd, video, g_input_status, &i->status); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - i->type = V4L2_INPUT_TYPE_CAMERA; - - if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) { - i->capabilities = V4L2_IN_CAP_DV_TIMINGS; - i->std = 0; - } else { - i->capabilities = V4L2_IN_CAP_STD; - i->std = vin->vdev.tvnorms; - } - - strscpy(i->name, "Camera", sizeof(i->name)); - - return 0; -} - -static int rvin_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int rvin_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - return 0; -} - -static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - - return v4l2_subdev_call(sd, video, querystd, a); -} - -static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) -{ - struct rvin_dev *vin = video_drvdata(file); - int ret; - - ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a); - if (ret < 0) - return ret; - - vin->std = a; - - /* Changing the standard will change the width/height */ - return rvin_reset_format(vin); -} - -static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) -{ - struct rvin_dev *vin = video_drvdata(file); - - if (v4l2_subdev_has_op(vin_to_source(vin), pad, dv_timings_cap)) - return -ENOIOCTLCMD; - - *a = vin->std; - - return 0; -} - static int rvin_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 2, NULL); case V4L2_EVENT_SOURCE_CHANGE: return v4l2_event_subscribe(fh, sub, 4, NULL); } return v4l2_ctrl_subscribe_event(fh, sub); } -static int rvin_enum_dv_timings(struct file *file, void *priv_fh, - struct v4l2_enum_dv_timings *timings) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - int ret; - - if (timings->pad) - return -EINVAL; - - timings->pad = vin->parallel.sink_pad; - - ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); - - timings->pad = 0; - - return ret; -} - -static int rvin_s_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - int ret; - - ret = v4l2_subdev_call(sd, pad, s_dv_timings, - vin->parallel.sink_pad, timings); - if (ret) - return ret; - - /* Changing the timings will change the width/height */ - return rvin_reset_format(vin); -} - -static int rvin_g_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - - return v4l2_subdev_call(sd, pad, g_dv_timings, - vin->parallel.sink_pad, timings); -} - -static int rvin_query_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - - return v4l2_subdev_call(sd, pad, query_dv_timings, - vin->parallel.sink_pad, timings); -} - -static int rvin_dv_timings_cap(struct file *file, void *priv_fh, - struct v4l2_dv_timings_cap *cap) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - int ret; - - if (cap->pad) - return -EINVAL; - - cap->pad = vin->parallel.sink_pad; - - ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); - - cap->pad = 0; - - return ret; -} - -static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - int ret; - - if (edid->pad) - return -EINVAL; - - edid->pad = vin->parallel.sink_pad; - - ret = v4l2_subdev_call(sd, pad, get_edid, edid); - - edid->pad = 0; - - return ret; -} - -static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) -{ - struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - int ret; - - if (edid->pad) - return -EINVAL; - - edid->pad = vin->parallel.sink_pad; - - ret = v4l2_subdev_call(sd, pad, set_edid, edid); - - edid->pad = 0; - - return ret; -} - -static const struct v4l2_ioctl_ops rvin_ioctl_ops = { - .vidioc_querycap = rvin_querycap, - .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = rvin_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap, - - .vidioc_g_selection = rvin_g_selection, - .vidioc_s_selection = rvin_s_selection, - - .vidioc_g_parm = rvin_g_parm, - .vidioc_s_parm = rvin_s_parm, - - .vidioc_g_pixelaspect = rvin_g_pixelaspect, - - .vidioc_enum_input = rvin_enum_input, - .vidioc_g_input = rvin_g_input, - .vidioc_s_input = rvin_s_input, - - .vidioc_dv_timings_cap = rvin_dv_timings_cap, - .vidioc_enum_dv_timings = rvin_enum_dv_timings, - .vidioc_g_dv_timings = rvin_g_dv_timings, - .vidioc_s_dv_timings = rvin_s_dv_timings, - .vidioc_query_dv_timings = rvin_query_dv_timings, - - .vidioc_g_edid = rvin_g_edid, - .vidioc_s_edid = rvin_s_edid, - - .vidioc_querystd = rvin_querystd, - .vidioc_g_std = rvin_g_std, - .vidioc_s_std = rvin_s_std, - - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = rvin_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 Media Controller - */ - static void rvin_mc_try_format(struct rvin_dev *vin, struct v4l2_pix_format *pix) { @@ -979,19 +575,6 @@ static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = { * File Operations */ -static int rvin_power_parallel(struct rvin_dev *vin, bool on) -{ - struct v4l2_subdev *sd = vin_to_source(vin); - int power = on ? 1 : 0; - int ret; - - ret = v4l2_subdev_call(sd, core, s_power, power); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - return 0; -} - static int rvin_open(struct file *file) { struct rvin_dev *vin = video_drvdata(file); @@ -1011,11 +594,7 @@ static int rvin_open(struct file *file) if (ret) goto err_unlock; - if (vin->info->use_mc) - ret = v4l2_pipeline_pm_get(&vin->vdev.entity); - else if (v4l2_fh_is_singular_file(file)) - ret = rvin_power_parallel(vin, true); - + ret = v4l2_pipeline_pm_get(&vin->vdev.entity); if (ret < 0) goto err_open; @@ -1027,10 +606,7 @@ static int rvin_open(struct file *file) return 0; err_power: - if (vin->info->use_mc) - v4l2_pipeline_pm_put(&vin->vdev.entity); - else if (v4l2_fh_is_singular_file(file)) - rvin_power_parallel(vin, false); + v4l2_pipeline_pm_put(&vin->vdev.entity); err_open: v4l2_fh_release(file); err_unlock: @@ -1044,23 +620,14 @@ err_pm: static int rvin_release(struct file *file) { struct rvin_dev *vin = video_drvdata(file); - bool fh_singular; int ret; mutex_lock(&vin->lock); - /* Save the singular status before we call the clean-up helper */ - fh_singular = v4l2_fh_is_singular_file(file); - /* the release helper will cleanup any on-going streaming */ ret = _vb2_fop_release(file, NULL); - if (vin->info->use_mc) { - v4l2_pipeline_pm_put(&vin->vdev.entity); - } else { - if (fh_singular) - rvin_power_parallel(vin, false); - } + v4l2_pipeline_pm_put(&vin->vdev.entity); mutex_unlock(&vin->lock); @@ -1091,18 +658,6 @@ void rvin_v4l2_unregister(struct rvin_dev *vin) video_unregister_device(&vin->vdev); } -static void rvin_notify_video_device(struct rvin_dev *vin, - unsigned int notification, void *arg) -{ - switch (notification) { - case V4L2_DEVICE_NOTIFY_EVENT: - v4l2_event_queue(&vin->vdev, arg); - break; - default: - break; - } -} - static void rvin_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg) { @@ -1113,12 +668,6 @@ static void rvin_notify(struct v4l2_subdev *sd, container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev); unsigned int i; - /* If no media controller, no need to route the event. */ - if (!vin->info->use_mc) { - rvin_notify_video_device(vin, notification, arg); - return; - } - group = vin->group; for (i = 0; i < RCAR_VIN_NUM; i++) { @@ -1134,7 +683,13 @@ static void rvin_notify(struct v4l2_subdev *sd, if (remote != sd) continue; - rvin_notify_video_device(vin, notification, arg); + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: + v4l2_event_queue(&vin->vdev, arg); + break; + default: + break; + } } } @@ -1153,7 +708,8 @@ int rvin_v4l2_register(struct rvin_dev *vin) vdev->lock = &vin->lock; vdev->fops = &rvin_fops; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; + V4L2_CAP_READWRITE | V4L2_CAP_IO_MC; + vdev->ioctl_ops = &rvin_mc_ioctl_ops; /* Set a default format */ vin->format.pixelformat = RVIN_DEFAULT_FORMAT; @@ -1162,14 +718,6 @@ int rvin_v4l2_register(struct rvin_dev *vin) vin->format.field = RVIN_DEFAULT_FIELD; vin->format.colorspace = RVIN_DEFAULT_COLORSPACE; - if (vin->info->use_mc) { - vdev->device_caps |= V4L2_CAP_IO_MC; - vdev->ioctl_ops = &rvin_mc_ioctl_ops; - } else { - vdev->ioctl_ops = &rvin_ioctl_ops; - rvin_reset_format(vin); - } - rvin_format_align(vin, &vin->format); ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1); diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h index 83d1b2734c41..74bef5b8adad 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h @@ -2,12 +2,11 @@ /* * Driver for Renesas R-Car VIN * + * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se> * Copyright (C) 2016 Renesas Electronics Corp. * Copyright (C) 2011-2013 Renesas Solutions Corp. * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com> * Copyright (C) 2008 Magnus Damm - * - * Based on the soc-camera rcar_vin driver */ #ifndef __RCAR_VIN__ @@ -79,8 +78,6 @@ struct rvin_video_format { * @mbus_type: media bus type * @bus: media bus parallel configuration * @source_pad: source pad of remote subdevice - * @sink_pad: sink pad of remote subdevice - * */ struct rvin_parallel_entity { struct v4l2_async_connection *asc; @@ -90,7 +87,6 @@ struct rvin_parallel_entity { struct v4l2_mbus_config_parallel bus; unsigned int source_pad; - unsigned int sink_pad; }; /** @@ -117,7 +113,6 @@ struct rvin_group_route { /** * struct rvin_info - Information about the particular VIN implementation * @model: VIN model - * @use_mc: use media controller instead of controlling subdevice * @use_isp: the VIN is connected to the ISP and not to the CSI-2 * @nv12: support outputting NV12 pixel format * @raw10: support outputting RAW10 pixel format @@ -129,7 +124,6 @@ struct rvin_group_route { */ struct rvin_info { enum model_id model; - bool use_mc; bool use_isp; bool nv12; bool raw10; @@ -149,7 +143,6 @@ struct rvin_info { * @vdev: V4L2 video device associated with VIN * @v4l2_dev: V4L2 device * @ctrl_handler: V4L2 control handler - * @notifier: V4L2 asynchronous subdevs notifier * * @parallel: parallel input subdevice descriptor * @@ -177,7 +170,6 @@ struct rvin_info { * @crop: active cropping * @compose: active composing * @scaler: Optional scaler - * @std: active video standard of the video source * * @alpha: Alpha component to fill in for supported pixel formats */ @@ -189,7 +181,6 @@ struct rvin_dev { struct video_device vdev; struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_async_notifier notifier; struct rvin_parallel_entity parallel; @@ -220,7 +211,6 @@ struct rvin_dev { struct v4l2_rect crop; struct v4l2_rect compose; void (*scaler)(struct rvin_dev *vin); - v4l2_std_id std; unsigned int alpha; }; @@ -242,6 +232,7 @@ struct rvin_dev { * @lock: protects the count, notifier, vin and csi members * @count: number of enabled VIN instances found in DT * @notifier: group notifier for CSI-2 async connections + * @info: Platform dependent information about the VIN instances * @vin: VIN instances which are part of the group * @link_setup: Callback to create all links for the media graph * @remotes: array of pairs of async connection and subdev pointers @@ -255,9 +246,10 @@ struct rvin_group { struct mutex lock; unsigned int count; struct v4l2_async_notifier notifier; + const struct rvin_info *info; struct rvin_dev *vin[RCAR_VIN_NUM]; - int (*link_setup)(struct rvin_dev *vin); + int (*link_setup)(struct rvin_group *group); struct { struct v4l2_async_connection *asc; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c index 5fa73ab2db53..806acc8f9728 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c @@ -366,7 +366,7 @@ static const struct rzg2l_cru_info rzg3e_cru_info = { .irq_handler = rzg3e_cru_irq, .enable_interrupts = rzg3e_cru_enable_interrupts, .disable_interrupts = rzg3e_cru_disable_interrupts, - .fifo_empty = rz3e_fifo_empty, + .fifo_empty = rzg3e_fifo_empty, .csi_setup = rzg3e_cru_csi2_setup, }; @@ -403,7 +403,7 @@ static const u16 rzg2l_cru_regs[] = { [ICnDMR] = 0x26c, }; -static const struct rzg2l_cru_info rzgl2_cru_info = { +static const struct rzg2l_cru_info rzg2l_cru_info = { .max_width = 2800, .max_height = 4095, .image_conv = ICnMC, @@ -422,7 +422,7 @@ static const struct of_device_id rzg2l_cru_of_id_table[] = { }, { .compatible = "renesas,rzg2l-cru", - .data = &rzgl2_cru_info, + .data = &rzg2l_cru_info, }, { /* sentinel */ } }; diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index c30f3b281284..be95b41c37df 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -64,19 +64,21 @@ struct rzg2l_cru_ip { /** * struct rzg2l_cru_ip_format - CRU IP format - * @code: Media bus code + * @codes: Array of up to four media bus codes * @datatype: MIPI CSI2 data type * @format: 4CC format identifier (V4L2_PIX_FMT_*) * @icndmr: ICnDMR register value - * @bpp: bytes per pixel * @yuv: Flag to indicate whether the format is YUV-based. */ struct rzg2l_cru_ip_format { - u32 code; + /* + * RAW output formats might be produced by RAW media codes with any one + * of the 4 common bayer patterns. + */ + u32 codes[4]; u32 datatype; u32 format; u32 icndmr; - u8 bpp; bool yuv; }; @@ -192,6 +194,8 @@ struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru); const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code); const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format); const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index); +bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt, + unsigned int code); void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru); void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru); @@ -199,7 +203,7 @@ void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru); void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru); bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru); -bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru); +bool rzg3e_fifo_empty(struct rzg2l_cru_dev *cru); void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, const struct rzg2l_cru_ip_format *ip_fmt, u8 csi_vc); diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index 9243306e2aa9..1520211e7418 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -232,6 +232,18 @@ static const struct rzg2l_csi2_format rzg2l_csi2_formats[] = { { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, }, { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, }, { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, }, + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, }, + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, }, + { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, }, + { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, }, + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, }, + { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14, }, + { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14, }, + { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14, }, + { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14, }, }; static inline struct rzg2l_csi2 *sd_to_csi2(struct v4l2_subdev *sd) @@ -282,15 +294,18 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2) const struct rzg2l_csi2_format *format; const struct v4l2_mbus_framefmt *fmt; struct v4l2_subdev_state *state; - struct v4l2_ctrl *ctrl; + struct media_pad *remote_pad; u64 mbps; + s64 ret; - /* Read the pixel rate control from remote. */ - ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - dev_err(csi2->dev, "no pixel rate control in subdev %s\n", - source->name); - return -EINVAL; + if (!csi2->remote_source) + return -ENODEV; + + remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]); + if (IS_ERR(remote_pad)) { + dev_err(csi2->dev, "can't get source pad of %s (%ld)\n", + csi2->remote_source->name, PTR_ERR(remote_pad)); + return PTR_ERR(remote_pad); } state = v4l2_subdev_lock_and_get_active_state(&csi2->subdev); @@ -298,12 +313,16 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2) format = rzg2l_csi2_code_to_fmt(fmt->code); v4l2_subdev_unlock_state(state); - /* - * Calculate hsfreq in Mbps - * hsfreq = (pixel_rate * bits_per_sample) / number_of_lanes - */ - mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * format->bpp; - do_div(mbps, csi2->lanes * 1000000); + /* Read the link frequency from remote subdevice. */ + ret = v4l2_get_link_freq(remote_pad, format->bpp, csi2->lanes * 2); + if (ret < 0) { + dev_err(csi2->dev, "can't retrieve link freq from subdev %s\n", + source->name); + return -EINVAL; + } + + mbps = ret * 2; + do_div(mbps, 1000000); return mbps; } diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c index 7836c7cd53dc..5f2c87858bfe 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c @@ -13,42 +13,83 @@ static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = { { - .code = MEDIA_BUS_FMT_UYVY8_1X16, + .codes = { + MEDIA_BUS_FMT_UYVY8_1X16, + }, .datatype = MIPI_CSI2_DT_YUV422_8B, .format = V4L2_PIX_FMT_UYVY, - .bpp = 2, .icndmr = ICnDMR_YCMODE_UYVY, .yuv = true, }, { - .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .codes = { + MEDIA_BUS_FMT_SBGGR8_1X8, + }, .format = V4L2_PIX_FMT_SBGGR8, .datatype = MIPI_CSI2_DT_RAW8, - .bpp = 1, .icndmr = 0, .yuv = false, }, { - .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .codes = { + MEDIA_BUS_FMT_SGBRG8_1X8, + }, .format = V4L2_PIX_FMT_SGBRG8, .datatype = MIPI_CSI2_DT_RAW8, - .bpp = 1, .icndmr = 0, .yuv = false, }, { - .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .codes = { + MEDIA_BUS_FMT_SGRBG8_1X8, + }, .format = V4L2_PIX_FMT_SGRBG8, .datatype = MIPI_CSI2_DT_RAW8, - .bpp = 1, .icndmr = 0, .yuv = false, }, { - .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .codes = { + MEDIA_BUS_FMT_SRGGB8_1X8, + }, .format = V4L2_PIX_FMT_SRGGB8, .datatype = MIPI_CSI2_DT_RAW8, - .bpp = 1, + .icndmr = 0, + .yuv = false, + }, + { + .codes = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10 + }, + .format = V4L2_PIX_FMT_RAW_CRU10, + .datatype = MIPI_CSI2_DT_RAW10, + .icndmr = 0, + .yuv = false, + }, + { + .codes = { + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12 + }, + .format = V4L2_PIX_FMT_RAW_CRU12, + .datatype = MIPI_CSI2_DT_RAW12, + .icndmr = 0, + .yuv = false, + }, + { + .codes = { + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14 + }, + .format = V4L2_PIX_FMT_RAW_CRU14, + .datatype = MIPI_CSI2_DT_RAW14, .icndmr = 0, .yuv = false, }, @@ -56,11 +97,14 @@ static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = { const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code) { - unsigned int i; + unsigned int i, j; - for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) - if (rzg2l_cru_ip_formats[i].code == code) - return &rzg2l_cru_ip_formats[i]; + for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { + for (j = 0; j < ARRAY_SIZE(rzg2l_cru_ip_formats[i].codes); j++) { + if (rzg2l_cru_ip_formats[i].codes[j] == code) + return &rzg2l_cru_ip_formats[i]; + } + } return NULL; } @@ -85,6 +129,17 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index) return &rzg2l_cru_ip_formats[index]; } +bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt, + unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fmt->codes); i++) + if (fmt->codes[i] == code) + return true; + + return false; +} struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru) { struct v4l2_subdev_state *state; @@ -162,7 +217,7 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd, sink_format = v4l2_subdev_state_get_format(state, fmt->pad); if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code)) - sink_format->code = rzg2l_cru_ip_formats[0].code; + sink_format->code = rzg2l_cru_ip_formats[0].codes[0]; else sink_format->code = fmt->format.code; @@ -188,11 +243,26 @@ static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index >= ARRAY_SIZE(rzg2l_cru_ip_formats)) - return -EINVAL; + unsigned int index = code->index; + unsigned int i, j; - code->code = rzg2l_cru_ip_formats[code->index].code; - return 0; + for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) { + const struct rzg2l_cru_ip_format *fmt = &rzg2l_cru_ip_formats[i]; + + for (j = 0; j < ARRAY_SIZE(fmt->codes); j++) { + if (!fmt->codes[j]) + continue; + + if (!index) { + code->code = fmt->codes[j]; + return 0; + } + + index--; + } + } + + return -EINVAL; } static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd, diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 067c6af14e95..a8817a7066b2 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -323,7 +323,7 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru, return 0; } -bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru) +bool rzg3e_fifo_empty(struct rzg2l_cru_dev *cru) { u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR); @@ -345,8 +345,6 @@ bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru) amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR; amnfifopntr_r_y = (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16; - if (amnfifopntr_w == amnfifopntr_r_y) - return true; return amnfifopntr_w == amnfifopntr_r_y; } @@ -941,15 +939,7 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, v4l_bound_align_image(&pix->width, 320, info->max_width, 1, &pix->height, 240, info->max_height, 2, 0); - if (info->has_stride) { - u32 stride = clamp(pix->bytesperline, pix->width * fmt->bpp, - RZG2L_CRU_STRIDE_MAX); - pix->bytesperline = round_up(stride, RZG2L_CRU_STRIDE_ALIGN); - } else { - pix->bytesperline = pix->width * fmt->bpp; - } - - pix->sizeimage = pix->bytesperline * pix->height; + v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height); dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", pix->width, pix->height, pix->bytesperline, pix->sizeimage); @@ -1031,6 +1021,31 @@ static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv, return 0; } +static int rzg2l_cru_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct rzg2l_cru_dev *cru = video_drvdata(file); + const struct rzg2l_cru_info *info = cru->info; + const struct rzg2l_cru_ip_format *fmt; + + if (fsize->index) + return -EINVAL; + + fmt = rzg2l_cru_ip_format_to_fmt(fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + fsize->stepwise.min_width = RZG2L_CRU_MIN_INPUT_WIDTH; + fsize->stepwise.max_width = info->max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = RZG2L_CRU_MIN_INPUT_HEIGHT; + fsize->stepwise.max_height = info->max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = { .vidioc_querycap = rzg2l_cru_querycap, .vidioc_try_fmt_vid_cap = rzg2l_cru_try_fmt_vid_cap, @@ -1047,6 +1062,7 @@ static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = { .vidioc_prepare_buf = vb2_ioctl_prepare_buf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_framesizes = rzg2l_cru_enum_framesizes, }; /* ----------------------------------------------------------------------------- @@ -1129,7 +1145,7 @@ static int rzg2l_cru_video_link_validate(struct media_link *link) if (fmt.format.width != cru->format.width || fmt.format.height != cru->format.height || fmt.format.field != cru->format.field || - video_fmt->code != fmt.format.code) + !rzg2l_cru_ip_fmt_supports_mbus_code(video_fmt, fmt.format.code)) return -EPIPE; return 0; diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile index de8c802e1d1a..2057c8f7be47 100644 --- a/drivers/media/platform/renesas/vsp1/Makefile +++ b/drivers/media/platform/renesas/vsp1/Makefile @@ -6,5 +6,6 @@ vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o vsp1-y += vsp1_iif.o vsp1_lif.o vsp1_uif.o +vsp1-y += vsp1_vspx.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index f97a1a31bfab..94de2e85792e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h @@ -111,6 +111,7 @@ struct vsp1_device { struct media_entity_operations media_ops; struct vsp1_drm *drm; + struct vsp1_vspx *vspx; }; int vsp1_device_get(struct vsp1_device *vsp1); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index bb8228b19824..d732b4ed1180 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -10,6 +10,7 @@ #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/gfp.h> +#include <linux/lockdep.h> #include <linux/refcount.h> #include <linux/slab.h> #include <linux/workqueue.h> @@ -176,6 +177,7 @@ struct vsp1_dl_cmd_pool { * @bodies: list of extra display list bodies * @pre_cmd: pre command to be issued through extended dl header * @post_cmd: post command to be issued through extended dl header + * @allocated: flag to detect double list release * @has_chain: if true, indicates that there's a partition chain * @chain: entry in the display list partition chain * @flags: display list flags, a combination of VSP1_DL_FRAME_END_* @@ -194,6 +196,8 @@ struct vsp1_dl_list { struct vsp1_dl_ext_cmd *pre_cmd; struct vsp1_dl_ext_cmd *post_cmd; + bool allocated; + bool has_chain; struct list_head chain; @@ -212,6 +216,7 @@ struct vsp1_dl_list { * @pending: list waiting to be queued to the hardware * @pool: body pool for the display list bodies * @cmdpool: commands pool for extended display list + * @list_count: number of allocated display lists */ struct vsp1_dl_manager { unsigned int index; @@ -226,6 +231,8 @@ struct vsp1_dl_manager { struct vsp1_dl_body_pool *pool; struct vsp1_dl_cmd_pool *cmdpool; + + size_t list_count; }; /* ----------------------------------------------------------------------------- @@ -606,6 +613,8 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) struct vsp1_dl_list *dl = NULL; unsigned long flags; + lockdep_assert_not_held(&dlm->lock); + spin_lock_irqsave(&dlm->lock, flags); if (!list_empty(&dlm->free)) { @@ -617,6 +626,7 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) * display list can assert list_empty() if it is not in a chain. */ INIT_LIST_HEAD(&dl->chain); + dl->allocated = true; } spin_unlock_irqrestore(&dlm->lock, flags); @@ -632,6 +642,8 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) if (!dl) return; + lockdep_assert_held(&dl->dlm->lock); + /* * Release any linked display-lists which were chained for a single * hardware operation. @@ -657,6 +669,13 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) */ dl->body0->num_entries = 0; + /* + * Return the display list to the 'free' pool. If the list had already + * been returned be loud about it. + */ + WARN_ON_ONCE(!dl->allocated); + dl->allocated = false; + list_add_tail(&dl->list, &dl->dlm->free); } @@ -1067,6 +1086,7 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1) void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) { unsigned long flags; + size_t list_count; spin_lock_irqsave(&dlm->lock, flags); @@ -1074,8 +1094,11 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) __vsp1_dl_list_put(dlm->queued); __vsp1_dl_list_put(dlm->pending); + list_count = list_count_nodes(&dlm->free); spin_unlock_irqrestore(&dlm->lock, flags); + WARN_ON_ONCE(list_count != dlm->list_count); + dlm->active = NULL; dlm->queued = NULL; dlm->pending = NULL; @@ -1145,6 +1168,8 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, list_add_tail(&dl->list, &dlm->free); } + dlm->list_count = prealloc; + if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { dlm->cmdpool = vsp1_dl_cmd_pool_create(vsp1, VSP1_EXTCMD_AUTOFLD, prealloc); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index fe55e8747b05..15d266439564 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/dma-mapping.h> +#include <linux/export.h> #include <linux/slab.h> #include <media/media-entity.h> diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 8270a9d207cb..b8d06e88c475 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -33,11 +33,13 @@ #include "vsp1_lif.h" #include "vsp1_lut.h" #include "vsp1_pipe.h" +#include "vsp1_regs.h" #include "vsp1_rwpf.h" #include "vsp1_sru.h" #include "vsp1_uds.h" #include "vsp1_uif.h" #include "vsp1_video.h" +#include "vsp1_vspx.h" /* ----------------------------------------------------------------------------- * Interrupt Handling @@ -490,7 +492,10 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) ret = media_device_register(mdev); } else { - ret = vsp1_drm_init(vsp1); + if (vsp1->info->version == VI6_IP_VERSION_MODEL_VSPX_GEN4) + ret = vsp1_vspx_init(vsp1); + else + ret = vsp1_drm_init(vsp1); } done: @@ -502,7 +507,9 @@ done: int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index) { + u32 version = vsp1->version & VI6_IP_VERSION_MODEL_MASK; unsigned int timeout; + int ret = 0; u32 status; status = vsp1_read(vsp1, VI6_STATUS); @@ -523,7 +530,11 @@ int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index) return -ETIMEDOUT; } - return 0; + if (version == VI6_IP_VERSION_MODEL_VSPD_GEN3 || + version == VI6_IP_VERSION_MODEL_VSPD_GEN4) + ret = rcar_fcp_soft_reset(vsp1->fcp); + + return ret; } static int vsp1_device_init(struct vsp1_device *vsp1) @@ -851,6 +862,13 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uif_count = 2, .wpf_count = 1, .num_bru_inputs = 5, + }, { + .version = VI6_IP_VERSION_MODEL_VSPX_GEN4, + .model = "VSP2-X", + .gen = 4, + .features = VSP1_HAS_IIF, + .rpf_count = 2, + .wpf_count = 1, }, }; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 3cbb768cf6ad..5d769cc42fe1 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -9,6 +9,7 @@ #include <linux/delay.h> #include <linux/list.h> +#include <linux/lockdep.h> #include <linux/sched.h> #include <linux/wait.h> @@ -473,6 +474,8 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + lockdep_assert_held(&pipe->irqlock); + if (pipe->state == VSP1_PIPELINE_STOPPED) { vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h index 86e47c2d991f..10cfbcd1b6e0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h @@ -799,6 +799,7 @@ #define VI6_IP_VERSION_MODEL_VSPDL_GEN3 (0x19 << 8) #define VI6_IP_VERSION_MODEL_VSPBS_GEN3 (0x1a << 8) #define VI6_IP_VERSION_MODEL_VSPD_GEN4 (0x1c << 8) +#define VI6_IP_VERSION_MODEL_VSPX_GEN4 (0x1d << 8) /* RZ/G2L SoCs have no version register, So use 0x80 as the model version */ #define VI6_IP_VERSION_MODEL_VSPD_RZG2L (0x80 << 8) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.c b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c new file mode 100644 index 000000000000..a754b92232bd --- /dev/null +++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c @@ -0,0 +1,633 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vsp1_vspx.c -- R-Car Gen 4 VSPX + * + * Copyright (C) 2025 Ideas On Board Oy + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#include "vsp1_vspx.h" + +#include <linux/cleanup.h> +#include <linux/container_of.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/export.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> +#include <media/vsp1.h> + +#include "vsp1_dl.h" +#include "vsp1_iif.h" +#include "vsp1_pipe.h" +#include "vsp1_rwpf.h" + +/* + * struct vsp1_vspx_pipeline - VSPX pipeline + * @pipe: the VSP1 pipeline + * @partition: the pre-calculated partition used by the pipeline + * @mutex: protects the streaming start/stop sequences + * @lock: protect access to the enabled flag + * @enabled: the enable flag + * @vspx_frame_end: frame end callback + * @frame_end_data: data for the frame end callback + */ +struct vsp1_vspx_pipeline { + struct vsp1_pipeline pipe; + struct vsp1_partition partition; + + /* + * Protects the streaming start/stop sequences. + * + * The start/stop sequences cannot be locked with the 'lock' spinlock + * as they acquire mutexes when handling the pm runtime and the vsp1 + * pipe start/stop operations. Provide a dedicated mutex for this + * reason. + */ + struct mutex mutex; + + /* + * Protects the enable flag. + * + * The enabled flag is contended between the start/stop streaming + * routines and the job_run one, which cannot take a mutex as it is + * called from the ISP irq context. + */ + spinlock_t lock; + bool enabled; + + void (*vspx_frame_end)(void *frame_end_data); + void *frame_end_data; +}; + +static inline struct vsp1_vspx_pipeline * +to_vsp1_vspx_pipeline(struct vsp1_pipeline *pipe) +{ + return container_of(pipe, struct vsp1_vspx_pipeline, pipe); +} + +/* + * struct vsp1_vspx - VSPX device + * @vsp1: the VSP1 device + * @pipe: the VSPX pipeline + */ +struct vsp1_vspx { + struct vsp1_device *vsp1; + struct vsp1_vspx_pipeline pipe; +}; + +/* Apply the given width, height and fourcc to the RWPF's subdevice */ +static int vsp1_vspx_rwpf_set_subdev_fmt(struct vsp1_device *vsp1, + struct vsp1_rwpf *rwpf, + u32 isp_fourcc, + unsigned int width, + unsigned int height) +{ + struct vsp1_entity *ent = &rwpf->entity; + struct v4l2_subdev_format format = {}; + u32 vspx_fourcc; + + switch (isp_fourcc) { + case V4L2_PIX_FMT_GREY: + /* 8 bit RAW Bayer image. */ + vspx_fourcc = V4L2_PIX_FMT_RGB332; + break; + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y12: + case V4L2_PIX_FMT_Y16: + /* 10, 12 and 16 bit RAW Bayer image. */ + vspx_fourcc = V4L2_PIX_FMT_RGB565; + break; + case V4L2_META_FMT_GENERIC_8: + /* ConfigDMA parameters buffer. */ + vspx_fourcc = V4L2_PIX_FMT_XBGR32; + break; + default: + return -EINVAL; + } + + rwpf->fmtinfo = vsp1_get_format_info(vsp1, vspx_fourcc); + + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = RWPF_PAD_SINK; + format.format.width = width; + format.format.height = height; + format.format.field = V4L2_FIELD_NONE; + format.format.code = rwpf->fmtinfo->mbus; + + return v4l2_subdev_call(&ent->subdev, pad, set_fmt, NULL, &format); +} + +/* Configure the RPF->IIF->WPF pipeline for ConfigDMA or RAW image transfer. */ +static int vsp1_vspx_pipeline_configure(struct vsp1_device *vsp1, + dma_addr_t addr, u32 isp_fourcc, + unsigned int width, unsigned int height, + unsigned int stride, + unsigned int iif_sink_pad, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe; + struct vsp1_pipeline *pipe = &vspx_pipe->pipe; + struct vsp1_rwpf *rpf0 = pipe->inputs[0]; + int ret; + + ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, rpf0, isp_fourcc, width, + height); + if (ret) + return ret; + + ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, pipe->output, isp_fourcc, + width, height); + if (ret) + return ret; + + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], width, 0); + rpf0->format.plane_fmt[0].bytesperline = stride; + rpf0->format.num_planes = 1; + rpf0->mem.addr[0] = addr; + + /* + * Connect RPF0 to the IIF sink pad corresponding to the config or image + * path. + */ + rpf0->entity.sink_pad = iif_sink_pad; + + vsp1_entity_route_setup(&rpf0->entity, pipe, dlb); + vsp1_entity_configure_stream(&rpf0->entity, rpf0->entity.state, pipe, + dl, dlb); + vsp1_entity_configure_partition(&rpf0->entity, pipe, + &pipe->part_table[0], dl, dlb); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Interrupt handling + */ + +static void vsp1_vspx_pipeline_frame_end(struct vsp1_pipeline *pipe, + unsigned int completion) +{ + struct vsp1_vspx_pipeline *vspx_pipe = to_vsp1_vspx_pipeline(pipe); + + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + /* + * Operating the vsp1_pipe in singleshot mode requires to + * manually set the pipeline state to stopped when a transfer + * is completed. + */ + pipe->state = VSP1_PIPELINE_STOPPED; + } + + if (vspx_pipe->vspx_frame_end) + vspx_pipe->vspx_frame_end(vspx_pipe->frame_end_data); +} + +/* ----------------------------------------------------------------------------- + * ISP Driver API (include/media/vsp1.h) + */ + +/** + * vsp1_isp_init() - Initialize the VSPX + * @dev: The VSP1 struct device + * + * Return: %0 on success or a negative error code on failure + */ +int vsp1_isp_init(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + if (!vsp1) + return -EPROBE_DEFER; + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_isp_init); + +/** + * vsp1_isp_get_bus_master - Get VSPX bus master + * @dev: The VSP1 struct device + * + * The VSPX accesses memory through an FCPX instance. When allocating memory + * buffers that will have to be accessed by the VSPX the 'struct device' of + * the FCPX should be used. Use this function to get a reference to it. + * + * Return: a pointer to the bus master's device + */ +struct device *vsp1_isp_get_bus_master(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + if (!vsp1) + return ERR_PTR(-ENODEV); + + return vsp1->bus_master; +} +EXPORT_SYMBOL_GPL(vsp1_isp_get_bus_master); + +/** + * vsp1_isp_alloc_buffer - Allocate a buffer in the VSPX address space + * @dev: The VSP1 struct device + * @size: The size of the buffer to be allocated by the VSPX + * @buffer_desc: The buffer descriptor. Will be filled with the buffer + * CPU-mapped address, the bus address and the size of the + * allocated buffer + * + * Allocate a buffer that will be later accessed by the VSPX. Buffers allocated + * using vsp1_isp_alloc_buffer() shall be released with a call to + * vsp1_isp_free_buffer(). This function is used by the ISP driver to allocate + * memory for the ConfigDMA parameters buffer. + * + * Return: %0 on success or a negative error code on failure + */ +int vsp1_isp_alloc_buffer(struct device *dev, size_t size, + struct vsp1_isp_buffer_desc *buffer_desc) +{ + struct device *bus_master = vsp1_isp_get_bus_master(dev); + + if (IS_ERR_OR_NULL(bus_master)) + return -ENODEV; + + buffer_desc->cpu_addr = dma_alloc_coherent(bus_master, size, + &buffer_desc->dma_addr, + GFP_KERNEL); + if (!buffer_desc->cpu_addr) + return -ENOMEM; + + buffer_desc->size = size; + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_isp_alloc_buffer); + +/** + * vsp1_isp_free_buffer - Release a buffer allocated by vsp1_isp_alloc_buffer() + * @dev: The VSP1 struct device + * @buffer_desc: The descriptor of the buffer to release as returned by + * vsp1_isp_alloc_buffer() + * + * Release memory in the VSPX address space allocated by + * vsp1_isp_alloc_buffer(). + */ +void vsp1_isp_free_buffer(struct device *dev, + struct vsp1_isp_buffer_desc *buffer_desc) +{ + struct device *bus_master = vsp1_isp_get_bus_master(dev); + + if (IS_ERR_OR_NULL(bus_master)) + return; + + dma_free_coherent(bus_master, buffer_desc->size, buffer_desc->cpu_addr, + buffer_desc->dma_addr); +} + +/** + * vsp1_isp_start_streaming - Start processing VSPX jobs + * @dev: The VSP1 struct device + * @frame_end: The frame end callback description + * + * Start the VSPX and prepare for accepting buffer transfer job requests. + * The caller is responsible for tracking the started state of the VSPX. + * Attempting to start an already started VSPX instance is an error. + * + * Return: %0 on success or a negative error code on failure + */ +int vsp1_isp_start_streaming(struct device *dev, + struct vsp1_vspx_frame_end *frame_end) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe; + struct vsp1_pipeline *pipe = &vspx_pipe->pipe; + u32 value; + int ret; + + if (!frame_end) + return -EINVAL; + + guard(mutex)(&vspx_pipe->mutex); + + scoped_guard(spinlock_irq, &vspx_pipe->lock) { + if (vspx_pipe->enabled) + return -EBUSY; + } + + vspx_pipe->vspx_frame_end = frame_end->vspx_frame_end; + vspx_pipe->frame_end_data = frame_end->frame_end_data; + + /* Enable the VSP1 and prepare for streaming. */ + vsp1_pipeline_dump(pipe, "VSPX job"); + + ret = vsp1_device_get(vsp1); + if (ret < 0) + return ret; + + /* + * Make sure VSPX is not active. This should never happen in normal + * usage + */ + value = vsp1_read(vsp1, VI6_CMD(0)); + if (value & VI6_CMD_STRCMD) { + dev_err(vsp1->dev, + "%s: Starting of WPF0 already reserved\n", __func__); + ret = -EBUSY; + goto error_put; + } + + value = vsp1_read(vsp1, VI6_STATUS); + if (value & VI6_STATUS_SYS_ACT(0)) { + dev_err(vsp1->dev, + "%s: WPF0 has not entered idle state\n", __func__); + ret = -EBUSY; + goto error_put; + } + + scoped_guard(spinlock_irq, &vspx_pipe->lock) { + vspx_pipe->enabled = true; + } + + return 0; + +error_put: + vsp1_device_put(vsp1); + return ret; +} +EXPORT_SYMBOL_GPL(vsp1_isp_start_streaming); + +/** + * vsp1_isp_stop_streaming - Stop the VSPX + * @dev: The VSP1 struct device + * + * Stop the VSPX operation by stopping the vsp1 pipeline and waiting for the + * last frame in transfer, if any, to complete. + * + * The caller is responsible for tracking the stopped state of the VSPX. + * Attempting to stop an already stopped VSPX instance is a nop. + */ +void vsp1_isp_stop_streaming(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe; + struct vsp1_pipeline *pipe = &vspx_pipe->pipe; + + guard(mutex)(&vspx_pipe->mutex); + + scoped_guard(spinlock_irq, &vspx_pipe->lock) { + if (!vspx_pipe->enabled) + return; + + vspx_pipe->enabled = false; + } + + WARN_ON_ONCE(vsp1_pipeline_stop(pipe)); + + vspx_pipe->vspx_frame_end = NULL; + vsp1_dlm_reset(pipe->output->dlm); + vsp1_device_put(vsp1); +} +EXPORT_SYMBOL_GPL(vsp1_isp_stop_streaming); + +/** + * vsp1_isp_job_prepare - Prepare a new buffer transfer job + * @dev: The VSP1 struct device + * @job: The job description + * + * Prepare a new buffer transfer job by populating a display list that will be + * later executed by a call to vsp1_isp_job_run(). All pending jobs must be + * released after stopping the streaming operations with a call to + * vsp1_isp_job_release(). + * + * In order for the VSPX to accept new jobs to prepare the VSPX must have been + * started. + * + * Return: %0 on success or a negative error code on failure + */ +int vsp1_isp_job_prepare(struct device *dev, struct vsp1_isp_job_desc *job) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe; + struct vsp1_pipeline *pipe = &vspx_pipe->pipe; + const struct v4l2_pix_format_mplane *pix_mp; + struct vsp1_dl_list *second_dl = NULL; + struct vsp1_dl_body *dlb; + struct vsp1_dl_list *dl; + int ret; + + /* + * Transfer the buffers described in the job: an optional ConfigDMA + * parameters buffer and a RAW image. + */ + + job->dl = vsp1_dl_list_get(pipe->output->dlm); + if (!job->dl) + return -ENOMEM; + + dl = job->dl; + dlb = vsp1_dl_list_get_body0(dl); + + /* Configure IIF routing and enable IIF function. */ + vsp1_entity_route_setup(pipe->iif, pipe, dlb); + vsp1_entity_configure_stream(pipe->iif, pipe->iif->state, pipe, + dl, dlb); + + /* Configure WPF0 to enable RPF0 as source. */ + vsp1_entity_route_setup(&pipe->output->entity, pipe, dlb); + vsp1_entity_configure_stream(&pipe->output->entity, + pipe->output->entity.state, pipe, + dl, dlb); + + if (job->config.pairs) { + /* + * Writing less than 17 pairs corrupts the output images ( < 16 + * pairs) or freezes the VSPX operations (= 16 pairs). Only + * allow more than 16 pairs to be written. + */ + if (job->config.pairs <= 16) { + ret = -EINVAL; + goto error_put_dl; + } + + /* + * Configure RPF0 for ConfigDMA data. Transfer the number of + * configuration pairs plus 2 words for the header. + */ + ret = vsp1_vspx_pipeline_configure(vsp1, job->config.mem, + V4L2_META_FMT_GENERIC_8, + job->config.pairs * 2 + 2, 1, + job->config.pairs * 2 + 2, + VSPX_IIF_SINK_PAD_CONFIG, + dl, dlb); + if (ret) + goto error_put_dl; + + second_dl = vsp1_dl_list_get(pipe->output->dlm); + if (!second_dl) { + ret = -ENOMEM; + goto error_put_dl; + } + + dl = second_dl; + dlb = vsp1_dl_list_get_body0(dl); + } + + /* Configure RPF0 for RAW image transfer. */ + pix_mp = &job->img.fmt; + ret = vsp1_vspx_pipeline_configure(vsp1, job->img.mem, + pix_mp->pixelformat, + pix_mp->width, pix_mp->height, + pix_mp->plane_fmt[0].bytesperline, + VSPX_IIF_SINK_PAD_IMG, dl, dlb); + if (ret) + goto error_put_dl; + + if (second_dl) + vsp1_dl_list_add_chain(job->dl, second_dl); + + return 0; + +error_put_dl: + if (second_dl) + vsp1_dl_list_put(second_dl); + vsp1_dl_list_put(job->dl); + job->dl = NULL; + return ret; +} +EXPORT_SYMBOL_GPL(vsp1_isp_job_prepare); + +/** + * vsp1_isp_job_run - Run a buffer transfer job + * @dev: The VSP1 struct device + * @job: The job to be run + * + * Run the display list contained in the job description provided by the caller. + * The job must have been prepared with a call to vsp1_isp_job_prepare() and + * the job's display list shall be valid. + * + * Jobs can be run only on VSPX instances which have been started. Requests + * to run a job after the VSPX has been stopped return -EINVAL and the job + * resources shall be released by the caller with vsp1_isp_job_release(). + * When a job is run successfully all the resources acquired by + * vsp1_isp_job_prepare() are released by this function and no further action + * is required to the caller. + * + * Return: %0 on success or a negative error code on failure + */ +int vsp1_isp_job_run(struct device *dev, struct vsp1_isp_job_desc *job) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe; + struct vsp1_pipeline *pipe = &vspx_pipe->pipe; + u32 value; + + /* Make sure VSPX is not busy processing a frame. */ + value = vsp1_read(vsp1, VI6_CMD(0)); + if (value) { + dev_err(vsp1->dev, + "%s: Starting of WPF0 already reserved\n", __func__); + return -EBUSY; + } + + scoped_guard(spinlock_irqsave, &vspx_pipe->lock) { + /* + * If a new job is scheduled when the VSPX is stopped, do not + * run it. + */ + if (!vspx_pipe->enabled) + return -EINVAL; + + vsp1_dl_list_commit(job->dl, 0); + + /* + * The display list is now under control of the display list + * manager and will be released automatically when the job + * completes. + */ + job->dl = NULL; + } + + scoped_guard(spinlock_irqsave, &pipe->irqlock) { + vsp1_pipeline_run(pipe); + } + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_isp_job_run); + +/** + * vsp1_isp_job_release - Release a non processed transfer job + * @dev: The VSP1 struct device + * @job: The job to release + * + * Release a job prepared by a call to vsp1_isp_job_prepare() and not yet + * run. All pending jobs shall be released after streaming has been stopped. + */ +void vsp1_isp_job_release(struct device *dev, + struct vsp1_isp_job_desc *job) +{ + vsp1_dl_list_put(job->dl); +} +EXPORT_SYMBOL_GPL(vsp1_isp_job_release); + +/* ----------------------------------------------------------------------------- + * Initialization and cleanup + */ + +int vsp1_vspx_init(struct vsp1_device *vsp1) +{ + struct vsp1_vspx_pipeline *vspx_pipe; + struct vsp1_pipeline *pipe; + + vsp1->vspx = devm_kzalloc(vsp1->dev, sizeof(*vsp1->vspx), GFP_KERNEL); + if (!vsp1->vspx) + return -ENOMEM; + + vsp1->vspx->vsp1 = vsp1; + + vspx_pipe = &vsp1->vspx->pipe; + vspx_pipe->enabled = false; + + pipe = &vspx_pipe->pipe; + + vsp1_pipeline_init(pipe); + + pipe->partitions = 1; + pipe->part_table = &vspx_pipe->partition; + pipe->interlaced = false; + pipe->frame_end = vsp1_vspx_pipeline_frame_end; + + mutex_init(&vspx_pipe->mutex); + spin_lock_init(&vspx_pipe->lock); + + /* + * Initialize RPF0 as input for VSPX and use it unconditionally for + * now. + */ + pipe->inputs[0] = vsp1->rpf[0]; + pipe->inputs[0]->entity.pipe = pipe; + pipe->inputs[0]->entity.sink = &vsp1->iif->entity; + list_add_tail(&pipe->inputs[0]->entity.list_pipe, &pipe->entities); + + pipe->iif = &vsp1->iif->entity; + pipe->iif->pipe = pipe; + pipe->iif->sink = &vsp1->wpf[0]->entity; + pipe->iif->sink_pad = RWPF_PAD_SINK; + list_add_tail(&pipe->iif->list_pipe, &pipe->entities); + + pipe->output = vsp1->wpf[0]; + pipe->output->entity.pipe = pipe; + list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities); + + return 0; +} + +void vsp1_vspx_cleanup(struct vsp1_device *vsp1) +{ + struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe; + + mutex_destroy(&vspx_pipe->mutex); +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.h b/drivers/media/platform/renesas/vsp1/vsp1_vspx.h new file mode 100644 index 000000000000..f871bf9e7dec --- /dev/null +++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vsp1_vspx.h -- R-Car Gen 4 VSPX + * + * Copyright (C) 2025 Ideas On Board Oy + * Copyright (C) 2025 Renesas Electronics Corporation + */ +#ifndef __VSP1_VSPX_H__ +#define __VSP1_VSPX_H__ + +#include "vsp1.h" + +int vsp1_vspx_init(struct vsp1_device *vsp1); +void vsp1_vspx_cleanup(struct vsp1_device *vsp1); + +#endif /* __VSP1_VSPX_H__ */ diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig index b41d3960c1b4..9bbeec4996aa 100644 --- a/drivers/media/platform/rockchip/Kconfig +++ b/drivers/media/platform/rockchip/Kconfig @@ -4,3 +4,4 @@ comment "Rockchip media platform drivers" source "drivers/media/platform/rockchip/rga/Kconfig" source "drivers/media/platform/rockchip/rkisp1/Kconfig" +source "drivers/media/platform/rockchip/rkvdec/Kconfig" diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile index 4f782b876ac9..286dc5c53f7e 100644 --- a/drivers/media/platform/rockchip/Makefile +++ b/drivers/media/platform/rockchip/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += rga/ obj-y += rkisp1/ +obj-y += rkvdec/ diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index ca952fd0829b..5f187f9efc7b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -415,6 +415,8 @@ struct rkisp1_params { spinlock_t config_lock; /* locks the buffers list 'params' */ struct list_head params; + struct v4l2_ctrl_handler ctrls; + const struct v4l2_meta_format *metafmt; enum v4l2_quantization quantization; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index b28f4140c8a3..f1585f8fa0f4 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -5,6 +5,7 @@ * Copyright (C) 2017 Rockchip Electronics Co., Ltd. */ +#include <linux/bitfield.h> #include <linux/math.h> #include <linux/string.h> @@ -60,6 +61,7 @@ union rkisp1_ext_params_config { struct rkisp1_ext_params_afc_config afc; struct rkisp1_ext_params_compand_bls_config compand_bls; struct rkisp1_ext_params_compand_curve_config compand_curve; + struct rkisp1_ext_params_wdr_config wdr; }; enum rkisp1_params_formats { @@ -1348,6 +1350,73 @@ rkisp1_compand_compress_config(struct rkisp1_params *params, arg->x); } +static void rkisp1_wdr_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_wdr_config *arg) +{ + unsigned int i; + u32 value; + + value = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_WDR_CTRL) + & ~(RKISP1_CIF_ISP_WDR_USE_IREF | + RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT | + RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE | + RKISP1_CIF_ISP_WDR_USE_Y9_8 | + RKISP1_CIF_ISP_WDR_USE_RGB7_8 | + RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT | + RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK); + + /* Colorspace and chrominance mapping */ + if (arg->use_rgb_colorspace) + value |= RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT; + + if (!arg->use_rgb_colorspace && arg->bypass_chroma_mapping) + value |= RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE; + + /* Illumination reference */ + if (arg->use_iref) { + value |= RKISP1_CIF_ISP_WDR_USE_IREF; + + if (arg->iref_config.use_y9_8) + value |= RKISP1_CIF_ISP_WDR_USE_Y9_8; + + if (arg->iref_config.use_rgb7_8) + value |= RKISP1_CIF_ISP_WDR_USE_RGB7_8; + + if (arg->iref_config.disable_transient) + value |= RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT; + + value |= FIELD_PREP(RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK, + min(arg->iref_config.rgb_factor, + RKISP1_CIF_ISP_WDR_RGB_FACTOR_MAX)); + } + + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_CTRL, value); + + /* RGB and Luminance offsets */ + value = FIELD_PREP(RKISP1_CIF_ISP_WDR_RGB_OFFSET_MASK, + arg->rgb_offset) + | FIELD_PREP(RKISP1_CIF_ISP_WDR_LUM_OFFSET_MASK, + arg->luma_offset); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_OFFSET, value); + + /* DeltaMin */ + value = FIELD_PREP(RKISP1_CIF_ISP_WDR_DMIN_THRESH_MASK, + arg->dmin_thresh) + | FIELD_PREP(RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK, + min(arg->dmin_strength, + RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX)); + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_DELTAMIN, value); + + /* Tone curve */ + for (i = 0; i < RKISP1_CIF_ISP_WDR_CURVE_NUM_DY_REGS; i++) + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_TONECURVE(i), + arg->tone_curve.dY[i]); + for (i = 0; i < RKISP1_CIF_ISP_WDR_CURVE_NUM_COEFF; i++) + rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_TONECURVE_YM(i), + arg->tone_curve.ym[i] & + RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK); +} + static void rkisp1_isp_isr_other_config(struct rkisp1_params *params, const struct rkisp1_params_cfg *new_params) @@ -2005,6 +2074,25 @@ static void rkisp1_ext_params_compand_compress(struct rkisp1_params *params, RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE); } +static void rkisp1_ext_params_wdr(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_wdr_config *wdr = &block->wdr; + + if (wdr->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_WDR_CTRL, + RKISP1_CIF_ISP_WDR_CTRL_ENABLE); + return; + } + + rkisp1_wdr_config(params, &wdr->config); + + if ((wdr->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(wdr->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_WDR_CTRL, + RKISP1_CIF_ISP_WDR_CTRL_ENABLE); +} + typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, const union rkisp1_ext_params_config *config); @@ -2118,6 +2206,11 @@ static const struct rkisp1_ext_params_handler { .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, .features = RKISP1_FEATURE_COMPAND, }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR] = { + .size = sizeof(struct rkisp1_ext_params_wdr_config), + .handler = rkisp1_ext_params_wdr, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, }; static void rkisp1_ext_params_config(struct rkisp1_params *params, @@ -2736,6 +2829,44 @@ static int rkisp1_params_init_vb2_queue(struct vb2_queue *q, return vb2_queue_init(q); } +static int rkisp1_params_ctrl_init(struct rkisp1_params *params) +{ + struct v4l2_ctrl_config ctrl_config = { + .id = RKISP1_CID_SUPPORTED_PARAMS_BLOCKS, + .name = "Supported Params Blocks", + .type = V4L2_CTRL_TYPE_BITMASK, + .flags = V4L2_CTRL_FLAG_READ_ONLY, + }; + int ret; + + v4l2_ctrl_handler_init(¶ms->ctrls, 1); + + for (unsigned int i = 0; i < ARRAY_SIZE(rkisp1_ext_params_handlers); i++) { + const struct rkisp1_ext_params_handler *block_handler; + + block_handler = &rkisp1_ext_params_handlers[i]; + ctrl_config.max |= BIT(i); + + if ((params->rkisp1->info->features & block_handler->features) != + block_handler->features) + continue; + + ctrl_config.def |= BIT(i); + } + + v4l2_ctrl_new_custom(¶ms->ctrls, &ctrl_config, NULL); + + params->vnode.vdev.ctrl_handler = ¶ms->ctrls; + + if (params->ctrls.error) { + ret = params->ctrls.error; + v4l2_ctrl_handler_free(¶ms->ctrls); + return ret; + } + + return 0; +} + int rkisp1_params_register(struct rkisp1_device *rkisp1) { struct rkisp1_params *params = &rkisp1->params; @@ -2763,7 +2894,9 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1) vdev->queue = &node->buf_queue; vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT; vdev->vfl_dir = VFL_DIR_TX; - rkisp1_params_init_vb2_queue(vdev->queue, params); + ret = rkisp1_params_init_vb2_queue(vdev->queue, params); + if (ret) + goto err_media; params->metafmt = &rkisp1_params_formats[RKISP1_PARAMS_FIXED]; @@ -2777,18 +2910,26 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1) node->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret) - goto error; + goto err_media; + + ret = rkisp1_params_ctrl_init(params); + if (ret) { + dev_err(rkisp1->dev, "Control initialization error %d\n", ret); + goto err_media; + } ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); - goto error; + goto err_ctrl; } return 0; -error: +err_ctrl: + v4l2_ctrl_handler_free(¶ms->ctrls); +err_media: media_entity_cleanup(&vdev->entity); mutex_destroy(&node->vlock); return ret; @@ -2804,6 +2945,7 @@ void rkisp1_params_unregister(struct rkisp1_device *rkisp1) return; vb2_video_unregister_device(vdev); + v4l2_ctrl_handler_free(¶ms->ctrls); media_entity_cleanup(&vdev->entity); mutex_destroy(&node->vlock); } diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index 139177db9c6d..fbeb186cde0d 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -703,6 +703,27 @@ #define RKISP1_CIF_ISP_COMPAND_CTRL_SOFT_RESET_FLAG BIT(2) #define RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE BIT(3) +/* WDR */ +/* ISP_WDR_CTRL */ +#define RKISP1_CIF_ISP_WDR_CTRL_ENABLE BIT(0) +#define RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT BIT(1) +#define RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE BIT(2) +#define RKISP1_CIF_ISP_WDR_USE_IREF BIT(3) +#define RKISP1_CIF_ISP_WDR_USE_Y9_8 BIT(4) +#define RKISP1_CIF_ISP_WDR_USE_RGB7_8 BIT(5) +#define RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT BIT(6) +#define RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK GENMASK(11, 8) +#define RKISP1_CIF_ISP_WDR_RGB_FACTOR_MAX 8U +/* ISP_WDR_TONE_CURVE_YM */ +#define RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK GENMASK(12, 0) +/* ISP_WDR_OFFSET */ +#define RKISP1_CIF_ISP_WDR_RGB_OFFSET_MASK GENMASK(11, 0) +#define RKISP1_CIF_ISP_WDR_LUM_OFFSET_MASK GENMASK(27, 16) +/* ISP_WDR_DELTAMIN */ +#define RKISP1_CIF_ISP_WDR_DMIN_THRESH_MASK GENMASK(11, 0) +#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK GENMASK(20, 16) +#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX 16U + /* =================================================================== */ /* CIF Registers */ /* =================================================================== */ @@ -1295,82 +1316,12 @@ #define RKISP1_CIF_ISP_WDR_BASE 0x00002a00 #define RKISP1_CIF_ISP_WDR_CTRL (RKISP1_CIF_ISP_WDR_BASE + 0x00000000) -#define RKISP1_CIF_ISP_WDR_TONECURVE_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000004) -#define RKISP1_CIF_ISP_WDR_TONECURVE_2 (RKISP1_CIF_ISP_WDR_BASE + 0x00000008) -#define RKISP1_CIF_ISP_WDR_TONECURVE_3 (RKISP1_CIF_ISP_WDR_BASE + 0x0000000c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000010) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0 (RKISP1_CIF_ISP_WDR_BASE + 0x00000014) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000018) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2 (RKISP1_CIF_ISP_WDR_BASE + 0x0000001c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3 (RKISP1_CIF_ISP_WDR_BASE + 0x00000020) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000024) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5 (RKISP1_CIF_ISP_WDR_BASE + 0x00000028) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6 (RKISP1_CIF_ISP_WDR_BASE + 0x0000002c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7 (RKISP1_CIF_ISP_WDR_BASE + 0x00000030) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8 (RKISP1_CIF_ISP_WDR_BASE + 0x00000034) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9 (RKISP1_CIF_ISP_WDR_BASE + 0x00000038) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10 (RKISP1_CIF_ISP_WDR_BASE + 0x0000003c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11 (RKISP1_CIF_ISP_WDR_BASE + 0x00000040) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12 (RKISP1_CIF_ISP_WDR_BASE + 0x00000044) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13 (RKISP1_CIF_ISP_WDR_BASE + 0x00000048) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14 (RKISP1_CIF_ISP_WDR_BASE + 0x0000004c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15 (RKISP1_CIF_ISP_WDR_BASE + 0x00000050) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16 (RKISP1_CIF_ISP_WDR_BASE + 0x00000054) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17 (RKISP1_CIF_ISP_WDR_BASE + 0x00000058) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18 (RKISP1_CIF_ISP_WDR_BASE + 0x0000005c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19 (RKISP1_CIF_ISP_WDR_BASE + 0x00000060) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20 (RKISP1_CIF_ISP_WDR_BASE + 0x00000064) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21 (RKISP1_CIF_ISP_WDR_BASE + 0x00000068) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22 (RKISP1_CIF_ISP_WDR_BASE + 0x0000006c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23 (RKISP1_CIF_ISP_WDR_BASE + 0x00000070) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24 (RKISP1_CIF_ISP_WDR_BASE + 0x00000074) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25 (RKISP1_CIF_ISP_WDR_BASE + 0x00000078) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26 (RKISP1_CIF_ISP_WDR_BASE + 0x0000007c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27 (RKISP1_CIF_ISP_WDR_BASE + 0x00000080) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28 (RKISP1_CIF_ISP_WDR_BASE + 0x00000084) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29 (RKISP1_CIF_ISP_WDR_BASE + 0x00000088) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30 (RKISP1_CIF_ISP_WDR_BASE + 0x0000008c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31 (RKISP1_CIF_ISP_WDR_BASE + 0x00000090) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32 (RKISP1_CIF_ISP_WDR_BASE + 0x00000094) +#define RKISP1_CIF_ISP_WDR_TONECURVE(n) (RKISP1_CIF_ISP_WDR_BASE + 0x00000004 + (n) * 4) +#define RKISP1_CIF_ISP_WDR_TONECURVE_YM(n) (RKISP1_CIF_ISP_WDR_BASE + 0x00000014 + (n) * 4) #define RKISP1_CIF_ISP_WDR_OFFSET (RKISP1_CIF_ISP_WDR_BASE + 0x00000098) #define RKISP1_CIF_ISP_WDR_DELTAMIN (RKISP1_CIF_ISP_WDR_BASE + 0x0000009c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a0) -#define RKISP1_CIF_ISP_WDR_TONECURVE_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a4) -#define RKISP1_CIF_ISP_WDR_TONECURVE_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a8) -#define RKISP1_CIF_ISP_WDR_TONECURVE_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000ac) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b0) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b4) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b8) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000bc) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c0) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c4) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c8) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000cc) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d0) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d4) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d8) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000dc) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e0) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e4) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e8) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000ec) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f0) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f4) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f8) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000fc) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000100) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000104) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000108) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000010c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000110) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000114) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000118) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000011c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000120) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000124) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000128) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000012c) -#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000130) +#define RKISP1_CIF_ISP_WDR_TONECURVE_SHD(n) (RKISP1_CIF_ISP_WDR_BASE + 0x000000a0 + (n) * 4) +#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_SHD(n) (RKISP1_CIF_ISP_WDR_BASE + 0x000000b0 + (n) * 4) #define RKISP1_CIF_ISP_HIST_BASE_V12 0x00002c00 #define RKISP1_CIF_ISP_HIST_CTRL_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x00000000) diff --git a/drivers/media/platform/rockchip/rkvdec/Kconfig b/drivers/media/platform/rockchip/rkvdec/Kconfig new file mode 100644 index 000000000000..5f3bdd848a2c --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_ROCKCHIP_VDEC + tristate "Rockchip Video Decoder driver" + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + select V4L2_H264 + select V4L2_VP9 + help + Support for the Rockchip Video Decoder IP present on Rockchip SoCs, + which accelerates video decoding. + To compile this driver as a module, choose M here: the module + will be called rockchip-vdec. diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile new file mode 100644 index 000000000000..cb86b429cfaa --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o + +rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-vp9.o diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c new file mode 100644 index 000000000000..d14b4d173448 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c @@ -0,0 +1,1212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Video Decoder H264 backend + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Jeffy Chen <jeffy.chen@rock-chips.com> + */ + +#include <media/v4l2-h264.h> +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" +#include "rkvdec-regs.h" + +/* Size with u32 units. */ +#define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128) +#define RKV_RPS_SIZE ((128 + 128) / 4) +#define RKV_ERROR_INFO_SIZE (256 * 144 * 4) + +#define RKVDEC_NUM_REFLIST 3 + +struct rkvdec_h264_scaling_list { + u8 scaling_list_4x4[6][16]; + u8 scaling_list_8x8[6][64]; + u8 padding[128]; +}; + +struct rkvdec_sps_pps_packet { + u32 info[8]; +}; + +struct rkvdec_ps_field { + u16 offset; + u8 len; +}; + +#define PS_FIELD(_offset, _len) \ + ((struct rkvdec_ps_field){ _offset, _len }) + +#define SEQ_PARAMETER_SET_ID PS_FIELD(0, 4) +#define PROFILE_IDC PS_FIELD(4, 8) +#define CONSTRAINT_SET3_FLAG PS_FIELD(12, 1) +#define CHROMA_FORMAT_IDC PS_FIELD(13, 2) +#define BIT_DEPTH_LUMA PS_FIELD(15, 3) +#define BIT_DEPTH_CHROMA PS_FIELD(18, 3) +#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG PS_FIELD(21, 1) +#define LOG2_MAX_FRAME_NUM_MINUS4 PS_FIELD(22, 4) +#define MAX_NUM_REF_FRAMES PS_FIELD(26, 5) +#define PIC_ORDER_CNT_TYPE PS_FIELD(31, 2) +#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 PS_FIELD(33, 4) +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG PS_FIELD(37, 1) +#define PIC_WIDTH_IN_MBS PS_FIELD(38, 9) +#define PIC_HEIGHT_IN_MBS PS_FIELD(47, 9) +#define FRAME_MBS_ONLY_FLAG PS_FIELD(56, 1) +#define MB_ADAPTIVE_FRAME_FIELD_FLAG PS_FIELD(57, 1) +#define DIRECT_8X8_INFERENCE_FLAG PS_FIELD(58, 1) +#define MVC_EXTENSION_ENABLE PS_FIELD(59, 1) +#define NUM_VIEWS PS_FIELD(60, 2) +#define VIEW_ID(i) PS_FIELD(62 + ((i) * 10), 10) +#define NUM_ANCHOR_REFS_L(i) PS_FIELD(82 + ((i) * 11), 1) +#define ANCHOR_REF_L(i) PS_FIELD(83 + ((i) * 11), 10) +#define NUM_NON_ANCHOR_REFS_L(i) PS_FIELD(104 + ((i) * 11), 1) +#define NON_ANCHOR_REFS_L(i) PS_FIELD(105 + ((i) * 11), 10) +#define PIC_PARAMETER_SET_ID PS_FIELD(128, 8) +#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(136, 5) +#define ENTROPY_CODING_MODE_FLAG PS_FIELD(141, 1) +#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG PS_FIELD(142, 1) +#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) PS_FIELD(143 + ((i) * 5), 5) +#define WEIGHTED_PRED_FLAG PS_FIELD(153, 1) +#define WEIGHTED_BIPRED_IDC PS_FIELD(154, 2) +#define PIC_INIT_QP_MINUS26 PS_FIELD(156, 7) +#define PIC_INIT_QS_MINUS26 PS_FIELD(163, 6) +#define CHROMA_QP_INDEX_OFFSET PS_FIELD(169, 5) +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG PS_FIELD(174, 1) +#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(175, 1) +#define REDUNDANT_PIC_CNT_PRESENT PS_FIELD(176, 1) +#define TRANSFORM_8X8_MODE_FLAG PS_FIELD(177, 1) +#define SECOND_CHROMA_QP_INDEX_OFFSET PS_FIELD(178, 5) +#define SCALING_LIST_ENABLE_FLAG PS_FIELD(183, 1) +#define SCALING_LIST_ADDRESS PS_FIELD(184, 32) +#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1) + +#define DPB_OFFS(i, j) (288 + ((j) * 32 * 7) + ((i) * 7)) +#define DPB_INFO(i, j) PS_FIELD(DPB_OFFS(i, j), 5) +#define BOTTOM_FLAG(i, j) PS_FIELD(DPB_OFFS(i, j) + 5, 1) +#define VIEW_INDEX_OFF(i, j) PS_FIELD(DPB_OFFS(i, j) + 6, 1) + +/* Data structure describing auxiliary buffer format. */ +struct rkvdec_h264_priv_tbl { + s8 cabac_table[4][464][2]; + struct rkvdec_h264_scaling_list scaling_list; + u32 rps[RKV_RPS_SIZE]; + struct rkvdec_sps_pps_packet param_set[256]; + u8 err_info[RKV_ERROR_INFO_SIZE]; +}; + +struct rkvdec_h264_reflists { + struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; +}; + +struct rkvdec_h264_run { + struct rkvdec_run base; + const struct v4l2_ctrl_h264_decode_params *decode_params; + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; + const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; + struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES]; +}; + +struct rkvdec_h264_ctx { + struct rkvdec_aux_buf priv_tbl; + struct rkvdec_h264_reflists reflists; +}; + +#define CABAC_ENTRY(ctxidx, idc0_m, idc0_n, idc1_m, idc1_n, \ + idc2_m, idc2_n, intra_m, intra_n) \ + [0][(ctxidx)] = {idc0_m, idc0_n}, \ + [1][(ctxidx)] = {idc1_m, idc1_n}, \ + [2][(ctxidx)] = {idc2_m, idc2_n}, \ + [3][(ctxidx)] = {intra_m, intra_n} + +/* + * Constant CABAC table. + * Built from the tables described in section '9.3.1.1 Initialisation process + * for context variables' of the H264 spec. + */ +static const s8 rkvdec_h264_cabac_table[4][464][2] = { + /* Table 9-12 – Values of variables m and n for ctxIdx from 0 to 10 */ + CABAC_ENTRY(0, 20, -15, 20, -15, 20, -15, 20, -15), + CABAC_ENTRY(1, 2, 54, 2, 54, 2, 54, 2, 54), + CABAC_ENTRY(2, 3, 74, 3, 74, 3, 74, 3, 74), + CABAC_ENTRY(3, 20, -15, 20, -15, 20, -15, 20, -15), + CABAC_ENTRY(4, 2, 54, 2, 54, 2, 54, 2, 54), + CABAC_ENTRY(5, 3, 74, 3, 74, 3, 74, 3, 74), + CABAC_ENTRY(6, -28, 127, -28, 127, -28, 127, -28, 127), + CABAC_ENTRY(7, -23, 104, -23, 104, -23, 104, -23, 104), + CABAC_ENTRY(8, -6, 53, -6, 53, -6, 53, -6, 53), + CABAC_ENTRY(9, -1, 54, -1, 54, -1, 54, -1, 54), + CABAC_ENTRY(10, 7, 51, 7, 51, 7, 51, 7, 51), + + /* Table 9-13 – Values of variables m and n for ctxIdx from 11 to 23 */ + CABAC_ENTRY(11, 23, 33, 22, 25, 29, 16, 0, 0), + CABAC_ENTRY(12, 23, 2, 34, 0, 25, 0, 0, 0), + CABAC_ENTRY(13, 21, 0, 16, 0, 14, 0, 0, 0), + CABAC_ENTRY(14, 1, 9, -2, 9, -10, 51, 0, 0), + CABAC_ENTRY(15, 0, 49, 4, 41, -3, 62, 0, 0), + CABAC_ENTRY(16, -37, 118, -29, 118, -27, 99, 0, 0), + CABAC_ENTRY(17, 5, 57, 2, 65, 26, 16, 0, 0), + CABAC_ENTRY(18, -13, 78, -6, 71, -4, 85, 0, 0), + CABAC_ENTRY(19, -11, 65, -13, 79, -24, 102, 0, 0), + CABAC_ENTRY(20, 1, 62, 5, 52, 5, 57, 0, 0), + CABAC_ENTRY(21, 12, 49, 9, 50, 6, 57, 0, 0), + CABAC_ENTRY(22, -4, 73, -3, 70, -17, 73, 0, 0), + CABAC_ENTRY(23, 17, 50, 10, 54, 14, 57, 0, 0), + + /* Table 9-14 – Values of variables m and n for ctxIdx from 24 to 39 */ + CABAC_ENTRY(24, 18, 64, 26, 34, 20, 40, 0, 0), + CABAC_ENTRY(25, 9, 43, 19, 22, 20, 10, 0, 0), + CABAC_ENTRY(26, 29, 0, 40, 0, 29, 0, 0, 0), + CABAC_ENTRY(27, 26, 67, 57, 2, 54, 0, 0, 0), + CABAC_ENTRY(28, 16, 90, 41, 36, 37, 42, 0, 0), + CABAC_ENTRY(29, 9, 104, 26, 69, 12, 97, 0, 0), + CABAC_ENTRY(30, -46, 127, -45, 127, -32, 127, 0, 0), + CABAC_ENTRY(31, -20, 104, -15, 101, -22, 117, 0, 0), + CABAC_ENTRY(32, 1, 67, -4, 76, -2, 74, 0, 0), + CABAC_ENTRY(33, -13, 78, -6, 71, -4, 85, 0, 0), + CABAC_ENTRY(34, -11, 65, -13, 79, -24, 102, 0, 0), + CABAC_ENTRY(35, 1, 62, 5, 52, 5, 57, 0, 0), + CABAC_ENTRY(36, -6, 86, 6, 69, -6, 93, 0, 0), + CABAC_ENTRY(37, -17, 95, -13, 90, -14, 88, 0, 0), + CABAC_ENTRY(38, -6, 61, 0, 52, -6, 44, 0, 0), + CABAC_ENTRY(39, 9, 45, 8, 43, 4, 55, 0, 0), + + /* Table 9-15 – Values of variables m and n for ctxIdx from 40 to 53 */ + CABAC_ENTRY(40, -3, 69, -2, 69, -11, 89, 0, 0), + CABAC_ENTRY(41, -6, 81, -5, 82, -15, 103, 0, 0), + CABAC_ENTRY(42, -11, 96, -10, 96, -21, 116, 0, 0), + CABAC_ENTRY(43, 6, 55, 2, 59, 19, 57, 0, 0), + CABAC_ENTRY(44, 7, 67, 2, 75, 20, 58, 0, 0), + CABAC_ENTRY(45, -5, 86, -3, 87, 4, 84, 0, 0), + CABAC_ENTRY(46, 2, 88, -3, 100, 6, 96, 0, 0), + CABAC_ENTRY(47, 0, 58, 1, 56, 1, 63, 0, 0), + CABAC_ENTRY(48, -3, 76, -3, 74, -5, 85, 0, 0), + CABAC_ENTRY(49, -10, 94, -6, 85, -13, 106, 0, 0), + CABAC_ENTRY(50, 5, 54, 0, 59, 5, 63, 0, 0), + CABAC_ENTRY(51, 4, 69, -3, 81, 6, 75, 0, 0), + CABAC_ENTRY(52, -3, 81, -7, 86, -3, 90, 0, 0), + CABAC_ENTRY(53, 0, 88, -5, 95, -1, 101, 0, 0), + + /* Table 9-16 – Values of variables m and n for ctxIdx from 54 to 59 */ + CABAC_ENTRY(54, -7, 67, -1, 66, 3, 55, 0, 0), + CABAC_ENTRY(55, -5, 74, -1, 77, -4, 79, 0, 0), + CABAC_ENTRY(56, -4, 74, 1, 70, -2, 75, 0, 0), + CABAC_ENTRY(57, -5, 80, -2, 86, -12, 97, 0, 0), + CABAC_ENTRY(58, -7, 72, -5, 72, -7, 50, 0, 0), + CABAC_ENTRY(59, 1, 58, 0, 61, 1, 60, 0, 0), + + /* Table 9-17 – Values of variables m and n for ctxIdx from 60 to 69 */ + CABAC_ENTRY(60, 0, 41, 0, 41, 0, 41, 0, 41), + CABAC_ENTRY(61, 0, 63, 0, 63, 0, 63, 0, 63), + CABAC_ENTRY(62, 0, 63, 0, 63, 0, 63, 0, 63), + CABAC_ENTRY(63, 0, 63, 0, 63, 0, 63, 0, 63), + CABAC_ENTRY(64, -9, 83, -9, 83, -9, 83, -9, 83), + CABAC_ENTRY(65, 4, 86, 4, 86, 4, 86, 4, 86), + CABAC_ENTRY(66, 0, 97, 0, 97, 0, 97, 0, 97), + CABAC_ENTRY(67, -7, 72, -7, 72, -7, 72, -7, 72), + CABAC_ENTRY(68, 13, 41, 13, 41, 13, 41, 13, 41), + CABAC_ENTRY(69, 3, 62, 3, 62, 3, 62, 3, 62), + + /* Table 9-18 – Values of variables m and n for ctxIdx from 70 to 104 */ + CABAC_ENTRY(70, 0, 45, 13, 15, 7, 34, 0, 11), + CABAC_ENTRY(71, -4, 78, 7, 51, -9, 88, 1, 55), + CABAC_ENTRY(72, -3, 96, 2, 80, -20, 127, 0, 69), + CABAC_ENTRY(73, -27, 126, -39, 127, -36, 127, -17, 127), + CABAC_ENTRY(74, -28, 98, -18, 91, -17, 91, -13, 102), + CABAC_ENTRY(75, -25, 101, -17, 96, -14, 95, 0, 82), + CABAC_ENTRY(76, -23, 67, -26, 81, -25, 84, -7, 74), + CABAC_ENTRY(77, -28, 82, -35, 98, -25, 86, -21, 107), + CABAC_ENTRY(78, -20, 94, -24, 102, -12, 89, -27, 127), + CABAC_ENTRY(79, -16, 83, -23, 97, -17, 91, -31, 127), + CABAC_ENTRY(80, -22, 110, -27, 119, -31, 127, -24, 127), + CABAC_ENTRY(81, -21, 91, -24, 99, -14, 76, -18, 95), + CABAC_ENTRY(82, -18, 102, -21, 110, -18, 103, -27, 127), + CABAC_ENTRY(83, -13, 93, -18, 102, -13, 90, -21, 114), + CABAC_ENTRY(84, -29, 127, -36, 127, -37, 127, -30, 127), + CABAC_ENTRY(85, -7, 92, 0, 80, 11, 80, -17, 123), + CABAC_ENTRY(86, -5, 89, -5, 89, 5, 76, -12, 115), + CABAC_ENTRY(87, -7, 96, -7, 94, 2, 84, -16, 122), + CABAC_ENTRY(88, -13, 108, -4, 92, 5, 78, -11, 115), + CABAC_ENTRY(89, -3, 46, 0, 39, -6, 55, -12, 63), + CABAC_ENTRY(90, -1, 65, 0, 65, 4, 61, -2, 68), + CABAC_ENTRY(91, -1, 57, -15, 84, -14, 83, -15, 84), + CABAC_ENTRY(92, -9, 93, -35, 127, -37, 127, -13, 104), + CABAC_ENTRY(93, -3, 74, -2, 73, -5, 79, -3, 70), + CABAC_ENTRY(94, -9, 92, -12, 104, -11, 104, -8, 93), + CABAC_ENTRY(95, -8, 87, -9, 91, -11, 91, -10, 90), + CABAC_ENTRY(96, -23, 126, -31, 127, -30, 127, -30, 127), + CABAC_ENTRY(97, 5, 54, 3, 55, 0, 65, -1, 74), + CABAC_ENTRY(98, 6, 60, 7, 56, -2, 79, -6, 97), + CABAC_ENTRY(99, 6, 59, 7, 55, 0, 72, -7, 91), + CABAC_ENTRY(100, 6, 69, 8, 61, -4, 92, -20, 127), + CABAC_ENTRY(101, -1, 48, -3, 53, -6, 56, -4, 56), + CABAC_ENTRY(102, 0, 68, 0, 68, 3, 68, -5, 82), + CABAC_ENTRY(103, -4, 69, -7, 74, -8, 71, -7, 76), + CABAC_ENTRY(104, -8, 88, -9, 88, -13, 98, -22, 125), + + /* Table 9-19 – Values of variables m and n for ctxIdx from 105 to 165 */ + CABAC_ENTRY(105, -2, 85, -13, 103, -4, 86, -7, 93), + CABAC_ENTRY(106, -6, 78, -13, 91, -12, 88, -11, 87), + CABAC_ENTRY(107, -1, 75, -9, 89, -5, 82, -3, 77), + CABAC_ENTRY(108, -7, 77, -14, 92, -3, 72, -5, 71), + CABAC_ENTRY(109, 2, 54, -8, 76, -4, 67, -4, 63), + CABAC_ENTRY(110, 5, 50, -12, 87, -8, 72, -4, 68), + CABAC_ENTRY(111, -3, 68, -23, 110, -16, 89, -12, 84), + CABAC_ENTRY(112, 1, 50, -24, 105, -9, 69, -7, 62), + CABAC_ENTRY(113, 6, 42, -10, 78, -1, 59, -7, 65), + CABAC_ENTRY(114, -4, 81, -20, 112, 5, 66, 8, 61), + CABAC_ENTRY(115, 1, 63, -17, 99, 4, 57, 5, 56), + CABAC_ENTRY(116, -4, 70, -78, 127, -4, 71, -2, 66), + CABAC_ENTRY(117, 0, 67, -70, 127, -2, 71, 1, 64), + CABAC_ENTRY(118, 2, 57, -50, 127, 2, 58, 0, 61), + CABAC_ENTRY(119, -2, 76, -46, 127, -1, 74, -2, 78), + CABAC_ENTRY(120, 11, 35, -4, 66, -4, 44, 1, 50), + CABAC_ENTRY(121, 4, 64, -5, 78, -1, 69, 7, 52), + CABAC_ENTRY(122, 1, 61, -4, 71, 0, 62, 10, 35), + CABAC_ENTRY(123, 11, 35, -8, 72, -7, 51, 0, 44), + CABAC_ENTRY(124, 18, 25, 2, 59, -4, 47, 11, 38), + CABAC_ENTRY(125, 12, 24, -1, 55, -6, 42, 1, 45), + CABAC_ENTRY(126, 13, 29, -7, 70, -3, 41, 0, 46), + CABAC_ENTRY(127, 13, 36, -6, 75, -6, 53, 5, 44), + CABAC_ENTRY(128, -10, 93, -8, 89, 8, 76, 31, 17), + CABAC_ENTRY(129, -7, 73, -34, 119, -9, 78, 1, 51), + CABAC_ENTRY(130, -2, 73, -3, 75, -11, 83, 7, 50), + CABAC_ENTRY(131, 13, 46, 32, 20, 9, 52, 28, 19), + CABAC_ENTRY(132, 9, 49, 30, 22, 0, 67, 16, 33), + CABAC_ENTRY(133, -7, 100, -44, 127, -5, 90, 14, 62), + CABAC_ENTRY(134, 9, 53, 0, 54, 1, 67, -13, 108), + CABAC_ENTRY(135, 2, 53, -5, 61, -15, 72, -15, 100), + CABAC_ENTRY(136, 5, 53, 0, 58, -5, 75, -13, 101), + CABAC_ENTRY(137, -2, 61, -1, 60, -8, 80, -13, 91), + CABAC_ENTRY(138, 0, 56, -3, 61, -21, 83, -12, 94), + CABAC_ENTRY(139, 0, 56, -8, 67, -21, 64, -10, 88), + CABAC_ENTRY(140, -13, 63, -25, 84, -13, 31, -16, 84), + CABAC_ENTRY(141, -5, 60, -14, 74, -25, 64, -10, 86), + CABAC_ENTRY(142, -1, 62, -5, 65, -29, 94, -7, 83), + CABAC_ENTRY(143, 4, 57, 5, 52, 9, 75, -13, 87), + CABAC_ENTRY(144, -6, 69, 2, 57, 17, 63, -19, 94), + CABAC_ENTRY(145, 4, 57, 0, 61, -8, 74, 1, 70), + CABAC_ENTRY(146, 14, 39, -9, 69, -5, 35, 0, 72), + CABAC_ENTRY(147, 4, 51, -11, 70, -2, 27, -5, 74), + CABAC_ENTRY(148, 13, 68, 18, 55, 13, 91, 18, 59), + CABAC_ENTRY(149, 3, 64, -4, 71, 3, 65, -8, 102), + CABAC_ENTRY(150, 1, 61, 0, 58, -7, 69, -15, 100), + CABAC_ENTRY(151, 9, 63, 7, 61, 8, 77, 0, 95), + CABAC_ENTRY(152, 7, 50, 9, 41, -10, 66, -4, 75), + CABAC_ENTRY(153, 16, 39, 18, 25, 3, 62, 2, 72), + CABAC_ENTRY(154, 5, 44, 9, 32, -3, 68, -11, 75), + CABAC_ENTRY(155, 4, 52, 5, 43, -20, 81, -3, 71), + CABAC_ENTRY(156, 11, 48, 9, 47, 0, 30, 15, 46), + CABAC_ENTRY(157, -5, 60, 0, 44, 1, 7, -13, 69), + CABAC_ENTRY(158, -1, 59, 0, 51, -3, 23, 0, 62), + CABAC_ENTRY(159, 0, 59, 2, 46, -21, 74, 0, 65), + CABAC_ENTRY(160, 22, 33, 19, 38, 16, 66, 21, 37), + CABAC_ENTRY(161, 5, 44, -4, 66, -23, 124, -15, 72), + CABAC_ENTRY(162, 14, 43, 15, 38, 17, 37, 9, 57), + CABAC_ENTRY(163, -1, 78, 12, 42, 44, -18, 16, 54), + CABAC_ENTRY(164, 0, 60, 9, 34, 50, -34, 0, 62), + CABAC_ENTRY(165, 9, 69, 0, 89, -22, 127, 12, 72), + + /* Table 9-20 – Values of variables m and n for ctxIdx from 166 to 226 */ + CABAC_ENTRY(166, 11, 28, 4, 45, 4, 39, 24, 0), + CABAC_ENTRY(167, 2, 40, 10, 28, 0, 42, 15, 9), + CABAC_ENTRY(168, 3, 44, 10, 31, 7, 34, 8, 25), + CABAC_ENTRY(169, 0, 49, 33, -11, 11, 29, 13, 18), + CABAC_ENTRY(170, 0, 46, 52, -43, 8, 31, 15, 9), + CABAC_ENTRY(171, 2, 44, 18, 15, 6, 37, 13, 19), + CABAC_ENTRY(172, 2, 51, 28, 0, 7, 42, 10, 37), + CABAC_ENTRY(173, 0, 47, 35, -22, 3, 40, 12, 18), + CABAC_ENTRY(174, 4, 39, 38, -25, 8, 33, 6, 29), + CABAC_ENTRY(175, 2, 62, 34, 0, 13, 43, 20, 33), + CABAC_ENTRY(176, 6, 46, 39, -18, 13, 36, 15, 30), + CABAC_ENTRY(177, 0, 54, 32, -12, 4, 47, 4, 45), + CABAC_ENTRY(178, 3, 54, 102, -94, 3, 55, 1, 58), + CABAC_ENTRY(179, 2, 58, 0, 0, 2, 58, 0, 62), + CABAC_ENTRY(180, 4, 63, 56, -15, 6, 60, 7, 61), + CABAC_ENTRY(181, 6, 51, 33, -4, 8, 44, 12, 38), + CABAC_ENTRY(182, 6, 57, 29, 10, 11, 44, 11, 45), + CABAC_ENTRY(183, 7, 53, 37, -5, 14, 42, 15, 39), + CABAC_ENTRY(184, 6, 52, 51, -29, 7, 48, 11, 42), + CABAC_ENTRY(185, 6, 55, 39, -9, 4, 56, 13, 44), + CABAC_ENTRY(186, 11, 45, 52, -34, 4, 52, 16, 45), + CABAC_ENTRY(187, 14, 36, 69, -58, 13, 37, 12, 41), + CABAC_ENTRY(188, 8, 53, 67, -63, 9, 49, 10, 49), + CABAC_ENTRY(189, -1, 82, 44, -5, 19, 58, 30, 34), + CABAC_ENTRY(190, 7, 55, 32, 7, 10, 48, 18, 42), + CABAC_ENTRY(191, -3, 78, 55, -29, 12, 45, 10, 55), + CABAC_ENTRY(192, 15, 46, 32, 1, 0, 69, 17, 51), + CABAC_ENTRY(193, 22, 31, 0, 0, 20, 33, 17, 46), + CABAC_ENTRY(194, -1, 84, 27, 36, 8, 63, 0, 89), + CABAC_ENTRY(195, 25, 7, 33, -25, 35, -18, 26, -19), + CABAC_ENTRY(196, 30, -7, 34, -30, 33, -25, 22, -17), + CABAC_ENTRY(197, 28, 3, 36, -28, 28, -3, 26, -17), + CABAC_ENTRY(198, 28, 4, 38, -28, 24, 10, 30, -25), + CABAC_ENTRY(199, 32, 0, 38, -27, 27, 0, 28, -20), + CABAC_ENTRY(200, 34, -1, 34, -18, 34, -14, 33, -23), + CABAC_ENTRY(201, 30, 6, 35, -16, 52, -44, 37, -27), + CABAC_ENTRY(202, 30, 6, 34, -14, 39, -24, 33, -23), + CABAC_ENTRY(203, 32, 9, 32, -8, 19, 17, 40, -28), + CABAC_ENTRY(204, 31, 19, 37, -6, 31, 25, 38, -17), + CABAC_ENTRY(205, 26, 27, 35, 0, 36, 29, 33, -11), + CABAC_ENTRY(206, 26, 30, 30, 10, 24, 33, 40, -15), + CABAC_ENTRY(207, 37, 20, 28, 18, 34, 15, 41, -6), + CABAC_ENTRY(208, 28, 34, 26, 25, 30, 20, 38, 1), + CABAC_ENTRY(209, 17, 70, 29, 41, 22, 73, 41, 17), + CABAC_ENTRY(210, 1, 67, 0, 75, 20, 34, 30, -6), + CABAC_ENTRY(211, 5, 59, 2, 72, 19, 31, 27, 3), + CABAC_ENTRY(212, 9, 67, 8, 77, 27, 44, 26, 22), + CABAC_ENTRY(213, 16, 30, 14, 35, 19, 16, 37, -16), + CABAC_ENTRY(214, 18, 32, 18, 31, 15, 36, 35, -4), + CABAC_ENTRY(215, 18, 35, 17, 35, 15, 36, 38, -8), + CABAC_ENTRY(216, 22, 29, 21, 30, 21, 28, 38, -3), + CABAC_ENTRY(217, 24, 31, 17, 45, 25, 21, 37, 3), + CABAC_ENTRY(218, 23, 38, 20, 42, 30, 20, 38, 5), + CABAC_ENTRY(219, 18, 43, 18, 45, 31, 12, 42, 0), + CABAC_ENTRY(220, 20, 41, 27, 26, 27, 16, 35, 16), + CABAC_ENTRY(221, 11, 63, 16, 54, 24, 42, 39, 22), + CABAC_ENTRY(222, 9, 59, 7, 66, 0, 93, 14, 48), + CABAC_ENTRY(223, 9, 64, 16, 56, 14, 56, 27, 37), + CABAC_ENTRY(224, -1, 94, 11, 73, 15, 57, 21, 60), + CABAC_ENTRY(225, -2, 89, 10, 67, 26, 38, 12, 68), + CABAC_ENTRY(226, -9, 108, -10, 116, -24, 127, 2, 97), + + /* Table 9-21 – Values of variables m and n for ctxIdx from 227 to 275 */ + CABAC_ENTRY(227, -6, 76, -23, 112, -24, 115, -3, 71), + CABAC_ENTRY(228, -2, 44, -15, 71, -22, 82, -6, 42), + CABAC_ENTRY(229, 0, 45, -7, 61, -9, 62, -5, 50), + CABAC_ENTRY(230, 0, 52, 0, 53, 0, 53, -3, 54), + CABAC_ENTRY(231, -3, 64, -5, 66, 0, 59, -2, 62), + CABAC_ENTRY(232, -2, 59, -11, 77, -14, 85, 0, 58), + CABAC_ENTRY(233, -4, 70, -9, 80, -13, 89, 1, 63), + CABAC_ENTRY(234, -4, 75, -9, 84, -13, 94, -2, 72), + CABAC_ENTRY(235, -8, 82, -10, 87, -11, 92, -1, 74), + CABAC_ENTRY(236, -17, 102, -34, 127, -29, 127, -9, 91), + CABAC_ENTRY(237, -9, 77, -21, 101, -21, 100, -5, 67), + CABAC_ENTRY(238, 3, 24, -3, 39, -14, 57, -5, 27), + CABAC_ENTRY(239, 0, 42, -5, 53, -12, 67, -3, 39), + CABAC_ENTRY(240, 0, 48, -7, 61, -11, 71, -2, 44), + CABAC_ENTRY(241, 0, 55, -11, 75, -10, 77, 0, 46), + CABAC_ENTRY(242, -6, 59, -15, 77, -21, 85, -16, 64), + CABAC_ENTRY(243, -7, 71, -17, 91, -16, 88, -8, 68), + CABAC_ENTRY(244, -12, 83, -25, 107, -23, 104, -10, 78), + CABAC_ENTRY(245, -11, 87, -25, 111, -15, 98, -6, 77), + CABAC_ENTRY(246, -30, 119, -28, 122, -37, 127, -10, 86), + CABAC_ENTRY(247, 1, 58, -11, 76, -10, 82, -12, 92), + CABAC_ENTRY(248, -3, 29, -10, 44, -8, 48, -15, 55), + CABAC_ENTRY(249, -1, 36, -10, 52, -8, 61, -10, 60), + CABAC_ENTRY(250, 1, 38, -10, 57, -8, 66, -6, 62), + CABAC_ENTRY(251, 2, 43, -9, 58, -7, 70, -4, 65), + CABAC_ENTRY(252, -6, 55, -16, 72, -14, 75, -12, 73), + CABAC_ENTRY(253, 0, 58, -7, 69, -10, 79, -8, 76), + CABAC_ENTRY(254, 0, 64, -4, 69, -9, 83, -7, 80), + CABAC_ENTRY(255, -3, 74, -5, 74, -12, 92, -9, 88), + CABAC_ENTRY(256, -10, 90, -9, 86, -18, 108, -17, 110), + CABAC_ENTRY(257, 0, 70, 2, 66, -4, 79, -11, 97), + CABAC_ENTRY(258, -4, 29, -9, 34, -22, 69, -20, 84), + CABAC_ENTRY(259, 5, 31, 1, 32, -16, 75, -11, 79), + CABAC_ENTRY(260, 7, 42, 11, 31, -2, 58, -6, 73), + CABAC_ENTRY(261, 1, 59, 5, 52, 1, 58, -4, 74), + CABAC_ENTRY(262, -2, 58, -2, 55, -13, 78, -13, 86), + CABAC_ENTRY(263, -3, 72, -2, 67, -9, 83, -13, 96), + CABAC_ENTRY(264, -3, 81, 0, 73, -4, 81, -11, 97), + CABAC_ENTRY(265, -11, 97, -8, 89, -13, 99, -19, 117), + CABAC_ENTRY(266, 0, 58, 3, 52, -13, 81, -8, 78), + CABAC_ENTRY(267, 8, 5, 7, 4, -6, 38, -5, 33), + CABAC_ENTRY(268, 10, 14, 10, 8, -13, 62, -4, 48), + CABAC_ENTRY(269, 14, 18, 17, 8, -6, 58, -2, 53), + CABAC_ENTRY(270, 13, 27, 16, 19, -2, 59, -3, 62), + CABAC_ENTRY(271, 2, 40, 3, 37, -16, 73, -13, 71), + CABAC_ENTRY(272, 0, 58, -1, 61, -10, 76, -10, 79), + CABAC_ENTRY(273, -3, 70, -5, 73, -13, 86, -12, 86), + CABAC_ENTRY(274, -6, 79, -1, 70, -9, 83, -13, 90), + CABAC_ENTRY(275, -8, 85, -4, 78, -10, 87, -14, 97), + + /* Table 9-22 – Values of variables m and n for ctxIdx from 277 to 337 */ + CABAC_ENTRY(277, -13, 106, -21, 126, -22, 127, -6, 93), + CABAC_ENTRY(278, -16, 106, -23, 124, -25, 127, -6, 84), + CABAC_ENTRY(279, -10, 87, -20, 110, -25, 120, -8, 79), + CABAC_ENTRY(280, -21, 114, -26, 126, -27, 127, 0, 66), + CABAC_ENTRY(281, -18, 110, -25, 124, -19, 114, -1, 71), + CABAC_ENTRY(282, -14, 98, -17, 105, -23, 117, 0, 62), + CABAC_ENTRY(283, -22, 110, -27, 121, -25, 118, -2, 60), + CABAC_ENTRY(284, -21, 106, -27, 117, -26, 117, -2, 59), + CABAC_ENTRY(285, -18, 103, -17, 102, -24, 113, -5, 75), + CABAC_ENTRY(286, -21, 107, -26, 117, -28, 118, -3, 62), + CABAC_ENTRY(287, -23, 108, -27, 116, -31, 120, -4, 58), + CABAC_ENTRY(288, -26, 112, -33, 122, -37, 124, -9, 66), + CABAC_ENTRY(289, -10, 96, -10, 95, -10, 94, -1, 79), + CABAC_ENTRY(290, -12, 95, -14, 100, -15, 102, 0, 71), + CABAC_ENTRY(291, -5, 91, -8, 95, -10, 99, 3, 68), + CABAC_ENTRY(292, -9, 93, -17, 111, -13, 106, 10, 44), + CABAC_ENTRY(293, -22, 94, -28, 114, -50, 127, -7, 62), + CABAC_ENTRY(294, -5, 86, -6, 89, -5, 92, 15, 36), + CABAC_ENTRY(295, 9, 67, -2, 80, 17, 57, 14, 40), + CABAC_ENTRY(296, -4, 80, -4, 82, -5, 86, 16, 27), + CABAC_ENTRY(297, -10, 85, -9, 85, -13, 94, 12, 29), + CABAC_ENTRY(298, -1, 70, -8, 81, -12, 91, 1, 44), + CABAC_ENTRY(299, 7, 60, -1, 72, -2, 77, 20, 36), + CABAC_ENTRY(300, 9, 58, 5, 64, 0, 71, 18, 32), + CABAC_ENTRY(301, 5, 61, 1, 67, -1, 73, 5, 42), + CABAC_ENTRY(302, 12, 50, 9, 56, 4, 64, 1, 48), + CABAC_ENTRY(303, 15, 50, 0, 69, -7, 81, 10, 62), + CABAC_ENTRY(304, 18, 49, 1, 69, 5, 64, 17, 46), + CABAC_ENTRY(305, 17, 54, 7, 69, 15, 57, 9, 64), + CABAC_ENTRY(306, 10, 41, -7, 69, 1, 67, -12, 104), + CABAC_ENTRY(307, 7, 46, -6, 67, 0, 68, -11, 97), + CABAC_ENTRY(308, -1, 51, -16, 77, -10, 67, -16, 96), + CABAC_ENTRY(309, 7, 49, -2, 64, 1, 68, -7, 88), + CABAC_ENTRY(310, 8, 52, 2, 61, 0, 77, -8, 85), + CABAC_ENTRY(311, 9, 41, -6, 67, 2, 64, -7, 85), + CABAC_ENTRY(312, 6, 47, -3, 64, 0, 68, -9, 85), + CABAC_ENTRY(313, 2, 55, 2, 57, -5, 78, -13, 88), + CABAC_ENTRY(314, 13, 41, -3, 65, 7, 55, 4, 66), + CABAC_ENTRY(315, 10, 44, -3, 66, 5, 59, -3, 77), + CABAC_ENTRY(316, 6, 50, 0, 62, 2, 65, -3, 76), + CABAC_ENTRY(317, 5, 53, 9, 51, 14, 54, -6, 76), + CABAC_ENTRY(318, 13, 49, -1, 66, 15, 44, 10, 58), + CABAC_ENTRY(319, 4, 63, -2, 71, 5, 60, -1, 76), + CABAC_ENTRY(320, 6, 64, -2, 75, 2, 70, -1, 83), + CABAC_ENTRY(321, -2, 69, -1, 70, -2, 76, -7, 99), + CABAC_ENTRY(322, -2, 59, -9, 72, -18, 86, -14, 95), + CABAC_ENTRY(323, 6, 70, 14, 60, 12, 70, 2, 95), + CABAC_ENTRY(324, 10, 44, 16, 37, 5, 64, 0, 76), + CABAC_ENTRY(325, 9, 31, 0, 47, -12, 70, -5, 74), + CABAC_ENTRY(326, 12, 43, 18, 35, 11, 55, 0, 70), + CABAC_ENTRY(327, 3, 53, 11, 37, 5, 56, -11, 75), + CABAC_ENTRY(328, 14, 34, 12, 41, 0, 69, 1, 68), + CABAC_ENTRY(329, 10, 38, 10, 41, 2, 65, 0, 65), + CABAC_ENTRY(330, -3, 52, 2, 48, -6, 74, -14, 73), + CABAC_ENTRY(331, 13, 40, 12, 41, 5, 54, 3, 62), + CABAC_ENTRY(332, 17, 32, 13, 41, 7, 54, 4, 62), + CABAC_ENTRY(333, 7, 44, 0, 59, -6, 76, -1, 68), + CABAC_ENTRY(334, 7, 38, 3, 50, -11, 82, -13, 75), + CABAC_ENTRY(335, 13, 50, 19, 40, -2, 77, 11, 55), + CABAC_ENTRY(336, 10, 57, 3, 66, -2, 77, 5, 64), + CABAC_ENTRY(337, 26, 43, 18, 50, 25, 42, 12, 70), + + /* Table 9-23 – Values of variables m and n for ctxIdx from 338 to 398 */ + CABAC_ENTRY(338, 14, 11, 19, -6, 17, -13, 15, 6), + CABAC_ENTRY(339, 11, 14, 18, -6, 16, -9, 6, 19), + CABAC_ENTRY(340, 9, 11, 14, 0, 17, -12, 7, 16), + CABAC_ENTRY(341, 18, 11, 26, -12, 27, -21, 12, 14), + CABAC_ENTRY(342, 21, 9, 31, -16, 37, -30, 18, 13), + CABAC_ENTRY(343, 23, -2, 33, -25, 41, -40, 13, 11), + CABAC_ENTRY(344, 32, -15, 33, -22, 42, -41, 13, 15), + CABAC_ENTRY(345, 32, -15, 37, -28, 48, -47, 15, 16), + CABAC_ENTRY(346, 34, -21, 39, -30, 39, -32, 12, 23), + CABAC_ENTRY(347, 39, -23, 42, -30, 46, -40, 13, 23), + CABAC_ENTRY(348, 42, -33, 47, -42, 52, -51, 15, 20), + CABAC_ENTRY(349, 41, -31, 45, -36, 46, -41, 14, 26), + CABAC_ENTRY(350, 46, -28, 49, -34, 52, -39, 14, 44), + CABAC_ENTRY(351, 38, -12, 41, -17, 43, -19, 17, 40), + CABAC_ENTRY(352, 21, 29, 32, 9, 32, 11, 17, 47), + CABAC_ENTRY(353, 45, -24, 69, -71, 61, -55, 24, 17), + CABAC_ENTRY(354, 53, -45, 63, -63, 56, -46, 21, 21), + CABAC_ENTRY(355, 48, -26, 66, -64, 62, -50, 25, 22), + CABAC_ENTRY(356, 65, -43, 77, -74, 81, -67, 31, 27), + CABAC_ENTRY(357, 43, -19, 54, -39, 45, -20, 22, 29), + CABAC_ENTRY(358, 39, -10, 52, -35, 35, -2, 19, 35), + CABAC_ENTRY(359, 30, 9, 41, -10, 28, 15, 14, 50), + CABAC_ENTRY(360, 18, 26, 36, 0, 34, 1, 10, 57), + CABAC_ENTRY(361, 20, 27, 40, -1, 39, 1, 7, 63), + CABAC_ENTRY(362, 0, 57, 30, 14, 30, 17, -2, 77), + CABAC_ENTRY(363, -14, 82, 28, 26, 20, 38, -4, 82), + CABAC_ENTRY(364, -5, 75, 23, 37, 18, 45, -3, 94), + CABAC_ENTRY(365, -19, 97, 12, 55, 15, 54, 9, 69), + CABAC_ENTRY(366, -35, 125, 11, 65, 0, 79, -12, 109), + CABAC_ENTRY(367, 27, 0, 37, -33, 36, -16, 36, -35), + CABAC_ENTRY(368, 28, 0, 39, -36, 37, -14, 36, -34), + CABAC_ENTRY(369, 31, -4, 40, -37, 37, -17, 32, -26), + CABAC_ENTRY(370, 27, 6, 38, -30, 32, 1, 37, -30), + CABAC_ENTRY(371, 34, 8, 46, -33, 34, 15, 44, -32), + CABAC_ENTRY(372, 30, 10, 42, -30, 29, 15, 34, -18), + CABAC_ENTRY(373, 24, 22, 40, -24, 24, 25, 34, -15), + CABAC_ENTRY(374, 33, 19, 49, -29, 34, 22, 40, -15), + CABAC_ENTRY(375, 22, 32, 38, -12, 31, 16, 33, -7), + CABAC_ENTRY(376, 26, 31, 40, -10, 35, 18, 35, -5), + CABAC_ENTRY(377, 21, 41, 38, -3, 31, 28, 33, 0), + CABAC_ENTRY(378, 26, 44, 46, -5, 33, 41, 38, 2), + CABAC_ENTRY(379, 23, 47, 31, 20, 36, 28, 33, 13), + CABAC_ENTRY(380, 16, 65, 29, 30, 27, 47, 23, 35), + CABAC_ENTRY(381, 14, 71, 25, 44, 21, 62, 13, 58), + CABAC_ENTRY(382, 8, 60, 12, 48, 18, 31, 29, -3), + CABAC_ENTRY(383, 6, 63, 11, 49, 19, 26, 26, 0), + CABAC_ENTRY(384, 17, 65, 26, 45, 36, 24, 22, 30), + CABAC_ENTRY(385, 21, 24, 22, 22, 24, 23, 31, -7), + CABAC_ENTRY(386, 23, 20, 23, 22, 27, 16, 35, -15), + CABAC_ENTRY(387, 26, 23, 27, 21, 24, 30, 34, -3), + CABAC_ENTRY(388, 27, 32, 33, 20, 31, 29, 34, 3), + CABAC_ENTRY(389, 28, 23, 26, 28, 22, 41, 36, -1), + CABAC_ENTRY(390, 28, 24, 30, 24, 22, 42, 34, 5), + CABAC_ENTRY(391, 23, 40, 27, 34, 16, 60, 32, 11), + CABAC_ENTRY(392, 24, 32, 18, 42, 15, 52, 35, 5), + CABAC_ENTRY(393, 28, 29, 25, 39, 14, 60, 34, 12), + CABAC_ENTRY(394, 23, 42, 18, 50, 3, 78, 39, 11), + CABAC_ENTRY(395, 19, 57, 12, 70, -16, 123, 30, 29), + CABAC_ENTRY(396, 22, 53, 21, 54, 21, 53, 34, 26), + CABAC_ENTRY(397, 22, 61, 14, 71, 22, 56, 29, 39), + CABAC_ENTRY(398, 11, 86, 11, 83, 25, 61, 19, 66), + + /* Values of variables m and n for ctxIdx from 399 to 463 (not documented) */ + CABAC_ENTRY(399, 12, 40, 25, 32, 21, 33, 31, 21), + CABAC_ENTRY(400, 11, 51, 21, 49, 19, 50, 31, 31), + CABAC_ENTRY(401, 14, 59, 21, 54, 17, 61, 25, 50), + CABAC_ENTRY(402, -4, 79, -5, 85, -3, 78, -17, 120), + CABAC_ENTRY(403, -7, 71, -6, 81, -8, 74, -20, 112), + CABAC_ENTRY(404, -5, 69, -10, 77, -9, 72, -18, 114), + CABAC_ENTRY(405, -9, 70, -7, 81, -10, 72, -11, 85), + CABAC_ENTRY(406, -8, 66, -17, 80, -18, 75, -15, 92), + CABAC_ENTRY(407, -10, 68, -18, 73, -12, 71, -14, 89), + CABAC_ENTRY(408, -19, 73, -4, 74, -11, 63, -26, 71), + CABAC_ENTRY(409, -12, 69, -10, 83, -5, 70, -15, 81), + CABAC_ENTRY(410, -16, 70, -9, 71, -17, 75, -14, 80), + CABAC_ENTRY(411, -15, 67, -9, 67, -14, 72, 0, 68), + CABAC_ENTRY(412, -20, 62, -1, 61, -16, 67, -14, 70), + CABAC_ENTRY(413, -19, 70, -8, 66, -8, 53, -24, 56), + CABAC_ENTRY(414, -16, 66, -14, 66, -14, 59, -23, 68), + CABAC_ENTRY(415, -22, 65, 0, 59, -9, 52, -24, 50), + CABAC_ENTRY(416, -20, 63, 2, 59, -11, 68, -11, 74), + CABAC_ENTRY(417, 9, -2, 17, -10, 9, -2, 23, -13), + CABAC_ENTRY(418, 26, -9, 32, -13, 30, -10, 26, -13), + CABAC_ENTRY(419, 33, -9, 42, -9, 31, -4, 40, -15), + CABAC_ENTRY(420, 39, -7, 49, -5, 33, -1, 49, -14), + CABAC_ENTRY(421, 41, -2, 53, 0, 33, 7, 44, 3), + CABAC_ENTRY(422, 45, 3, 64, 3, 31, 12, 45, 6), + CABAC_ENTRY(423, 49, 9, 68, 10, 37, 23, 44, 34), + CABAC_ENTRY(424, 45, 27, 66, 27, 31, 38, 33, 54), + CABAC_ENTRY(425, 36, 59, 47, 57, 20, 64, 19, 82), + CABAC_ENTRY(426, -6, 66, -5, 71, -9, 71, -3, 75), + CABAC_ENTRY(427, -7, 35, 0, 24, -7, 37, -1, 23), + CABAC_ENTRY(428, -7, 42, -1, 36, -8, 44, 1, 34), + CABAC_ENTRY(429, -8, 45, -2, 42, -11, 49, 1, 43), + CABAC_ENTRY(430, -5, 48, -2, 52, -10, 56, 0, 54), + CABAC_ENTRY(431, -12, 56, -9, 57, -12, 59, -2, 55), + CABAC_ENTRY(432, -6, 60, -6, 63, -8, 63, 0, 61), + CABAC_ENTRY(433, -5, 62, -4, 65, -9, 67, 1, 64), + CABAC_ENTRY(434, -8, 66, -4, 67, -6, 68, 0, 68), + CABAC_ENTRY(435, -8, 76, -7, 82, -10, 79, -9, 92), + CABAC_ENTRY(436, -5, 85, -3, 81, -3, 78, -14, 106), + CABAC_ENTRY(437, -6, 81, -3, 76, -8, 74, -13, 97), + CABAC_ENTRY(438, -10, 77, -7, 72, -9, 72, -15, 90), + CABAC_ENTRY(439, -7, 81, -6, 78, -10, 72, -12, 90), + CABAC_ENTRY(440, -17, 80, -12, 72, -18, 75, -18, 88), + CABAC_ENTRY(441, -18, 73, -14, 68, -12, 71, -10, 73), + CABAC_ENTRY(442, -4, 74, -3, 70, -11, 63, -9, 79), + CABAC_ENTRY(443, -10, 83, -6, 76, -5, 70, -14, 86), + CABAC_ENTRY(444, -9, 71, -5, 66, -17, 75, -10, 73), + CABAC_ENTRY(445, -9, 67, -5, 62, -14, 72, -10, 70), + CABAC_ENTRY(446, -1, 61, 0, 57, -16, 67, -10, 69), + CABAC_ENTRY(447, -8, 66, -4, 61, -8, 53, -5, 66), + CABAC_ENTRY(448, -14, 66, -9, 60, -14, 59, -9, 64), + CABAC_ENTRY(449, 0, 59, 1, 54, -9, 52, -5, 58), + CABAC_ENTRY(450, 2, 59, 2, 58, -11, 68, 2, 59), + CABAC_ENTRY(451, 21, -13, 17, -10, 9, -2, 21, -10), + CABAC_ENTRY(452, 33, -14, 32, -13, 30, -10, 24, -11), + CABAC_ENTRY(453, 39, -7, 42, -9, 31, -4, 28, -8), + CABAC_ENTRY(454, 46, -2, 49, -5, 33, -1, 28, -1), + CABAC_ENTRY(455, 51, 2, 53, 0, 33, 7, 29, 3), + CABAC_ENTRY(456, 60, 6, 64, 3, 31, 12, 29, 9), + CABAC_ENTRY(457, 61, 17, 68, 10, 37, 23, 35, 20), + CABAC_ENTRY(458, 55, 34, 66, 27, 31, 38, 29, 36), + CABAC_ENTRY(459, 42, 62, 47, 57, 20, 64, 14, 67), +}; + +static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) +{ + u8 bit = field.offset % 32, word = field.offset / 32; + u64 mask = GENMASK_ULL(bit + field.len - 1, bit); + u64 val = ((u64)value << bit) & mask; + + buf[word] &= ~mask; + buf[word] |= val; + if (bit + field.len > 32) { + buf[word + 1] &= ~(mask >> 32); + buf[word + 1] |= val >> 32; + } +} + +static void assemble_hw_pps(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; + struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; + struct rkvdec_sps_pps_packet *hw_ps; + dma_addr_t scaling_list_address; + u32 scaling_distance; + u32 i; + + /* + * HW read the SPS/PPS information from PPS packet index by PPS id. + * offset from the base can be calculated by PPS_id * 32 (size per PPS + * packet unit). so the driver copy SPS/PPS information to the exact PPS + * packet unit for HW accessing. + */ + hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; + memset(hw_ps, 0, sizeof(*hw_ps)); + +#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value) + /* write sps */ + WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID); + WRITE_PPS(sps->profile_idc, PROFILE_IDC); + WRITE_PPS(!!(sps->constraint_set_flags & (1 << 3)), CONSTRAINT_SET3_FLAG); + WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC); + WRITE_PPS(sps->bit_depth_luma_minus8, BIT_DEPTH_LUMA); + WRITE_PPS(sps->bit_depth_chroma_minus8, BIT_DEPTH_CHROMA); + WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS), + QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG); + WRITE_PPS(sps->log2_max_frame_num_minus4, LOG2_MAX_FRAME_NUM_MINUS4); + WRITE_PPS(sps->max_num_ref_frames, MAX_NUM_REF_FRAMES); + WRITE_PPS(sps->pic_order_cnt_type, PIC_ORDER_CNT_TYPE); + WRITE_PPS(sps->log2_max_pic_order_cnt_lsb_minus4, + LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4); + WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO), + DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG); + + /* + * Use the SPS values since they are already in macroblocks + * dimensions, height can be field height (halved) if + * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows + * decoding smaller images into larger allocation which can be used + * to implementing SVC spatial layer support. + */ + WRITE_PPS(sps->pic_width_in_mbs_minus1 + 1, PIC_WIDTH_IN_MBS); + WRITE_PPS(sps->pic_height_in_map_units_minus1 + 1, PIC_HEIGHT_IN_MBS); + + WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY), + FRAME_MBS_ONLY_FLAG); + WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD), + MB_ADAPTIVE_FRAME_FIELD_FLAG); + WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE), + DIRECT_8X8_INFERENCE_FLAG); + + /* write pps */ + WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID); + WRITE_PPS(pps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID); + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE), + ENTROPY_CODING_MODE_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT), + BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG); + WRITE_PPS(pps->num_ref_idx_l0_default_active_minus1, + NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0)); + WRITE_PPS(pps->num_ref_idx_l1_default_active_minus1, + NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1)); + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED), + WEIGHTED_PRED_FLAG); + WRITE_PPS(pps->weighted_bipred_idc, WEIGHTED_BIPRED_IDC); + WRITE_PPS(pps->pic_init_qp_minus26, PIC_INIT_QP_MINUS26); + WRITE_PPS(pps->pic_init_qs_minus26, PIC_INIT_QS_MINUS26); + WRITE_PPS(pps->chroma_qp_index_offset, CHROMA_QP_INDEX_OFFSET); + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT), + DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED), + CONSTRAINED_INTRA_PRED_FLAG); + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT), + REDUNDANT_PIC_CNT_PRESENT); + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE), + TRANSFORM_8X8_MODE_FLAG); + WRITE_PPS(pps->second_chroma_qp_index_offset, + SECOND_CHROMA_QP_INDEX_OFFSET); + + WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT), + SCALING_LIST_ENABLE_FLAG); + /* To be on the safe side, program the scaling matrix address */ + scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list); + scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance; + WRITE_PPS(scaling_list_address, SCALING_LIST_ADDRESS); + + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + u32 is_longterm = 0; + + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) + is_longterm = 1; + + WRITE_PPS(is_longterm, IS_LONG_TERM(i)); + } +} + +static void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + u32 i; + + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb; + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *buf = NULL; + + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) { + buf = vb2_find_buffer(cap_q, dpb[i].reference_ts); + if (!buf) + pr_debug("No buffer for reference_ts %llu", + dpb[i].reference_ts); + } + + run->ref_buf[i] = buf; + } +} + +static void assemble_hw_rps(struct rkvdec_ctx *ctx, + struct v4l2_h264_reflist_builder *builder, + struct rkvdec_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; + + u32 *hw_rps = priv_tbl->rps; + u32 i, j; + u16 *p = (u16 *)hw_rps; + + memset(hw_rps, 0, sizeof(priv_tbl->rps)); + + /* + * Assign an invalid pic_num if DPB entry at that position is inactive. + * If we assign 0 in that position hardware will treat that as a real + * reference picture with pic_num 0, triggering output picture + * corruption. + */ + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + continue; + + p[i] = builder->refs[i].frame_num; + } + + for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { + for (i = 0; i < builder->num_valid; i++) { + struct v4l2_h264_reference *ref; + bool dpb_valid; + bool bottom; + + switch (j) { + case 0: + ref = &h264_ctx->reflists.p[i]; + break; + case 1: + ref = &h264_ctx->reflists.b0[i]; + break; + case 2: + ref = &h264_ctx->reflists.b1[i]; + break; + } + + if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb))) + continue; + + dpb_valid = run->ref_buf[ref->index] != NULL; + bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; + + set_ps_field(hw_rps, DPB_INFO(i, j), + ref->index | dpb_valid << 4); + set_ps_field(hw_rps, BOTTOM_FLAG(i, j), bottom); + } + } +} + +static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu; + + if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) + return; + + BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_4x4) != + sizeof(scaling->scaling_list_4x4)); + BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_8x8) != + sizeof(scaling->scaling_list_8x8)); + + memcpy(tbl->scaling_list.scaling_list_4x4, + scaling->scaling_list_4x4, + sizeof(scaling->scaling_list_4x4)); + + memcpy(tbl->scaling_list.scaling_list_8x8, + scaling->scaling_list_8x8, + sizeof(scaling->scaling_list_8x8)); +} + +/* + * dpb poc related registers table + */ +static const u32 poc_reg_tbl_top_field[16] = { + RKVDEC_REG_H264_POC_REFER0(0), + RKVDEC_REG_H264_POC_REFER0(2), + RKVDEC_REG_H264_POC_REFER0(4), + RKVDEC_REG_H264_POC_REFER0(6), + RKVDEC_REG_H264_POC_REFER0(8), + RKVDEC_REG_H264_POC_REFER0(10), + RKVDEC_REG_H264_POC_REFER0(12), + RKVDEC_REG_H264_POC_REFER0(14), + RKVDEC_REG_H264_POC_REFER1(1), + RKVDEC_REG_H264_POC_REFER1(3), + RKVDEC_REG_H264_POC_REFER1(5), + RKVDEC_REG_H264_POC_REFER1(7), + RKVDEC_REG_H264_POC_REFER1(9), + RKVDEC_REG_H264_POC_REFER1(11), + RKVDEC_REG_H264_POC_REFER1(13), + RKVDEC_REG_H264_POC_REFER2(0) +}; + +static const u32 poc_reg_tbl_bottom_field[16] = { + RKVDEC_REG_H264_POC_REFER0(1), + RKVDEC_REG_H264_POC_REFER0(3), + RKVDEC_REG_H264_POC_REFER0(5), + RKVDEC_REG_H264_POC_REFER0(7), + RKVDEC_REG_H264_POC_REFER0(9), + RKVDEC_REG_H264_POC_REFER0(11), + RKVDEC_REG_H264_POC_REFER0(13), + RKVDEC_REG_H264_POC_REFER1(0), + RKVDEC_REG_H264_POC_REFER1(2), + RKVDEC_REG_H264_POC_REFER1(4), + RKVDEC_REG_H264_POC_REFER1(6), + RKVDEC_REG_H264_POC_REFER1(8), + RKVDEC_REG_H264_POC_REFER1(10), + RKVDEC_REG_H264_POC_REFER1(12), + RKVDEC_REG_H264_POC_REFER1(14), + RKVDEC_REG_H264_POC_REFER2(1) +}; + +static void config_registers(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma; + const struct v4l2_pix_format_mplane *dst_fmt; + struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; + struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; + const struct v4l2_format *f; + dma_addr_t rlc_addr; + dma_addr_t refer_addr; + u32 rlc_len; + u32 hor_virstride; + u32 ver_virstride; + u32 y_virstride; + u32 yuv_virstride = 0; + u32 offset; + dma_addr_t dst_addr; + u32 reg, i; + + reg = RKVDEC_MODE(RKVDEC_MODE_H264); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL); + + f = &ctx->decoded_fmt; + dst_fmt = &f->fmt.pix_mp; + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; + ver_virstride = dst_fmt->height; + y_virstride = hor_virstride * ver_virstride; + + if (sps->chroma_format_idc == 0) + yuv_virstride = y_virstride; + else if (sps->chroma_format_idc == 1) + yuv_virstride = y_virstride + y_virstride / 2; + else if (sps->chroma_format_idc == 2) + yuv_virstride = 2 * y_virstride; + + reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) | + RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) | + RKVDEC_SLICE_NUM_HIGHBIT | + RKVDEC_SLICE_NUM_LOWBITS(0x7ff); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR); + + /* config rlc base address */ + rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); + writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_RLCWRITE_BASE); + + rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + reg = RKVDEC_STRM_LEN(rlc_len); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN); + + /* config cabac table */ + offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table); + writel_relaxed(priv_start_addr + offset, + rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); + + /* config output base address */ + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); + + reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); + + reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); + + /* config ref pic address & poc */ + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + struct vb2_buffer *vb_buf = run->ref_buf[i]; + + /* + * If a DPB entry is unused or invalid, address of current destination + * buffer is returned. + */ + if (!vb_buf) + vb_buf = &dst_buf->vb2_buf; + refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0); + + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) + refer_addr |= RKVDEC_COLMV_USED_FLAG_REF; + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD) + refer_addr |= RKVDEC_FIELD_REF; + + if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) + refer_addr |= RKVDEC_TOPFIELD_USED_REF; + if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) + refer_addr |= RKVDEC_BOTFIELD_USED_REF; + + writel_relaxed(dpb[i].top_field_order_cnt, + rkvdec->regs + poc_reg_tbl_top_field[i]); + writel_relaxed(dpb[i].bottom_field_order_cnt, + rkvdec->regs + poc_reg_tbl_bottom_field[i]); + + if (i < V4L2_H264_NUM_DPB_ENTRIES - 1) + writel_relaxed(refer_addr, + rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i)); + else + writel_relaxed(refer_addr, + rkvdec->regs + RKVDEC_REG_H264_BASE_REFER15); + } + + reg = RKVDEC_CUR_POC(dec_params->top_field_order_cnt); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0); + + reg = RKVDEC_CUR_POC(dec_params->bottom_field_order_cnt); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC1); + + /* config hw pps address */ + offset = offsetof(struct rkvdec_h264_priv_tbl, param_set); + writel_relaxed(priv_start_addr + offset, + rkvdec->regs + RKVDEC_REG_PPS_BASE); + + /* config hw rps address */ + offset = offsetof(struct rkvdec_h264_priv_tbl, rps); + writel_relaxed(priv_start_addr + offset, + rkvdec->regs + RKVDEC_REG_RPS_BASE); + + reg = RKVDEC_AXI_DDR_RDATA(0); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA); + + reg = RKVDEC_AXI_DDR_WDATA(0); + writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA); + + offset = offsetof(struct rkvdec_h264_priv_tbl, err_info); + writel_relaxed(priv_start_addr + offset, + rkvdec->regs + RKVDEC_REG_H264_ERRINFO_BASE); +} + +#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2 + +static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; + + fmt->num_planes = 1; + if (!fmt->plane_fmt[0].sizeimage) + fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * + RKVDEC_H264_MAX_DEPTH_IN_BYTES; + return 0; +} + +static enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (ctrl->id != V4L2_CID_STATELESS_H264_SPS) + return RKVDEC_IMG_FMT_ANY; + + if (sps->bit_depth_luma_minus8 == 0) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_8BIT; + else + return RKVDEC_IMG_FMT_420_8BIT; + } else if (sps->bit_depth_luma_minus8 == 2) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_10BIT; + else + return RKVDEC_IMG_FMT_420_10BIT; + } + + return RKVDEC_IMG_FMT_ANY; +} + +static int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, + const struct v4l2_ctrl_h264_sps *sps) +{ + unsigned int width, height; + + if (sps->chroma_format_idc > 2) + /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) + /* Only 8-bit and 10-bit is supported */ + return -EINVAL; + + width = (sps->pic_width_in_mbs_minus1 + 1) * 16; + height = (sps->pic_height_in_map_units_minus1 + 1) * 16; + + /* + * When frame_mbs_only_flag is not set, this is field height, + * which is half the final height (see (7-18) in the + * specification) + */ + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) + height *= 2; + + if (width > ctx->coded_fmt.fmt.pix_mp.width || + height > ctx->coded_fmt.fmt.pix_mp.height) + return -EINVAL; + + return 0; +} + +static int rkvdec_h264_start(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_priv_tbl *priv_tbl; + struct rkvdec_h264_ctx *h264_ctx; + struct v4l2_ctrl *ctrl; + int ret; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SPS); + if (!ctrl) + return -EINVAL; + + ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + if (ret) + return ret; + + h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL); + if (!h264_ctx) + return -ENOMEM; + + priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), + &h264_ctx->priv_tbl.dma, GFP_KERNEL); + if (!priv_tbl) { + ret = -ENOMEM; + goto err_free_ctx; + } + + h264_ctx->priv_tbl.size = sizeof(*priv_tbl); + h264_ctx->priv_tbl.cpu = priv_tbl; + memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table, + sizeof(rkvdec_h264_cabac_table)); + + ctx->priv = h264_ctx; + return 0; + +err_free_ctx: + kfree(h264_ctx); + return ret; +} + +static void rkvdec_h264_stop(struct rkvdec_ctx *ctx) +{ + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_dev *rkvdec = ctx->dev; + + dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size, + h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma); + kfree(h264_ctx); +} + +static void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_DECODE_PARAMS); + run->decode_params = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SPS); + run->sps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_PPS); + run->pps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SCALING_MATRIX); + run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; + + rkvdec_run_preamble(ctx, &run->base); +} + +static int rkvdec_h264_run(struct rkvdec_ctx *ctx) +{ + struct v4l2_h264_reflist_builder reflist_builder; + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_h264_run run; + + rkvdec_h264_run_preamble(ctx, &run); + + /* Build the P/B{0,1} ref lists. */ + v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params, + run.sps, run.decode_params->dpb); + v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); + v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, + h264_ctx->reflists.b1); + + assemble_hw_scaling_list(ctx, &run); + assemble_hw_pps(ctx, &run); + lookup_ref_buf_idx(ctx, &run); + assemble_hw_rps(ctx, &reflist_builder, &run); + config_registers(ctx, &run); + + rkvdec_run_postamble(ctx, &run.base); + + schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000)); + + writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); + writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E); + writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); + writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); + + /* Start decoding! */ + writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E | + RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E, + rkvdec->regs + RKVDEC_REG_INTERRUPT); + + return 0; +} + +static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) + return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + + return 0; +} + +const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops = { + .adjust_fmt = rkvdec_h264_adjust_fmt, + .start = rkvdec_h264_start, + .stop = rkvdec_h264_stop, + .run = rkvdec_h264_run, + .try_ctrl = rkvdec_h264_try_ctrl, + .get_image_fmt = rkvdec_h264_get_image_fmt, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h new file mode 100644 index 000000000000..15b9bee92016 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef RKVDEC_REGS_H_ +#define RKVDEC_REGS_H_ + +/* rkvcodec registers */ +#define RKVDEC_REG_INTERRUPT 0x004 +#define RKVDEC_INTERRUPT_DEC_E BIT(0) +#define RKVDEC_CONFIG_DEC_CLK_GATE_E BIT(1) +#define RKVDEC_E_STRMD_CLKGATE_DIS BIT(2) +#define RKVDEC_TIMEOUT_MODE BIT(3) +#define RKVDEC_IRQ_DIS BIT(4) +#define RKVDEC_TIMEOUT_E BIT(5) +#define RKVDEC_BUF_EMPTY_E BIT(6) +#define RKVDEC_STRM_E_WAITDECFIFO_EMPTY BIT(7) +#define RKVDEC_IRQ BIT(8) +#define RKVDEC_IRQ_RAW BIT(9) +#define RKVDEC_E_REWRITE_VALID BIT(10) +#define RKVDEC_COMMONIRQ_MODE BIT(11) +#define RKVDEC_RDY_STA BIT(12) +#define RKVDEC_BUS_STA BIT(13) +#define RKVDEC_ERR_STA BIT(14) +#define RKVDEC_TIMEOUT_STA BIT(15) +#define RKVDEC_BUF_EMPTY_STA BIT(16) +#define RKVDEC_COLMV_REF_ERR_STA BIT(17) +#define RKVDEC_CABU_END_STA BIT(18) +#define RKVDEC_H264ORVP9_ERR_MODE BIT(19) +#define RKVDEC_SOFTRST_EN_P BIT(20) +#define RKVDEC_FORCE_SOFTRESET_VALID BIT(21) +#define RKVDEC_SOFTRESET_RDY BIT(22) + +#define RKVDEC_REG_SYSCTRL 0x008 +#define RKVDEC_IN_ENDIAN BIT(0) +#define RKVDEC_IN_SWAP32_E BIT(1) +#define RKVDEC_IN_SWAP64_E BIT(2) +#define RKVDEC_STR_ENDIAN BIT(3) +#define RKVDEC_STR_SWAP32_E BIT(4) +#define RKVDEC_STR_SWAP64_E BIT(5) +#define RKVDEC_OUT_ENDIAN BIT(6) +#define RKVDEC_OUT_SWAP32_E BIT(7) +#define RKVDEC_OUT_CBCR_SWAP BIT(8) +#define RKVDEC_RLC_MODE_DIRECT_WRITE BIT(10) +#define RKVDEC_RLC_MODE BIT(11) +#define RKVDEC_STRM_START_BIT(x) (((x) & 0x7f) << 12) +#define RKVDEC_MODE(x) (((x) & 0x03) << 20) +#define RKVDEC_MODE_H264 1 +#define RKVDEC_MODE_VP9 2 +#define RKVDEC_RPS_MODE BIT(24) +#define RKVDEC_STRM_MODE BIT(25) +#define RKVDEC_H264_STRM_LASTPKT BIT(26) +#define RKVDEC_H264_FIRSTSLICE_FLAG BIT(27) +#define RKVDEC_H264_FRAME_ORSLICE BIT(28) +#define RKVDEC_BUSPR_SLOT_DIS BIT(29) + +#define RKVDEC_REG_PICPAR 0x00C +#define RKVDEC_Y_HOR_VIRSTRIDE(x) ((x) & 0x1ff) +#define RKVDEC_SLICE_NUM_HIGHBIT BIT(11) +#define RKVDEC_UV_HOR_VIRSTRIDE(x) (((x) & 0x1ff) << 12) +#define RKVDEC_SLICE_NUM_LOWBITS(x) (((x) & 0x7ff) << 21) + +#define RKVDEC_REG_STRM_RLC_BASE 0x010 + +#define RKVDEC_REG_STRM_LEN 0x014 +#define RKVDEC_STRM_LEN(x) ((x) & 0x7ffffff) + +#define RKVDEC_REG_CABACTBL_PROB_BASE 0x018 +#define RKVDEC_REG_DECOUT_BASE 0x01C + +#define RKVDEC_REG_Y_VIRSTRIDE 0x020 +#define RKVDEC_Y_VIRSTRIDE(x) ((x) & 0xfffff) + +#define RKVDEC_REG_YUV_VIRSTRIDE 0x024 +#define RKVDEC_YUV_VIRSTRIDE(x) ((x) & 0x1fffff) +#define RKVDEC_REG_H264_BASE_REFER(i) (((i) * 0x04) + 0x028) + +#define RKVDEC_REG_H264_BASE_REFER15 0x0C0 +#define RKVDEC_FIELD_REF BIT(0) +#define RKVDEC_TOPFIELD_USED_REF BIT(1) +#define RKVDEC_BOTFIELD_USED_REF BIT(2) +#define RKVDEC_COLMV_USED_FLAG_REF BIT(3) + +#define RKVDEC_REG_VP9_LAST_FRAME_BASE 0x02c +#define RKVDEC_REG_VP9_GOLDEN_FRAME_BASE 0x030 +#define RKVDEC_REG_VP9_ALTREF_FRAME_BASE 0x034 + +#define RKVDEC_REG_VP9_CPRHEADER_OFFSET 0x028 +#define RKVDEC_VP9_CPRHEADER_OFFSET(x) ((x) & 0xffff) + +#define RKVDEC_REG_VP9_REFERLAST_BASE 0x02C +#define RKVDEC_REG_VP9_REFERGOLDEN_BASE 0x030 +#define RKVDEC_REG_VP9_REFERALFTER_BASE 0x034 + +#define RKVDEC_REG_VP9COUNT_BASE 0x038 +#define RKVDEC_VP9COUNT_UPDATE_EN BIT(0) + +#define RKVDEC_REG_VP9_SEGIDLAST_BASE 0x03C +#define RKVDEC_REG_VP9_SEGIDCUR_BASE 0x040 +#define RKVDEC_REG_VP9_FRAME_SIZE(i) ((i) * 0x04 + 0x044) +#define RKVDEC_VP9_FRAMEWIDTH(x) (((x) & 0xffff) << 0) +#define RKVDEC_VP9_FRAMEHEIGHT(x) (((x) & 0xffff) << 16) + +#define RKVDEC_VP9_SEGID_GRP(i) ((i) * 0x04 + 0x050) +#define RKVDEC_SEGID_ABS_DELTA(x) ((x) & 0x1) +#define RKVDEC_SEGID_FRAME_QP_DELTA_EN(x) (((x) & 0x1) << 1) +#define RKVDEC_SEGID_FRAME_QP_DELTA(x) (((x) & 0x1ff) << 2) +#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(x) (((x) & 0x1) << 11) +#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(x) (((x) & 0x7f) << 12) +#define RKVDEC_SEGID_REFERINFO_EN(x) (((x) & 0x1) << 19) +#define RKVDEC_SEGID_REFERINFO(x) (((x) & 0x03) << 20) +#define RKVDEC_SEGID_FRAME_SKIP_EN(x) (((x) & 0x1) << 22) + +#define RKVDEC_VP9_CPRHEADER_CONFIG 0x070 +#define RKVDEC_VP9_TX_MODE(x) ((x) & 0x07) +#define RKVDEC_VP9_FRAME_REF_MODE(x) (((x) & 0x03) << 3) + +#define RKVDEC_VP9_REF_SCALE(i) ((i) * 0x04 + 0x074) +#define RKVDEC_VP9_REF_HOR_SCALE(x) ((x) & 0xffff) +#define RKVDEC_VP9_REF_VER_SCALE(x) (((x) & 0xffff) << 16) + +#define RKVDEC_VP9_REF_DELTAS_LASTFRAME 0x080 +#define RKVDEC_REF_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7)) + +#define RKVDEC_VP9_INFO_LASTFRAME 0x084 +#define RKVDEC_MODE_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7)) +#define RKVDEC_SEG_EN_LASTFRAME BIT(16) +#define RKVDEC_LAST_SHOW_FRAME BIT(17) +#define RKVDEC_LAST_INTRA_ONLY BIT(18) +#define RKVDEC_LAST_WIDHHEIGHT_EQCUR BIT(19) +#define RKVDEC_COLOR_SPACE_LASTKEYFRAME(x) (((x) & 0x07) << 20) + +#define RKVDEC_VP9_INTERCMD_BASE 0x088 + +#define RKVDEC_VP9_INTERCMD_NUM 0x08C +#define RKVDEC_INTERCMD_NUM(x) ((x) & 0xffffff) + +#define RKVDEC_VP9_LASTTILE_SIZE 0x090 +#define RKVDEC_LASTTILE_SIZE(x) ((x) & 0xffffff) + +#define RKVDEC_VP9_HOR_VIRSTRIDE(i) ((i) * 0x04 + 0x094) +#define RKVDEC_HOR_Y_VIRSTRIDE(x) ((x) & 0x1ff) +#define RKVDEC_HOR_UV_VIRSTRIDE(x) (((x) & 0x1ff) << 16) + +#define RKVDEC_REG_H264_POC_REFER0(i) (((i) * 0x04) + 0x064) +#define RKVDEC_REG_H264_POC_REFER1(i) (((i) * 0x04) + 0x0C4) +#define RKVDEC_REG_H264_POC_REFER2(i) (((i) * 0x04) + 0x120) +#define RKVDEC_POC_REFER(x) ((x) & 0xffffffff) + +#define RKVDEC_REG_CUR_POC0 0x0A0 +#define RKVDEC_REG_CUR_POC1 0x128 +#define RKVDEC_CUR_POC(x) ((x) & 0xffffffff) + +#define RKVDEC_REG_RLCWRITE_BASE 0x0A4 +#define RKVDEC_REG_PPS_BASE 0x0A8 +#define RKVDEC_REG_RPS_BASE 0x0AC + +#define RKVDEC_REG_STRMD_ERR_EN 0x0B0 +#define RKVDEC_STRMD_ERR_EN(x) ((x) & 0xffffffff) + +#define RKVDEC_REG_STRMD_ERR_STA 0x0B4 +#define RKVDEC_STRMD_ERR_STA(x) ((x) & 0xfffffff) +#define RKVDEC_COLMV_ERR_REF_PICIDX(x) (((x) & 0x0f) << 28) + +#define RKVDEC_REG_STRMD_ERR_CTU 0x0B8 +#define RKVDEC_STRMD_ERR_CTU(x) ((x) & 0xff) +#define RKVDEC_STRMD_ERR_CTU_YOFFSET(x) (((x) & 0xff) << 8) +#define RKVDEC_STRMFIFO_SPACE2FULL(x) (((x) & 0x7f) << 16) +#define RKVDEC_VP9_ERR_EN_CTU0 BIT(24) + +#define RKVDEC_REG_SAO_CTU_POS 0x0BC +#define RKVDEC_SAOWR_XOFFSET(x) ((x) & 0x1ff) +#define RKVDEC_SAOWR_YOFFSET(x) (((x) & 0x3ff) << 16) + +#define RKVDEC_VP9_LAST_FRAME_YSTRIDE 0x0C0 +#define RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE 0x0C4 +#define RKVDEC_VP9_ALTREF_FRAME_YSTRIDE 0x0C8 +#define RKVDEC_VP9_REF_YSTRIDE(x) (((x) & 0xfffff) << 0) + +#define RKVDEC_VP9_LAST_FRAME_YUVSTRIDE 0x0CC +#define RKVDEC_VP9_REF_YUVSTRIDE(x) (((x) & 0x1fffff) << 0) + +#define RKVDEC_VP9_REF_COLMV_BASE 0x0D0 + +#define RKVDEC_REG_PERFORMANCE_CYCLE 0x100 +#define RKVDEC_PERFORMANCE_CYCLE(x) ((x) & 0xffffffff) + +#define RKVDEC_REG_AXI_DDR_RDATA 0x104 +#define RKVDEC_AXI_DDR_RDATA(x) ((x) & 0xffffffff) + +#define RKVDEC_REG_AXI_DDR_WDATA 0x108 +#define RKVDEC_AXI_DDR_WDATA(x) ((x) & 0xffffffff) + +#define RKVDEC_REG_FPGADEBUG_RESET 0x10C +#define RKVDEC_BUSIFD_RESETN BIT(0) +#define RKVDEC_CABAC_RESETN BIT(1) +#define RKVDEC_DEC_CTRL_RESETN BIT(2) +#define RKVDEC_TRANSD_RESETN BIT(3) +#define RKVDEC_INTRA_RESETN BIT(4) +#define RKVDEC_INTER_RESETN BIT(5) +#define RKVDEC_RECON_RESETN BIT(6) +#define RKVDEC_FILER_RESETN BIT(7) + +#define RKVDEC_REG_PERFORMANCE_SEL 0x110 +#define RKVDEC_PERF_SEL_CNT0(x) ((x) & 0x3f) +#define RKVDEC_PERF_SEL_CNT1(x) (((x) & 0x3f) << 8) +#define RKVDEC_PERF_SEL_CNT2(x) (((x) & 0x3f) << 16) + +#define RKVDEC_REG_PERFORMANCE_CNT(i) ((i) * 0x04 + 0x114) +#define RKVDEC_PERF_CNT(x) ((x) & 0xffffffff) + +#define RKVDEC_REG_H264_ERRINFO_BASE 0x12C + +#define RKVDEC_REG_H264_ERRINFO_NUM 0x130 +#define RKVDEC_SLICEDEC_NUM(x) ((x) & 0x3fff) +#define RKVDEC_STRMD_DECT_ERR_FLAG BIT(15) +#define RKVDEC_ERR_PKT_NUM(x) (((x) & 0x3fff) << 16) + +#define RKVDEC_REG_H264_ERR_E 0x134 +#define RKVDEC_H264_ERR_EN_HIGHBITS(x) ((x) & 0x3fffffff) + +#define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410 +#define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450 + +#endif /* RKVDEC_REGS_H_ */ diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c new file mode 100644 index 000000000000..0e7e16f20eeb --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c @@ -0,0 +1,1072 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Video Decoder VP9 backend + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + * Copyright (C) 2021 Collabora, Ltd. + * Andrzej Pietrasiewicz <andrzej.p@collabora.com> + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Alpha Lin <Alpha.Lin@rock-chips.com> + */ + +/* + * For following the vp9 spec please start reading this driver + * code from rkvdec_vp9_run() followed by rkvdec_vp9_done(). + */ + +#include <linux/kernel.h> +#include <linux/vmalloc.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-vp9.h> + +#include "rkvdec.h" +#include "rkvdec-regs.h" + +#define RKVDEC_VP9_PROBE_SIZE 4864 +#define RKVDEC_VP9_COUNT_SIZE 13232 +#define RKVDEC_VP9_MAX_SEGMAP_SIZE 73728 + +struct rkvdec_vp9_intra_mode_probs { + u8 y_mode[105]; + u8 uv_mode[23]; +}; + +struct rkvdec_vp9_intra_only_frame_probs { + u8 coef_intra[4][2][128]; + struct rkvdec_vp9_intra_mode_probs intra_mode[10]; +}; + +struct rkvdec_vp9_inter_frame_probs { + u8 y_mode[4][9]; + u8 comp_mode[5]; + u8 comp_ref[5]; + u8 single_ref[5][2]; + u8 inter_mode[7][3]; + u8 interp_filter[4][2]; + u8 padding0[11]; + u8 coef[2][4][2][128]; + u8 uv_mode_0_2[3][9]; + u8 padding1[5]; + u8 uv_mode_3_5[3][9]; + u8 padding2[5]; + u8 uv_mode_6_8[3][9]; + u8 padding3[5]; + u8 uv_mode_9[9]; + u8 padding4[7]; + u8 padding5[16]; + struct { + u8 joint[3]; + u8 sign[2]; + u8 classes[2][10]; + u8 class0_bit[2]; + u8 bits[2][10]; + u8 class0_fr[2][2][3]; + u8 fr[2][3]; + u8 class0_hp[2]; + u8 hp[2]; + } mv; +}; + +struct rkvdec_vp9_probs { + u8 partition[16][3]; + u8 pred[3]; + u8 tree[7]; + u8 skip[3]; + u8 tx32[2][3]; + u8 tx16[2][2]; + u8 tx8[2][1]; + u8 is_inter[4]; + /* 128 bit alignment */ + u8 padding0[3]; + union { + struct rkvdec_vp9_inter_frame_probs inter; + struct rkvdec_vp9_intra_only_frame_probs intra_only; + }; + /* 128 bit alignment */ + u8 padding1[11]; +}; + +/* Data structure describing auxiliary buffer format. */ +struct rkvdec_vp9_priv_tbl { + struct rkvdec_vp9_probs probs; + u8 segmap[2][RKVDEC_VP9_MAX_SEGMAP_SIZE]; +}; + +struct rkvdec_vp9_refs_counts { + u32 eob[2]; + u32 coeff[3]; +}; + +struct rkvdec_vp9_inter_frame_symbol_counts { + u32 partition[16][4]; + u32 skip[3][2]; + u32 inter[4][2]; + u32 tx32p[2][4]; + u32 tx16p[2][4]; + u32 tx8p[2][2]; + u32 y_mode[4][10]; + u32 uv_mode[10][10]; + u32 comp[5][2]; + u32 comp_ref[5][2]; + u32 single_ref[5][2][2]; + u32 mv_mode[7][4]; + u32 filter[4][3]; + u32 mv_joint[4]; + u32 sign[2][2]; + /* add 1 element for align */ + u32 classes[2][11 + 1]; + u32 class0[2][2]; + u32 bits[2][10][2]; + u32 class0_fp[2][2][4]; + u32 fp[2][4]; + u32 class0_hp[2][2]; + u32 hp[2][2]; + struct rkvdec_vp9_refs_counts ref_cnt[2][4][2][6][6]; +}; + +struct rkvdec_vp9_intra_frame_symbol_counts { + u32 partition[4][4][4]; + u32 skip[3][2]; + u32 intra[4][2]; + u32 tx32p[2][4]; + u32 tx16p[2][4]; + u32 tx8p[2][2]; + struct rkvdec_vp9_refs_counts ref_cnt[2][4][2][6][6]; +}; + +struct rkvdec_vp9_run { + struct rkvdec_run base; + const struct v4l2_ctrl_vp9_frame *decode_params; +}; + +struct rkvdec_vp9_frame_info { + u32 valid : 1; + u32 segmapid : 1; + u32 frame_context_idx : 2; + u32 reference_mode : 2; + u32 tx_mode : 3; + u32 interpolation_filter : 3; + u32 flags; + u64 timestamp; + struct v4l2_vp9_segmentation seg; + struct v4l2_vp9_loop_filter lf; +}; + +struct rkvdec_vp9_ctx { + struct rkvdec_aux_buf priv_tbl; + struct rkvdec_aux_buf count_tbl; + struct v4l2_vp9_frame_symbol_counts inter_cnts; + struct v4l2_vp9_frame_symbol_counts intra_cnts; + struct v4l2_vp9_frame_context probability_tables; + struct v4l2_vp9_frame_context frame_context[4]; + struct rkvdec_vp9_frame_info cur; + struct rkvdec_vp9_frame_info last; +}; + +static void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane) +{ + unsigned int idx = 0, byte_count = 0; + int k, m, n; + u8 p; + + for (k = 0; k < 6; k++) { + for (m = 0; m < 6; m++) { + for (n = 0; n < 3; n++) { + p = coef[k][m][n]; + coeff_plane[idx++] = p; + byte_count++; + if (byte_count == 27) { + idx += 5; + byte_count = 0; + } + } + } + } +} + +static void init_intra_only_probs(struct rkvdec_ctx *ctx, + const struct rkvdec_vp9_run *run) +{ + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu; + struct rkvdec_vp9_intra_only_frame_probs *rkprobs; + const struct v4l2_vp9_frame_context *probs; + unsigned int i, j, k; + + rkprobs = &tbl->probs.intra_only; + probs = &vp9_ctx->probability_tables; + + /* + * intra only 149 x 128 bits ,aligned to 152 x 128 bits coeff related + * prob 64 x 128 bits + */ + for (i = 0; i < ARRAY_SIZE(probs->coef); i++) { + for (j = 0; j < ARRAY_SIZE(probs->coef[0]); j++) + write_coeff_plane(probs->coef[i][j][0], + rkprobs->coef_intra[i][j]); + } + + /* intra mode prob 80 x 128 bits */ + for (i = 0; i < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob); i++) { + unsigned int byte_count = 0; + int idx = 0; + + /* vp9_kf_y_mode_prob */ + for (j = 0; j < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob[0]); j++) { + for (k = 0; k < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob[0][0]); + k++) { + u8 val = v4l2_vp9_kf_y_mode_prob[i][j][k]; + + rkprobs->intra_mode[i].y_mode[idx++] = val; + byte_count++; + if (byte_count == 27) { + byte_count = 0; + idx += 5; + } + } + } + } + + for (i = 0; i < sizeof(v4l2_vp9_kf_uv_mode_prob); ++i) { + const u8 *ptr = (const u8 *)v4l2_vp9_kf_uv_mode_prob; + + rkprobs->intra_mode[i / 23].uv_mode[i % 23] = ptr[i]; + } +} + +static void init_inter_probs(struct rkvdec_ctx *ctx, + const struct rkvdec_vp9_run *run) +{ + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu; + struct rkvdec_vp9_inter_frame_probs *rkprobs; + const struct v4l2_vp9_frame_context *probs; + unsigned int i, j, k; + + rkprobs = &tbl->probs.inter; + probs = &vp9_ctx->probability_tables; + + /* + * inter probs + * 151 x 128 bits, aligned to 152 x 128 bits + * inter only + * intra_y_mode & inter_block info 6 x 128 bits + */ + + memcpy(rkprobs->y_mode, probs->y_mode, sizeof(rkprobs->y_mode)); + memcpy(rkprobs->comp_mode, probs->comp_mode, + sizeof(rkprobs->comp_mode)); + memcpy(rkprobs->comp_ref, probs->comp_ref, + sizeof(rkprobs->comp_ref)); + memcpy(rkprobs->single_ref, probs->single_ref, + sizeof(rkprobs->single_ref)); + memcpy(rkprobs->inter_mode, probs->inter_mode, + sizeof(rkprobs->inter_mode)); + memcpy(rkprobs->interp_filter, probs->interp_filter, + sizeof(rkprobs->interp_filter)); + + /* 128 x 128 bits coeff related */ + for (i = 0; i < ARRAY_SIZE(probs->coef); i++) { + for (j = 0; j < ARRAY_SIZE(probs->coef[0]); j++) { + for (k = 0; k < ARRAY_SIZE(probs->coef[0][0]); k++) + write_coeff_plane(probs->coef[i][j][k], + rkprobs->coef[k][i][j]); + } + } + + /* intra uv mode 6 x 128 */ + memcpy(rkprobs->uv_mode_0_2, &probs->uv_mode[0], + sizeof(rkprobs->uv_mode_0_2)); + memcpy(rkprobs->uv_mode_3_5, &probs->uv_mode[3], + sizeof(rkprobs->uv_mode_3_5)); + memcpy(rkprobs->uv_mode_6_8, &probs->uv_mode[6], + sizeof(rkprobs->uv_mode_6_8)); + memcpy(rkprobs->uv_mode_9, &probs->uv_mode[9], + sizeof(rkprobs->uv_mode_9)); + + /* mv related 6 x 128 */ + memcpy(rkprobs->mv.joint, probs->mv.joint, + sizeof(rkprobs->mv.joint)); + memcpy(rkprobs->mv.sign, probs->mv.sign, + sizeof(rkprobs->mv.sign)); + memcpy(rkprobs->mv.classes, probs->mv.classes, + sizeof(rkprobs->mv.classes)); + memcpy(rkprobs->mv.class0_bit, probs->mv.class0_bit, + sizeof(rkprobs->mv.class0_bit)); + memcpy(rkprobs->mv.bits, probs->mv.bits, + sizeof(rkprobs->mv.bits)); + memcpy(rkprobs->mv.class0_fr, probs->mv.class0_fr, + sizeof(rkprobs->mv.class0_fr)); + memcpy(rkprobs->mv.fr, probs->mv.fr, + sizeof(rkprobs->mv.fr)); + memcpy(rkprobs->mv.class0_hp, probs->mv.class0_hp, + sizeof(rkprobs->mv.class0_hp)); + memcpy(rkprobs->mv.hp, probs->mv.hp, + sizeof(rkprobs->mv.hp)); +} + +static void init_probs(struct rkvdec_ctx *ctx, + const struct rkvdec_vp9_run *run) +{ + const struct v4l2_ctrl_vp9_frame *dec_params; + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu; + struct rkvdec_vp9_probs *rkprobs = &tbl->probs; + const struct v4l2_vp9_segmentation *seg; + const struct v4l2_vp9_frame_context *probs; + bool intra_only; + + dec_params = run->decode_params; + probs = &vp9_ctx->probability_tables; + seg = &dec_params->seg; + + memset(rkprobs, 0, sizeof(*rkprobs)); + + intra_only = !!(dec_params->flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | + V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); + + /* sb info 5 x 128 bit */ + memcpy(rkprobs->partition, + intra_only ? v4l2_vp9_kf_partition_probs : probs->partition, + sizeof(rkprobs->partition)); + + memcpy(rkprobs->pred, seg->pred_probs, sizeof(rkprobs->pred)); + memcpy(rkprobs->tree, seg->tree_probs, sizeof(rkprobs->tree)); + memcpy(rkprobs->skip, probs->skip, sizeof(rkprobs->skip)); + memcpy(rkprobs->tx32, probs->tx32, sizeof(rkprobs->tx32)); + memcpy(rkprobs->tx16, probs->tx16, sizeof(rkprobs->tx16)); + memcpy(rkprobs->tx8, probs->tx8, sizeof(rkprobs->tx8)); + memcpy(rkprobs->is_inter, probs->is_inter, sizeof(rkprobs->is_inter)); + + if (intra_only) + init_intra_only_probs(ctx, run); + else + init_inter_probs(ctx, run); +} + +struct rkvdec_vp9_ref_reg { + u32 reg_frm_size; + u32 reg_hor_stride; + u32 reg_y_stride; + u32 reg_yuv_stride; + u32 reg_ref_base; +}; + +static struct rkvdec_vp9_ref_reg ref_regs[] = { + { + .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(0), + .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(0), + .reg_y_stride = RKVDEC_VP9_LAST_FRAME_YSTRIDE, + .reg_yuv_stride = RKVDEC_VP9_LAST_FRAME_YUVSTRIDE, + .reg_ref_base = RKVDEC_REG_VP9_LAST_FRAME_BASE, + }, + { + .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(1), + .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(1), + .reg_y_stride = RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE, + .reg_yuv_stride = 0, + .reg_ref_base = RKVDEC_REG_VP9_GOLDEN_FRAME_BASE, + }, + { + .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(2), + .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(2), + .reg_y_stride = RKVDEC_VP9_ALTREF_FRAME_YSTRIDE, + .reg_yuv_stride = 0, + .reg_ref_base = RKVDEC_REG_VP9_ALTREF_FRAME_BASE, + } +}; + +static struct rkvdec_decoded_buffer * +get_ref_buf(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) +{ + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *buf; + + /* + * If a ref is unused or invalid, address of current destination + * buffer is returned. + */ + buf = vb2_find_buffer(cap_q, timestamp); + if (!buf) + buf = &dst->vb2_buf; + + return vb2_to_rkvdec_decoded_buf(buf); +} + +static dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf) +{ + unsigned int aligned_pitch, aligned_height, yuv_len; + + aligned_height = round_up(buf->vp9.height, 64); + aligned_pitch = round_up(buf->vp9.width * buf->vp9.bit_depth, 512) / 8; + yuv_len = (aligned_height * aligned_pitch * 3) / 2; + + return vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 0) + + yuv_len; +} + +static void config_ref_registers(struct rkvdec_ctx *ctx, + const struct rkvdec_vp9_run *run, + struct rkvdec_decoded_buffer *ref_buf, + struct rkvdec_vp9_ref_reg *ref_reg) +{ + unsigned int aligned_pitch, aligned_height, y_len, yuv_len; + struct rkvdec_dev *rkvdec = ctx->dev; + + aligned_height = round_up(ref_buf->vp9.height, 64); + writel_relaxed(RKVDEC_VP9_FRAMEWIDTH(ref_buf->vp9.width) | + RKVDEC_VP9_FRAMEHEIGHT(ref_buf->vp9.height), + rkvdec->regs + ref_reg->reg_frm_size); + + writel_relaxed(vb2_dma_contig_plane_dma_addr(&ref_buf->base.vb.vb2_buf, 0), + rkvdec->regs + ref_reg->reg_ref_base); + + if (&ref_buf->base.vb == run->base.bufs.dst) + return; + + aligned_pitch = round_up(ref_buf->vp9.width * ref_buf->vp9.bit_depth, 512) / 8; + y_len = aligned_height * aligned_pitch; + yuv_len = (y_len * 3) / 2; + + writel_relaxed(RKVDEC_HOR_Y_VIRSTRIDE(aligned_pitch / 16) | + RKVDEC_HOR_UV_VIRSTRIDE(aligned_pitch / 16), + rkvdec->regs + ref_reg->reg_hor_stride); + writel_relaxed(RKVDEC_VP9_REF_YSTRIDE(y_len / 16), + rkvdec->regs + ref_reg->reg_y_stride); + + if (!ref_reg->reg_yuv_stride) + return; + + writel_relaxed(RKVDEC_VP9_REF_YUVSTRIDE(yuv_len / 16), + rkvdec->regs + ref_reg->reg_yuv_stride); +} + +static void config_seg_registers(struct rkvdec_ctx *ctx, unsigned int segid) +{ + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + const struct v4l2_vp9_segmentation *seg; + struct rkvdec_dev *rkvdec = ctx->dev; + s16 feature_val; + int feature_id; + u32 val = 0; + + seg = vp9_ctx->last.valid ? &vp9_ctx->last.seg : &vp9_ctx->cur.seg; + feature_id = V4L2_VP9_SEG_LVL_ALT_Q; + if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { + feature_val = seg->feature_data[segid][feature_id]; + val |= RKVDEC_SEGID_FRAME_QP_DELTA_EN(1) | + RKVDEC_SEGID_FRAME_QP_DELTA(feature_val); + } + + feature_id = V4L2_VP9_SEG_LVL_ALT_L; + if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { + feature_val = seg->feature_data[segid][feature_id]; + val |= RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(1) | + RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(feature_val); + } + + feature_id = V4L2_VP9_SEG_LVL_REF_FRAME; + if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { + feature_val = seg->feature_data[segid][feature_id]; + val |= RKVDEC_SEGID_REFERINFO_EN(1) | + RKVDEC_SEGID_REFERINFO(feature_val); + } + + feature_id = V4L2_VP9_SEG_LVL_SKIP; + if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) + val |= RKVDEC_SEGID_FRAME_SKIP_EN(1); + + if (!segid && + (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE)) + val |= RKVDEC_SEGID_ABS_DELTA(1); + + writel_relaxed(val, rkvdec->regs + RKVDEC_VP9_SEGID_GRP(segid)); +} + +static void update_dec_buf_info(struct rkvdec_decoded_buffer *buf, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + buf->vp9.width = dec_params->frame_width_minus_1 + 1; + buf->vp9.height = dec_params->frame_height_minus_1 + 1; + buf->vp9.bit_depth = dec_params->bit_depth; +} + +static void update_ctx_cur_info(struct rkvdec_vp9_ctx *vp9_ctx, + struct rkvdec_decoded_buffer *buf, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + vp9_ctx->cur.valid = true; + vp9_ctx->cur.reference_mode = dec_params->reference_mode; + vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter; + vp9_ctx->cur.flags = dec_params->flags; + vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp; + vp9_ctx->cur.seg = dec_params->seg; + vp9_ctx->cur.lf = dec_params->lf; +} + +static void update_ctx_last_info(struct rkvdec_vp9_ctx *vp9_ctx) +{ + vp9_ctx->last = vp9_ctx->cur; +} + +static void config_registers(struct rkvdec_ctx *ctx, + const struct rkvdec_vp9_run *run) +{ + unsigned int y_len, uv_len, yuv_len, bit_depth, aligned_height, aligned_pitch, stream_len; + const struct v4l2_ctrl_vp9_frame *dec_params; + struct rkvdec_decoded_buffer *ref_bufs[3]; + struct rkvdec_decoded_buffer *dst, *last, *mv_ref; + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + u32 val, last_frame_info = 0; + const struct v4l2_vp9_segmentation *seg; + struct rkvdec_dev *rkvdec = ctx->dev; + dma_addr_t addr; + bool intra_only; + unsigned int i; + + dec_params = run->decode_params; + dst = vb2_to_rkvdec_decoded_buf(&run->base.bufs.dst->vb2_buf); + ref_bufs[0] = get_ref_buf(ctx, &dst->base.vb, dec_params->last_frame_ts); + ref_bufs[1] = get_ref_buf(ctx, &dst->base.vb, dec_params->golden_frame_ts); + ref_bufs[2] = get_ref_buf(ctx, &dst->base.vb, dec_params->alt_frame_ts); + + if (vp9_ctx->last.valid) + last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx->last.timestamp); + else + last = dst; + + update_dec_buf_info(dst, dec_params); + update_ctx_cur_info(vp9_ctx, dst, dec_params); + seg = &dec_params->seg; + + intra_only = !!(dec_params->flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | + V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); + + writel_relaxed(RKVDEC_MODE(RKVDEC_MODE_VP9), + rkvdec->regs + RKVDEC_REG_SYSCTRL); + + bit_depth = dec_params->bit_depth; + aligned_height = round_up(ctx->decoded_fmt.fmt.pix_mp.height, 64); + + aligned_pitch = round_up(ctx->decoded_fmt.fmt.pix_mp.width * + bit_depth, + 512) / 8; + y_len = aligned_height * aligned_pitch; + uv_len = y_len / 2; + yuv_len = y_len + uv_len; + + writel_relaxed(RKVDEC_Y_HOR_VIRSTRIDE(aligned_pitch / 16) | + RKVDEC_UV_HOR_VIRSTRIDE(aligned_pitch / 16), + rkvdec->regs + RKVDEC_REG_PICPAR); + writel_relaxed(RKVDEC_Y_VIRSTRIDE(y_len / 16), + rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); + writel_relaxed(RKVDEC_YUV_VIRSTRIDE(yuv_len / 16), + rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); + + stream_len = vb2_get_plane_payload(&run->base.bufs.src->vb2_buf, 0); + writel_relaxed(RKVDEC_STRM_LEN(stream_len), + rkvdec->regs + RKVDEC_REG_STRM_LEN); + + /* + * Reset count buffer, because decoder only output intra related syntax + * counts when decoding intra frame, but update entropy need to update + * all the probabilities. + */ + if (intra_only) + memset(vp9_ctx->count_tbl.cpu, 0, vp9_ctx->count_tbl.size); + + vp9_ctx->cur.segmapid = vp9_ctx->last.segmapid; + if (!intra_only && + !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && + (!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED) || + (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP))) + vp9_ctx->cur.segmapid++; + + for (i = 0; i < ARRAY_SIZE(ref_bufs); i++) + config_ref_registers(ctx, run, ref_bufs[i], &ref_regs[i]); + + for (i = 0; i < 8; i++) + config_seg_registers(ctx, i); + + writel_relaxed(RKVDEC_VP9_TX_MODE(vp9_ctx->cur.tx_mode) | + RKVDEC_VP9_FRAME_REF_MODE(dec_params->reference_mode), + rkvdec->regs + RKVDEC_VP9_CPRHEADER_CONFIG); + + if (!intra_only) { + const struct v4l2_vp9_loop_filter *lf; + s8 delta; + + if (vp9_ctx->last.valid) + lf = &vp9_ctx->last.lf; + else + lf = &vp9_ctx->cur.lf; + + val = 0; + for (i = 0; i < ARRAY_SIZE(lf->ref_deltas); i++) { + delta = lf->ref_deltas[i]; + val |= RKVDEC_REF_DELTAS_LASTFRAME(i, delta); + } + + writel_relaxed(val, + rkvdec->regs + RKVDEC_VP9_REF_DELTAS_LASTFRAME); + + for (i = 0; i < ARRAY_SIZE(lf->mode_deltas); i++) { + delta = lf->mode_deltas[i]; + last_frame_info |= RKVDEC_MODE_DELTAS_LASTFRAME(i, + delta); + } + } + + if (vp9_ctx->last.valid && !intra_only && + vp9_ctx->last.seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED) + last_frame_info |= RKVDEC_SEG_EN_LASTFRAME; + + if (vp9_ctx->last.valid && + vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME) + last_frame_info |= RKVDEC_LAST_SHOW_FRAME; + + if (vp9_ctx->last.valid && + vp9_ctx->last.flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY)) + last_frame_info |= RKVDEC_LAST_INTRA_ONLY; + + if (vp9_ctx->last.valid && + last->vp9.width == dst->vp9.width && + last->vp9.height == dst->vp9.height) + last_frame_info |= RKVDEC_LAST_WIDHHEIGHT_EQCUR; + + writel_relaxed(last_frame_info, + rkvdec->regs + RKVDEC_VP9_INFO_LASTFRAME); + + writel_relaxed(stream_len - dec_params->compressed_header_size - + dec_params->uncompressed_header_size, + rkvdec->regs + RKVDEC_VP9_LASTTILE_SIZE); + + for (i = 0; !intra_only && i < ARRAY_SIZE(ref_bufs); i++) { + unsigned int refw = ref_bufs[i]->vp9.width; + unsigned int refh = ref_bufs[i]->vp9.height; + u32 hscale, vscale; + + hscale = (refw << 14) / dst->vp9.width; + vscale = (refh << 14) / dst->vp9.height; + writel_relaxed(RKVDEC_VP9_REF_HOR_SCALE(hscale) | + RKVDEC_VP9_REF_VER_SCALE(vscale), + rkvdec->regs + RKVDEC_VP9_REF_SCALE(i)); + } + + addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 0); + writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); + addr = vb2_dma_contig_plane_dma_addr(&run->base.bufs.src->vb2_buf, 0); + writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); + writel_relaxed(vp9_ctx->priv_tbl.dma + + offsetof(struct rkvdec_vp9_priv_tbl, probs), + rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); + writel_relaxed(vp9_ctx->count_tbl.dma, + rkvdec->regs + RKVDEC_REG_VP9COUNT_BASE); + + writel_relaxed(vp9_ctx->priv_tbl.dma + + offsetof(struct rkvdec_vp9_priv_tbl, segmap) + + (RKVDEC_VP9_MAX_SEGMAP_SIZE * vp9_ctx->cur.segmapid), + rkvdec->regs + RKVDEC_REG_VP9_SEGIDCUR_BASE); + writel_relaxed(vp9_ctx->priv_tbl.dma + + offsetof(struct rkvdec_vp9_priv_tbl, segmap) + + (RKVDEC_VP9_MAX_SEGMAP_SIZE * (!vp9_ctx->cur.segmapid)), + rkvdec->regs + RKVDEC_REG_VP9_SEGIDLAST_BASE); + + if (!intra_only && + !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && + vp9_ctx->last.valid) + mv_ref = last; + else + mv_ref = dst; + + writel_relaxed(get_mv_base_addr(mv_ref), + rkvdec->regs + RKVDEC_VP9_REF_COLMV_BASE); + + writel_relaxed(ctx->decoded_fmt.fmt.pix_mp.width | + (ctx->decoded_fmt.fmt.pix_mp.height << 16), + rkvdec->regs + RKVDEC_REG_PERFORMANCE_CYCLE); +} + +static int validate_dec_params(struct rkvdec_ctx *ctx, + const struct v4l2_ctrl_vp9_frame *dec_params) +{ + unsigned int aligned_width, aligned_height; + + /* We only support profile 0. */ + if (dec_params->profile != 0) { + dev_err(ctx->dev->dev, "unsupported profile %d\n", + dec_params->profile); + return -EINVAL; + } + + aligned_width = round_up(dec_params->frame_width_minus_1 + 1, 64); + aligned_height = round_up(dec_params->frame_height_minus_1 + 1, 64); + + /* + * Userspace should update the capture/decoded format when the + * resolution changes. + */ + if (aligned_width != ctx->decoded_fmt.fmt.pix_mp.width || + aligned_height != ctx->decoded_fmt.fmt.pix_mp.height) { + dev_err(ctx->dev->dev, + "unexpected bitstream resolution %dx%d\n", + dec_params->frame_width_minus_1 + 1, + dec_params->frame_height_minus_1 + 1); + return -EINVAL; + } + + return 0; +} + +static int rkvdec_vp9_run_preamble(struct rkvdec_ctx *ctx, + struct rkvdec_vp9_run *run) +{ + const struct v4l2_ctrl_vp9_frame *dec_params; + const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates; + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct v4l2_ctrl *ctrl; + unsigned int fctx_idx; + int ret; + + /* v4l2-specific stuff */ + rkvdec_run_preamble(ctx, &run->base); + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_VP9_FRAME); + if (WARN_ON(!ctrl)) + return -EINVAL; + dec_params = ctrl->p_cur.p; + + ret = validate_dec_params(ctx, dec_params); + if (ret) + return ret; + + run->decode_params = dec_params; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, V4L2_CID_STATELESS_VP9_COMPRESSED_HDR); + if (WARN_ON(!ctrl)) + return -EINVAL; + prob_updates = ctrl->p_cur.p; + vp9_ctx->cur.tx_mode = prob_updates->tx_mode; + + /* + * vp9 stuff + * + * by this point the userspace has done all parts of 6.2 uncompressed_header() + * except this fragment: + * if ( FrameIsIntra || error_resilient_mode ) { + * setup_past_independence ( ) + * if ( frame_type == KEY_FRAME || error_resilient_mode == 1 || + * reset_frame_context == 3 ) { + * for ( i = 0; i < 4; i ++ ) { + * save_probs( i ) + * } + * } else if ( reset_frame_context == 2 ) { + * save_probs( frame_context_idx ) + * } + * frame_context_idx = 0 + * } + */ + fctx_idx = v4l2_vp9_reset_frame_ctx(dec_params, vp9_ctx->frame_context); + vp9_ctx->cur.frame_context_idx = fctx_idx; + + /* 6.1 frame(sz): load_probs() and load_probs2() */ + vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx]; + + /* + * The userspace has also performed 6.3 compressed_header(), but handling the + * probs in a special way. All probs which need updating, except MV-related, + * have been read from the bitstream and translated through inv_map_table[], + * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The values passed + * by userspace are either translated values (there are no 0 values in + * inv_map_table[]), or zero to indicate no update. All MV-related probs which need + * updating have been read from the bitstream and (mv_prob << 1) | 1 has been + * performed. The values passed by userspace are either new values + * to replace old ones (the above mentioned shift and bitwise or never result in + * a zero) or zero to indicate no update. + * fw_update_probs() performs actual probs updates or leaves probs as-is + * for values for which a zero was passed from userspace. + */ + v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, prob_updates, dec_params); + + return 0; +} + +static int rkvdec_vp9_run(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_vp9_run run = { }; + int ret; + + ret = rkvdec_vp9_run_preamble(ctx, &run); + if (ret) { + rkvdec_run_postamble(ctx, &run.base); + return ret; + } + + /* Prepare probs. */ + init_probs(ctx, &run); + + /* Configure hardware registers. */ + config_registers(ctx, &run); + + rkvdec_run_postamble(ctx, &run.base); + + schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000)); + + writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); + writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); + + writel(0xe, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); + /* Start decoding! */ + writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E | + RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E, + rkvdec->regs + RKVDEC_REG_INTERRUPT); + + return 0; +} + +#define copy_tx_and_skip(p1, p2) \ +do { \ + memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8)); \ + memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16)); \ + memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32)); \ + memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip)); \ +} while (0) + +static void rkvdec_vp9_done(struct rkvdec_ctx *ctx, + struct vb2_v4l2_buffer *src_buf, + struct vb2_v4l2_buffer *dst_buf, + enum vb2_buffer_state result) +{ + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + unsigned int fctx_idx; + + /* v4l2-specific stuff */ + if (result == VB2_BUF_STATE_ERROR) + goto out_update_last; + + /* + * vp9 stuff + * + * 6.1.2 refresh_probs() + * + * In the spec a complementary condition goes last in 6.1.2 refresh_probs(), + * but it makes no sense to perform all the activities from the first "if" + * there if we actually are not refreshing the frame context. On top of that, + * because of 6.2 uncompressed_header() whenever error_resilient_mode == 1, + * refresh_frame_context == 0. Consequently, if we don't jump to out_update_last + * it means error_resilient_mode must be 0. + */ + if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX)) + goto out_update_last; + + fctx_idx = vp9_ctx->cur.frame_context_idx; + + if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) { + /* error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 */ + struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; + bool frame_is_intra = vp9_ctx->cur.flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY); + struct tx_and_skip { + u8 tx8[2][1]; + u8 tx16[2][2]; + u8 tx32[2][3]; + u8 skip[3]; + } _tx_skip, *tx_skip = &_tx_skip; + struct v4l2_vp9_frame_symbol_counts *counts; + + /* buffer the forward-updated TX and skip probs */ + if (frame_is_intra) + copy_tx_and_skip(tx_skip, probs); + + /* 6.1.2 refresh_probs(): load_probs() and load_probs2() */ + *probs = vp9_ctx->frame_context[fctx_idx]; + + /* if FrameIsIntra then undo the effect of load_probs2() */ + if (frame_is_intra) + copy_tx_and_skip(probs, tx_skip); + + counts = frame_is_intra ? &vp9_ctx->intra_cnts : &vp9_ctx->inter_cnts; + v4l2_vp9_adapt_coef_probs(probs, counts, + !vp9_ctx->last.valid || + vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME, + frame_is_intra); + if (!frame_is_intra) { + const struct rkvdec_vp9_inter_frame_symbol_counts *inter_cnts; + u32 classes[2][11]; + int i; + + inter_cnts = vp9_ctx->count_tbl.cpu; + for (i = 0; i < ARRAY_SIZE(classes); ++i) + memcpy(classes[i], inter_cnts->classes[i], sizeof(classes[0])); + counts->classes = &classes; + + /* load_probs2() already done */ + v4l2_vp9_adapt_noncoef_probs(&vp9_ctx->probability_tables, counts, + vp9_ctx->cur.reference_mode, + vp9_ctx->cur.interpolation_filter, + vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags); + } + } + + /* 6.1.2 refresh_probs(): save_probs(fctx_idx) */ + vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables; + +out_update_last: + update_ctx_last_info(vp9_ctx); +} + +static void rkvdec_init_v4l2_vp9_count_tbl(struct rkvdec_ctx *ctx) +{ + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct rkvdec_vp9_intra_frame_symbol_counts *intra_cnts = vp9_ctx->count_tbl.cpu; + struct rkvdec_vp9_inter_frame_symbol_counts *inter_cnts = vp9_ctx->count_tbl.cpu; + int i, j, k, l, m; + + vp9_ctx->inter_cnts.partition = &inter_cnts->partition; + vp9_ctx->inter_cnts.skip = &inter_cnts->skip; + vp9_ctx->inter_cnts.intra_inter = &inter_cnts->inter; + vp9_ctx->inter_cnts.tx32p = &inter_cnts->tx32p; + vp9_ctx->inter_cnts.tx16p = &inter_cnts->tx16p; + vp9_ctx->inter_cnts.tx8p = &inter_cnts->tx8p; + + vp9_ctx->intra_cnts.partition = (u32 (*)[16][4])(&intra_cnts->partition); + vp9_ctx->intra_cnts.skip = &intra_cnts->skip; + vp9_ctx->intra_cnts.intra_inter = &intra_cnts->intra; + vp9_ctx->intra_cnts.tx32p = &intra_cnts->tx32p; + vp9_ctx->intra_cnts.tx16p = &intra_cnts->tx16p; + vp9_ctx->intra_cnts.tx8p = &intra_cnts->tx8p; + + vp9_ctx->inter_cnts.y_mode = &inter_cnts->y_mode; + vp9_ctx->inter_cnts.uv_mode = &inter_cnts->uv_mode; + vp9_ctx->inter_cnts.comp = &inter_cnts->comp; + vp9_ctx->inter_cnts.comp_ref = &inter_cnts->comp_ref; + vp9_ctx->inter_cnts.single_ref = &inter_cnts->single_ref; + vp9_ctx->inter_cnts.mv_mode = &inter_cnts->mv_mode; + vp9_ctx->inter_cnts.filter = &inter_cnts->filter; + vp9_ctx->inter_cnts.mv_joint = &inter_cnts->mv_joint; + vp9_ctx->inter_cnts.sign = &inter_cnts->sign; + /* + * rk hardware actually uses "u32 classes[2][11 + 1];" + * instead of "u32 classes[2][11];", so this must be explicitly + * copied into vp9_ctx->classes when passing the data to the + * vp9 library function + */ + vp9_ctx->inter_cnts.class0 = &inter_cnts->class0; + vp9_ctx->inter_cnts.bits = &inter_cnts->bits; + vp9_ctx->inter_cnts.class0_fp = &inter_cnts->class0_fp; + vp9_ctx->inter_cnts.fp = &inter_cnts->fp; + vp9_ctx->inter_cnts.class0_hp = &inter_cnts->class0_hp; + vp9_ctx->inter_cnts.hp = &inter_cnts->hp; + +#define INNERMOST_LOOP \ + do { \ + for (m = 0; m < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0][0][0]); ++m) {\ + vp9_ctx->inter_cnts.coeff[i][j][k][l][m] = \ + &inter_cnts->ref_cnt[k][i][j][l][m].coeff; \ + vp9_ctx->inter_cnts.eob[i][j][k][l][m][0] = \ + &inter_cnts->ref_cnt[k][i][j][l][m].eob[0]; \ + vp9_ctx->inter_cnts.eob[i][j][k][l][m][1] = \ + &inter_cnts->ref_cnt[k][i][j][l][m].eob[1]; \ + \ + vp9_ctx->intra_cnts.coeff[i][j][k][l][m] = \ + &intra_cnts->ref_cnt[k][i][j][l][m].coeff; \ + vp9_ctx->intra_cnts.eob[i][j][k][l][m][0] = \ + &intra_cnts->ref_cnt[k][i][j][l][m].eob[0]; \ + vp9_ctx->intra_cnts.eob[i][j][k][l][m][1] = \ + &intra_cnts->ref_cnt[k][i][j][l][m].eob[1]; \ + } \ + } while (0) + + for (i = 0; i < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff); ++i) + for (j = 0; j < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0]); ++j) + for (k = 0; k < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0]); ++k) + for (l = 0; l < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0][0]); ++l) + INNERMOST_LOOP; +#undef INNERMOST_LOOP +} + +static int rkvdec_vp9_start(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_vp9_priv_tbl *priv_tbl; + struct rkvdec_vp9_ctx *vp9_ctx; + unsigned char *count_tbl; + int ret; + + vp9_ctx = kzalloc(sizeof(*vp9_ctx), GFP_KERNEL); + if (!vp9_ctx) + return -ENOMEM; + + ctx->priv = vp9_ctx; + + BUILD_BUG_ON(sizeof(priv_tbl->probs) % 16); /* ensure probs size is 128-bit aligned */ + priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), + &vp9_ctx->priv_tbl.dma, GFP_KERNEL); + if (!priv_tbl) { + ret = -ENOMEM; + goto err_free_ctx; + } + + vp9_ctx->priv_tbl.size = sizeof(*priv_tbl); + vp9_ctx->priv_tbl.cpu = priv_tbl; + + count_tbl = dma_alloc_coherent(rkvdec->dev, RKVDEC_VP9_COUNT_SIZE, + &vp9_ctx->count_tbl.dma, GFP_KERNEL); + if (!count_tbl) { + ret = -ENOMEM; + goto err_free_priv_tbl; + } + + vp9_ctx->count_tbl.size = RKVDEC_VP9_COUNT_SIZE; + vp9_ctx->count_tbl.cpu = count_tbl; + rkvdec_init_v4l2_vp9_count_tbl(ctx); + + return 0; + +err_free_priv_tbl: + dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size, + vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma); + +err_free_ctx: + kfree(vp9_ctx); + return ret; +} + +static void rkvdec_vp9_stop(struct rkvdec_ctx *ctx) +{ + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct rkvdec_dev *rkvdec = ctx->dev; + + dma_free_coherent(rkvdec->dev, vp9_ctx->count_tbl.size, + vp9_ctx->count_tbl.cpu, vp9_ctx->count_tbl.dma); + dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size, + vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma); + kfree(vp9_ctx); +} + +static int rkvdec_vp9_adjust_fmt(struct rkvdec_ctx *ctx, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; + + fmt->num_planes = 1; + if (!fmt->plane_fmt[0].sizeimage) + fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * 2; + return 0; +} + +const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops = { + .adjust_fmt = rkvdec_vp9_adjust_fmt, + .start = rkvdec_vp9_start, + .stop = rkvdec_vp9_stop, + .run = rkvdec_vp9_run, + .done = rkvdec_vp9_done, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c new file mode 100644 index 000000000000..d707088ec0dc --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c @@ -0,0 +1,1251 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Video Decoder driver + * + * Copyright (C) 2019 Collabora, Ltd. + * + * Based on rkvdec driver by Google LLC. (Tomasz Figa <tfiga@chromium.org>) + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/iommu.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/workqueue.h> +#include <media/v4l2-event.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-vmalloc.h> + +#include "rkvdec.h" +#include "rkvdec-regs.h" + +static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1, + enum rkvdec_image_fmt fmt2) +{ + return fmt1 == fmt2 || fmt2 == RKVDEC_IMG_FMT_ANY || + fmt1 == RKVDEC_IMG_FMT_ANY; +} + +static bool rkvdec_image_fmt_changed(struct rkvdec_ctx *ctx, + enum rkvdec_image_fmt image_fmt) +{ + if (image_fmt == RKVDEC_IMG_FMT_ANY) + return false; + + return ctx->image_fmt != image_fmt; +} + +static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index, + enum rkvdec_image_fmt image_fmt) +{ + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + int fmt_idx = -1; + unsigned int i; + + if (WARN_ON(!desc)) + return 0; + + for (i = 0; i < desc->num_decoded_fmts; i++) { + if (!rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, + image_fmt)) + continue; + fmt_idx++; + if (index == fmt_idx) + return desc->decoded_fmts[i].fourcc; + } + + return 0; +} + +static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc, + enum rkvdec_image_fmt image_fmt) +{ + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + unsigned int i; + + for (i = 0; i < desc->num_decoded_fmts; i++) { + if (rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt, + image_fmt) && + desc->decoded_fmts[i].fourcc == fourcc) + return true; + } + + return false; +} + +static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, + struct v4l2_pix_format_mplane *pix_mp) +{ + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + pix_mp->plane_fmt[0].sizeimage += 128 * + DIV_ROUND_UP(pix_mp->width, 16) * + DIV_ROUND_UP(pix_mp->height, 16); +} + +static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, + u32 fourcc) +{ + memset(f, 0, sizeof(*f)); + f->fmt.pix_mp.pixelformat = fourcc; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx) +{ + struct v4l2_format *f = &ctx->decoded_fmt; + u32 fourcc; + + fourcc = rkvdec_enum_decoded_fmt(ctx, 0, ctx->image_fmt); + rkvdec_reset_fmt(ctx, f, fourcc); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width; + f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height; + rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp); +} + +static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + + if (desc->ops->try_ctrl) + return desc->ops->try_ctrl(ctx, ctrl); + + return 0; +} + +static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl); + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + enum rkvdec_image_fmt image_fmt; + struct vb2_queue *vq; + + /* Check if this change requires a capture format reset */ + if (!desc->ops->get_image_fmt) + return 0; + + image_fmt = desc->ops->get_image_fmt(ctx, ctrl); + if (rkvdec_image_fmt_changed(ctx, image_fmt)) { + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + ctx->image_fmt = image_fmt; + rkvdec_reset_decoded_fmt(ctx); + } + + return 0; +} + +static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { + .try_ctrl = rkvdec_try_ctrl, + .s_ctrl = rkvdec_s_ctrl, +}; + +static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_SPS, + .cfg.ops = &rkvdec_ctrl_ops, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_PPS, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_START_CODE, + .cfg.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .cfg.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE, + .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA, + .cfg.menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE), + .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + }, +}; + +static const struct rkvdec_ctrls rkvdec_h264_ctrls = { + .ctrls = rkvdec_h264_ctrl_descs, + .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs), +}; + +static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = RKVDEC_IMG_FMT_420_8BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV15, + .image_fmt = RKVDEC_IMG_FMT_420_10BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .image_fmt = RKVDEC_IMG_FMT_422_8BIT, + }, + { + .fourcc = V4L2_PIX_FMT_NV20, + .image_fmt = RKVDEC_IMG_FMT_422_10BIT, + }, +}; + +static const struct rkvdec_ctrl_desc rkvdec_vp9_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_VP9_FRAME, + }, + { + .cfg.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .cfg.max = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .cfg.def = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + }, +}; + +static const struct rkvdec_ctrls rkvdec_vp9_ctrls = { + .ctrls = rkvdec_vp9_ctrl_descs, + .num_ctrls = ARRAY_SIZE(rkvdec_vp9_ctrl_descs), +}; + +static const struct rkvdec_decoded_fmt_desc rkvdec_vp9_decoded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .image_fmt = RKVDEC_IMG_FMT_420_8BIT, + }, +}; + +static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 64, + .min_height = 48, + .max_height = 2560, + .step_height = 16, + }, + .ctrls = &rkvdec_h264_ctrls, + .ops = &rkvdec_h264_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), + .decoded_fmts = rkvdec_h264_decoded_fmts, + .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + }, + { + .fourcc = V4L2_PIX_FMT_VP9_FRAME, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 64, + .min_height = 64, + .max_height = 2304, + .step_height = 64, + }, + .ctrls = &rkvdec_vp9_ctrls, + .ops = &rkvdec_vp9_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts), + .decoded_fmts = rkvdec_vp9_decoded_fmts, + } +}; + +static const struct rkvdec_coded_fmt_desc * +rkvdec_find_coded_fmt_desc(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { + if (rkvdec_coded_fmts[i].fourcc == fourcc) + return &rkvdec_coded_fmts[i]; + } + + return NULL; +} + +static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx) +{ + struct v4l2_format *f = &ctx->coded_fmt; + + ctx->coded_fmt_desc = &rkvdec_coded_fmts[0]; + rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc); + + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width; + f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height; + + if (ctx->coded_fmt_desc->ops->adjust_fmt) + ctx->coded_fmt_desc->ops->adjust_fmt(ctx, f); +} + +static int rkvdec_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + const struct rkvdec_coded_fmt_desc *fmt; + + if (fsize->index != 0) + return -EINVAL; + + fmt = rkvdec_find_coded_fmt_desc(fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = 1; + fsize->stepwise.max_width = fmt->frmsize.max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = 1; + fsize->stepwise.max_height = fmt->frmsize.max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int rkvdec_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rkvdec_dev *rkvdec = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + + strscpy(cap->driver, rkvdec->dev->driver->name, + sizeof(cap->driver)); + strscpy(cap->card, vdev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + rkvdec->dev->driver->name); + return 0; +} + +static int rkvdec_try_capture_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + const struct rkvdec_coded_fmt_desc *coded_desc; + + /* + * The codec context should point to a coded format desc, if the format + * on the coded end has not been set yet, it should point to the + * default value. + */ + coded_desc = ctx->coded_fmt_desc; + if (WARN_ON(!coded_desc)) + return -EINVAL; + + if (!rkvdec_is_valid_fmt(ctx, pix_mp->pixelformat, ctx->image_fmt)) + pix_mp->pixelformat = rkvdec_enum_decoded_fmt(ctx, 0, + ctx->image_fmt); + + /* Always apply the frmsize constraint of the coded end. */ + pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width); + pix_mp->height = max(pix_mp->height, ctx->coded_fmt.fmt.pix_mp.height); + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &coded_desc->frmsize); + + rkvdec_fill_decoded_pixfmt(ctx, pix_mp); + pix_mp->field = V4L2_FIELD_NONE; + + return 0; +} + +static int rkvdec_try_output_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + const struct rkvdec_coded_fmt_desc *desc; + + desc = rkvdec_find_coded_fmt_desc(pix_mp->pixelformat); + if (!desc) { + pix_mp->pixelformat = rkvdec_coded_fmts[0].fourcc; + desc = &rkvdec_coded_fmts[0]; + } + + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &desc->frmsize); + + pix_mp->field = V4L2_FIELD_NONE; + /* All coded formats are considered single planar for now. */ + pix_mp->num_planes = 1; + + if (desc->ops->adjust_fmt) { + int ret; + + ret = desc->ops->adjust_fmt(ctx, f); + if (ret) + return ret; + } + + return 0; +} + +static int rkvdec_s_capture_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + struct vb2_queue *vq; + int ret; + + /* Change not allowed if queue is busy */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(vq)) + return -EBUSY; + + ret = rkvdec_try_capture_fmt(file, priv, f); + if (ret) + return ret; + + ctx->decoded_fmt = *f; + return 0; +} + +static int rkvdec_s_output_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + const struct rkvdec_coded_fmt_desc *desc; + struct v4l2_format *cap_fmt; + struct vb2_queue *peer_vq, *vq; + int ret; + + /* + * In order to support dynamic resolution change, the decoder admits + * a resolution change, as long as the pixelformat remains. Can't be + * done if streaming. + */ + vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (vb2_is_streaming(vq) || + (vb2_is_busy(vq) && + f->fmt.pix_mp.pixelformat != ctx->coded_fmt.fmt.pix_mp.pixelformat)) + return -EBUSY; + + /* + * Since format change on the OUTPUT queue will reset the CAPTURE + * queue, we can't allow doing so when the CAPTURE queue has buffers + * allocated. + */ + peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (vb2_is_busy(peer_vq)) + return -EBUSY; + + ret = rkvdec_try_output_fmt(file, priv, f); + if (ret) + return ret; + + desc = rkvdec_find_coded_fmt_desc(f->fmt.pix_mp.pixelformat); + if (!desc) + return -EINVAL; + ctx->coded_fmt_desc = desc; + ctx->coded_fmt = *f; + + /* + * Current decoded format might have become invalid with newly + * selected codec, so reset it to default just to be safe and + * keep internal driver state sane. User is mandated to set + * the decoded format again after we return, so we don't need + * anything smarter. + * + * Note that this will propagates any size changes to the decoded format. + */ + ctx->image_fmt = RKVDEC_IMG_FMT_ANY; + rkvdec_reset_decoded_fmt(ctx); + + /* Propagate colorspace information to capture. */ + cap_fmt = &ctx->decoded_fmt; + cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace; + cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func; + cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization; + + /* Enable format specific queue features */ + vq->subsystem_flags |= desc->subsystem_flags; + + return 0; +} + +static int rkvdec_g_output_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + + *f = ctx->coded_fmt; + return 0; +} + +static int rkvdec_g_capture_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + + *f = ctx->decoded_fmt; + return 0; +} + +static int rkvdec_enum_output_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(rkvdec_coded_fmts)) + return -EINVAL; + + f->pixelformat = rkvdec_coded_fmts[f->index].fourcc; + return 0; +} + +static int rkvdec_enum_capture_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv); + u32 fourcc; + + fourcc = rkvdec_enum_decoded_fmt(ctx, f->index, ctx->image_fmt); + if (!fourcc) + return -EINVAL; + + f->pixelformat = fourcc; + return 0; +} + +static const struct v4l2_ioctl_ops rkvdec_ioctl_ops = { + .vidioc_querycap = rkvdec_querycap, + .vidioc_enum_framesizes = rkvdec_enum_framesizes, + + .vidioc_try_fmt_vid_cap_mplane = rkvdec_try_capture_fmt, + .vidioc_try_fmt_vid_out_mplane = rkvdec_try_output_fmt, + .vidioc_s_fmt_vid_out_mplane = rkvdec_s_output_fmt, + .vidioc_s_fmt_vid_cap_mplane = rkvdec_s_capture_fmt, + .vidioc_g_fmt_vid_out_mplane = rkvdec_g_output_fmt, + .vidioc_g_fmt_vid_cap_mplane = rkvdec_g_capture_fmt, + .vidioc_enum_fmt_vid_out = rkvdec_enum_output_fmt, + .vidioc_enum_fmt_vid_cap = rkvdec_enum_capture_fmt, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd, +}; + +static int rkvdec_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_format *f; + unsigned int i; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + f = &ctx->coded_fmt; + else + f = &ctx->decoded_fmt; + + if (*num_planes) { + if (*num_planes != f->fmt.pix_mp.num_planes) + return -EINVAL; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage) + return -EINVAL; + } + } else { + *num_planes = f->fmt.pix_mp.num_planes; + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) + sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + return 0; +} + +static int rkvdec_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_format *f; + unsigned int i; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + f = &ctx->coded_fmt; + else + f = &ctx->decoded_fmt; + + for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) { + u32 sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage; + + if (vb2_plane_size(vb, i) < sizeimage) + return -EINVAL; + } + + /* + * Buffer's bytesused must be written by driver for CAPTURE buffers. + * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets + * it to buffer length). + */ + if (V4L2_TYPE_IS_CAPTURE(vq->type)) + vb2_set_plane_payload(vb, 0, f->fmt.pix_mp.plane_fmt[0].sizeimage); + + return 0; +} + +static void rkvdec_buf_queue(struct vb2_buffer *vb) +{ + struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int rkvdec_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +static void rkvdec_buf_request_complete(struct vb2_buffer *vb) +{ + struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); +} + +static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); + const struct rkvdec_coded_fmt_desc *desc; + int ret; + + if (V4L2_TYPE_IS_CAPTURE(q->type)) + return 0; + + desc = ctx->coded_fmt_desc; + if (WARN_ON(!desc)) + return -EINVAL; + + if (desc->ops->start) { + ret = desc->ops->start(ctx); + if (ret) + return ret; + } + + return 0; +} + +static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state) +{ + struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq); + + while (true) { + struct vb2_v4l2_buffer *vbuf; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (!vbuf) + break; + + v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, + &ctx->ctrl_hdl); + v4l2_m2m_buf_done(vbuf, state); + } +} + +static void rkvdec_stop_streaming(struct vb2_queue *q) +{ + struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + + if (WARN_ON(!desc)) + return; + + if (desc->ops->stop) + desc->ops->stop(ctx); + } + + rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops rkvdec_queue_ops = { + .queue_setup = rkvdec_queue_setup, + .buf_prepare = rkvdec_buf_prepare, + .buf_queue = rkvdec_buf_queue, + .buf_out_validate = rkvdec_buf_out_validate, + .buf_request_complete = rkvdec_buf_request_complete, + .start_streaming = rkvdec_start_streaming, + .stop_streaming = rkvdec_stop_streaming, +}; + +static int rkvdec_request_validate(struct media_request *req) +{ + unsigned int count; + + count = vb2_request_buffer_cnt(req); + if (!count) + return -ENOENT; + else if (count > 1) + return -EINVAL; + + return vb2_request_validate(req); +} + +static const struct media_device_ops rkvdec_media_ops = { + .req_validate = rkvdec_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + +static void rkvdec_job_finish_no_pm(struct rkvdec_ctx *ctx, + enum vb2_buffer_state result) +{ + if (ctx->coded_fmt_desc->ops->done) { + struct vb2_v4l2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + ctx->coded_fmt_desc->ops->done(ctx, src_buf, dst_buf, result); + } + + v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, + result); +} + +static void rkvdec_job_finish(struct rkvdec_ctx *ctx, + enum vb2_buffer_state result) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + + pm_runtime_mark_last_busy(rkvdec->dev); + pm_runtime_put_autosuspend(rkvdec->dev); + rkvdec_job_finish_no_pm(ctx, result); +} + +void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) +{ + struct media_request *src_req; + + memset(run, 0, sizeof(*run)); + + run->bufs.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + run->bufs.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + /* Apply request(s) controls if needed. */ + src_req = run->bufs.src->vb2_buf.req_obj.req; + if (src_req) + v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl); + + v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst, true); +} + +void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run) +{ + struct media_request *src_req = run->bufs.src->vb2_buf.req_obj.req; + + if (src_req) + v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl); +} + +static void rkvdec_device_run(void *priv) +{ + struct rkvdec_ctx *ctx = priv; + struct rkvdec_dev *rkvdec = ctx->dev; + const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; + int ret; + + if (WARN_ON(!desc)) + return; + + ret = pm_runtime_resume_and_get(rkvdec->dev); + if (ret < 0) { + rkvdec_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR); + return; + } + + ret = desc->ops->run(ctx); + if (ret) + rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR); +} + +static const struct v4l2_m2m_ops rkvdec_m2m_ops = { + .device_run = rkvdec_device_run, +}; + +static int rkvdec_queue_init(void *priv, + struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct rkvdec_ctx *ctx = priv; + struct rkvdec_dev *rkvdec = ctx->dev; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->ops = &rkvdec_queue_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + /* + * Driver does mostly sequential access, so sacrifice TLB efficiency + * for faster allocation. Also, no CPU access on the source queue, + * so no kernel mapping needed. + */ + src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | + DMA_ATTR_NO_KERNEL_MAPPING; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &rkvdec->vdev_lock; + src_vq->dev = rkvdec->v4l2_dev.dev; + src_vq->supports_requests = true; + src_vq->requires_requests = true; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->bidirectional = true; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | + DMA_ATTR_NO_KERNEL_MAPPING; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->ops = &rkvdec_queue_ops; + dst_vq->buf_struct_size = sizeof(struct rkvdec_decoded_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &rkvdec->vdev_lock; + dst_vq->dev = rkvdec->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +static int rkvdec_add_ctrls(struct rkvdec_ctx *ctx, + const struct rkvdec_ctrls *ctrls) +{ + unsigned int i; + + for (i = 0; i < ctrls->num_ctrls; i++) { + const struct v4l2_ctrl_config *cfg = &ctrls->ctrls[i].cfg; + + v4l2_ctrl_new_custom(&ctx->ctrl_hdl, cfg, ctx); + if (ctx->ctrl_hdl.error) + return ctx->ctrl_hdl.error; + } + + return 0; +} + +static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx) +{ + unsigned int i, nctrls = 0; + int ret; + + for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) + nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls; + + v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls); + + for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { + ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls); + if (ret) + goto err_free_handler; + } + + ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + if (ret) + goto err_free_handler; + + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + return ret; +} + +static int rkvdec_open(struct file *filp) +{ + struct rkvdec_dev *rkvdec = video_drvdata(filp); + struct rkvdec_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = rkvdec; + rkvdec_reset_coded_fmt(ctx); + rkvdec_reset_decoded_fmt(ctx); + v4l2_fh_init(&ctx->fh, video_devdata(filp)); + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rkvdec->m2m_dev, ctx, + rkvdec_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_free_ctx; + } + + ret = rkvdec_init_ctrls(ctx); + if (ret) + goto err_cleanup_m2m_ctx; + + filp->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + return 0; + +err_cleanup_m2m_ctx: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + +err_free_ctx: + kfree(ctx); + return ret; +} + +static int rkvdec_release(struct file *filp) +{ + struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(filp->private_data); + + v4l2_fh_del(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations rkvdec_fops = { + .owner = THIS_MODULE, + .open = rkvdec_open, + .release = rkvdec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int rkvdec_v4l2_init(struct rkvdec_dev *rkvdec) +{ + int ret; + + ret = v4l2_device_register(rkvdec->dev, &rkvdec->v4l2_dev); + if (ret) { + dev_err(rkvdec->dev, "Failed to register V4L2 device\n"); + return ret; + } + + rkvdec->m2m_dev = v4l2_m2m_init(&rkvdec_m2m_ops); + if (IS_ERR(rkvdec->m2m_dev)) { + v4l2_err(&rkvdec->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(rkvdec->m2m_dev); + goto err_unregister_v4l2; + } + + rkvdec->mdev.dev = rkvdec->dev; + strscpy(rkvdec->mdev.model, "rkvdec", sizeof(rkvdec->mdev.model)); + strscpy(rkvdec->mdev.bus_info, "platform:rkvdec", + sizeof(rkvdec->mdev.bus_info)); + media_device_init(&rkvdec->mdev); + rkvdec->mdev.ops = &rkvdec_media_ops; + rkvdec->v4l2_dev.mdev = &rkvdec->mdev; + + rkvdec->vdev.lock = &rkvdec->vdev_lock; + rkvdec->vdev.v4l2_dev = &rkvdec->v4l2_dev; + rkvdec->vdev.fops = &rkvdec_fops; + rkvdec->vdev.release = video_device_release_empty; + rkvdec->vdev.vfl_dir = VFL_DIR_M2M; + rkvdec->vdev.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; + rkvdec->vdev.ioctl_ops = &rkvdec_ioctl_ops; + video_set_drvdata(&rkvdec->vdev, rkvdec); + strscpy(rkvdec->vdev.name, "rkvdec", sizeof(rkvdec->vdev.name)); + + ret = video_register_device(&rkvdec->vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(&rkvdec->v4l2_dev, "Failed to register video device\n"); + goto err_cleanup_mc; + } + + ret = v4l2_m2m_register_media_controller(rkvdec->m2m_dev, &rkvdec->vdev, + MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (ret) { + v4l2_err(&rkvdec->v4l2_dev, + "Failed to initialize V4L2 M2M media controller\n"); + goto err_unregister_vdev; + } + + ret = media_device_register(&rkvdec->mdev); + if (ret) { + v4l2_err(&rkvdec->v4l2_dev, "Failed to register media device\n"); + goto err_unregister_mc; + } + + return 0; + +err_unregister_mc: + v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev); + +err_unregister_vdev: + video_unregister_device(&rkvdec->vdev); + +err_cleanup_mc: + media_device_cleanup(&rkvdec->mdev); + v4l2_m2m_release(rkvdec->m2m_dev); + +err_unregister_v4l2: + v4l2_device_unregister(&rkvdec->v4l2_dev); + return ret; +} + +static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec) +{ + media_device_unregister(&rkvdec->mdev); + v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev); + video_unregister_device(&rkvdec->vdev); + media_device_cleanup(&rkvdec->mdev); + v4l2_m2m_release(rkvdec->m2m_dev); + v4l2_device_unregister(&rkvdec->v4l2_dev); +} + +static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec) +{ + if (rkvdec->empty_domain) { + /* + * To rewrite mapping into the attached IOMMU core, attach a new empty domain that + * will program an empty table, then detach it to restore the default domain and + * all cached mappings. + * This is safely done in this interrupt handler to make sure no memory get mapped + * through the IOMMU while the empty domain is attached. + */ + iommu_attach_device(rkvdec->empty_domain, rkvdec->dev); + iommu_detach_device(rkvdec->empty_domain, rkvdec->dev); + } +} + +static irqreturn_t rkvdec_irq_handler(int irq, void *priv) +{ + struct rkvdec_dev *rkvdec = priv; + struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); + enum vb2_buffer_state state; + u32 status; + + status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT); + writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT); + + if (status & RKVDEC_RDY_STA) { + state = VB2_BUF_STATE_DONE; + } else { + state = VB2_BUF_STATE_ERROR; + if (status & RKVDEC_SOFTRESET_RDY) + rkvdec_iommu_restore(rkvdec); + } + + if (cancel_delayed_work(&rkvdec->watchdog_work)) + rkvdec_job_finish(ctx, state); + + return IRQ_HANDLED; +} + +static void rkvdec_watchdog_func(struct work_struct *work) +{ + struct rkvdec_dev *rkvdec; + struct rkvdec_ctx *ctx; + + rkvdec = container_of(to_delayed_work(work), struct rkvdec_dev, + watchdog_work); + ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); + if (ctx) { + dev_err(rkvdec->dev, "Frame processing timed out!\n"); + writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT); + writel(0, rkvdec->regs + RKVDEC_REG_SYSCTRL); + rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR); + } +} + +static const struct of_device_id of_rkvdec_match[] = { + { .compatible = "rockchip,rk3399-vdec" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_rkvdec_match); + +static const char * const rkvdec_clk_names[] = { + "axi", "ahb", "cabac", "core" +}; + +static int rkvdec_probe(struct platform_device *pdev) +{ + struct rkvdec_dev *rkvdec; + unsigned int i; + int ret, irq; + + rkvdec = devm_kzalloc(&pdev->dev, sizeof(*rkvdec), GFP_KERNEL); + if (!rkvdec) + return -ENOMEM; + + platform_set_drvdata(pdev, rkvdec); + rkvdec->dev = &pdev->dev; + mutex_init(&rkvdec->vdev_lock); + INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func); + + rkvdec->clocks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), + sizeof(*rkvdec->clocks), GFP_KERNEL); + if (!rkvdec->clocks) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(rkvdec_clk_names); i++) + rkvdec->clocks[i].id = rkvdec_clk_names[i]; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), + rkvdec->clocks); + if (ret) + return ret; + + rkvdec->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rkvdec->regs)) + return PTR_ERR(rkvdec->regs); + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Could not set DMA coherent mask.\n"); + return ret; + } + + if (iommu_get_domain_for_dev(&pdev->dev)) { + rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev); + + if (!rkvdec->empty_domain) + dev_warn(rkvdec->dev, "cannot alloc new empty domain\n"); + } + + vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -ENXIO; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + rkvdec_irq_handler, IRQF_ONESHOT, + dev_name(&pdev->dev), rkvdec); + if (ret) { + dev_err(&pdev->dev, "Could not request vdec IRQ\n"); + return ret; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, 100); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = rkvdec_v4l2_init(rkvdec); + if (ret) + goto err_disable_runtime_pm; + + return 0; + +err_disable_runtime_pm: + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static void rkvdec_remove(struct platform_device *pdev) +{ + struct rkvdec_dev *rkvdec = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&rkvdec->watchdog_work); + + rkvdec_v4l2_cleanup(rkvdec); + pm_runtime_disable(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + + if (rkvdec->empty_domain) + iommu_domain_free(rkvdec->empty_domain); +} + +#ifdef CONFIG_PM +static int rkvdec_runtime_resume(struct device *dev) +{ + struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(ARRAY_SIZE(rkvdec_clk_names), + rkvdec->clocks); +} + +static int rkvdec_runtime_suspend(struct device *dev) +{ + struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(ARRAY_SIZE(rkvdec_clk_names), + rkvdec->clocks); + return 0; +} +#endif + +static const struct dev_pm_ops rkvdec_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(rkvdec_runtime_suspend, rkvdec_runtime_resume, NULL) +}; + +static struct platform_driver rkvdec_driver = { + .probe = rkvdec_probe, + .remove = rkvdec_remove, + .driver = { + .name = "rkvdec", + .of_match_table = of_rkvdec_match, + .pm = &rkvdec_pm_ops, + }, +}; +module_platform_driver(rkvdec_driver); + +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>"); +MODULE_DESCRIPTION("Rockchip Video Decoder driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h new file mode 100644 index 000000000000..f6e8bf38add3 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Video Decoder driver + * + * Copyright (C) 2019 Collabora, Ltd. + * + * Based on rkvdec driver by Google LLC. (Tomasz Figa <tfiga@chromium.org>) + * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + */ +#ifndef RKVDEC_H_ +#define RKVDEC_H_ + +#include <linux/platform_device.h> +#include <linux/videodev2.h> +#include <linux/wait.h> +#include <linux/clk.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +struct rkvdec_ctx; + +struct rkvdec_ctrl_desc { + struct v4l2_ctrl_config cfg; +}; + +struct rkvdec_ctrls { + const struct rkvdec_ctrl_desc *ctrls; + unsigned int num_ctrls; +}; + +struct rkvdec_run { + struct { + struct vb2_v4l2_buffer *src; + struct vb2_v4l2_buffer *dst; + } bufs; +}; + +struct rkvdec_vp9_decoded_buffer_info { + /* Info needed when the decoded frame serves as a reference frame. */ + unsigned short width; + unsigned short height; + unsigned int bit_depth : 4; +}; + +struct rkvdec_decoded_buffer { + /* Must be the first field in this struct. */ + struct v4l2_m2m_buffer base; + + union { + struct rkvdec_vp9_decoded_buffer_info vp9; + }; +}; + +static inline struct rkvdec_decoded_buffer * +vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf) +{ + return container_of(buf, struct rkvdec_decoded_buffer, + base.vb.vb2_buf); +} + +struct rkvdec_coded_fmt_ops { + int (*adjust_fmt)(struct rkvdec_ctx *ctx, + struct v4l2_format *f); + int (*start)(struct rkvdec_ctx *ctx); + void (*stop)(struct rkvdec_ctx *ctx); + int (*run)(struct rkvdec_ctx *ctx); + void (*done)(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *src_buf, + struct vb2_v4l2_buffer *dst_buf, + enum vb2_buffer_state result); + int (*try_ctrl)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl); + enum rkvdec_image_fmt (*get_image_fmt)(struct rkvdec_ctx *ctx, + struct v4l2_ctrl *ctrl); +}; + +enum rkvdec_image_fmt { + RKVDEC_IMG_FMT_ANY = 0, + RKVDEC_IMG_FMT_420_8BIT, + RKVDEC_IMG_FMT_420_10BIT, + RKVDEC_IMG_FMT_422_8BIT, + RKVDEC_IMG_FMT_422_10BIT, +}; + +struct rkvdec_decoded_fmt_desc { + u32 fourcc; + enum rkvdec_image_fmt image_fmt; +}; + +struct rkvdec_coded_fmt_desc { + u32 fourcc; + struct v4l2_frmsize_stepwise frmsize; + const struct rkvdec_ctrls *ctrls; + const struct rkvdec_coded_fmt_ops *ops; + unsigned int num_decoded_fmts; + const struct rkvdec_decoded_fmt_desc *decoded_fmts; + u32 subsystem_flags; +}; + +struct rkvdec_dev { + struct v4l2_device v4l2_dev; + struct media_device mdev; + struct video_device vdev; + struct v4l2_m2m_dev *m2m_dev; + struct device *dev; + struct clk_bulk_data *clocks; + void __iomem *regs; + struct mutex vdev_lock; /* serializes ioctls */ + struct delayed_work watchdog_work; + struct iommu_domain *empty_domain; +}; + +struct rkvdec_ctx { + struct v4l2_fh fh; + struct v4l2_format coded_fmt; + struct v4l2_format decoded_fmt; + const struct rkvdec_coded_fmt_desc *coded_fmt_desc; + struct v4l2_ctrl_handler ctrl_hdl; + struct rkvdec_dev *dev; + enum rkvdec_image_fmt image_fmt; + void *priv; +}; + +static inline struct rkvdec_ctx *fh_to_rkvdec_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct rkvdec_ctx, fh); +} + +struct rkvdec_aux_buf { + void *cpu; + dma_addr_t dma; + size_t size; +}; + +void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); +void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); + +extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops; +extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops; + +#endif /* RKVDEC_H_ */ diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c index b243cbb1d010..b5b37b6f8fa8 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c @@ -131,7 +131,7 @@ static const struct dev_pm_ops fimc_is_i2c_pm_ops = { }; static const struct of_device_id fimc_is_i2c_of_match[] = { - { .compatible = FIMC_IS_I2C_COMPATIBLE }, + { .compatible = "samsung,exynos4212-i2c-isp" }, { }, }; diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h index a23bd20be6c8..69d597e5c297 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h @@ -6,7 +6,5 @@ * Sylwester Nawrocki <s.nawrocki@samsung.com> */ -#define FIMC_IS_I2C_COMPATIBLE "samsung,exynos4212-i2c-isp" - int fimc_is_register_i2c_driver(void); void fimc_is_unregister_i2c_driver(void); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index 2e8fe9e49735..ed7b7ca16f71 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -207,7 +207,7 @@ static int fimc_is_register_subdevs(struct fimc_is *is) if (ret < 0) return ret; - for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) { + for_each_compatible_node(i2c_bus, NULL, "samsung,exynos4212-i2c-isp") { for_each_available_child_of_node(i2c_bus, child) { ret = fimc_is_parse_sensor_config(is, index, child); diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c index b5ee3c547789..c781853586fd 100644 --- a/drivers/media/platform/samsung/exynos4-is/media-dev.c +++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c @@ -482,15 +482,12 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, static int fimc_md_parse_port_node(struct fimc_md *fmd, struct device_node *port) { - struct device_node *ep; int ret; - for_each_child_of_node(port, ep) { + for_each_child_of_node_scoped(port, ep) { ret = fimc_md_parse_one_endpoint(fmd, ep); - if (ret < 0) { - of_node_put(ep); + if (ret < 0) return ret; - } } return 0; @@ -501,7 +498,6 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct device_node *parent = fmd->pdev->dev.of_node; struct device_node *ports = NULL; - struct device_node *node; int ret; /* @@ -518,7 +514,7 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fmd->num_sensors = 0; /* Attach sensors linked to MIPI CSI-2 receivers */ - for_each_available_child_of_node(parent, node) { + for_each_available_child_of_node_scoped(parent, node) { struct device_node *port; if (!of_node_name_eq(node, "csis")) @@ -530,10 +526,8 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) ret = fimc_md_parse_port_node(fmd, port); of_node_put(port); - if (ret < 0) { - of_node_put(node); + if (ret < 0) goto cleanup; - } } /* Attach sensors listed in the parallel-ports node */ @@ -541,12 +535,10 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) if (!ports) goto rpm_put; - for_each_child_of_node(ports, node) { + for_each_child_of_node_scoped(ports, node) { ret = fimc_md_parse_port_node(fmd, node); - if (ret < 0) { - of_node_put(node); + if (ret < 0) goto cleanup; - } } of_node_put(ports); @@ -736,10 +728,9 @@ dev_unlock: static int fimc_md_register_platform_entities(struct fimc_md *fmd, struct device_node *parent) { - struct device_node *node; int ret = 0; - for_each_available_child_of_node(parent, node) { + for_each_available_child_of_node_scoped(parent, node) { struct platform_device *pdev; int plat_entity = -1; @@ -762,10 +753,8 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd, ret = fimc_md_register_platform_entity(fmd, pdev, plat_entity); put_device(&pdev->dev); - if (ret < 0) { - of_node_put(node); + if (ret < 0) break; - } } return ret; diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index 602c37cbe177..89bd15a4d26a 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c @@ -658,7 +658,7 @@ static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv) static int c8sectpfe_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *child, *np = dev->of_node; + struct device_node *np = dev->of_node; struct c8sectpfei *fei; struct resource *res; int ret, index = 0; @@ -742,17 +742,15 @@ static int c8sectpfe_probe(struct platform_device *pdev) return PTR_ERR(fei->pinctrl); } - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { struct device_node *i2c_bus; fei->channel_data[index] = devm_kzalloc(dev, sizeof(struct channel_info), GFP_KERNEL); - if (!fei->channel_data[index]) { - ret = -ENOMEM; - goto err_node_put; - } + if (!fei->channel_data[index]) + return -ENOMEM; tsin = fei->channel_data[index]; @@ -761,7 +759,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id); if (ret) { dev_err(&pdev->dev, "No tsin_num found\n"); - goto err_node_put; + return ret; } /* sanity check value */ @@ -769,8 +767,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) dev_err(&pdev->dev, "tsin-num %d specified greater than number\n\tof input block hw in SoC! (%d)", tsin->tsin_id, fei->hw_stats.num_ib); - ret = -EINVAL; - goto err_node_put; + return -EINVAL; } tsin->invert_ts_clk = of_property_read_bool(child, @@ -786,22 +783,20 @@ static int c8sectpfe_probe(struct platform_device *pdev) &tsin->dvb_card); if (ret) { dev_err(&pdev->dev, "No dvb-card found\n"); - goto err_node_put; + return ret; } i2c_bus = of_parse_phandle(child, "i2c-bus", 0); if (!i2c_bus) { dev_err(&pdev->dev, "No i2c-bus found\n"); - ret = -ENODEV; - goto err_node_put; + return -ENODEV; } tsin->i2c_adapter = of_find_i2c_adapter_by_node(i2c_bus); of_node_put(i2c_bus); if (!tsin->i2c_adapter) { dev_err(&pdev->dev, "No i2c adapter found\n"); - ret = -ENODEV; - goto err_node_put; + return -ENODEV; } /* Acquire reset GPIO and activate it */ @@ -813,7 +808,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) if (ret && ret != -EBUSY) { dev_err(dev, "Can't request tsin%d reset gpio\n", fei->channel_data[index]->tsin_id); - goto err_node_put; + return ret; } if (!ret) { @@ -855,10 +850,6 @@ static int c8sectpfe_probe(struct platform_device *pdev) c8sectpfe_debugfs_init(fei); return 0; - -err_node_put: - of_node_put(child); - return ret; } static void c8sectpfe_remove(struct platform_device *pdev) @@ -897,16 +888,15 @@ static void c8sectpfe_remove(struct platform_device *pdev) static int configure_channels(struct c8sectpfei *fei) { int index = 0, ret; - struct device_node *child, *np = fei->dev->of_node; + struct device_node *np = fei->dev->of_node; /* iterate round each tsin and configure memdma descriptor and IB hw */ - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { ret = configure_memdma_and_inputblock(fei, fei->channel_data[index]); if (ret) { dev_err(fei->dev, "configure_memdma_and_inputblock failed\n"); - of_node_put(child); goto err_unmap; } index++; diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 6412a00be8ea..b628d6e081db 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -619,6 +619,7 @@ static void ti_csi2rx_dma_callback(void *param) if (ti_csi2rx_start_dma(csi, buf)) { dev_err(csi->dev, "Failed to queue the next buffer for DMA\n"); + list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } else { list_move_tail(&buf->list, &dma->submitted); @@ -895,6 +896,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi) q->dev = dmaengine_get_dma_device(csi->dma.chan); q->lock = &csi->mutex; q->min_queued_buffers = 1; + q->allow_cache_hints = 1; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c index da90d7f03f82..bb8a8bd7980c 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -552,38 +552,6 @@ EXPORT_SYMBOL(vpdma_submit_descs); static void dump_dtd(struct vpdma_dtd *dtd); -void vpdma_update_dma_addr(struct vpdma_data *vpdma, - struct vpdma_desc_list *list, dma_addr_t dma_addr, - void *write_dtd, int drop, int idx) -{ - struct vpdma_dtd *dtd = list->buf.addr; - dma_addr_t write_desc_addr; - int offset; - - dtd += idx; - vpdma_unmap_desc_buf(vpdma, &list->buf); - - dtd->start_addr = dma_addr; - - /* Calculate write address from the offset of write_dtd from start - * of the list->buf - */ - offset = (void *)write_dtd - list->buf.addr; - write_desc_addr = list->buf.dma_addr + offset; - - if (drop) - dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, - 1, 1, 0); - else - dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, - 1, 0, 0); - - vpdma_map_desc_buf(vpdma, &list->buf); - - dump_dtd(dtd); -} -EXPORT_SYMBOL(vpdma_update_dma_addr); - void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, u32 width, u32 height) { diff --git a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h index 393fcbb3cb40..e4d7941c6207 100644 --- a/drivers/media/platform/ti/vpe/vpdma.h +++ b/drivers/media/platform/ti/vpe/vpdma.h @@ -222,9 +222,6 @@ void vpdma_free_desc_list(struct vpdma_desc_list *list); int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list, int list_num); bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num); -void vpdma_update_dma_addr(struct vpdma_data *vpdma, - struct vpdma_desc_list *list, dma_addr_t dma_addr, - void *write_dtd, int drop, int idx); /* VPDMA hardware list funcs */ int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv); diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h index edc217eed293..81328c63b796 100644 --- a/drivers/media/platform/verisilicon/hantro.h +++ b/drivers/media/platform/verisilicon/hantro.h @@ -323,6 +323,8 @@ struct hantro_postproc_regs { struct hantro_reg output_fmt; struct hantro_reg orig_width; struct hantro_reg display_width; + struct hantro_reg input_width_ext; + struct hantro_reg input_height_ext; }; struct hantro_vp9_decoded_buffer_info { diff --git a/drivers/media/platform/verisilicon/hantro_g1_regs.h b/drivers/media/platform/verisilicon/hantro_g1_regs.h index c623b3b0be18..7874d76c6898 100644 --- a/drivers/media/platform/verisilicon/hantro_g1_regs.h +++ b/drivers/media/platform/verisilicon/hantro_g1_regs.h @@ -350,7 +350,7 @@ #define G1_REG_PP_CONTROL_OUT_WIDTH(v) (((v) << 4) & GENMASK(14, 4)) #define G1_REG_PP_MASK1_ORIG_WIDTH G1_SWREG(88) #define G1_REG_PP_ORIG_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) -#define G1_REG_PP_DISPLAY_WIDTH G1_SWREG(92) +#define G1_REG_PP_DISPLAY_WIDTH_IN_EXT G1_SWREG(92) #define G1_REG_PP_FUSE G1_SWREG(99) #endif /* HANTRO_G1_REGS_H_ */ diff --git a/drivers/media/platform/verisilicon/hantro_h264.c b/drivers/media/platform/verisilicon/hantro_h264.c index 4e9a0ecf5c13..2414782f1eb6 100644 --- a/drivers/media/platform/verisilicon/hantro_h264.c +++ b/drivers/media/platform/verisilicon/hantro_h264.c @@ -325,12 +325,12 @@ static void update_dpb(struct hantro_ctx *ctx) continue; *cdpb = *ndpb; - set_bit(j, used); + __set_bit(j, used); break; } if (j == ARRAY_SIZE(ctx->h264_dec.dpb)) - set_bit(i, new); + __set_bit(i, new); } /* For entries that could not be matched, use remaining free slots. */ @@ -349,7 +349,7 @@ static void update_dpb(struct hantro_ctx *ctx) cdpb = &ctx->h264_dec.dpb[j]; *cdpb = *ndpb; - set_bit(j, used); + __set_bit(j, used); } } diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index 9f559a13d409..e94d1ba5ef10 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -49,7 +49,9 @@ static const struct hantro_postproc_regs hantro_g1_postproc_regs = { .input_fmt = {G1_REG_PP_CONTROL, 29, 0x7}, .output_fmt = {G1_REG_PP_CONTROL, 26, 0x7}, .orig_width = {G1_REG_PP_MASK1_ORIG_WIDTH, 23, 0x1ff}, - .display_width = {G1_REG_PP_DISPLAY_WIDTH, 0, 0xfff}, + .display_width = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 0, 0xfff}, + .input_width_ext = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 26, 0x7}, + .input_height_ext = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 29, 0x7}, }; bool hantro_needs_postproc(const struct hantro_ctx *ctx, @@ -103,6 +105,8 @@ static void hantro_postproc_g1_enable(struct hantro_ctx *ctx) HANTRO_PP_REG_WRITE(vpu, output_height, ctx->dst_fmt.height); HANTRO_PP_REG_WRITE(vpu, orig_width, MB_WIDTH(ctx->dst_fmt.width)); HANTRO_PP_REG_WRITE(vpu, display_width, ctx->dst_fmt.width); + HANTRO_PP_REG_WRITE(vpu, input_width_ext, MB_WIDTH(ctx->dst_fmt.width) >> 9); + HANTRO_PP_REG_WRITE(vpu, input_height_ext, MB_HEIGHT(ctx->dst_fmt.height >> 8)); } static int down_scale_factor(struct hantro_ctx *ctx) diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c index acd29fa41d2d..02673be9878e 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c @@ -17,7 +17,6 @@ #define RK3066_ACLK_MAX_FREQ (300 * 1000 * 1000) #define RK3288_ACLK_MAX_FREQ (400 * 1000 * 1000) -#define RK3588_ACLK_MAX_FREQ (300 * 1000 * 1000) #define ROCKCHIP_VPU981_MIN_SIZE 64 @@ -454,13 +453,6 @@ static int rk3066_vpu_hw_init(struct hantro_dev *vpu) return 0; } -static int rk3588_vpu981_hw_init(struct hantro_dev *vpu) -{ - /* Bump ACLKs to max. possible freq. to improve performance. */ - clk_set_rate(vpu->clocks[0].clk, RK3588_ACLK_MAX_FREQ); - return 0; -} - static int rockchip_vpu_hw_init(struct hantro_dev *vpu) { /* Bump ACLK to max. possible freq. to improve performance. */ @@ -821,7 +813,6 @@ const struct hantro_variant rk3588_vpu981_variant = { .codec_ops = rk3588_vpu981_codec_ops, .irqs = rk3588_vpu981_irqs, .num_irqs = ARRAY_SIZE(rk3588_vpu981_irqs), - .init = rk3588_vpu981_hw_init, .clk_names = rk3588_vpu981_vpu_clk_names, .num_clocks = ARRAY_SIZE(rk3588_vpu981_vpu_clk_names) }; diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index 024b439feec9..30675f681410 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -450,7 +450,6 @@ static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev, static int xvip_graph_dma_init(struct xvip_composite_device *xdev) { struct device_node *ports; - struct device_node *port; int ret = 0; ports = of_get_child_by_name(xdev->dev->of_node, "ports"); @@ -459,12 +458,10 @@ static int xvip_graph_dma_init(struct xvip_composite_device *xdev) return -EINVAL; } - for_each_child_of_node(ports, port) { + for_each_child_of_node_scoped(ports, port) { ret = xvip_graph_dma_init_one(xdev, port); - if (ret) { - of_node_put(port); + if (ret) break; - } } of_node_put(ports); |