// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2016 BayLibre, SAS * Author: Neil Armstrong * Copyright (C) 2015 Amlogic, Inc. All rights reserved. */ #include #include #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, 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: CbYCr */ reg |= VPU_HDMI_OUTPUT_CBYCR; /* * Write rate to the async FIFO between VENC and HDMI. * One write every 2 wr_clk. */ if (venc_repeat) 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, "amlogic,meson-g12a-vpu")) { 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; }