blob: 02e30aed29a039ef84cafb5362e781bcf9eabb7f [file]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vout/vout_notify.h>
#include <linux/amlogic/media/frame_provider/tvin/tvin_v4l2.h>
#include "../tvin_global.h"
#include "../tvin_frontend.h"
#include "../tvin_format_table.h"
#define DEVICE_NAME "viuin"
#define MODULE_NAME "viuin"
#define AMVIUIN_DEC_START 1
#define AMVIUIN_DEC_STOP 0
/* register */
#define VIU_MISC_CTRL1 0x1a07
#define VPU_VIU_VENC_MUX_CTRL 0x271a
#define ENCI_INFO_READ 0x271c
#define ENCP_INFO_READ 0x271d
#define ENCT_INFO_READ 0x271e
#define ENCL_INFO_READ 0x271f
#define VPU_VIU2VDIN_HDN_CTRL 0x2780
/* t7 enable hsync reg */
#define VIU_FRM_CTRL 0x1a51
#define VIU1_FRM_CTRL 0x1a8d
#define VIU2_FRM_CTRL 0x1a8e
/* ENCL/ENCT polarity, positive:begin > end, negative: begin < end */
#define ENCL_VIDEO_VSO_BLINE 0x1cb9
#define ENCL_VIDEO_VSO_ELINE 0x1cba
/* ENCP polarity, positive:begin > end, negative: begin < end */
#define ENCP_VIDEO_VSO_BLINE 0x1bab
#define ENCP_VIDEO_VSO_ELINE 0x1bac
/* ENCI polarity, always negative, no need control dynamically */
/*g12a new add*/
#define VPU_VIU_ASYNC_MASK 0x2781
#define VDIN_MISC_CTRL 0x2782
#define VPU_VIU_VDIN_IF_MUX_CTRL 0x2783
#define VPU_VIU2VDIN1_HDN_CTRL 0x2784
#define VPU_VENCX_CLK_CTRL 0x2785
#define VPP_WR_BAK_CTRL 0x1df9
#define VPP1_WR_BAK_CTRL 0x5981
#define VPP2_WR_BAK_CTRL 0x59c1
#define VPU_422TO444_CTRL0 0x274b
#define VPU_422TO444_CTRL1 0x274c
#define VPU_422TO444_CTRL2 0x274d
#define WR_BACK_MISC_CTRL 0x1a0d
/* s5 */
#define VIU_WR_BAK_CTRL 0x1a25
#define S5_VPP_POST_HOLD_CTRL 0x1d1f
/* t3x */
#define VPP_POST_HOLD_CTRL_T3X 0x1d19
#define VPU_VIU_VDIN_IF_MUX_CTRL_T3X 0x2783
#define VPU_VIU2VDIN0_BUF_SIZE_T3X 0x2784
#define VDIN_LITE_CORE_MAX_PIXEL_CLOCK (4096 * 2160 * 60UL)
/* txhd2 for keystone dump vdin1 */
#define VPP_MISC 0x1d26
static unsigned int vsync_enter_line_curr;
module_param(vsync_enter_line_curr, uint, 0664);
MODULE_PARM_DESC(vsync_enter_line_curr,
"\n encoder process line num when enter isr.\n");
static unsigned int vsync_enter_line_max;
module_param(vsync_enter_line_max, uint, 0664);
MODULE_PARM_DESC(vsync_enter_line_max,
"\n max encoder process line num when enter isr.\n");
static unsigned int vsync_enter_line_max_threshold = 10000;
module_param(vsync_enter_line_max_threshold, uint, 0664);
MODULE_PARM_DESC(vsync_enter_line_max_threshold,
"\n max encoder process line num over threshold drop the frame.\n");
static unsigned int vsync_enter_line_min_threshold = 10000;
module_param(vsync_enter_line_min_threshold, uint, 0664);
MODULE_PARM_DESC(vsync_enter_line_min_threshold,
"\n max encoder process line num less threshold drop the frame.\n");
static unsigned int vsync_enter_line_threshold_overflow_count;
module_param(vsync_enter_line_threshold_overflow_count, uint, 0664);
MODULE_PARM_DESC(vsync_enter_line_threshold_overflow_count,
"\ncnt overflow encoder process line no over threshold drop the frame\n");
static unsigned short v_cut_offset;
module_param(v_cut_offset, ushort, 0664);
MODULE_PARM_DESC(v_cut_offset, "the cut window vertical offset for viuin");
static unsigned short open_cnt;
module_param(open_cnt, ushort, 0664);
MODULE_PARM_DESC(open_cnt, "open_cnt for vdin0/1");
struct viuin_s {
unsigned int flag;
struct vframe_prop_s *prop;
/*add for tvin frontend*/
struct tvin_frontend_s frontend;
struct vdin_parm_s parm;
unsigned int enc_info_addr;
};
static inline u32 rd_viu(u32 reg)
{
return (u32)aml_read_vcbus(reg);
}
static inline void wr_viu(u32 reg,
const u32 val)
{
aml_write_vcbus(reg, val);
}
static inline void wr_bits_viu(u32 reg,
const u32 value,
const u32 start,
const u32 len)
{
aml_write_vcbus(reg, ((aml_read_vcbus(reg) &
~(((1L << (len)) - 1) << (start))) |
(((value) & ((1L << (len)) - 1)) << (start))));
}
static inline u32 rd_bits_viu(u32 reg,
const u32 start,
const u32 len)
{
u32 val;
val = ((aml_read_vcbus(reg) >> (start)) & ((1L << (len)) - 1));
return val;
}
static int viuin_support(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
if ((port >= TVIN_PORT_VIU1 && port < TVIN_PORT_VIU1_MAX) ||
(port >= TVIN_PORT_VIU2 && port < TVIN_PORT_VIU2_MAX) ||
(port >= TVIN_PORT_VIU3 && port < TVIN_PORT_VIU3_MAX) ||
(port >= TVIN_PORT_VENC && port < TVIN_PORT_VENC_MAX) ||
port == TVIN_PORT_MIPI || port == TVIN_PORT_ISP)
return 0;
else
return -1;
}
void viuin_check_venc_line(struct viuin_s *devp_local)
{
unsigned int vencv_line_cur, cnt;
cnt = 0;
do {
vencv_line_cur =
(rd_viu(devp_local->enc_info_addr) >> 16) & 0x1fff;
udelay(9);
cnt++;
if (cnt > 100000)
break;
} while (vencv_line_cur != 1);
if (vencv_line_cur != 1)
pr_info("**************%s,vencv_line_cur:%d,cnt:%d***********\n",
__func__, vencv_line_cur, cnt);
}
void viuin_select_loopback_path(void)
{
/* set VPU_VIU_VDIN_IF_MUX_CTRL */
wr_viu(VPU_VIU_VDIN_IF_MUX_CTRL_T3X, 0x8080808);
/* set VPP_WRBAK_CTRL */
wr_viu(VPP_WR_BAK_CTRL, 0xff0006);
/* wrback hsync en */
//if (port >= TVIN_PORT_VIU1_VIDEO &&
// port <= TVIN_PORT_VIU1_WB0_POST_BLEND) {
wr_bits_viu(WR_BACK_MISC_CTRL, 1, 0, 1);/*vd0 hsync*/
wr_bits_viu(WR_BACK_MISC_CTRL, 0, 1, 1);/*vd1 hsync*/
//} else {
// wr_bits_viu(WR_BACK_MISC_CTRL, 0xf, 0, 4);
//}
//if (cpu_after_eq(MESON_CPU_MAJOR_ID_T5M)) {
wr_viu(VIU_FRM_CTRL, 0x3000004);
//}
}
EXPORT_SYMBOL(viuin_select_loopback_path);
void viuin_clear_loopback_path(void)
{
wr_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0);
wr_viu(VPP_WR_BAK_CTRL, 0);
}
EXPORT_SYMBOL(viuin_clear_loopback_path);
static void viuin_set_vdin_if_mux_ctrl(enum tvin_port_e port)
{
unsigned int viu_mux = 0;
unsigned int viu_sel = 0;
switch (port) {
/* wr_bak_chan1_sel wb_chan_sel*/
case TVIN_PORT_VIU1_VIDEO:
case TVIN_PORT_VIU1_WB0_VD1:
case TVIN_PORT_VIU1_WB0_VD2:
case TVIN_PORT_VIU1_WB0_OSD1:
case TVIN_PORT_VIU1_WB0_OSD2:
case TVIN_PORT_VIU1_WB0_POST_BLEND:
case TVIN_PORT_VIU1_WB0_VPP:
viu_mux = VIU_MUX_SEL_WB0;
break;
case TVIN_PORT_VIU1_WB1_VDIN_BIST:
/* wr_bak_chan1_sel wb_chan_sel*/
case TVIN_PORT_VIU1_WB1_VIDEO:
case TVIN_PORT_VIU1_WB1_VD1:
case TVIN_PORT_VIU1_WB1_VD2:
case TVIN_PORT_VIU1_WB1_OSD1:
case TVIN_PORT_VIU1_WB1_OSD2:
case TVIN_PORT_VIU1_WB1_POST_BLEND:
case TVIN_PORT_VIU1_WB1_VPP:
viu_mux = VIU_MUX_SEL_WB1;
break;
case TVIN_PORT_VIU2_ENCL:
case TVIN_PORT_VENC1:
viu_mux = VIU_MUX_SEL_ENCL;
break;
case TVIN_PORT_VIU2_ENCI:
case TVIN_PORT_VENC2:
viu_mux = VIU_MUX_SEL_ENCI;
break;
case TVIN_PORT_VIU2_ENCP:
case TVIN_PORT_VENC0:
viu_mux = VIU_MUX_SEL_ENCP;
break;
case TVIN_PORT_VIU2_VD1:
case TVIN_PORT_VIU2_OSD1:
case TVIN_PORT_VIU2_VPP:
viu_mux = VIU_MUX_SEL_WB2;
break;
case TVIN_PORT_VIU3_VD1:
case TVIN_PORT_VIU3_OSD1:
case TVIN_PORT_VIU3_VPP:
viu_mux = VIU_MUX_SEL_WB3;
break;
default:
viu_mux = 0;
break;
}
if (cpu_after_eq(MESON_CPU_MAJOR_ID_TM2)) {
viu_sel = 1;
} else {
/*old chip*/
switch (port & ~0xff) {
case TVIN_PORT_VIU1:
viu_sel = 1;
break;
case TVIN_PORT_VIU2:
viu_sel = 2;
break;
default:
break;
}
}
if (viu_sel == 1) {
if (is_meson_t3x_cpu()) {
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL_T3X, viu_mux, 0, 7);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL_T3X, viu_mux, 8, 7);
} else if (cpu_after_eq(MESON_CPU_MAJOR_ID_T7)) {
/* for vdi6 , vdin source 7*/
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 0, 7);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 0, 7);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 8, 7);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 8, 7);
/* for vdi8 , vdin source 9*/
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 16, 7);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 16, 7);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 24, 7);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 24, 7);
} else {
/* for vdi6 , vdin source 7*/
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 0, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 0, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 8, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 8, 5);
/* for vdi8 , vdin source 9*/
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 16, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 16, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 24, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 24, 5);
}
} else if (viu_sel == 2) {
/* for vdi6 , vdin source 7*/
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 0, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 0, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 8, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 8, 5);
/* for vdi8 , vdin source 9*/
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 16, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 16, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0, 24, 5);
wr_bits_viu(VPU_VIU_VDIN_IF_MUX_CTRL, viu_mux, 24, 5);
} else {
wr_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0);
}
}
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
static void viuin_set_wr_bak_ctrl_s5(enum tvin_port_e port)
{
const struct vinfo_s *vinfo = NULL;
vinfo = get_current_vinfo();
if (!vinfo)
pr_info("%s vinfo == NULL\n", __func__);
else
pr_info("cur_enc_ppc = %d\n", vinfo->cur_enc_ppc);
/* wr_bak use 4ppc always on S5 */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 3, 21, 2);
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 2, 30, 2);
switch (port) {
/* wr_bak_chan1_sel wb_chan_sel*/
//wr_bak_chan0_sel : 0:vd1 1:vd2 2:vd3 3:osd1 4:osd2 5:post_blend_out;
case TVIN_PORT_VIU1_WB0_VD1:
wr_bits_viu(VIU_WR_BAK_CTRL, 0, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_VD2:
wr_bits_viu(VIU_WR_BAK_CTRL, 1, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_OSD1:
wr_bits_viu(VIU_WR_BAK_CTRL, 3, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_OSD2:
wr_bits_viu(VIU_WR_BAK_CTRL, 4, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_VPP://s5 no vppout
case TVIN_PORT_VIU1_WB0_POST_BLEND:
wr_bits_viu(VIU_WR_BAK_CTRL, 5, 0, 4);
if (vinfo && vinfo->cur_enc_ppc == 4) { //4 slice
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 0, 26, 2);
//wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 2, 30, 2);
} else if (vinfo && vinfo->cur_enc_ppc == 2) { //2 slice
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 26, 2);
//wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 30, 2);
} else { //1 slice
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 0, 26, 2);
//wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 2, 30, 2);
}
break;
case TVIN_PORT_VIU1_VIDEO:
wr_bits_viu(VIU_WR_BAK_CTRL, 7, 0, 4);
break;
/* wr_bak_chan1_sel wb_chan_sel*/
case TVIN_PORT_VIU1_WB1_VD1:
case TVIN_PORT_VIU1_WB1_VD2:
case TVIN_PORT_VIU1_WB1_OSD1:
case TVIN_PORT_VIU1_WB1_OSD2:
case TVIN_PORT_VIU1_WB1_POST_BLEND:
case TVIN_PORT_VIU1_WB1_VPP:
case TVIN_PORT_VIU1_WB1_VIDEO:
break;
case TVIN_PORT_VIU2_ENCL:
case TVIN_PORT_VIU2_ENCI:
case TVIN_PORT_VIU2_ENCP:
wr_bits_viu(VIU_WR_BAK_CTRL, 6, 0, 4);
/* increase h banking in case vdin afifo overflow
* pre chip has 8bits
* tm2_revb increased 4bits, all 12bit
*/
wr_bits_viu(VIU_WR_BAK_CTRL, 0xff, 16, 8);
break;
case TVIN_PORT_VIU2_VD1:
wr_bits_viu(VPP1_WR_BAK_CTRL, 1, 0, 2);
break;
case TVIN_PORT_VIU2_OSD1:
wr_bits_viu(VPP1_WR_BAK_CTRL, 2, 0, 2);
break;
case TVIN_PORT_VIU2_VPP:
wr_bits_viu(VPP1_WR_BAK_CTRL, 3, 0, 2);
break;
case TVIN_PORT_VIU3_VD1:
wr_bits_viu(VPP2_WR_BAK_CTRL, 1, 0, 2);
break;
case TVIN_PORT_VIU3_OSD1:
wr_bits_viu(VPP2_WR_BAK_CTRL, 2, 0, 2);
break;
case TVIN_PORT_VIU3_VPP:
wr_bits_viu(VPP2_WR_BAK_CTRL, 3, 0, 2);
break;
case TVIN_PORT_VENC0:
case TVIN_PORT_VENC1:
case TVIN_PORT_VENC2:
if (vinfo && vinfo->cur_enc_ppc == 4) { //4 slice
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 2, 26, 2);
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 2, 30, 2);
} else if (vinfo && vinfo->cur_enc_ppc == 2) { //2 slice
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 26, 2);
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 30, 2);
} else { //1 slice
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 0, 26, 2);
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 0, 30, 2);
}
break;
default:
wr_bits_viu(VIU_WR_BAK_CTRL, 0, 0, 4);
wr_bits_viu(VIU_WR_BAK_CTRL, 0, 4, 4);
wr_bits_viu(VPP1_WR_BAK_CTRL, 0, 0, 2);
wr_bits_viu(VPP2_WR_BAK_CTRL, 0, 0, 2);
break;
}
}
static void viuin_set_wr_bak_ctrl_t3x(enum tvin_port_e port)
{
const struct vinfo_s *vinfo = NULL;
vinfo = get_current_vinfo();
if (!vinfo) {
pr_info("%s vinfo == NULL\n", __func__);
} else {
pr_info("ppc:%d,w:%d,h:%d,dur:%d\n", vinfo->cur_enc_ppc,
vinfo->width, vinfo->height, vinfo->std_duration);
wr_viu(VPU_VIU2VDIN_HDN_CTRL, vinfo->width);
}
if (vinfo && vinfo->width * vinfo->height * vinfo->std_duration >
VDIN_LITE_CORE_MAX_PIXEL_CLOCK) { //need skip
/* din_mode,2ppc */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 30, 2);
/* vskip:1 */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 24, 1);
/* dout_mode:2 */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 2, 21, 2);
/* dn_ratio:1 */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 18, 2);
} else if (vinfo->cur_enc_ppc == 2) {
/* din_mode,2ppc */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 1, 30, 2);
/* no dn_ratio and flt_mode */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 3, 21, 2);/* dout_mode 4ppc */
} else { //1 slice(not support 4 slices)
/* vpp 1 slice:din_mode,1ppc */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 0, 30, 2);
/* no dn_ratio and flt_mode */
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, 3, 21, 2);/* dout_mode 4ppc */
}
switch (port) {
/* wr_bak_chan1_sel wb_chan_sel*/
//wr_bak_chan0_sel : 0:vd1 1:vd2 2:vd3 3:osd1 4:osd2 5:post_blend_out;
case TVIN_PORT_VIU1_WB0_VD1:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_VD1, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_VD2:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_VD2, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_VD3:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_VD3, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_OSD1:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_OSD1, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_OSD2:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_OSD2, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_OSD3:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_OSD3, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_VPP:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_OUT, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_POST_BLEND:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP0_POST_BLD_OUT, 0, 4);
break;
case TVIN_PORT_VIU1_VIDEO:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP_POST_VD1_BEFORE, 0, 4);
break;
/* wr_bak_chan1_sel wb_chan_sel*/
case TVIN_PORT_VIU1_WB1_VD1:
case TVIN_PORT_VIU1_WB1_VD2:
case TVIN_PORT_VIU1_WB1_OSD1:
case TVIN_PORT_VIU1_WB1_OSD2:
case TVIN_PORT_VIU1_WB1_POST_BLEND:
case TVIN_PORT_VIU1_WB1_VPP:
case TVIN_PORT_VIU1_WB1_VIDEO:
break;
case TVIN_PORT_VIU2_ENCL:
case TVIN_PORT_VIU2_ENCI:
case TVIN_PORT_VIU2_ENCP:
case TVIN_PORT_VIU2_VD1:
case TVIN_PORT_VIU2_OSD1:
case TVIN_PORT_VIU2_VPP:
wr_bits_viu(VIU_WR_BAK_CTRL, T3X_WRBACK_VPP1_POST_BLD_OUT, 0, 4);
break;
case TVIN_PORT_VIU3_VD1:
case TVIN_PORT_VIU3_OSD1:
case TVIN_PORT_VIU3_VPP:
break;
case TVIN_PORT_VENC0:
case TVIN_PORT_VENC1:
case TVIN_PORT_VENC2:
break;
default:
pr_info("do not support prot = %#x\n", port);
wr_bits_viu(VIU_WR_BAK_CTRL, 9, 0, 4);
break;
}
}
#endif
static void viuin_set_wr_bak_ctrl(enum tvin_port_e port)
{
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
if (is_meson_s5_cpu()) {
viuin_set_wr_bak_ctrl_s5(port);
return;
} else if (is_meson_t3x_cpu()) {
viuin_set_wr_bak_ctrl_t3x(port);
return;
}
#endif
switch (port) {
/* wr_bak_chan1_sel wb_chan_sel*/
case TVIN_PORT_VIU1_WB0_VD1:
wr_bits_viu(VPP_WR_BAK_CTRL, 1, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_VD2:
wr_bits_viu(VPP_WR_BAK_CTRL, 2, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_OSD1:
wr_bits_viu(VPP_WR_BAK_CTRL, 3, 0, 4);
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
if (is_meson_txhd2_cpu() && rd_bits_viu(VPP_MISC, 27, 1))
wr_bits_viu(VPP_WR_BAK_CTRL, 1, 11, 1);
#endif
break;
case TVIN_PORT_VIU1_WB0_OSD2:
wr_bits_viu(VPP_WR_BAK_CTRL, 4, 0, 4);
break;
case TVIN_PORT_VIU1_WB0_POST_BLEND:
wr_bits_viu(VPP_WR_BAK_CTRL, 5, 0, 4);
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
if (is_meson_txhd2_cpu() && rd_bits_viu(VPP_MISC, 27, 1))
wr_bits_viu(VPP_WR_BAK_CTRL, 1, 11, 1);
#endif
break;
case TVIN_PORT_VIU1_WB0_VPP:
wr_bits_viu(VPP_WR_BAK_CTRL, 6, 0, 4);
/* increase h banking in case vdin afifo overflow
* pre chip has 8bits
* tm2_revb increased 4bits, all 12bit
*/
wr_bits_viu(VPP_WR_BAK_CTRL, 0xff, 16, 8);
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
if (is_meson_txhd2_cpu() && rd_bits_viu(VPP_MISC, 27, 1))
wr_bits_viu(VPP_WR_BAK_CTRL, 1, 11, 1);
#endif
break;
case TVIN_PORT_VIU1_VIDEO:
wr_bits_viu(VPP_WR_BAK_CTRL, 7, 0, 4);
break;
/* wr_bak_chan1_sel wb_chan_sel*/
case TVIN_PORT_VIU1_WB1_VD1:
wr_bits_viu(VPP_WR_BAK_CTRL, 1, 4, 4);
break;
case TVIN_PORT_VIU1_WB1_VD2:
wr_bits_viu(VPP_WR_BAK_CTRL, 2, 4, 4);
break;
case TVIN_PORT_VIU1_WB1_OSD1:
wr_bits_viu(VPP_WR_BAK_CTRL, 3, 4, 4);
break;
case TVIN_PORT_VIU1_WB1_OSD2:
wr_bits_viu(VPP_WR_BAK_CTRL, 4, 4, 4);
break;
case TVIN_PORT_VIU1_WB1_POST_BLEND:
wr_bits_viu(VPP_WR_BAK_CTRL, 5, 4, 4);
break;
case TVIN_PORT_VIU1_WB1_VPP:
wr_bits_viu(VPP_WR_BAK_CTRL, 6, 4, 4);
/* increase h banking in case vdin afifo overflow
* pre chip has 8bits
* tm2_revb increased 4bits, all 12bit
*/
wr_bits_viu(VPP_WR_BAK_CTRL, 0xff, 16, 8);
break;
case TVIN_PORT_VIU1_WB1_VIDEO:
wr_bits_viu(VPP_WR_BAK_CTRL, 7, 4, 4);
break;
case TVIN_PORT_VIU2_ENCL:
case TVIN_PORT_VIU2_ENCI:
case TVIN_PORT_VIU2_ENCP:
wr_bits_viu(VPP_WR_BAK_CTRL, 6, 0, 4);
/* increase h banking in case vdin afifo overflow
* pre chip has 8bits
* tm2_revb increased 4bits, all 12bit
*/
wr_bits_viu(VPP_WR_BAK_CTRL, 0xff, 16, 8);
break;
case TVIN_PORT_VIU2_VD1:
wr_bits_viu(VPP1_WR_BAK_CTRL, 1, 0, 2);
break;
case TVIN_PORT_VIU2_OSD1:
wr_bits_viu(VPP1_WR_BAK_CTRL, 2, 0, 2);
break;
case TVIN_PORT_VIU2_VPP:
wr_bits_viu(VPP1_WR_BAK_CTRL, 3, 0, 2);
break;
case TVIN_PORT_VIU3_VD1:
wr_bits_viu(VPP2_WR_BAK_CTRL, 1, 0, 2);
break;
case TVIN_PORT_VIU3_OSD1:
wr_bits_viu(VPP2_WR_BAK_CTRL, 2, 0, 2);
break;
case TVIN_PORT_VIU3_VPP:
wr_bits_viu(VPP2_WR_BAK_CTRL, 3, 0, 2);
break;
case TVIN_PORT_VENC0:
case TVIN_PORT_VENC1:
case TVIN_PORT_VENC2:
wr_bits_viu(VPP_WR_BAK_CTRL, 6, 0, 4);
/* increase h banking in case vdin afifo overflow
* pre chip has 8bits
* tm2_revb increased 4bits, all 12bit
*/
wr_bits_viu(VPP_WR_BAK_CTRL, 0xff, 16, 8);
break;
default:
wr_bits_viu(VPP_WR_BAK_CTRL, 0, 0, 4);
wr_bits_viu(VPP_WR_BAK_CTRL, 0, 4, 4);
wr_bits_viu(VPP1_WR_BAK_CTRL, 0, 0, 2);
wr_bits_viu(VPP2_WR_BAK_CTRL, 0, 0, 2);
break;
}
}
/*g12a/g12b and before: use viu_loop encl/encp*/
/*tl1: use viu_loop vpp */
static int viuin_open(struct tvin_frontend_s *fe, enum tvin_port_e port,
enum tvin_port_type_e port_type)
{
struct viuin_s *devp = container_of(fe, struct viuin_s, frontend);
unsigned int viu_mux = 0;
if (!memcpy(&devp->parm, fe->private_data,
sizeof(struct vdin_parm_s))) {
pr_info("[viuin..]%s memcpy error.\n", __func__);
return -1;
}
/*open the venc to vdin path*/
pr_info("viu1_sel_venc: %d\n", rd_bits_viu(VPU_VIU_VENC_MUX_CTRL, 0, 2));
pr_info("viu2_sel_venc: %d\n", rd_bits_viu(VPU_VIU_VENC_MUX_CTRL, 2, 2));
switch (rd_bits_viu(VPU_VIU_VENC_MUX_CTRL, 0, 2)) {
case 0: /* ENCL */
if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A))
viu_mux = 0x4;
else
viu_mux = 0x8;
/* wr_bits(VPU_VIU_VENC_MUX_CTRL,0x88,4,8); */
devp->enc_info_addr = ENCL_INFO_READ;
break;
case 1: /* ENCI */
viu_mux = 0x1;/* wr_bits(VPU_VIU_VENC_MUX_CTRL,0x11,4,8); */
devp->enc_info_addr = ENCI_INFO_READ;
break;
case 2: /* ENCP */
viu_mux = 0x2;/* wr_bits(VPU_VIU_VENC_MUX_CTRL,0x22,4,8); */
devp->enc_info_addr = ENCP_INFO_READ;
break;
case 3: /* ENCT */
viu_mux = 0x4;/* wr_bits(VPU_VIU_VENC_MUX_CTRL,0x44,4,8); */
devp->enc_info_addr = ENCT_INFO_READ;
break;
default:
break;
}
/*no need check here, will timeout sometimes*/
/*viuin_check_venc_line(devp);*/
if (port == TVIN_PORT_VIU1_VIDEO && !is_meson_s5_cpu() && !is_meson_t3x_cpu()) {/*old chip*/
/* enable hsync for vdin loop */
wr_bits_viu(VIU_MISC_CTRL1, 1, 28, 1);
viu_mux = 0x4;
}
/* enable write back hsync */
if (cpu_after_eq(MESON_CPU_MAJOR_ID_T7) && !is_meson_s5_cpu() && !is_meson_t3x_cpu()) {
wr_bits_viu(VIU_FRM_CTRL, 3, 24, 2);
wr_bits_viu(VIU1_FRM_CTRL, 3, 24, 2);
wr_bits_viu(VIU2_FRM_CTRL, 3, 24, 2);
}
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
if (is_meson_gxbb_cpu() || is_meson_gxm_cpu() || is_meson_gxl_cpu()) {
#ifndef CONFIG_AMLOGIC_REMOVE_OLD
if (devp->parm.v_active == 2160 && devp->parm.frame_rate > 30)
/* 1/2 down scaling */
wr_viu(VPU_VIU2VDIN_HDN_CTRL, 0x40f00);
#endif
} else if (is_meson_s5_cpu()) {
wr_viu(VPU_VIU2VDIN_HDN_CTRL, devp->parm.h_active);
//wr_viu(S5_VPP_POST_HOLD_CTRL, 0xc77f0412);
} else if (is_meson_t3x_cpu()) {
wr_viu(VPU_VIU2VDIN0_BUF_SIZE_T3X,
(devp->parm.v_active << 16) | (devp->parm.h_active << 0));
wr_viu(VPP_POST_HOLD_CTRL_T3X, (1 << 31) |
(1 << 30) |
((devp->parm.h_active - 1) << 16) |
(4 << 8) |
(1 << 4) |
(2 << 0));
} else
#endif
{
wr_bits_viu(VPU_VIU2VDIN_HDN_CTRL, devp->parm.h_active, 0, 14);
}
/* select loopback path */
if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) {
/* set VPU_VIU_VDIN_IF_MUX_CTRL */
viuin_set_vdin_if_mux_ctrl(port);
/* set VPP_WR_BAK_CTRL */
viuin_set_wr_bak_ctrl(port);
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
if (!is_meson_s5_cpu() && !is_meson_t3x_cpu()) {
/* wr_back hsync en */
if (port >= TVIN_PORT_VIU1_VIDEO &&
port <= TVIN_PORT_VIU1_WB0_POST_BLEND) {
wr_bits_viu(WR_BACK_MISC_CTRL, 1, 0, 1);/*vd0 hsync*/
wr_bits_viu(WR_BACK_MISC_CTRL, 0, 1, 1);/*vd1 hsync*/
} else {
wr_bits_viu(WR_BACK_MISC_CTRL, 0xf, 0, 4);
}
}
#endif
} else {
wr_bits_viu(VPU_VIU_VENC_MUX_CTRL, viu_mux, 4, 4);
wr_bits_viu(VPU_VIU_VENC_MUX_CTRL, viu_mux, 8, 4);
}
devp->flag = 0;
open_cnt++;
return 0;
}
static void viuin_close(struct tvin_frontend_s *fe, enum tvin_port_type_e port_type)
{
struct viuin_s *devp = container_of(fe, struct viuin_s, frontend);
/*no need check here, will timeout sometimes*/
/*viuin_check_venc_line(devp);*/
memset(&devp->parm, 0, sizeof(struct vdin_parm_s));
/*close the venc to vdin path*/
if (open_cnt)
open_cnt--;
if (open_cnt == 0) {
if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) {
wr_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0);
wr_viu(VPP_WR_BAK_CTRL, 0);
} else {
wr_bits_viu(VPU_VIU_VENC_MUX_CTRL, 0, 8, 4);
wr_bits_viu(VPU_VIU_VENC_MUX_CTRL, 0, 4, 4);
}
}
if (rd_viu(VPU_VIU2VDIN_HDN_CTRL) != 0)
wr_viu(VPU_VIU2VDIN_HDN_CTRL, 0x0);
}
static void viuin_start(struct tvin_frontend_s *fe, enum tvin_sig_fmt_e fmt,
enum tvin_port_type_e port_type)
{
/* do something the same as start_amvdec_viu_in */
struct viuin_s *devp = container_of(fe, struct viuin_s, frontend);
if (devp->flag && AMVIUIN_DEC_START) {
pr_info("[viuin..]%s viu_in is started already.\n", __func__);
return;
}
vsync_enter_line_max = 0;
vsync_enter_line_threshold_overflow_count = 0;
devp->flag = AMVIUIN_DEC_START;
}
static void viuin_stop(struct tvin_frontend_s *fe, enum tvin_port_e port,
enum tvin_port_type_e port_type)
{
struct viuin_s *devp = container_of(fe, struct viuin_s, frontend);
if (devp->flag && AMVIUIN_DEC_START)
devp->flag |= AMVIUIN_DEC_STOP;
else
pr_info("[viuin..]%s viu in dec isn't start.\n", __func__);
//pr_info("%s %d Disable VIU to VDIN\n", __func__, __LINE__);
wr_viu(VPU_VIU_VDIN_IF_MUX_CTRL, 0);
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
/* txhd2 keystone path close */
if (is_meson_txhd2_cpu() && rd_bits_viu(VPP_WR_BAK_CTRL, 11, 1))
wr_bits_viu(VPP_WR_BAK_CTRL, 0, 11, 1);
#endif
}
static int viuin_isr(struct tvin_frontend_s *fe, unsigned int hcnt64,
enum tvin_port_type_e port_type)
{
int curr_port;
struct viuin_s *devp = container_of(fe, struct viuin_s, frontend);
curr_port = rd_bits_viu(VPU_VIU_VENC_MUX_CTRL, 0, 2);
vsync_enter_line_curr = (rd_viu(devp->enc_info_addr) >> 16) & 0x1fff;
if (vsync_enter_line_curr > vsync_enter_line_max)
vsync_enter_line_max = vsync_enter_line_curr;
if (vsync_enter_line_max_threshold > vsync_enter_line_min_threshold &&
curr_port == 0) {
if (vsync_enter_line_curr > vsync_enter_line_max_threshold ||
vsync_enter_line_curr < vsync_enter_line_min_threshold) {
vsync_enter_line_threshold_overflow_count++;
return TVIN_BUF_SKIP;
}
}
return 0;
}
static struct tvin_decoder_ops_s viu_dec_ops = {
.support = viuin_support,
.open = viuin_open,
.start = viuin_start,
.stop = viuin_stop,
.close = viuin_close,
.decode_isr = viuin_isr,
};
static void viuin_sig_property(struct tvin_frontend_s *fe,
struct tvin_sig_property_s *prop,
enum tvin_port_type_e port_type)
{
static const struct vinfo_s *vinfo;
struct viuin_s *devp = container_of(fe, struct viuin_s, frontend);
unsigned int line_begin = 0, line_end = 0;
unsigned int venc_offset = 0;
switch (devp->parm.port) {
case TVIN_PORT_VIU1_VIDEO:
case TVIN_PORT_VIU1_WB0_POST_BLEND:
case TVIN_PORT_VIU2_VD1:
prop->color_format = TVIN_YUV444;
break;
case TVIN_PORT_VIU1:
case TVIN_PORT_VIU2:
case TVIN_PORT_VIU1_WB0_VPP:
case TVIN_PORT_VIU1_WB1_VPP:
vinfo = get_current_vinfo();
prop->color_format = (enum tvin_color_fmt_e)vinfo->viu_color_fmt;
break;
case TVIN_PORT_VIU2_VPP:
#ifdef CONFIG_AMLOGIC_VOUT2_SERVE
vinfo = get_current_vinfo2();
prop->color_format = (enum tvin_color_fmt_e)vinfo->viu_color_fmt;
pr_info("color_format:%#x\n", prop->color_format);
#else
prop->color_format = TVIN_YUV444;
pr_info("def color_format:%#x\n", prop->color_format);
#endif
break;
/* ENCL/ENCI/ENCP is only for viu2 loopback currently
* though hw also support viu1 loopback through ENC
*/
case TVIN_PORT_VIU2_ENCL:
vinfo = get_current_vinfo2();
line_begin = rd_viu(ENCL_VIDEO_VSO_BLINE);
line_end = rd_viu(ENCL_VIDEO_VSO_ELINE);
if (line_begin < line_end)
prop->polarity_vs = 1; /* negative */
else if (line_begin > line_end)
prop->polarity_vs = 0; /* positive */
else
pr_err("error: TVIN_PORT_VIU2_ENCL line begin = end\n");
prop->color_format = (enum tvin_color_fmt_e)vinfo->viu_color_fmt;
break;
case TVIN_PORT_VIU2_ENCI:
vinfo = get_current_vinfo2();
/* always negative */
prop->polarity_vs = 1; /* negative */
prop->color_format = (enum tvin_color_fmt_e)vinfo->viu_color_fmt;
break;
case TVIN_PORT_VIU2_ENCP:
vinfo = get_current_vinfo2();
line_begin = rd_viu(ENCP_VIDEO_VSO_BLINE);
line_end = rd_viu(ENCP_VIDEO_VSO_ELINE);
if (line_begin < line_end)
prop->polarity_vs = 1; /* negative */
else if (line_begin > line_end)
prop->polarity_vs = 0; /* positive */
else
pr_err("error: TVIN_PORT_VIU2_ENCP line begin = end\n");
prop->color_format = (enum tvin_color_fmt_e)vinfo->viu_color_fmt;
break;
case TVIN_PORT_VENC0:
case TVIN_PORT_VENC1:
case TVIN_PORT_VENC2:
#ifndef CONFIG_AMLOGIC_ZAPPER_CUT
if (is_meson_s5_cpu())
vinfo = get_current_vinfo();
else
#endif
vinfo = get_current_vinfo2();
if (devp->parm.port == TVIN_PORT_VENC1)
venc_offset = 0x600;
else if (devp->parm.port == TVIN_PORT_VENC2)
venc_offset = 0x800;
else
venc_offset = 0;
if ((vinfo->viu_mux & 0xf) == VIU_MUX_ENCL) {
line_begin = rd_viu(ENCL_VIDEO_VSO_BLINE + venc_offset);
line_end = rd_viu(ENCL_VIDEO_VSO_ELINE + venc_offset);
} else if ((vinfo->viu_mux & 0xf) == VIU_MUX_ENCI) {
prop->polarity_vs = 1;
} else if ((vinfo->viu_mux & 0xf) == VIU_MUX_ENCP) {
line_begin = rd_viu(ENCP_VIDEO_VSO_BLINE + venc_offset);
line_end = rd_viu(ENCP_VIDEO_VSO_ELINE + venc_offset);
}
if (line_begin < line_end || cpu_after_eq(MESON_CPU_MAJOR_ID_T7))
prop->polarity_vs = 1; /* negative */
else if (line_begin > line_end)
prop->polarity_vs = 0; /* positive */
else
pr_err("error: TVIN_PORT_VIU2_ENCP line begin = end\n");
prop->color_format = (enum tvin_color_fmt_e)vinfo->viu_color_fmt;
pr_info("%s cfmt=%d;line:%d,%d,ppc:%d,name:%s,mode:%d,w:%d,h:%d\n",
__func__, prop->color_format, line_begin, line_end,
vinfo->cur_enc_ppc, vinfo->name, vinfo->mode,
vinfo->width, vinfo->height);
break;
default:
prop->color_format = devp->parm.cfmt;
break;
}
prop->dest_cfmt = devp->parm.dfmt;
prop->scaling4w = devp->parm.dest_h_active;
prop->scaling4h = devp->parm.dest_v_active;
prop->vs = v_cut_offset;
prop->ve = 0;
prop->hs = 0;
prop->he = 0;
prop->decimation_ratio = 0;
}
static bool viu_check_frame_skip(struct tvin_frontend_s *fe)
{
struct viuin_s *devp = container_of(fe, struct viuin_s, frontend);
if (devp->parm.skip_count > 0) {
devp->parm.skip_count--;
return true;
}
return false;
}
static struct tvin_state_machine_ops_s viu_sm_ops = {
.get_sig_property = viuin_sig_property,
.check_frame_skip = viu_check_frame_skip,
};
static int viuin_probe(struct platform_device *pdev)
{
struct viuin_s *viuin_devp;
viuin_devp = kmalloc(sizeof(*viuin_devp), GFP_KERNEL);
if (!viuin_devp)
return -ENOMEM;
memset(viuin_devp, 0, sizeof(struct viuin_s));
sprintf(viuin_devp->frontend.name, "%s", DEVICE_NAME);
if (!tvin_frontend_init(&viuin_devp->frontend,
&viu_dec_ops, &viu_sm_ops, 0)) {
if (tvin_reg_frontend(&viuin_devp->frontend))
pr_info("[viuin..]%s register viu frontend error.\n",
__func__);
}
platform_set_drvdata(pdev, viuin_devp);
pr_info("[viuin..]%s probe ok.\n", __func__);
return 0;
}
static int viuin_remove(struct platform_device *pdev)
{
struct viuin_s *devp = platform_get_drvdata(pdev);
if (devp) {
tvin_unreg_frontend(&devp->frontend);
kfree(devp);
}
return 0;
}
static struct platform_driver viuin_driver = {
.probe = viuin_probe,
.remove = viuin_remove,
.driver = {
.name = DEVICE_NAME,
}
};
static struct platform_device *viuin_device;
int __init viuin_init_module(void)
{
pr_info("[viuin..]%s viuin module init\n", __func__);
viuin_device = platform_device_alloc(DEVICE_NAME, 0);
if (!viuin_device) {
pr_err("[viuin..]%s failed to alloc viuin_device.\n",
__func__);
return -ENOMEM;
}
if (platform_device_add(viuin_device)) {
platform_device_put(viuin_device);
pr_err("[viuin..]%sfailed to add viuin_device.\n", __func__);
return -ENODEV;
}
if (platform_driver_register(&viuin_driver)) {
pr_err("[viuin..]%sfailed to register viuin driver.\n",
__func__);
platform_device_del(viuin_device);
platform_device_put(viuin_device);
return -ENODEV;
}
pr_info("[viuin..]%s done\n", __func__);
return 0;
}
void __exit viuin_exit_module(void)
{
pr_info("[viuin..]%s viuin module remove.\n", __func__);
platform_driver_unregister(&viuin_driver);
platform_device_unregister(viuin_device);
}
//MODULE_DESCRIPTION("AMLOGIC viu input driver");
//MODULE_LICENSE("GPL");
//MODULE_VERSION("3.0.0");