diff options
Diffstat (limited to 'drivers/gpu/drm/meson/meson_venc.c')
-rw-r--r-- | drivers/gpu/drm/meson/meson_venc.c | 1792 |
1 files changed, 1792 insertions, 0 deletions
diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c new file mode 100644 index 000000000..f93c725b6 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -0,0 +1,1792 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/export.h> + +#include <drm/drm_modes.h> + +#include "meson_drv.h" +#include "meson_registers.h" +#include "meson_venc.h" +#include "meson_vpp.h" + +/** + * DOC: Video Encoder + * + * VENC Handle the pixels encoding to the output formats. + * We handle the following encodings : + * + * - CVBS Encoding via the ENCI encoder and VDAC digital to analog converter + * - TMDS/HDMI Encoding via ENCI_DIV and ENCP + * - Setup of more clock rates for HDMI modes + * + * What is missing : + * + * - LCD Panel encoding via ENCL + * - TV Panel encoding via ENCT + * + * VENC paths : + * + * .. code:: + * + * _____ _____ ____________________ + * vd1---| |-| | | VENC /---------|----VDAC + * vd2---| VIU |-| VPP |-|-----ENCI/-ENCI_DVI-|-| + * osd1--| |-| | | \ | X--HDMI-TX + * osd2--|_____|-|_____| | |\-ENCP--ENCP_DVI-|-| + * | | | + * | \--ENCL-----------|----LVDS + * |____________________| + * + * The ENCI is designed for PAl or NTSC encoding and can go through the VDAC + * directly for CVBS encoding or through the ENCI_DVI encoder for HDMI. + * The ENCP is designed for Progressive encoding but can also generate + * 1080i interlaced pixels, and was initialy desined to encode pixels for + * VDAC to output RGB ou YUV analog outputs. + * It's output is only used through the ENCP_DVI encoder for HDMI. + * The ENCL LVDS encoder is not implemented. + * + * The ENCI and ENCP encoders needs specially defined parameters for each + * supported mode and thus cannot be determined from standard video timings. + * + * The ENCI end ENCP DVI encoders are more generic and can generate any timings + * from the pixel data generated by ENCI or ENCP, so can use the standard video + * timings are source for HW parameters. + */ + +/* HHI Registers */ +#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ +#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbb offset in data sheet */ +#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ +#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbc offset in data sheet */ +#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */ + +struct meson_cvbs_enci_mode meson_cvbs_enci_pal = { + .mode_tag = MESON_VENC_MODE_CVBS_PAL, + .hso_begin = 3, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 7, + .video_prog_mode = 0xff, + .video_mode = 0x13, + .sch_adjust = 0x28, + .yc_delay = 0x343, + .pixel_start = 251, + .pixel_end = 1691, + .top_field_line_start = 22, + .top_field_line_end = 310, + .bottom_field_line_start = 23, + .bottom_field_line_end = 311, + .video_saturation = 9, + .video_contrast = 0, + .video_brightness = 0, + .video_hue = 0, + .analog_sync_adj = 0x8080, +}; + +struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc = { + .mode_tag = MESON_VENC_MODE_CVBS_NTSC, + .hso_begin = 5, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0xb, + .video_prog_mode = 0xf0, + .video_mode = 0x8, + .sch_adjust = 0x20, + .yc_delay = 0x333, + .pixel_start = 227, + .pixel_end = 1667, + .top_field_line_start = 18, + .top_field_line_end = 258, + .bottom_field_line_start = 19, + .bottom_field_line_end = 259, + .video_saturation = 18, + .video_contrast = 3, + .video_brightness = 0, + .video_hue = 0, + .analog_sync_adj = 0x9c00, +}; + +union meson_hdmi_venc_mode { + struct { + unsigned int mode_tag; + unsigned int hso_begin; + unsigned int hso_end; + unsigned int vso_even; + unsigned int vso_odd; + unsigned int macv_max_amp; + unsigned int video_prog_mode; + unsigned int video_mode; + unsigned int sch_adjust; + unsigned int yc_delay; + unsigned int pixel_start; + unsigned int pixel_end; + unsigned int top_field_line_start; + unsigned int top_field_line_end; + unsigned int bottom_field_line_start; + unsigned int bottom_field_line_end; + } enci; + struct { + unsigned int dvi_settings; + unsigned int video_mode; + unsigned int video_mode_adv; + unsigned int video_prog_mode; + bool video_prog_mode_present; + unsigned int video_sync_mode; + bool video_sync_mode_present; + unsigned int video_yc_dly; + bool video_yc_dly_present; + unsigned int video_rgb_ctrl; + bool video_rgb_ctrl_present; + unsigned int video_filt_ctrl; + bool video_filt_ctrl_present; + unsigned int video_ofld_voav_ofst; + bool video_ofld_voav_ofst_present; + unsigned int yfp1_htime; + unsigned int yfp2_htime; + unsigned int max_pxcnt; + unsigned int hspuls_begin; + unsigned int hspuls_end; + unsigned int hspuls_switch; + unsigned int vspuls_begin; + unsigned int vspuls_end; + unsigned int vspuls_bline; + unsigned int vspuls_eline; + unsigned int eqpuls_begin; + bool eqpuls_begin_present; + unsigned int eqpuls_end; + bool eqpuls_end_present; + unsigned int eqpuls_bline; + bool eqpuls_bline_present; + unsigned int eqpuls_eline; + bool eqpuls_eline_present; + unsigned int havon_begin; + unsigned int havon_end; + unsigned int vavon_bline; + unsigned int vavon_eline; + unsigned int hso_begin; + unsigned int hso_end; + unsigned int vso_begin; + unsigned int vso_end; + unsigned int vso_bline; + unsigned int vso_eline; + bool vso_eline_present; + unsigned int sy_val; + bool sy_val_present; + unsigned int sy2_val; + bool sy2_val_present; + unsigned int max_lncnt; + } encp; +}; + +union meson_hdmi_venc_mode meson_hdmi_enci_mode_480i = { + .enci = { + .hso_begin = 5, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0xb, + .video_prog_mode = 0xf0, + .video_mode = 0x8, + .sch_adjust = 0x20, + .yc_delay = 0, + .pixel_start = 227, + .pixel_end = 1667, + .top_field_line_start = 18, + .top_field_line_end = 258, + .bottom_field_line_start = 19, + .bottom_field_line_end = 259, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_enci_mode_576i = { + .enci = { + .hso_begin = 3, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0x7, + .video_prog_mode = 0xff, + .video_mode = 0x13, + .sch_adjust = 0x28, + .yc_delay = 0x333, + .pixel_start = 251, + .pixel_end = 1691, + .top_field_line_start = 22, + .top_field_line_end = 310, + .bottom_field_line_start = 23, + .bottom_field_line_end = 311, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_480p = { + .encp = { + .dvi_settings = 0x21, + .video_mode = 0x4000, + .video_mode_adv = 0x9, + .video_prog_mode = 0, + .video_prog_mode_present = true, + .video_sync_mode = 7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x2052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 244, + .yfp2_htime = 1630, + .max_pxcnt = 1715, + .hspuls_begin = 0x22, + .hspuls_end = 0xa0, + .hspuls_switch = 88, + .vspuls_begin = 0, + .vspuls_end = 1589, + .vspuls_bline = 0, + .vspuls_eline = 5, + .havon_begin = 249, + .havon_end = 1689, + .vavon_bline = 42, + .vavon_eline = 521, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 3, + .hso_end = 5, + .vso_begin = 3, + .vso_end = 5, + .vso_bline = 0, + /* vso_eline */ + .sy_val = 8, + .sy_val_present = true, + .sy2_val = 0x1d8, + .sy2_val_present = true, + .max_lncnt = 524, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_576p = { + .encp = { + .dvi_settings = 0x21, + .video_mode = 0x4000, + .video_mode_adv = 0x9, + .video_prog_mode = 0, + .video_prog_mode_present = true, + .video_sync_mode = 7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x52, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 235, + .yfp2_htime = 1674, + .max_pxcnt = 1727, + .hspuls_begin = 0, + .hspuls_end = 0x80, + .hspuls_switch = 88, + .vspuls_begin = 0, + .vspuls_end = 1599, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 235, + .havon_end = 1674, + .vavon_bline = 44, + .vavon_eline = 619, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 0x80, + .hso_end = 0, + .vso_begin = 0, + .vso_end = 5, + .vso_bline = 0, + /* vso_eline */ + .sy_val = 8, + .sy_val_present = true, + .sy2_val = 0x1d8, + .sy2_val_present = true, + .max_lncnt = 624, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p60 = { + .encp = { + .dvi_settings = 0x2029, + .video_mode = 0x4040, + .video_mode_adv = 0x19, + /* video_prog_mode */ + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 648, + .yfp2_htime = 3207, + .max_pxcnt = 3299, + .hspuls_begin = 80, + .hspuls_end = 240, + .hspuls_switch = 80, + .vspuls_begin = 688, + .vspuls_end = 3248, + .vspuls_bline = 4, + .vspuls_eline = 8, + .havon_begin = 648, + .havon_end = 3207, + .vavon_bline = 29, + .vavon_eline = 748, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 256, + .hso_end = 168, + .vso_begin = 168, + .vso_end = 256, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 749, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p50 = { + .encp = { + .dvi_settings = 0x202d, + .video_mode = 0x4040, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x407, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 648, + .yfp2_htime = 3207, + .max_pxcnt = 3959, + .hspuls_begin = 80, + .hspuls_end = 240, + .hspuls_switch = 80, + .vspuls_begin = 688, + .vspuls_end = 3248, + .vspuls_bline = 4, + .vspuls_eline = 8, + .havon_begin = 648, + .havon_end = 3207, + .vavon_bline = 29, + .vavon_eline = 748, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 128, + .hso_end = 208, + .vso_begin = 128, + .vso_end = 128, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 749, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i60 = { + .encp = { + .dvi_settings = 0x2029, + .video_mode = 0x5ffc, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x207, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + .video_ofld_voav_ofst = 0x11, + .video_ofld_voav_ofst_present = true, + .yfp1_htime = 516, + .yfp2_htime = 4355, + .max_pxcnt = 4399, + .hspuls_begin = 88, + .hspuls_end = 264, + .hspuls_switch = 88, + .vspuls_begin = 440, + .vspuls_end = 2200, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 516, + .havon_end = 4355, + .vavon_bline = 20, + .vavon_eline = 559, + .eqpuls_begin = 2288, + .eqpuls_begin_present = true, + .eqpuls_end = 2464, + .eqpuls_end_present = true, + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 264, + .hso_end = 176, + .vso_begin = 88, + .vso_end = 88, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i50 = { + .encp = { + .dvi_settings = 0x202d, + .video_mode = 0x5ffc, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + .video_ofld_voav_ofst = 0x11, + .video_ofld_voav_ofst_present = true, + .yfp1_htime = 526, + .yfp2_htime = 4365, + .max_pxcnt = 5279, + .hspuls_begin = 88, + .hspuls_end = 264, + .hspuls_switch = 88, + .vspuls_begin = 440, + .vspuls_end = 2200, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 526, + .havon_end = 4365, + .vavon_bline = 20, + .vavon_eline = 559, + .eqpuls_begin = 2288, + .eqpuls_begin_present = true, + .eqpuls_end = 2464, + .eqpuls_end_present = true, + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 142, + .hso_end = 230, + .vso_begin = 142, + .vso_end = 142, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p24 = { + .encp = { + .dvi_settings = 0xd, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + .video_rgb_ctrl = 2, + .video_rgb_ctrl_present = true, + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 271, + .yfp2_htime = 2190, + .max_pxcnt = 2749, + .hspuls_begin = 44, + .hspuls_end = 132, + .hspuls_switch = 44, + .vspuls_begin = 220, + .vspuls_end = 2140, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 271, + .havon_end = 2190, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 79, + .hso_end = 123, + .vso_begin = 79, + .vso_end = 79, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p30 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 2060, + .max_pxcnt = 2199, + .hspuls_begin = 2156, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 2067, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156, + .vso_begin = 2100, + .vso_end = 2164, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p50 = { + .encp = { + .dvi_settings = 0xd, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + .video_rgb_ctrl = 2, + .video_rgb_ctrl_present = true, + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 271, + .yfp2_htime = 2190, + .max_pxcnt = 2639, + .hspuls_begin = 44, + .hspuls_end = 132, + .hspuls_switch = 44, + .vspuls_begin = 220, + .vspuls_end = 2140, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 271, + .havon_end = 2190, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 79, + .hso_end = 123, + .vso_begin = 79, + .vso_end = 79, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 2060, + .max_pxcnt = 2199, + .hspuls_begin = 2156, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 2067, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156, + .vso_begin = 2100, + .vso_end = 2164, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p24 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+1660-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p25 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+1440-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p30 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+560-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +struct meson_hdmi_venc_vic_mode { + unsigned int vic; + union meson_hdmi_venc_mode *mode; +} meson_hdmi_venc_vic_modes[] = { + { 6, &meson_hdmi_enci_mode_480i }, + { 7, &meson_hdmi_enci_mode_480i }, + { 21, &meson_hdmi_enci_mode_576i }, + { 22, &meson_hdmi_enci_mode_576i }, + { 2, &meson_hdmi_encp_mode_480p }, + { 3, &meson_hdmi_encp_mode_480p }, + { 17, &meson_hdmi_encp_mode_576p }, + { 18, &meson_hdmi_encp_mode_576p }, + { 4, &meson_hdmi_encp_mode_720p60 }, + { 19, &meson_hdmi_encp_mode_720p50 }, + { 5, &meson_hdmi_encp_mode_1080i60 }, + { 20, &meson_hdmi_encp_mode_1080i50 }, + { 32, &meson_hdmi_encp_mode_1080p24 }, + { 33, &meson_hdmi_encp_mode_1080p50 }, + { 34, &meson_hdmi_encp_mode_1080p30 }, + { 31, &meson_hdmi_encp_mode_1080p50 }, + { 16, &meson_hdmi_encp_mode_1080p60 }, + { 93, &meson_hdmi_encp_mode_2160p24 }, + { 94, &meson_hdmi_encp_mode_2160p25 }, + { 95, &meson_hdmi_encp_mode_2160p30 }, + { 96, &meson_hdmi_encp_mode_2160p25 }, + { 97, &meson_hdmi_encp_mode_2160p30 }, + { 0, NULL}, /* sentinel */ +}; + +static signed int to_signed(unsigned int a) +{ + if (a <= 7) + return a; + else + return a - 16; +} + +static unsigned long modulo(unsigned long a, unsigned long b) +{ + if (a >= b) + return a - b; + else + return a; +} + +enum drm_mode_status +meson_venc_hdmi_supported_mode(const struct drm_display_mode *mode) +{ + if (mode->flags & ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC | + DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)) + return MODE_BAD; + + if (mode->hdisplay < 640 || mode->hdisplay > 1920) + return MODE_BAD_HVALUE; + + if (mode->vdisplay < 480 || mode->vdisplay > 1200) + return MODE_BAD_VVALUE; + + return MODE_OK; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_supported_mode); + +bool meson_venc_hdmi_supported_vic(int vic) +{ + struct meson_hdmi_venc_vic_mode *vmode = meson_hdmi_venc_vic_modes; + + while (vmode->vic && vmode->mode) { + if (vmode->vic == vic) + return true; + vmode++; + } + + return false; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_supported_vic); + +void meson_venc_hdmi_get_dmt_vmode(const struct drm_display_mode *mode, + union meson_hdmi_venc_mode *dmt_mode) +{ + memset(dmt_mode, 0, sizeof(*dmt_mode)); + + dmt_mode->encp.dvi_settings = 0x21; + dmt_mode->encp.video_mode = 0x4040; + dmt_mode->encp.video_mode_adv = 0x18; + dmt_mode->encp.max_pxcnt = mode->htotal - 1; + dmt_mode->encp.havon_begin = mode->htotal - mode->hsync_start; + dmt_mode->encp.havon_end = dmt_mode->encp.havon_begin + + mode->hdisplay - 1; + dmt_mode->encp.vavon_bline = mode->vtotal - mode->vsync_start; + dmt_mode->encp.vavon_eline = dmt_mode->encp.vavon_bline + + mode->vdisplay - 1; + dmt_mode->encp.hso_begin = 0; + dmt_mode->encp.hso_end = mode->hsync_end - mode->hsync_start; + dmt_mode->encp.vso_begin = 30; + dmt_mode->encp.vso_end = 50; + dmt_mode->encp.vso_bline = 0; + dmt_mode->encp.vso_eline = mode->vsync_end - mode->vsync_start; + dmt_mode->encp.vso_eline_present = true; + dmt_mode->encp.max_lncnt = mode->vtotal - 1; +} + +static union meson_hdmi_venc_mode *meson_venc_hdmi_get_vic_vmode(int vic) +{ + struct meson_hdmi_venc_vic_mode *vmode = meson_hdmi_venc_vic_modes; + + while (vmode->vic && vmode->mode) { + if (vmode->vic == vic) + return vmode->mode; + vmode++; + } + + return NULL; +} + +bool meson_venc_hdmi_venc_repeat(int vic) +{ + /* Repeat VENC pixels for 480/576i/p, 720p50/60 and 1080p50/60 */ + if (vic == 6 || vic == 7 || /* 480i */ + vic == 21 || vic == 22 || /* 576i */ + vic == 17 || vic == 18 || /* 576p */ + vic == 2 || vic == 3 || /* 480p */ + vic == 4 || /* 720p60 */ + vic == 19 || /* 720p50 */ + vic == 5 || /* 1080i60 */ + vic == 20) /* 1080i50 */ + return true; + + return false; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat); + +void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, + unsigned int ycrcb_map, + bool yuv420_mode, + const struct drm_display_mode *mode) +{ + union meson_hdmi_venc_mode *vmode = NULL; + union meson_hdmi_venc_mode vmode_dmt; + bool use_enci = false; + bool venc_repeat = false; + bool hdmi_repeat = false; + unsigned int venc_hdmi_latency = 2; + unsigned long total_pixels_venc = 0; + unsigned long active_pixels_venc = 0; + unsigned long front_porch_venc = 0; + unsigned long hsync_pixels_venc = 0; + unsigned long de_h_begin = 0; + unsigned long de_h_end = 0; + unsigned long de_v_begin_even = 0; + unsigned long de_v_end_even = 0; + unsigned long de_v_begin_odd = 0; + unsigned long de_v_end_odd = 0; + unsigned long hs_begin = 0; + unsigned long hs_end = 0; + unsigned long vs_adjust = 0; + unsigned long vs_bline_evn = 0; + unsigned long vs_eline_evn = 0; + unsigned long vs_bline_odd = 0; + unsigned long vs_eline_odd = 0; + unsigned long vso_begin_evn = 0; + unsigned long vso_begin_odd = 0; + unsigned int eof_lines; + unsigned int sof_lines; + unsigned int vsync_lines; + u32 reg; + + /* Use VENCI for 480i and 576i and double HDMI pixels */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) { + hdmi_repeat = true; + use_enci = true; + venc_hdmi_latency = 1; + } + + if (meson_venc_hdmi_supported_vic(vic)) { + vmode = meson_venc_hdmi_get_vic_vmode(vic); + if (!vmode) { + dev_err(priv->dev, "%s: Fatal Error, unsupported mode " + DRM_MODE_FMT "\n", __func__, + DRM_MODE_ARG(mode)); + return; + } + } else { + meson_venc_hdmi_get_dmt_vmode(mode, &vmode_dmt); + vmode = &vmode_dmt; + use_enci = false; + } + + /* Repeat VENC pixels for 480/576i/p, 720p50/60 and 1080p50/60 */ + if (meson_venc_hdmi_venc_repeat(vic)) + venc_repeat = true; + + eof_lines = mode->vsync_start - mode->vdisplay; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + eof_lines /= 2; + sof_lines = mode->vtotal - mode->vsync_end; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + sof_lines /= 2; + vsync_lines = mode->vsync_end - mode->vsync_start; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + vsync_lines /= 2; + + total_pixels_venc = mode->htotal; + if (hdmi_repeat) + total_pixels_venc /= 2; + if (venc_repeat) + total_pixels_venc *= 2; + + active_pixels_venc = mode->hdisplay; + if (hdmi_repeat) + active_pixels_venc /= 2; + if (venc_repeat) + active_pixels_venc *= 2; + + front_porch_venc = (mode->hsync_start - mode->hdisplay); + if (hdmi_repeat) + front_porch_venc /= 2; + if (venc_repeat) + front_porch_venc *= 2; + + hsync_pixels_venc = (mode->hsync_end - mode->hsync_start); + if (hdmi_repeat) + hsync_pixels_venc /= 2; + if (venc_repeat) + hsync_pixels_venc *= 2; + + /* Disable VDACs */ + writel_bits_relaxed(0xff, 0xff, + priv->io_base + _REG(VENC_VDAC_SETTING)); + + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + + if (use_enci) { + unsigned int lines_f0; + unsigned int lines_f1; + + /* CVBS Filter settings */ + writel_relaxed(ENCI_CFILT_CMPT_SEL_HIGH | 0x10, + priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(ENCI_CFILT_CMPT_CR_DLY(2) | + ENCI_CFILT_CMPT_CB_DLY(1), + priv->io_base + _REG(ENCI_CFILT_CTRL2)); + + /* Digital Video Select : Interlace, clk27 clk, external */ + writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING)); + + /* Reset Video Mode */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE)); + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + /* Horizontal sync signal output */ + writel_relaxed(vmode->enci.hso_begin, + priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN)); + writel_relaxed(vmode->enci.hso_end, + priv->io_base + _REG(ENCI_SYNC_HSO_END)); + + /* Vertical Sync lines */ + writel_relaxed(vmode->enci.vso_even, + priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN)); + writel_relaxed(vmode->enci.vso_odd, + priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN)); + + /* Macrovision max amplitude change */ + writel_relaxed(ENCI_MACV_MAX_AMP_ENABLE_CHANGE | + ENCI_MACV_MAX_AMP_VAL(vmode->enci.macv_max_amp), + priv->io_base + _REG(ENCI_MACV_MAX_AMP)); + + /* Video mode */ + writel_relaxed(vmode->enci.video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + writel_relaxed(vmode->enci.video_mode, + priv->io_base + _REG(ENCI_VIDEO_MODE)); + + /* + * Advanced Video Mode : + * Demux shifting 0x2 + * Blank line end at line17/22 + * High bandwidth Luma Filter + * Low bandwidth Chroma Filter + * Bypass luma low pass filter + * No macrovision on CSYNC + */ + writel_relaxed(ENCI_VIDEO_MODE_ADV_DMXMD(2) | + ENCI_VIDEO_MODE_ADV_VBICTL_LINE_17_22 | + ENCI_VIDEO_MODE_ADV_YBW_HIGH, + priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + writel(vmode->enci.sch_adjust, + priv->io_base + _REG(ENCI_VIDEO_SCH)); + + /* Sync mode : MASTER Master mode, free run, send HSO/VSO out */ + writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE)); + + if (vmode->enci.yc_delay) + writel_relaxed(vmode->enci.yc_delay, + priv->io_base + _REG(ENCI_YC_DELAY)); + + + /* UNreset Interlaced TV Encoder */ + writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST)); + + /* + * Enable Vfifo2vd and set Y_Cb_Y_Cr: + * Corresponding value: + * Y => 00 or 10 + * Cb => 01 + * Cr => 11 + * Ex: 0x4e => 01001110 would mean Cb/Y/Cr/Y + */ + writel_relaxed(ENCI_VFIFO2VD_CTL_ENABLE | + ENCI_VFIFO2VD_CTL_VD_SEL(0x4e), + priv->io_base + _REG(ENCI_VFIFO2VD_CTL)); + + /* Timings */ + writel_relaxed(vmode->enci.pixel_start, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START)); + writel_relaxed(vmode->enci.pixel_end, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END)); + + writel_relaxed(vmode->enci.top_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + writel_relaxed(vmode->enci.top_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END)); + + writel_relaxed(vmode->enci.bottom_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + writel_relaxed(vmode->enci.bottom_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END)); + + /* Select ENCI for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI); + + /* Interlace video enable */ + writel_relaxed(ENCI_VIDEO_EN_ENABLE, + priv->io_base + _REG(ENCI_VIDEO_EN)); + + lines_f0 = mode->vtotal >> 1; + lines_f1 = lines_f0 + 1; + + de_h_begin = modulo(readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_PIXEL_START)) + + venc_hdmi_latency, + total_pixels_venc); + de_h_end = modulo(de_h_begin + active_pixels_venc, + total_pixels_venc); + + writel_relaxed(de_h_begin, + priv->io_base + _REG(ENCI_DE_H_BEGIN)); + writel_relaxed(de_h_end, + priv->io_base + _REG(ENCI_DE_H_END)); + + de_v_begin_even = readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + de_v_end_even = de_v_begin_even + mode->vdisplay; + de_v_begin_odd = readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + de_v_end_odd = de_v_begin_odd + mode->vdisplay; + + writel_relaxed(de_v_begin_even, + priv->io_base + _REG(ENCI_DE_V_BEGIN_EVEN)); + writel_relaxed(de_v_end_even, + priv->io_base + _REG(ENCI_DE_V_END_EVEN)); + writel_relaxed(de_v_begin_odd, + priv->io_base + _REG(ENCI_DE_V_BEGIN_ODD)); + writel_relaxed(de_v_end_odd, + priv->io_base + _REG(ENCI_DE_V_END_ODD)); + + /* Program Hsync timing */ + hs_begin = de_h_end + front_porch_venc; + if (de_h_end + front_porch_venc >= total_pixels_venc) { + hs_begin -= total_pixels_venc; + vs_adjust = 1; + } else { + hs_begin = de_h_end + front_porch_venc; + vs_adjust = 0; + } + + hs_end = modulo(hs_begin + hsync_pixels_venc, + total_pixels_venc); + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_HSO_BEGIN)); + writel_relaxed(hs_end, + priv->io_base + _REG(ENCI_DVI_HSO_END)); + + /* Program Vsync timing for even field */ + if (((de_v_end_odd - 1) + eof_lines + vs_adjust) >= lines_f1) { + vs_bline_evn = (de_v_end_odd - 1) + + eof_lines + + vs_adjust + - lines_f1; + vs_eline_evn = vs_bline_evn + vsync_lines; + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_EVN)); + + writel_relaxed(vs_eline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_EVN)); + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_END_EVN)); + } else { + vs_bline_odd = (de_v_end_odd - 1) + + eof_lines + + vs_adjust; + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_ODD)); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_ODD)); + + if ((vs_bline_odd + vsync_lines) >= lines_f1) { + vs_eline_evn = vs_bline_odd + + vsync_lines + - lines_f1; + + writel_relaxed(vs_eline_evn, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(hs_begin, priv->io_base + + _REG(ENCI_DVI_VSO_END_EVN)); + } else { + vs_eline_odd = vs_bline_odd + + vsync_lines; + + writel_relaxed(vs_eline_odd, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + writel_relaxed(hs_begin, priv->io_base + + _REG(ENCI_DVI_VSO_END_ODD)); + } + } + + /* Program Vsync timing for odd field */ + if (((de_v_end_even - 1) + (eof_lines + 1)) >= lines_f0) { + vs_bline_odd = (de_v_end_even - 1) + + (eof_lines + 1) + - lines_f0; + vs_eline_odd = vs_bline_odd + vsync_lines; + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_ODD)); + + writel_relaxed(vs_eline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + vso_begin_odd = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCI_DVI_VSO_END_ODD)); + } else { + vs_bline_evn = (de_v_end_even - 1) + + (eof_lines + 1); + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_EVN)); + + vso_begin_evn = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_BEGIN_EVN)); + + if (vs_bline_evn + vsync_lines >= lines_f0) { + vs_eline_odd = vs_bline_evn + + vsync_lines + - lines_f0; + + writel_relaxed(vs_eline_odd, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_END_ODD)); + } else { + vs_eline_evn = vs_bline_evn + vsync_lines; + + writel_relaxed(vs_eline_evn, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_END_EVN)); + } + } + } else { + writel_relaxed(vmode->encp.dvi_settings, + priv->io_base + _REG(VENC_DVI_SETTING)); + writel_relaxed(vmode->encp.video_mode, + priv->io_base + _REG(ENCP_VIDEO_MODE)); + writel_relaxed(vmode->encp.video_mode_adv, + priv->io_base + _REG(ENCP_VIDEO_MODE_ADV)); + if (vmode->encp.video_prog_mode_present) + writel_relaxed(vmode->encp.video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + if (vmode->encp.video_sync_mode_present) + writel_relaxed(vmode->encp.video_sync_mode, + priv->io_base + _REG(ENCP_VIDEO_SYNC_MODE)); + if (vmode->encp.video_yc_dly_present) + writel_relaxed(vmode->encp.video_yc_dly, + priv->io_base + _REG(ENCP_VIDEO_YC_DLY)); + if (vmode->encp.video_rgb_ctrl_present) + writel_relaxed(vmode->encp.video_rgb_ctrl, + priv->io_base + _REG(ENCP_VIDEO_RGB_CTRL)); + if (vmode->encp.video_filt_ctrl_present) + writel_relaxed(vmode->encp.video_filt_ctrl, + priv->io_base + _REG(ENCP_VIDEO_FILT_CTRL)); + if (vmode->encp.video_ofld_voav_ofst_present) + writel_relaxed(vmode->encp.video_ofld_voav_ofst, + priv->io_base + + _REG(ENCP_VIDEO_OFLD_VOAV_OFST)); + writel_relaxed(vmode->encp.yfp1_htime, + priv->io_base + _REG(ENCP_VIDEO_YFP1_HTIME)); + writel_relaxed(vmode->encp.yfp2_htime, + priv->io_base + _REG(ENCP_VIDEO_YFP2_HTIME)); + writel_relaxed(vmode->encp.max_pxcnt, + priv->io_base + _REG(ENCP_VIDEO_MAX_PXCNT)); + writel_relaxed(vmode->encp.hspuls_begin, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_BEGIN)); + writel_relaxed(vmode->encp.hspuls_end, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_END)); + writel_relaxed(vmode->encp.hspuls_switch, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_SWITCH)); + writel_relaxed(vmode->encp.vspuls_begin, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_BEGIN)); + writel_relaxed(vmode->encp.vspuls_end, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_END)); + writel_relaxed(vmode->encp.vspuls_bline, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_BLINE)); + writel_relaxed(vmode->encp.vspuls_eline, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_ELINE)); + if (vmode->encp.eqpuls_begin_present) + writel_relaxed(vmode->encp.eqpuls_begin, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_BEGIN)); + if (vmode->encp.eqpuls_end_present) + writel_relaxed(vmode->encp.eqpuls_end, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_END)); + if (vmode->encp.eqpuls_bline_present) + writel_relaxed(vmode->encp.eqpuls_bline, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_BLINE)); + if (vmode->encp.eqpuls_eline_present) + writel_relaxed(vmode->encp.eqpuls_eline, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_ELINE)); + writel_relaxed(vmode->encp.havon_begin, + priv->io_base + _REG(ENCP_VIDEO_HAVON_BEGIN)); + writel_relaxed(vmode->encp.havon_end, + priv->io_base + _REG(ENCP_VIDEO_HAVON_END)); + writel_relaxed(vmode->encp.vavon_bline, + priv->io_base + _REG(ENCP_VIDEO_VAVON_BLINE)); + writel_relaxed(vmode->encp.vavon_eline, + priv->io_base + _REG(ENCP_VIDEO_VAVON_ELINE)); + writel_relaxed(vmode->encp.hso_begin, + priv->io_base + _REG(ENCP_VIDEO_HSO_BEGIN)); + writel_relaxed(vmode->encp.hso_end, + priv->io_base + _REG(ENCP_VIDEO_HSO_END)); + writel_relaxed(vmode->encp.vso_begin, + priv->io_base + _REG(ENCP_VIDEO_VSO_BEGIN)); + writel_relaxed(vmode->encp.vso_end, + priv->io_base + _REG(ENCP_VIDEO_VSO_END)); + writel_relaxed(vmode->encp.vso_bline, + priv->io_base + _REG(ENCP_VIDEO_VSO_BLINE)); + if (vmode->encp.vso_eline_present) + writel_relaxed(vmode->encp.vso_eline, + priv->io_base + _REG(ENCP_VIDEO_VSO_ELINE)); + if (vmode->encp.sy_val_present) + writel_relaxed(vmode->encp.sy_val, + priv->io_base + _REG(ENCP_VIDEO_SY_VAL)); + if (vmode->encp.sy2_val_present) + writel_relaxed(vmode->encp.sy2_val, + priv->io_base + _REG(ENCP_VIDEO_SY2_VAL)); + writel_relaxed(vmode->encp.max_lncnt, + priv->io_base + _REG(ENCP_VIDEO_MAX_LNCNT)); + + writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); + + /* Set DE signal’s polarity is active high */ + writel_bits_relaxed(ENCP_VIDEO_MODE_DE_V_HIGH, + ENCP_VIDEO_MODE_DE_V_HIGH, + priv->io_base + _REG(ENCP_VIDEO_MODE)); + + /* Program DE timing */ + de_h_begin = modulo(readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_HAVON_BEGIN)) + + venc_hdmi_latency, + total_pixels_venc); + de_h_end = modulo(de_h_begin + active_pixels_venc, + total_pixels_venc); + + writel_relaxed(de_h_begin, + priv->io_base + _REG(ENCP_DE_H_BEGIN)); + writel_relaxed(de_h_end, + priv->io_base + _REG(ENCP_DE_H_END)); + + /* Program DE timing for even field */ + de_v_begin_even = readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_VAVON_BLINE)); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + de_v_end_even = de_v_begin_even + + (mode->vdisplay / 2); + else + de_v_end_even = de_v_begin_even + mode->vdisplay; + + writel_relaxed(de_v_begin_even, + priv->io_base + _REG(ENCP_DE_V_BEGIN_EVEN)); + writel_relaxed(de_v_end_even, + priv->io_base + _REG(ENCP_DE_V_END_EVEN)); + + /* Program DE timing for odd field if needed */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + unsigned int ofld_voav_ofst = + readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_OFLD_VOAV_OFST)); + de_v_begin_odd = to_signed((ofld_voav_ofst & 0xf0) >> 4) + + de_v_begin_even + + ((mode->vtotal - 1) / 2); + de_v_end_odd = de_v_begin_odd + (mode->vdisplay / 2); + + writel_relaxed(de_v_begin_odd, + priv->io_base + _REG(ENCP_DE_V_BEGIN_ODD)); + writel_relaxed(de_v_end_odd, + priv->io_base + _REG(ENCP_DE_V_END_ODD)); + } + + /* Program Hsync timing */ + if ((de_h_end + front_porch_venc) >= total_pixels_venc) { + hs_begin = de_h_end + + front_porch_venc + - total_pixels_venc; + vs_adjust = 1; + } else { + hs_begin = de_h_end + + front_porch_venc; + vs_adjust = 0; + } + + hs_end = modulo(hs_begin + hsync_pixels_venc, + total_pixels_venc); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCP_DVI_HSO_BEGIN)); + writel_relaxed(hs_end, + priv->io_base + _REG(ENCP_DVI_HSO_END)); + + /* Program Vsync timing for even field */ + if (de_v_begin_even >= + (sof_lines + vsync_lines + (1 - vs_adjust))) + vs_bline_evn = de_v_begin_even + - sof_lines + - vsync_lines + - (1 - vs_adjust); + else + vs_bline_evn = mode->vtotal + + de_v_begin_even + - sof_lines + - vsync_lines + - (1 - vs_adjust); + + vs_eline_evn = modulo(vs_bline_evn + vsync_lines, + mode->vtotal); + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCP_DVI_VSO_BLINE_EVN)); + writel_relaxed(vs_eline_evn, + priv->io_base + _REG(ENCP_DVI_VSO_ELINE_EVN)); + + vso_begin_evn = hs_begin; + writel_relaxed(vso_begin_evn, + priv->io_base + _REG(ENCP_DVI_VSO_BEGIN_EVN)); + writel_relaxed(vso_begin_evn, + priv->io_base + _REG(ENCP_DVI_VSO_END_EVN)); + + /* Program Vsync timing for odd field if needed */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vs_bline_odd = (de_v_begin_odd - 1) + - sof_lines + - vsync_lines; + vs_eline_odd = (de_v_begin_odd - 1) + - vsync_lines; + vso_begin_odd = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCP_DVI_VSO_BLINE_ODD)); + writel_relaxed(vs_eline_odd, + priv->io_base + _REG(ENCP_DVI_VSO_ELINE_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCP_DVI_VSO_BEGIN_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCP_DVI_VSO_END_ODD)); + } + + /* Select ENCP for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCP); + } + + /* Set VPU HDMI setting */ + /* Select ENCP or ENCI data to HDMI */ + if (use_enci) + reg = VPU_HDMI_ENCI_DATA_TO_HDMI; + else + reg = VPU_HDMI_ENCP_DATA_TO_HDMI; + + /* Invert polarity of HSYNC from VENC */ + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + reg |= VPU_HDMI_INV_HSYNC; + + /* Invert polarity of VSYNC from VENC */ + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + reg |= VPU_HDMI_INV_VSYNC; + + /* Output data format */ + reg |= ycrcb_map; + + /* + * Write rate to the async FIFO between VENC and HDMI. + * One write every 2 wr_clk. + */ + if (venc_repeat || yuv420_mode) + reg |= VPU_HDMI_WR_RATE(2); + + /* + * Read rate to the async FIFO between VENC and HDMI. + * One read every 2 wr_clk. + */ + if (hdmi_repeat) + reg |= VPU_HDMI_RD_RATE(2); + + writel_relaxed(reg, priv->io_base + _REG(VPU_HDMI_SETTING)); + + priv->venc.hdmi_repeat = hdmi_repeat; + priv->venc.venc_repeat = venc_repeat; + priv->venc.hdmi_use_enci = use_enci; + + priv->venc.current_mode = MESON_VENC_MODE_HDMI; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_mode_set); + +void meson_venci_cvbs_mode_set(struct meson_drm *priv, + struct meson_cvbs_enci_mode *mode) +{ + u32 reg; + + if (mode->mode_tag == priv->venc.current_mode) + return; + + /* CVBS Filter settings */ + writel_relaxed(ENCI_CFILT_CMPT_SEL_HIGH | 0x10, + priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(ENCI_CFILT_CMPT_CR_DLY(2) | + ENCI_CFILT_CMPT_CB_DLY(1), + priv->io_base + _REG(ENCI_CFILT_CTRL2)); + + /* Digital Video Select : Interlace, clk27 clk, external */ + writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING)); + + /* Reset Video Mode */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE)); + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + /* Horizontal sync signal output */ + writel_relaxed(mode->hso_begin, + priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN)); + writel_relaxed(mode->hso_end, + priv->io_base + _REG(ENCI_SYNC_HSO_END)); + + /* Vertical Sync lines */ + writel_relaxed(mode->vso_even, + priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN)); + writel_relaxed(mode->vso_odd, + priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN)); + + /* Macrovision max amplitude change */ + writel_relaxed(ENCI_MACV_MAX_AMP_ENABLE_CHANGE | + ENCI_MACV_MAX_AMP_VAL(mode->macv_max_amp), + priv->io_base + _REG(ENCI_MACV_MAX_AMP)); + + /* Video mode */ + writel_relaxed(mode->video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + writel_relaxed(mode->video_mode, + priv->io_base + _REG(ENCI_VIDEO_MODE)); + + /* + * Advanced Video Mode : + * Demux shifting 0x2 + * Blank line end at line17/22 + * High bandwidth Luma Filter + * Low bandwidth Chroma Filter + * Bypass luma low pass filter + * No macrovision on CSYNC + */ + writel_relaxed(ENCI_VIDEO_MODE_ADV_DMXMD(2) | + ENCI_VIDEO_MODE_ADV_VBICTL_LINE_17_22 | + ENCI_VIDEO_MODE_ADV_YBW_HIGH, + priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + writel(mode->sch_adjust, priv->io_base + _REG(ENCI_VIDEO_SCH)); + + /* Sync mode : MASTER Master mode, free run, send HSO/VSO out */ + writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE)); + + /* 0x3 Y, C, and Component Y delay */ + writel_relaxed(mode->yc_delay, priv->io_base + _REG(ENCI_YC_DELAY)); + + /* Timings */ + writel_relaxed(mode->pixel_start, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START)); + writel_relaxed(mode->pixel_end, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END)); + + writel_relaxed(mode->top_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + writel_relaxed(mode->top_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END)); + + writel_relaxed(mode->bottom_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + writel_relaxed(mode->bottom_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END)); + + /* Internal Venc, Internal VIU Sync, Internal Vencoder */ + writel_relaxed(0, priv->io_base + _REG(VENC_SYNC_ROUTE)); + + /* UNreset Interlaced TV Encoder */ + writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST)); + + /* + * Enable Vfifo2vd and set Y_Cb_Y_Cr: + * Corresponding value: + * Y => 00 or 10 + * Cb => 01 + * Cr => 11 + * Ex: 0x4e => 01001110 would mean Cb/Y/Cr/Y + */ + writel_relaxed(ENCI_VFIFO2VD_CTL_ENABLE | + ENCI_VFIFO2VD_CTL_VD_SEL(0x4e), + priv->io_base + _REG(ENCI_VFIFO2VD_CTL)); + + /* Power UP Dacs */ + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_SETTING)); + + /* Video Upsampling */ + /* + * CTRL0, CTRL1 and CTRL2: + * Filter0: input data sample every 2 cloks + * Filter1: filtering and upsample enable + */ + reg = VENC_UPSAMPLE_CTRL_F0_2_CLK_RATIO | VENC_UPSAMPLE_CTRL_F1_EN | + VENC_UPSAMPLE_CTRL_F1_UPSAMPLE_EN; + + /* + * Upsample CTRL0: + * Interlace High Bandwidth Luma + */ + writel_relaxed(VENC_UPSAMPLE_CTRL_INTERLACE_HIGH_LUMA | reg, + priv->io_base + _REG(VENC_UPSAMPLE_CTRL0)); + + /* + * Upsample CTRL1: + * Interlace Pb + */ + writel_relaxed(VENC_UPSAMPLE_CTRL_INTERLACE_PB | reg, + priv->io_base + _REG(VENC_UPSAMPLE_CTRL1)); + + /* + * Upsample CTRL2: + * Interlace R + */ + writel_relaxed(VENC_UPSAMPLE_CTRL_INTERLACE_PR | reg, + priv->io_base + _REG(VENC_UPSAMPLE_CTRL2)); + + /* Select Interlace Y DACs */ + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL0)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL1)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL2)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL3)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL4)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL5)); + + /* Select ENCI for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI); + + /* Enable ENCI FIFO */ + writel_relaxed(VENC_VDAC_FIFO_EN_ENCI_ENABLE, + priv->io_base + _REG(VENC_VDAC_FIFO_CTRL)); + + /* Select ENCI DACs 0, 1, 4, and 5 */ + writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_0)); + writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_1)); + + /* Interlace video enable */ + writel_relaxed(ENCI_VIDEO_EN_ENABLE, + priv->io_base + _REG(ENCI_VIDEO_EN)); + + /* Configure Video Saturation / Contrast / Brightness / Hue */ + writel_relaxed(mode->video_saturation, + priv->io_base + _REG(ENCI_VIDEO_SAT)); + writel_relaxed(mode->video_contrast, + priv->io_base + _REG(ENCI_VIDEO_CONT)); + writel_relaxed(mode->video_brightness, + priv->io_base + _REG(ENCI_VIDEO_BRIGHT)); + writel_relaxed(mode->video_hue, + priv->io_base + _REG(ENCI_VIDEO_HUE)); + + /* Enable DAC0 Filter */ + writel_relaxed(VENC_VDAC_DAC0_FILT_CTRL0_EN, + priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL0)); + writel_relaxed(0xfc48, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL1)); + + /* 0 in Macrovision register 0 */ + writel_relaxed(0, priv->io_base + _REG(ENCI_MACV_N0)); + + /* Analog Synchronization and color burst value adjust */ + writel_relaxed(mode->analog_sync_adj, + priv->io_base + _REG(ENCI_SYNC_ADJ)); + + priv->venc.current_mode = mode->mode_tag; +} + +/* Returns the current ENCI field polarity */ +unsigned int meson_venci_get_field(struct meson_drm *priv) +{ + return readl_relaxed(priv->io_base + _REG(ENCI_INFO_READ)) & BIT(29); +} + +void meson_venc_enable_vsync(struct meson_drm *priv) +{ + writel_relaxed(VENC_INTCTRL_ENCI_LNRST_INT_EN, + priv->io_base + _REG(VENC_INTCTRL)); + regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25)); +} + +void meson_venc_disable_vsync(struct meson_drm *priv) +{ + regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0); + writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL)); +} + +void meson_venc_init(struct meson_drm *priv) +{ + /* Disable CVBS VDAC */ + if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { + regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 8); + } else { + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); + } + + /* Power Down Dacs */ + writel_relaxed(0xff, priv->io_base + _REG(VENC_VDAC_SETTING)); + + /* Disable HDMI PHY */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0); + + /* Disable HDMI */ + writel_bits_relaxed(VPU_HDMI_ENCI_DATA_TO_HDMI | + VPU_HDMI_ENCP_DATA_TO_HDMI, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + /* Disable all encoders */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); + + /* Disable VSync IRQ */ + meson_venc_disable_vsync(priv); + + priv->venc.current_mode = MESON_VENC_MODE_NONE; +} |