From: Neil Armstrong Date: Tue, 4 Apr 2017 12:15:25 +0000 (+0200) Subject: drm/meson: add support for HDMI clock support X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=2f4c95dc3d1d1518cb6a5e280050c48a79d3ee08;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git drm/meson: add support for HDMI clock support This patchs adds support for the supported HDMI modes clocks frequencies. Acked-by: Daniel Vetter Signed-off-by: Neil Armstrong --- diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c index 252cfd4b19b1..3731479746ca 100644 --- a/drivers/gpu/drm/meson/meson_vclk.c +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -27,9 +27,26 @@ * VCLK is the "Pixel Clock" frequency generator from a dedicated PLL. * We handle the following encodings : * - CVBS 27MHz generator via the VCLK2 to the VENCI and VDAC blocks - * - * What is missing : * - HDMI Pixel Clocks generation + * What is missing : + * - Genenate Pixel clocks for 2K/4K 10bit formats + * + * Clock generator scheme : + * __________ _________ _____ + * | | | | | |--ENCI + * | HDMI PLL |-| PLL_DIV |--- VCLK--| |--ENCL + * |__________| |_________| \ | MUX |--ENCP + * --VCLK2-| |--VDAC + * |_____|--HDMI-TX + * + * Final clocks can take input for either VCLK or VCLK2, but + * VCLK is the preferred path for HDMI clocking and VCLK2 is the + * preferred path for CVBS VDAC clocking. + * + * VCLK and VCLK2 have fixed divided clocks paths for /1, /2, /4, /6 or /12. + * + * The PLL_DIV can achieve an additional fractional dividing like + * 1.5, 3.5, 3.75... to generate special 2K and 4K 10bit clocks. */ /* HHI Registers */ @@ -50,11 +67,34 @@ #define VCLK2_SOFT_RESET BIT(15) #define VCLK2_DIV1_EN BIT(0) #define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */ +#define VCLK_DIV_MASK 0xff +#define VCLK_DIV_EN BIT(16) +#define VCLK_DIV_RESET BIT(17) +#define CTS_ENCP_SEL_MASK (0xf << 24) +#define CTS_ENCP_SEL_SHIFT 24 #define CTS_ENCI_SEL_MASK (0xf << 28) #define CTS_ENCI_SEL_SHIFT 28 +#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */ +#define VCLK_EN BIT(19) +#define VCLK_SEL_MASK (0x7 << 16) +#define VCLK_SEL_SHIFT 16 +#define VCLK_SOFT_RESET BIT(15) +#define VCLK_DIV1_EN BIT(0) +#define VCLK_DIV2_EN BIT(1) +#define VCLK_DIV4_EN BIT(2) +#define VCLK_DIV6_EN BIT(3) +#define VCLK_DIV12_EN BIT(4) #define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ #define CTS_ENCI_EN BIT(0) +#define CTS_ENCP_EN BIT(2) #define CTS_VDAC_EN BIT(4) +#define HDMI_TX_PIXEL_EN BIT(5) +#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */ +#define HDMI_TX_PIXEL_SEL_MASK (0xf << 16) +#define HDMI_TX_PIXEL_SEL_SHIFT 16 +#define CTS_HDMI_SYS_SEL_MASK (0x7 << 9) +#define CTS_HDMI_SYS_DIV_MASK (0x7f) +#define CTS_HDMI_SYS_EN BIT(8) #define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ #define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ @@ -69,6 +109,126 @@ #define HDMI_PLL_RESET BIT(28) #define HDMI_PLL_LOCK BIT(31) +/* VID PLL Dividers */ +enum { + VID_PLL_DIV_1 = 0, + VID_PLL_DIV_2, + VID_PLL_DIV_2p5, + VID_PLL_DIV_3, + VID_PLL_DIV_3p5, + VID_PLL_DIV_3p75, + VID_PLL_DIV_4, + VID_PLL_DIV_5, + VID_PLL_DIV_6, + VID_PLL_DIV_6p25, + VID_PLL_DIV_7, + VID_PLL_DIV_7p5, + VID_PLL_DIV_12, + VID_PLL_DIV_14, + VID_PLL_DIV_15, +}; + +void meson_vid_pll_set(struct meson_drm *priv, unsigned int div) +{ + unsigned int shift_val = 0; + unsigned int shift_sel = 0; + + /* Disable vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0); + + switch (div) { + case VID_PLL_DIV_2: + shift_val = 0x0aaa; + shift_sel = 0; + break; + case VID_PLL_DIV_2p5: + shift_val = 0x5294; + shift_sel = 2; + break; + case VID_PLL_DIV_3: + shift_val = 0x0db6; + shift_sel = 0; + break; + case VID_PLL_DIV_3p5: + shift_val = 0x36cc; + shift_sel = 1; + break; + case VID_PLL_DIV_3p75: + shift_val = 0x6666; + shift_sel = 2; + break; + case VID_PLL_DIV_4: + shift_val = 0x0ccc; + shift_sel = 0; + break; + case VID_PLL_DIV_5: + shift_val = 0x739c; + shift_sel = 2; + break; + case VID_PLL_DIV_6: + shift_val = 0x0e38; + shift_sel = 0; + break; + case VID_PLL_DIV_6p25: + shift_val = 0x0000; + shift_sel = 3; + break; + case VID_PLL_DIV_7: + shift_val = 0x3c78; + shift_sel = 1; + break; + case VID_PLL_DIV_7p5: + shift_val = 0x78f0; + shift_sel = 2; + break; + case VID_PLL_DIV_12: + shift_val = 0x0fc0; + shift_sel = 0; + break; + case VID_PLL_DIV_14: + shift_val = 0x3f80; + shift_sel = 1; + break; + case VID_PLL_DIV_15: + shift_val = 0x7f80; + shift_sel = 2; + break; + } + + if (div == VID_PLL_DIV_1) + /* Enable vid_pll bypass to HDMI pll */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_BYPASS, VID_PLL_BYPASS); + else { + /* Disable Bypass */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_BYPASS, 0); + /* Clear sel */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 3 << 16, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 0x7fff, 0); + + /* Setup sel and val */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 3 << 16, shift_sel << 16); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, VID_PLL_PRESET); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 0x7fff, shift_val); + + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, 0); + } + + /* Enable the vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_EN, VID_PLL_EN); +} + /* * Setup VCLK2 for 27MHz, and enable clocks for ENCI and VDAC * @@ -110,15 +270,8 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) /* Disable VCLK2 */ regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, 0); - /* Disable vid_pll output clock */ - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0); - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0); - /* Enable vid_pll bypass to HDMI pll */ - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, - VID_PLL_BYPASS, VID_PLL_BYPASS); - /* Enable the vid_pll output clock */ - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, - VID_PLL_EN, VID_PLL_EN); + /* Setup vid_pll to /1 */ + meson_vid_pll_set(priv, VID_PLL_DIV_1); /* Setup the VCLK2 divider value to achieve 27MHz */ regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, @@ -159,9 +312,454 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) CTS_VDAC_EN, CTS_VDAC_EN); } + +/* PLL O1 O2 O3 VP DV EN TX */ +/* 4320 /4 /4 /1 /5 /1 => /2 /2 */ +#define MESON_VCLK_HDMI_ENCI_54000 1 +/* 4320 /4 /4 /1 /5 /1 => /1 /2 */ +#define MESON_VCLK_HDMI_DDR_54000 2 +/* 2970 /4 /1 /1 /5 /1 => /1 /2 */ +#define MESON_VCLK_HDMI_DDR_148500 3 +/* 2970 /2 /2 /2 /5 /1 => /1 /1 */ +#define MESON_VCLK_HDMI_74250 4 +/* 2970 /1 /2 /2 /5 /1 => /1 /1 */ +#define MESON_VCLK_HDMI_148500 5 +/* 2970 /1 /1 /1 /5 /2 => /1 /1 */ +#define MESON_VCLK_HDMI_297000 6 +/* 5940 /1 /1 /2 /5 /1 => /1 /1 */ +#define MESON_VCLK_HDMI_594000 7 + +struct meson_vclk_params { + unsigned int pll_base_freq; + unsigned int pll_od1; + unsigned int pll_od2; + unsigned int pll_od3; + unsigned int vid_pll_div; + unsigned int vclk_div; +} params[] = { + [MESON_VCLK_HDMI_ENCI_54000] = { + .pll_base_freq = 4320000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_54000] = { + .pll_base_freq = 4320000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_148500] = { + .pll_base_freq = 2970000, + .pll_od1 = 4, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_74250] = { + .pll_base_freq = 2970000, + .pll_od1 = 2, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_148500] = { + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_297000] = { + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 2, + }, + [MESON_VCLK_HDMI_594000] = { + .pll_base_freq = 5940000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, +}; + +static inline unsigned int pll_od_to_reg(unsigned int od) +{ + switch (od) { + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 3; + } + + /* Invalid */ + return 0; +} + +void meson_hdmi_pll_set(struct meson_drm *priv, + unsigned int base, + unsigned int od1, + unsigned int od2, + unsigned int od3) +{ + unsigned int val; + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { + switch (base) { + case 2970000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + + /* Enable and unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 0x7 << 28, 0x4 << 28); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); + + /* div_frac */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0xFFFF, 0x4e00); + break; + + case 4320000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800025a); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + + /* unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + BIT(28), 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); + break; + + case 5940000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800027b); + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0xFFFF, 0x4c00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + + /* unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + BIT(28), 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); + break; + }; + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + switch (base) { + case 2970000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + break; + + case 4320000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002b4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + break; + + case 5940000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002f7); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb200); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + break; + + }; + + /* Reset PLL */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, HDMI_PLL_RESET); + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, + (val & HDMI_PLL_LOCK), 10, 0); + }; + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 16, pll_od_to_reg(od1) << 16); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 21, pll_od_to_reg(od1) << 21); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 22, pll_od_to_reg(od2) << 22); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 23, pll_od_to_reg(od2) << 23); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 18, pll_od_to_reg(od3) << 18); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 19, pll_od_to_reg(od3) << 19); +} + void meson_vclk_setup(struct meson_drm *priv, unsigned int target, - unsigned int freq) + unsigned int vclk_freq, unsigned int venc_freq, + unsigned int dac_freq, bool hdmi_use_enci) { - if (target == MESON_VCLK_TARGET_CVBS && freq == MESON_VCLK_CVBS) + unsigned int freq; + unsigned int hdmi_tx_div; + unsigned int venc_div; + + if (target == MESON_VCLK_TARGET_CVBS) { meson_venci_cvbs_clock_config(priv); + return; + } + + hdmi_tx_div = vclk_freq / dac_freq; + + if (hdmi_tx_div == 0) { + pr_err("Fatal Error, invalid HDMI-TX freq %d\n", + dac_freq); + return; + } + + venc_div = vclk_freq / venc_freq; + + if (venc_div == 0) { + pr_err("Fatal Error, invalid HDMI venc freq %d\n", + venc_freq); + return; + } + + switch (vclk_freq) { + case 54000: + if (hdmi_use_enci) + freq = MESON_VCLK_HDMI_ENCI_54000; + else + freq = MESON_VCLK_HDMI_DDR_54000; + break; + case 74250: + freq = MESON_VCLK_HDMI_74250; + break; + case 148500: + if (dac_freq != 148500) + freq = MESON_VCLK_HDMI_DDR_148500; + else + freq = MESON_VCLK_HDMI_148500; + break; + case 297000: + freq = MESON_VCLK_HDMI_297000; + break; + case 594000: + freq = MESON_VCLK_HDMI_594000; + break; + default: + pr_err("Fatal Error, invalid HDMI vclk freq %d\n", + vclk_freq); + return; + } + + /* Set HDMI-TX sys clock */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_SEL_MASK, 0); + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_DIV_MASK, 0); + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_EN, CTS_HDMI_SYS_EN); + + /* Set HDMI PLL rate */ + meson_hdmi_pll_set(priv, params[freq].pll_base_freq, + params[freq].pll_od1, + params[freq].pll_od2, + params[freq].pll_od3); + + /* Setup vid_pll divider */ + meson_vid_pll_set(priv, params[freq].vid_pll_div); + + /* Set VCLK div */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_SEL_MASK, 0); + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + VCLK_DIV_MASK, params[freq].vclk_div - 1); + + /* Set HDMI-TX source */ + switch (hdmi_tx_div) { + case 1: + /* enable vclk_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV1_EN, VCLK_DIV1_EN); + + /* select vclk_div1 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 0); + break; + case 2: + /* enable vclk_div2 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV2_EN, VCLK_DIV2_EN); + + /* select vclk_div2 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 1 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 4: + /* enable vclk_div4 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV4_EN, VCLK_DIV4_EN); + + /* select vclk_div4 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 2 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 6: + /* enable vclk_div6 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV6_EN, VCLK_DIV6_EN); + + /* select vclk_div6 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 3 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 12: + /* enable vclk_div12 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV12_EN, VCLK_DIV12_EN); + + /* select vclk_div12 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 4 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + } + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + HDMI_TX_PIXEL_EN, HDMI_TX_PIXEL_EN); + + /* Set ENCI/ENCP Source */ + switch (venc_div) { + case 1: + /* enable vclk_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV1_EN, VCLK_DIV1_EN); + + if (hdmi_use_enci) + /* select vclk_div1 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 0); + else + /* select vclk_div1 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 0); + break; + case 2: + /* enable vclk_div2 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV2_EN, VCLK_DIV2_EN); + + if (hdmi_use_enci) + /* select vclk_div2 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 1 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div2 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 1 << CTS_ENCP_SEL_SHIFT); + break; + case 4: + /* enable vclk_div4 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV4_EN, VCLK_DIV4_EN); + + if (hdmi_use_enci) + /* select vclk_div4 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 2 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div4 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 2 << CTS_ENCP_SEL_SHIFT); + break; + case 6: + /* enable vclk_div6 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV6_EN, VCLK_DIV6_EN); + + if (hdmi_use_enci) + /* select vclk_div6 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 3 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div6 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 3 << CTS_ENCP_SEL_SHIFT); + break; + case 12: + /* enable vclk_div12 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV12_EN, VCLK_DIV12_EN); + + if (hdmi_use_enci) + /* select vclk_div12 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 4 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div12 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 4 << CTS_ENCP_SEL_SHIFT); + break; + } + + if (hdmi_use_enci) + /* Enable ENCI clock gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCI_EN, CTS_ENCI_EN); + else + /* Enable ENCP clock gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCP_EN, CTS_ENCP_EN); + + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN); } +EXPORT_SYMBOL_GPL(meson_vclk_setup); diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h index ec62735996de..0401b5213471 100644 --- a/drivers/gpu/drm/meson/meson_vclk.h +++ b/drivers/gpu/drm/meson/meson_vclk.h @@ -23,12 +23,14 @@ enum { MESON_VCLK_TARGET_CVBS = 0, + MESON_VCLK_TARGET_HDMI = 1, }; /* 27MHz is the CVBS Pixel Clock */ -#define MESON_VCLK_CVBS 27000 +#define MESON_VCLK_CVBS 27000 void meson_vclk_setup(struct meson_drm *priv, unsigned int target, - unsigned int freq); + unsigned int vclk_freq, unsigned int venc_freq, + unsigned int dac_freq, bool hdmi_use_enci); #endif /* __MESON_VCLK_H */ diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c index a96fcb40f2c7..5d4f19ac98fc 100644 --- a/drivers/gpu/drm/meson/meson_venc_cvbs.c +++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c @@ -32,6 +32,7 @@ #include "meson_venc_cvbs.h" #include "meson_venc.h" +#include "meson_vclk.h" #include "meson_registers.h" /* HHI VDAC Registers */ @@ -194,14 +195,20 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder, { struct meson_venc_cvbs *meson_venc_cvbs = encoder_to_meson_venc_cvbs(encoder); + struct meson_drm *priv = meson_venc_cvbs->priv; int i; for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; if (drm_mode_equal(mode, &meson_mode->mode)) { - meson_venci_cvbs_mode_set(meson_venc_cvbs->priv, + meson_venci_cvbs_mode_set(priv, meson_mode->enci); + + /* Setup 27MHz vclk2 for ENCI and VDAC */ + meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, + MESON_VCLK_CVBS, MESON_VCLK_CVBS, + MESON_VCLK_CVBS, true); break; } }