linux-zen-server/drivers/gpu/drm/meson/meson_venc.c

1793 lines
48 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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 initially designed 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);
static 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 signals 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;
}