blob: 812d82ddcf4df6c74df5c5add0077aa10a6837dc [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/sched.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <linux/amlogic/media/video_sink/video_keeper.h>
#include "video_priv.h"
#include "video_reg.h"
#include <linux/amlogic/media/utils/vdec_reg.h>
#include <linux/amlogic/media/registers/register.h>
#include <linux/uaccess.h>
#include <linux/amlogic/media/utils/amports_config.h>
#include <linux/amlogic/media/vpu/vpu.h>
#include "videolog.h"
#include "video_reg.h"
#ifdef CONFIG_AM_VIDEO_LOG
#define AMLOG
#endif
#include <linux/amlogic/media/utils/amlog.h>
MODULE_AMLOG(LOG_LEVEL_ERROR, 0, LOG_DEFAULT_LEVEL_DESC, LOG_MASK_DESC);
#include <linux/amlogic/media/video_sink/vpp.h>
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
#include "../common/rdma/rdma.h"
#endif
#include <linux/amlogic/media/video_sink/video.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include "../common/vfm/vfm.h"
#if defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM)
#include <linux/amlogic/media/amvecm/amvecm.h>
#endif
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
#include <linux/amlogic/pm.h>
#endif
#ifdef CONFIG_AMLOGIC_MEDIA_SECURITY
#include <linux/amlogic/media/vpu_secure/vpu_secure.h>
#endif
#include <linux/math64.h>
#include "video_receiver.h"
struct video_layer_s vd_layer_vpp[2];
atomic_t video_inirq_flag_vpp[2] = {ATOMIC_INIT(0), ATOMIC_INIT(0)};
atomic_t video_unreg_flag_vpp[2] = {ATOMIC_INIT(0), ATOMIC_INIT(0)};
static int vsync_enter_line_max_vpp[2];
static int vsync_exit_line_max_vpp[2];
static bool rdma_enable_vppx_pre[2];
static unsigned int debug_flag1;
MODULE_PARM_DESC(debug_flag1, "\n debug_flag1\n");
module_param(debug_flag1, uint, 0664);
bool is_vpp0(u8 layer_id)
{
if (vd_layer[layer_id].vpp_index == VPP0)
return true;
else
return false;
}
bool is_vpp1(u8 layer_id)
{
if (layer_id < 1)
return false;
if (vd_layer_vpp[layer_id - 1].vpp_index == VPP1)
return true;
else
return false;
}
bool is_vpp2(u8 layer_id)
{
if (layer_id < 1)
return false;
if (vd_layer_vpp[layer_id - 1].vpp_index == VPP2)
return true;
else
return false;
}
int get_receiver_id(u8 layer_id)
{
int receiver_id = VFM_PATH_VIDEO_RENDER0;
switch (layer_id) {
case 0:
receiver_id = VFM_PATH_VIDEO_RENDER0;
break;
case 1:
if (is_vpp0(layer_id))
receiver_id = VFM_PATH_VIDEO_RENDER1;
else if (is_vpp1(layer_id))
receiver_id = VFM_PATH_VIDEO_RENDER5;
else if (is_vpp2(layer_id))
receiver_id = VFM_PATH_VIDEO_RENDER6;
break;
case 2:
if (is_vpp0(layer_id))
receiver_id = VFM_PATH_VIDEO_RENDER2;
else if (is_vpp1(layer_id))
receiver_id = VFM_PATH_VIDEO_RENDER5;
else if (is_vpp2(layer_id))
receiver_id = VFM_PATH_VIDEO_RENDER6;
break;
default:
break;
}
return receiver_id;
}
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
static void vsync_rdma_vppx_process(u8 vpp_index)
{
if (vpp_index == VPP1)
vsync_rdma_vpp1_config();
else if (vpp_index == VPP2)
vsync_rdma_vpp2_config();
}
static bool is_vsync_vppx_rdma_enable(u8 vpp_index)
{
bool enable = false;
if (vpp_index == VPP1)
enable = is_vsync_vpp1_rdma_enable();
else if (vpp_index == VPP2)
enable = is_vsync_vpp2_rdma_enable();
return enable;
}
#endif
static void pipx_render_frame(u8 vpp_index, const struct vinfo_s *vinfo)
{
u8 vpp_id = 0;
vpp_id = vpp_index - VPP1;
if (vpp_id < VPP2) {
if (vpp_index == VPP1)
pip_render_frame(&vd_layer_vpp[vpp_id], vinfo);
else if (vpp_index == VPP2)
pip2_render_frame(&vd_layer_vpp[vpp_id], vinfo);
}
}
static void pipx_swap_frame(u8 vpp_index, struct vframe_s *vf,
const struct vinfo_s *vinfo)
{
u8 vpp_id = 0;
vpp_id = vpp_index - VPP1;
if (vpp_id < VPP2) {
if (vpp_index == VPP1)
pip_swap_frame(&vd_layer_vpp[vpp_id], vf, vinfo);
else if (vpp_index == VPP2)
pip2_swap_frame(&vd_layer_vpp[vpp_id], vf, vinfo);
}
}
#define MAX_LOG_CNT 10
irqreturn_t vsync_isr_viux(u8 vpp_index, const struct vinfo_s *info)
{
int hold_line;
int enc_line;
s32 vout_type;
struct vframe_s *path0_new_frame = NULL;
struct vframe_s *new_frame = NULL;
u32 cur_blackout = 0;
static s32 cur_vd_path_id = VFM_PATH_INVALID;
int axis[4];
int crop[4];
u8 layer_id = 1, vpp_id = 0, recv_id = 0;
s32 vd_path_id = 0;
if (video_suspend && video_suspend_cycle >= 1) {
if (log_out)
pr_info("video suspend, vsync exit\n");
log_out = 0;
return IRQ_HANDLED;
}
if (debug_flag & DEBUG_FLAG_VSYNC_DONONE)
return IRQ_HANDLED;
vpp_id = vpp_index - 1;
recv_id = vpp_id;
layer_id = vd_layer_vpp[vpp_id].layer_id;
vd_path_id = glayer_info[layer_id].display_path_id;
if (cur_vd_path_id == 0xff)
cur_vd_path_id = vd_path_id;
vout_type = detect_vout_type(info);
hold_line = calc_hold_line();
glayer_info[layer_id].need_no_compress = false;
vd_layer_vpp[vpp_id].bypass_pps = bypass_pps;
vd_layer_vpp[vpp_id].global_debug = debug_flag;
vd_layer_vpp[vpp_id].vout_type = vout_type;
enc_line = get_cur_enc_line();
if (enc_line > vsync_enter_line_max_vpp[vpp_id])
vsync_enter_line_max_vpp[vpp_id] = enc_line;
#ifdef CONFIG_AMLOGIC_VIDEO_COMPOSER
vsync_notify_video_composer();
#endif
if (atomic_read(&video_unreg_flag_vpp[vpp_id]))
goto exit;
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
if (is_vsync_vppx_rdma_enable(vpp_index)) {
vd_layer_vpp[vpp_id].cur_canvas_id = vd_layer_vpp[vpp_id].next_canvas_id;
} else {
if (rdma_enable_vppx_pre[vpp_id])
goto exit;
vd_layer_vpp[vpp_id].cur_canvas_id = 0;
vd_layer_vpp[vpp_id].next_canvas_id = 1;
}
#endif
if (gvideo_recv_vpp[recv_id])
gvideo_recv_vpp[recv_id]->func->early_proc(gvideo_recv_vpp[recv_id], 0);
/* video_render.5/6 toggle frame */
if (gvideo_recv_vpp[recv_id]) {
if (debug_flag1 & 1)
pr_info("video_render5/6 dequeue\n");
path0_new_frame =
gvideo_recv_vpp[recv_id]->func->dequeue_frame(gvideo_recv_vpp[recv_id]);
#ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM
if (vd_path_id == gvideo_recv_vpp[recv_id]->path_id) {
amvecm_on_vs
((gvideo_recv_vpp[recv_id]->cur_buf !=
&gvideo_recv_vpp[recv_id]->local_buf)
? gvideo_recv_vpp[recv_id]->cur_buf : NULL,
path0_new_frame,
CSC_FLAG_CHECK_OUTPUT,
0,
0,
0,
0,
0,
0,
layer_id,
vpp_index);
}
#endif
}
if (!vd_layer_vpp[vpp_id].global_output) {
cur_vd_path_id = VFM_PATH_INVALID;
vd_path_id = VFM_PATH_INVALID;
}
vd_layer_vpp[vpp_id].force_switch_mode = force_switch_vf_mode;
if (gvideo_recv_vpp[recv_id] &&
vd_layer_vpp[vpp_id].dispbuf_mapping == &gvideo_recv_vpp[recv_id]->cur_buf &&
(gvideo_recv_vpp[recv_id]->cur_buf == &gvideo_recv_vpp[recv_id]->local_buf ||
!gvideo_recv_vpp[recv_id]->cur_buf) &&
vd_layer_vpp[vpp_id].dispbuf != gvideo_recv_vpp[recv_id]->cur_buf)
vd_layer_vpp[vpp_id].dispbuf = gvideo_recv_vpp[recv_id]->cur_buf;
if (vd_layer_vpp[vpp_id].switch_vf &&
vd_layer_vpp[vpp_id].dispbuf &&
vd_layer_vpp[vpp_id].dispbuf->vf_ext)
vd_layer_vpp[vpp_id].vf_ext =
(struct vframe_s *)vd_layer_vpp[vpp_id].dispbuf->vf_ext;
else
vd_layer_vpp[vpp_id].vf_ext = NULL;
/* vd2/3 config */
if (gvideo_recv_vpp[recv_id] &&
gvideo_recv_vpp[recv_id]->path_id == vd_path_id) {
/* video_render.5/6 display on VD2/3 */
new_frame = path0_new_frame;
if (!new_frame) {
if (!gvideo_recv_vpp[recv_id]->cur_buf) {
/* video_render.5/6 no frame in display */
if (cur_vd_path_id != vd_path_id)
safe_switch_videolayer(layer_id, false, true);
vd_layer_vpp[vpp_id].dispbuf = NULL;
} else if (gvideo_recv_vpp[recv_id]->cur_buf ==
&gvideo_recv_vpp[recv_id]->local_buf) {
/* video_render.5/6 keep frame */
vd_layer_vpp[vpp_id].dispbuf = gvideo_recv_vpp[recv_id]->cur_buf;
} else if (vd_layer_vpp[vpp_id].dispbuf
!= gvideo_recv_vpp[recv_id]->cur_buf) {
/* video_render.5/6 has frame in display */
new_frame = gvideo_recv_vpp[recv_id]->cur_buf;
}
}
if (new_frame || gvideo_recv_vpp[recv_id]->cur_buf)
vd_layer_vpp[vpp_id].dispbuf_mapping = &gvideo_recv_vpp[recv_id]->cur_buf;
cur_blackout = 1;
}
if (!new_frame && vd_layer_vpp[vpp_id].dispbuf &&
is_local_vf(vd_layer_vpp[vpp_id].dispbuf)) {
if (cur_blackout) {
vd_layer_vpp[vpp_id].property_changed = false;
} else if (vd_layer_vpp[vpp_id].dispbuf) {
if (vd_layer_vpp[vpp_id].switch_vf && vd_layer_vpp[vpp_id].vf_ext)
vd_layer_vpp[vpp_id].vf_ext->canvas0Addr =
get_layer_display_canvas(layer_id);
else
vd_layer_vpp[vpp_id].dispbuf->canvas0Addr =
get_layer_display_canvas(layer_id);
}
}
if (vd_layer_vpp[vpp_id].dispbuf &&
(vd_layer_vpp[vpp_id].dispbuf->flag & (VFRAME_FLAG_VIDEO_COMPOSER |
VFRAME_FLAG_VIDEO_DRM)) &&
!(debug_flag & DEBUG_FLAG_AXIS_NO_UPDATE)) {
axis[0] = vd_layer_vpp[vpp_id].dispbuf->axis[0];
axis[1] = vd_layer_vpp[vpp_id].dispbuf->axis[1];
axis[2] = vd_layer_vpp[vpp_id].dispbuf->axis[2];
axis[3] = vd_layer_vpp[vpp_id].dispbuf->axis[3];
crop[0] = vd_layer_vpp[vpp_id].dispbuf->crop[0];
crop[1] = vd_layer_vpp[vpp_id].dispbuf->crop[1];
crop[2] = vd_layer_vpp[vpp_id].dispbuf->crop[2];
crop[3] = vd_layer_vpp[vpp_id].dispbuf->crop[3];
_set_video_window(&glayer_info[layer_id], axis);
_set_video_crop(&glayer_info[layer_id], crop);
set_alpha_scpxn(&vd_layer_vpp[vpp_id],
vd_layer_vpp[vpp_id].dispbuf->componser_info);
glayer_info[layer_id].zorder = vd_layer_vpp[vpp_id].dispbuf->zorder;
}
/* setting video display property in underflow mode */
if (!new_frame &&
vd_layer_vpp[vpp_id].dispbuf &&
vd_layer_vpp[vpp_id].property_changed) {
pipx_swap_frame(vpp_index, vd_layer_vpp[vpp_id].dispbuf, info);
if (layer_id == 1)
need_disable_vd2 = false;
else if (layer_id == 2)
need_disable_vd3 = false;
} else if (new_frame) {
pipx_swap_frame(vpp_index, new_frame, info);
if (layer_id == 1)
need_disable_vd2 = false;
else if (layer_id == 2)
need_disable_vd3 = false;
}
vd_layer_vpp[vpp_id].keep_frame_id = 0xff;
#if defined(CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_VECM)
amvecm_on_vs
(!is_local_vf(vd_layer_vpp[vpp_id].dispbuf)
? vd_layer_vpp[vpp_id].dispbuf : NULL,
new_frame,
new_frame ? CSC_FLAG_TOGGLE_FRAME : 0,
vd_layer_vpp[vpp_id].cur_frame_par ?
vd_layer_vpp[vpp_id].cur_frame_par->supsc1_hori_ratio :
0,
vd_layer_vpp[vpp_id].cur_frame_par ?
vd_layer_vpp[vpp_id].cur_frame_par->supsc1_vert_ratio :
0,
vd_layer_vpp[vpp_id].cur_frame_par ?
vd_layer_vpp[vpp_id].cur_frame_par->spsc1_w_in :
0,
vd_layer_vpp[vpp_id].cur_frame_par ?
vd_layer_vpp[vpp_id].cur_frame_par->spsc1_h_in :
0,
vd_layer_vpp[vpp_id].cur_frame_par ?
vd_layer_vpp[vpp_id].cur_frame_par->cm_input_w :
0,
vd_layer_vpp[vpp_id].cur_frame_par ?
vd_layer_vpp[vpp_id].cur_frame_par->cm_input_h :
0,
layer_id,
vpp_index);
#endif
if (need_disable_vd2 || need_disable_vd3)
safe_switch_videolayer(layer_id, false, true);
/* filter setting management */
pipx_render_frame(vpp_index, info);
/* video_secure_set(); */
if (vd_layer_vpp[vpp_id].dispbuf &&
(vd_layer_vpp[vpp_id].dispbuf->flag & VFRAME_FLAG_FAKE_FRAME))
safe_switch_videolayer(layer_id, false, true);
/* all frames has been renderred, so reset new frame flag */
vd_layer_vpp[vpp_id].new_frame = false;
exit:
vppx_blend_update(info, vpp_index);
if (gvideo_recv_vpp[recv_id])
gvideo_recv_vpp[recv_id]->func->late_proc(gvideo_recv_vpp[recv_id]);
#ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA
vsync_rdma_vppx_process(vpp_index);
rdma_enable_vppx_pre[vpp_id] = is_vsync_vppx_rdma_enable(vpp_index);
#endif
enc_line = get_cur_enc_line();
if (enc_line > vsync_exit_line_max_vpp[vpp_id])
vsync_exit_line_max_vpp[vpp_id] = enc_line;
if (video_suspend)
video_suspend_cycle++;
vpu_work_process();
cur_vd_path_id = vd_path_id;
return IRQ_HANDLED;
}
irqreturn_t vsync_isr_viu2(int irq, void *dev_id)
{
irqreturn_t ret;
const struct vinfo_s *info = NULL;
atomic_set(&video_inirq_flag_vpp[0], 1);
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
info = get_current_vinfo2();
#endif
ret = vsync_isr_viux(VPP1, info);
atomic_set(&video_inirq_flag_vpp[0], 0);
return ret;
}
irqreturn_t vsync_isr_viu3(int irq, void *dev_id)
{
irqreturn_t ret;
const struct vinfo_s *info = NULL;
atomic_set(&video_inirq_flag_vpp[1], 1);
#ifdef CONFIG_AMLOGIC_VOUT3_SERVE
info = get_current_vinfo3();
#endif
ret = vsync_isr_viux(VPP2, info);
atomic_set(&video_inirq_flag_vpp[1], 0);
return ret;
}