blob: eaea68517954fd3bd6fc425b73074b102d938e01 [file] [log] [blame] [edit]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <amlogic/media/vout/hdmitx21/hdmitx.h>
#include "hdmitx_clk.h"
#include "hdmitx_misc.h"
#define MIN_HTXPLL_VCO 3000000 /* Min 3GHz */
#define MAX_HTXPLL_VCO 6000000 /* Max 6GHz */
#define MIN_FPLL_VCO 1600000 /* Min 1.6GHz */
#define MAX_FPLL_VCO 3200000 /* Max 3.2GHz */
#define MIN_GP2PLL_VCO 1600000 /* Min 1.6GHz */
#define MAX_GP2PLL_VCO 3200000 /* Max 3.2GHz */
/* for HTX_PLL_CNTL0 or HTX_PLL_CNTL3
* [31] lock_st [2] lock_st_rst [1] pll_rst
*/
#define WAIT_HDMIPLL_LOCK(_reg, _n_rst) \
do { \
typeof(_reg) reg = (_reg); \
typeof(_n_rst) n_rst = (_n_rst); \
unsigned int st = 0; \
int cnt = 10; \
while (cnt--) { \
usleep_range(50, 60); \
st = (((hd21_read_reg(reg) >> 31) & 0x1) == 1); \
if (st) \
break; \
else { \
/* reset hpll */ \
if (n_rst) { \
/* negative reset */ \
hd21_set_reg_bits(reg, 0, 1, 1); \
hd21_set_reg_bits(reg, 1, 1, 1); \
} else { \
/* normal reset */ \
hd21_set_reg_bits(reg, 1, 1, 1); \
hd21_set_reg_bits(reg, 0, 1, 1); \
} \
hd21_set_reg_bits(reg, 1, 2, 1); \
hd21_set_reg_bits(reg, 0, 2, 1); \
} \
} \
if (cnt < 9) \
pr_info("pll[0x%x] reset %d times\n", reg, 9 - cnt);\
} while (0)
/* for FPLL_CTRL0 or GP2PLL_CTRL0
* bit[29] pll rest PLL_ST bit[31] lock_st
*/
#define WAIT_FPLL_GP2PLL_LOCK(_reg_pll, _reg_st) \
do { \
typeof(_reg_pll) reg_pll = (_reg_pll); \
typeof(_reg_st) reg_st = (_reg_st); \
unsigned int st = 0; \
int cnt = 10; \
while (cnt--) { \
usleep_range(50, 60); \
st = (((hd21_read_reg(reg_st) >> 31) & 0x1) == 1); \
if (st) \
break; \
else { \
/* reset hpll */ \
hd21_set_reg_bits(reg_pll, 1, 29, 1); \
hd21_set_reg_bits(reg_pll, 0, 29, 1); \
} \
} \
if (cnt < 9) \
pr_info("pll[0x%x] reset %d times\n", reg_pll, 9 - cnt);\
} while (0)
#define usleep_range(a, b) udelay(a)
static int likely_frac_rate_mode(char *m);
/* local frac_rate flag */
static u32 frac_rate;
const static char od_map[9] = {
0, 0, 1, 0, 2, 0, 0, 0, 3,
};
void disable_hdmitx_s5_plls(struct hdmitx_dev *hdev)
{
hd21_write_reg(ANACTRL_HDMIPLL_CTRL0, 0);
hd21_write_reg(ANACTRL_HDMIPLL_CTRL3, 0);
hd21_write_reg(CLKCTRL_FPLL_CTRL0, 0);
hd21_write_reg(CLKCTRL_GP2PLL_CTRL0, 0);
}
/* htx pll VCO output: (3G, 6G), for tmds */
static void set_s5_htxpll_clk_other(const u32 clk, const bool frl_en)
{
u32 quotient;
u32 remainder;
u32 div0p5_en;
u32 rem_1;
u32 rem_2;
if (clk < 3000000 || clk >= 6000000) {
pr_err("%s[%d] clock should be 4~6G\n", __func__, __LINE__);
return;
}
// set sub-pll as 24M output
hd21_write_reg(ANACTRL_HDMIPLL_CTRL0, (1 << 16) | (100 << 8) | (1 << 1));
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL0, 1, 0, 1);
hd21_write_reg(ANACTRL_HDMIPLL_CTRL1, 0xf00022a5);
/* 0x8: lp_pll_clk selects pll_clk 0xb: selects lp_pll_clk24m */
hd21_write_reg(ANACTRL_HDMIPLL_CTRL2, 0x55813001 | (0xb << 4));
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL0, 0, 1, 1);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL0, 1, 2, 1);
div0p5_en = 1;
if (clk % 12000 == 0) {
quotient = clk / 12000;
remainder = 0;
} else {
quotient = clk / 12000;
remainder = clk - quotient * 12000;
/* remainder range: 0 ~ 99999, 0x1869f, 17bits */
/* convert remainder to 0 ~ 2^17 */
if (remainder) {
rem_1 = remainder / 16;
rem_2 = remainder - rem_1 * 16;
rem_1 *= 1 << 17;
rem_1 /= 750;
rem_2 *= 1 << 13;
rem_2 /= 750;
remainder = rem_1 + rem_2;
}
}
hd21_write_reg(ANACTRL_HDMIPLL_CTRL3, 0x000c0000 | (quotient << 8));
/* HDMIPLL_CTRL4[25] enable tx_phy_clk1618 */
/* bit16: spll_div_0p5_en */
hd21_write_reg(ANACTRL_HDMIPLL_CTRL4, 0x414412f2 | (frl_en << 25) | (div0p5_en << 16));
hd21_write_reg(ANACTRL_HDMIPLL_CTRL5, 0x00000203);
hd21_write_reg(ANACTRL_HDMIPLL_CTRL6, (!!remainder << 31) | remainder);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 0, 1);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 1, 1);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 2, 1);
}
/* htx pll VCO output: 4/5/6G, for FRL */
static void set_s5_htxpll_clk_4_5_6g(const u32 clk, const bool frl_en)
{
u32 htxpll_m = 0;
u32 htxpll_ref_clk_od = 0;
if (clk != 6000000 && clk != 5000000 && clk != 4000000) {
pr_err("%s[%d] clock should be 4, 5, or 6G\n", __func__, __LINE__);
return;
}
/* For 6G clock, here use the 24M as source */
if (clk == 6000000) {
/* 250 * 24M = 6G */
hd21_write_reg(ANACTRL_HDMIPLL_CTRL3, 0x000c0000 | (250 << 8));
hd21_write_reg(ANACTRL_HDMIPLL_CTRL0, 0x00006101 | (1 << 16));
hd21_write_reg(ANACTRL_HDMIPLL_CTRL1, 0xf0002211);
/* use the 24m instead of sub-pll */
/* 0x8: lp_pll_clk selects pll_clk 0xb: selects lp_pll_clk24m */
hd21_write_reg(ANACTRL_HDMIPLL_CTRL2, 0x55813000 | (0xb << 4));
hd21_write_reg(ANACTRL_HDMIPLL_CTRL4, 0x0144f2f2 | (frl_en << 25));
hd21_write_reg(ANACTRL_HDMIPLL_CTRL5, 0x00000203);
hd21_write_reg(ANACTRL_HDMIPLL_CTRL6, 0x0);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 0, 1);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 1, 1);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 2, 1);
return;
}
/* For 4, or 5G clock, here use the sub-pll generate 200M as source */
/* 24MHz * 100 / 12 = 200MHz */
hd21_write_reg(ANACTRL_HDMIPLL_CTRL0, (1 << 1) | (100 << 8) | (12 << 16));
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL0, 1, 0, 1);
hd21_write_reg(ANACTRL_HDMIPLL_CTRL1, 0xf00022a5);
hd21_write_reg(ANACTRL_HDMIPLL_CTRL2, 0x55813081);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL0, 0, 1, 1);
WAIT_HDMIPLL_LOCK(ANACTRL_HDMIPLL_CTRL0, 0);
if (clk == 5000000) {
htxpll_m = 50;
htxpll_ref_clk_od = 1;
}
if (clk == 4000000) {
htxpll_m = 20;
htxpll_ref_clk_od = 0;
}
hd21_write_reg(ANACTRL_HDMIPLL_CTRL3,
0x000c0000 | (htxpll_m << 8) | (htxpll_ref_clk_od << 4));
hd21_write_reg(ANACTRL_HDMIPLL_CTRL4, 0x03400293 | (frl_en << 25));
hd21_write_reg(ANACTRL_HDMIPLL_CTRL5, 0x00000203);
hd21_write_reg(ANACTRL_HDMIPLL_CTRL6, 0x00000000);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 0, 1);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 1, 1);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 2, 1);
}
void set21_s5_htxpll_clk_out(const u32 clk, const u32 div)
{
u32 div1;
u32 div2;
struct hdmitx_dev *hdev = get_hdmitx21_device();
enum hdmi_colorspace cs = HDMI_COLORSPACE_YUV444;
enum hdmi_color_depth cd = COLORDEPTH_24B;
if (!hdev || !hdev->para)
return;
cs = hdev->para->cs;
cd = hdev->para->cd;
pr_info("%s[%d] htxpll vco %d div %d\n", __func__, __LINE__, clk, div);
if (clk <= 3000000 || clk > 6000000) {
pr_info("%s[%d] %d out of htxpll range(3~6G]\n", __func__, __LINE__, clk);
return;
}
/* due to the VCO work performance, here needs to consider 3 cases
* 1. 3G to 4G * 2. 4G to 6G
* 3. 6G
*/
if (clk == 6000000 || clk == 5000000 || clk == 4000000)
set_s5_htxpll_clk_4_5_6g(clk, hdev->frl_rate ? 1 : 0);
else
set_s5_htxpll_clk_other(clk, hdev->frl_rate ? 1 : 0);
WAIT_HDMIPLL_LOCK(ANACTRL_HDMIPLL_CTRL3, 1);
/* setting htxpll div */
if (div > 8) {
div1 = 8;
div2 = div / 8;
} else {
div1 = div;
div2 = 1;
}
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, od_map[div1], 20, 2);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, od_map[div2], 22, 2);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 0, 24, 2);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL4, 0, 30, 2);
if (cs == HDMI_COLORSPACE_YUV420) {
if (cd == COLORDEPTH_24B)
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 20, 2);
else
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 0, 20, 2);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL4, 1, 30, 2);
}
}
void set_frl_hpll_od(enum frl_rate_enum rate)
{
if (rate == FRL_NONE || rate > FRL_12G4L) {
pr_info("hdmitx: frl: wrong rate %d\n", rate);
return;
}
/* fixed OD */
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 0, 22, 2);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 0, 24, 2);
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL4, 0, 30, 2);
switch (rate) {
case FRL_3G3L:
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 2, 20, 2);
break;
case FRL_6G3L:
case FRL_6G4L:
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 1, 20, 2);
break;
case FRL_8G4L:
case FRL_10G4L:
case FRL_12G4L:
hd21_set_reg_bits(ANACTRL_HDMIPLL_CTRL3, 0, 20, 2);
break;
default:
break;
};
}
// fpll: 2376M 2376/24=99=0x63
// 2376/16=148.5M
/* div: 1, 2, 4, 8, ... 64 */
/* pixel_od: 1:1, 1:1.25, 1:1.5, 1:2 */
void hdmitx_set_s5_fpll(u32 clk, u32 div, u32 pixel_od)
{
u32 div1;
u32 div2;
u32 quotient;
u32 remainder;
pr_info("%s[%d] clk %d div %d pixel_od %d\n", __func__, __LINE__, clk, div, pixel_od);
/* setting fpll vco */
quotient = clk / 24000;
remainder = clk - quotient * 24000;
/* remainder range: 0 ~ 23999, 0x5dbf, 15bits */
remainder *= 1 << 17;
remainder /= 24000;
hd21_write_reg(CLKCTRL_FPLL_CTRL0, 0x21210000 | quotient);
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL0, 1, 28, 1);
hd21_write_reg(CLKCTRL_FPLL_CTRL1, 0x03a00000 | remainder);
hd21_write_reg(CLKCTRL_FPLL_CTRL2, 0x00040000);
hd21_write_reg(CLKCTRL_FPLL_CTRL3, 0x0b0da000);
if (remainder)
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL3, 1, 27, 1); /* enable frac */
usleep_range(20, 30);
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL0, 0, 29, 1);
usleep_range(20, 30);
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL3, 1, 9, 1); /* enable pll_lock_rst */
WAIT_FPLL_GP2PLL_LOCK(CLKCTRL_FPLL_CTRL0, CLKCTRL_FPLL_STS);
/* setting fpll div */
if (div > 8) {
div1 = 8;
div2 = div / 8;
} else {
div1 = div;
div2 = 1;
}
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL0, od_map[div1], 23, 2); // fpll_tmds_od<3:2> div8
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL0, od_map[div2], 21, 2); // fpll_tmds_od<1:0> div2
/* setting pixel_od */
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL0, pixel_od, 13, 3); // pixel_od
}
// gp2pll: 2376M 2376/24=99=0x63
// 2376/16=148.5M
/* div: 1, 2, 4, 8, ... 64 */
void hdmitx_set_s5_gp2pll(u32 clk, u32 div)
{
u32 quotient;
u32 remainder;
pr_info("%s[%d] clk %d div %d\n", __func__, __LINE__, clk, div);
/* setting fpll vco */
quotient = clk / 24000;
remainder = clk - quotient * 24000;
/* remainder range: 0 ~ 23999, 0x5dbf, 15bits */
remainder *= 1 << 17;
remainder /= 24000;
hd21_write_reg(CLKCTRL_GP2PLL_CTRL0, 0x20010800 | quotient);
hd21_set_reg_bits(CLKCTRL_GP2PLL_CTRL0, 1, 28, 1);
hd21_write_reg(CLKCTRL_GP2PLL_CTRL1, 0x03a00000 | remainder);
hd21_write_reg(CLKCTRL_GP2PLL_CTRL2, 0x00040000);
hd21_write_reg(CLKCTRL_GP2PLL_CTRL3, 0x010da000);
if (remainder)
hd21_set_reg_bits(CLKCTRL_GP2PLL_CTRL3, 1, 27, 1); /* enable frac */
usleep_range(20, 30);
hd21_set_reg_bits(CLKCTRL_GP2PLL_CTRL0, 0, 29, 1);
usleep_range(20, 30);
hd21_set_reg_bits(CLKCTRL_GP2PLL_CTRL3, 1, 9, 1); /* enable pll_lock_rst */
WAIT_FPLL_GP2PLL_LOCK(CLKCTRL_GP2PLL_CTRL0, CLKCTRL_GP2PLL_STS);
hd21_set_reg_bits(CLKCTRL_FPLL_CTRL0, od_map[div], 23, 2); // gp2pll_tmds_od<2:0>
}
static void hdmitx_set_s5_clkdiv(struct hdmitx_dev *hdev)
{
if (!hdev && !hdev->para)
return;
/* cts_htx_tmds_clk selects the htx_tmds20_clk or fll_tmds_clk */
hd21_set_reg_bits(CLKCTRL_HTX_CLK_CTRL1, hdev->frl_rate ? 1 : 0, 25, 2);
if (!hdev->frl_rate && hdev->para->cs == HDMI_COLORSPACE_YUV420)
hd21_set_reg_bits(CLKCTRL_HTX_CLK_CTRL1, 1, 16, 7);
else
hd21_set_reg_bits(CLKCTRL_HTX_CLK_CTRL1, 0, 16, 7);
/* master_clk selects the vid_pll_clk or fpll_pixel_clk */
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL, hdev->frl_rate ? 4 : 0, 16, 3);
}
void hdmitx21_set_audioclk(bool en)
{
u32 data32;
// Enable hdmitx_aud_clk
// [10: 9] clk_sel for cts_hdmitx_aud_clk: 2=fclk_div3
// [ 8] clk_en for cts_hdmitx_aud_clk
// [ 6: 0] clk_div for cts_hdmitx_aud_clk: fclk_div3/aud_clk_div
data32 = 0;
data32 |= (2 << 9);
data32 |= (0 << 8);
data32 |= ((18 - 1) << 0);
hd21_set_reg_bits(CLKCTRL_HTX_CLK_CTRL1, 2, 9, 2);
hd21_set_reg_bits(CLKCTRL_HTX_CLK_CTRL1, 18 - 1, 0, 8);
// [ 8] clk_en for cts_hdmitx_aud_clk
hd21_set_reg_bits(CLKCTRL_HTX_CLK_CTRL1, en, 8, 1);
}
void hdmitx21_set_default_clk(void)
{
u32 data32;
// Enable clk81_hdmitx_pclk
hd21_set_reg_bits(CLKCTRL_SYS_CLK_EN0_REG2, 1, 4, 1);
// Enable fixed hdmitx_sys_clk
data32 = 0;
data32 |= (3 << 9); // [10: 9] clk_sel for cts_hdmitx_sys_clk: 3=fclk_div5
data32 |= (0 << 8); // [ 8] clk_en for cts_hdmitx_sys_clk
data32 |= (1 << 0); // [ 6: 0] clk_div for cts_hdmitx_sys_clk: fclk_dvi5/2=400/2=200M
hd21_write_reg(CLKCTRL_HDMI_CLK_CTRL, data32);
data32 |= (1 << 8); // [ 8] clk_en for cts_hdmitx_sys_clk
hd21_write_reg(CLKCTRL_HDMI_CLK_CTRL, data32);
// Enable fixed hdmitx_prif_clk, hdmitx_200m_clk
data32 = 0;
data32 |= (3 << 25); // [26:25] clk_sel for cts_hdmitx_200m_clk: 3=fclk_div5
data32 |= (0 << 24); // [ 24] clk_en for cts_hdmitx_200m_clk
data32 |= (1 << 16); // [22:16] clk_div for cts_hdmitx_200m_clk: fclk_dvi5/16=400/16=25M
data32 |= (3 << 9); // [10: 9] clk_sel for cts_hdmitx_prif_clk: 3=fclk_div5
data32 |= (0 << 8); // [ 8] clk_en for cts_hdmitx_prif_clk
data32 |= (1 << 0); // [ 6: 0] clk_div for cts_hdmitx_prif_clk: fclk_dvi5/2=400/2=200M
hd21_write_reg(CLKCTRL_HTX_CLK_CTRL0, data32);
data32 |= (1 << 24); // [ 24] clk_en for cts_hdmitx_200m_clk
data32 |= (1 << 8); // [ 8] clk_en for cts_hdmitx_prif_clk
hd21_write_reg(CLKCTRL_HTX_CLK_CTRL0, data32);
//hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL, 0, 0, 5);
// Bring HDMITX MEM output of power down
hd21_set_reg_bits(PWRCTRL_MEM_PD11, 0, 8, 8);
// Bring out of reset
hdmitx21_wr_reg(HDMITX_TOP_SW_RESET, 0);
// Test after initial out of reset, cannot write to IP register, unless enable access
hdmitx21_wr_reg(INTR3_MASK_IVCTX, 0xff);
hdmitx21_wr_reg(HDMITX_TOP_SEC_SCRATCH, 1);
}
void hdmitx21_set_cts_hdcp22_clk(struct hdmitx_dev *hdev)
{
//hd21_write_reg(CLKCTRL_HDCP22_CLK_CTRL, 0x01000100);
}
void hdmitx21_set_hdcp_pclk(struct hdmitx_dev *hdev)
{
/* top hdcp pixel clock */
hd21_set_reg_bits(CLKCTRL_SYS_CLK_EN0_REG2, 1, 3, 1);
}
/* --------------------------------------------------
* set_tmds_vid_clk_div
* --------------------------------------------------
* wire clk_final_en = control[19];
* wire clk_div1 = control[18];
* wire [1:0] clk_sel = control[17:16];
* wire set_preset = control[15];
* wire [14:0] shift_preset = control[14:0];
* div_src: 0 means divide the hdmi_tmds_out2 to tmds_clk
* 1 means divide the hdmi_tmds_out2 to vid_pll0_clk
*/
static void set_tmds_vid_clk_div(u8 div_src, u32 div_val)
{
u32 div_reg;
u32 shift_val = 0;
u32 shift_sel = 0;
div_reg = (div_src == 1) ? CLKCTRL_HDMI_VID_PLL_CLK_DIV : CLKCTRL_HDMI_PLL_TMDS_CLK_DIV;
// Disable the output clock
hd21_set_reg_bits(div_reg, 0, 15, 1);
hd21_set_reg_bits(div_reg, 0, 19, 1);
switch (div_val) {
case VID_PLL_DIV_1:
shift_val = 0xFFFF;
shift_sel = 0;
break;
case VID_PLL_DIV_2:
shift_val = 0x0aaa;
shift_sel = 0;
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 = 0x0c63;
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;
default:
pr_err("%s[%d] invalid div %d\n", __func__, __LINE__, div_val);
}
if (shift_val == 0xffff) { // if divide by 1
hd21_set_reg_bits(div_reg, 1, 18, 1);
} else {
hd21_set_reg_bits(div_reg, shift_val, 0, 15);
hd21_set_reg_bits(div_reg, 1, 15, 1);
hd21_set_reg_bits(div_reg, 0, 20, 1);
hd21_set_reg_bits(div_reg, shift_sel, 16, 3);
// Set the selector low
hd21_set_reg_bits(div_reg, 0, 15, 1);
}
// Enable the final output clock
hd21_set_reg_bits(div_reg, 1, 19, 1);
}
/* if vsync likes 24000, 30000, ... etc, return 1 */
static bool is_vsync_int(u32 clk)
{
if (clk % 3000 == 0)
return 1;
return 0;
}
/* if vsync likes 59940, ... etc, return 1 */
static bool is_vsync_frac(u32 clk)
{
clk += clk / 1000;
if (is_vsync_int(clk) || is_vsync_int(clk + 1))
return 1;
return 0;
}
/* for varied hdmi basic modes, such as
* vic/16, the vsync is 60, and may shift to 59.94
* but vic/2, the vsync is 59.94, and may shift to 60
* return values:
* 0: no any shift
* 1: shift down 0.1%
* 2: shift up 0.1%
*/
static u32 check_clock_shift(enum hdmi_vic vic, u32 frac_policy)
{
const struct hdmi_timing *timing = NULL;
timing = hdmitx21_gettiming_from_vic(vic);
if (!timing) {
pr_err("%s[%d] not valid vic %d\n", __func__, __LINE__, vic);
return 0;
}
/* only check such as 24hz, 30hz, 60hz, ... */
if (!likely_frac_rate_mode(timing->name))
return 0;
if (is_vsync_int(timing->v_freq)) {
if (frac_policy)
return 1;
else
return 0;
}
if (is_vsync_frac(timing->v_freq)) {
if (frac_policy)
return 0;
else
return 2;
}
return 0;
}
static void set_hdmitx_s5_htx_pll(struct hdmitx_dev *hdev)
{
enum hdmi_vic vic = HDMI_0_UNKNOWN;
enum hdmi_colorspace cs = HDMI_COLORSPACE_YUV444;
enum hdmi_color_depth cd = COLORDEPTH_24B;
u32 base_pixel_clk = 25200;
u32 htx_vco = 5940000;
u32 div = 1;
if (!hdev || !hdev->para)
return;
vic = hdev->para->timing.vic;
cs = hdev->para->cs;
cd = hdev->para->cd;
if (vic == HDMI_0_UNKNOWN) {
pr_err("%s[%d] not valid vic %d\n", __func__, __LINE__, vic);
return;
}
base_pixel_clk = hdev->para->timing.pixel_freq;
if (base_pixel_clk < 25175 || base_pixel_clk > 5940000) {
pr_err("%s[%d] not valid pixel clock %d\n", __func__, __LINE__, base_pixel_clk);
return;
}
/* For FRL modes */
if (hdev->frl_rate != FRL_NONE) {
pr_info("set hpll for frl_rate %d\n", hdev->frl_rate);
switch (hdev->frl_rate) {
case FRL_3G3L:
set21_s5_htxpll_clk_out(6000000, 8);
break;
case FRL_6G3L:
set21_s5_htxpll_clk_out(6000000, 4);
break;
case FRL_6G4L:
set21_s5_htxpll_clk_out(6000000, 2);
break;
case FRL_12G4L:
set21_s5_htxpll_clk_out(6000000, 1);
break;
case FRL_8G4L:
set21_s5_htxpll_clk_out(4000000, 1);
break;
case FRL_10G4L:
set21_s5_htxpll_clk_out(5000000, 1);
break;
default:
pr_err("not support frl_rate: %d\n", hdev->frl_rate);
break;
}
return;
}
pr_info("%s[%d] base_pixel_clk %d cs %d cd %d frac_rate %d\n",
__func__, __LINE__, base_pixel_clk, cs, cd, frac_rate);
/* for legacy TMDS modes */
if (cs != HDMI_COLORSPACE_YUV422) {
switch (cd) {
case COLORDEPTH_48B:
base_pixel_clk = base_pixel_clk * 2;
break;
case COLORDEPTH_36B:
base_pixel_clk = base_pixel_clk * 3 / 2;
break;
case COLORDEPTH_30B:
base_pixel_clk = base_pixel_clk * 5 / 4;
break;
case COLORDEPTH_24B:
default:
base_pixel_clk = base_pixel_clk * 1;
break;
}
}
if (check_clock_shift(vic, frac_rate) == 1)
base_pixel_clk = base_pixel_clk - base_pixel_clk / 1001;
if (check_clock_shift(vic, frac_rate) == 2)
base_pixel_clk = base_pixel_clk + base_pixel_clk / 1000;
base_pixel_clk = base_pixel_clk * 10; /* for tmds modes, here should multi 10 */
if (cs == HDMI_COLORSPACE_YUV420)
base_pixel_clk /= 2;
pr_info("%s[%d] calculate pixel_clk to %d\n", __func__, __LINE__, base_pixel_clk);
if (base_pixel_clk > MAX_HTXPLL_VCO) {
pr_err("%s[%d] base_pixel_clk %d over MAX_HTXPLL_VCO %d\n",
__func__, __LINE__, base_pixel_clk, MAX_HTXPLL_VCO);
}
div = 1;
/* the base pixel_clk range should be 250M ~ 5940M? */
htx_vco = base_pixel_clk;
do {
if (htx_vco >= MIN_HTXPLL_VCO && htx_vco < MAX_HTXPLL_VCO)
break;
div *= 2;
htx_vco *= 2;
} while (div <= 32);
/* the hdmi phy works under DUAL mode, and the div should be multiply 2 */
div *= 2;
set21_s5_htxpll_clk_out(htx_vco, div);
}
static void set_hdmitx_htx_pll(struct hdmitx_dev *hdev)
{
enum hdmi_colorspace cs = hdev->para->cs;
enum hdmi_color_depth cd = hdev->para->cd;
u8 clk_div_val = VID_PLL_DIV_5;
//if (hdev->pxp_mode) /* skip VCO setting */
// return;
if (1) {
set_hdmitx_s5_htx_pll(hdev);
if (hdev->frl_rate)
set_frl_hpll_od(hdev->frl_rate);
if (cs != HDMI_COLORSPACE_YUV422) {
if (cd == COLORDEPTH_36B)
clk_div_val = VID_PLL_DIV_7p5;
else if (cd == COLORDEPTH_30B)
clk_div_val = VID_PLL_DIV_6p25;
else
clk_div_val = VID_PLL_DIV_5;
}
set_tmds_vid_clk_div(0, VID_PLL_DIV_5);
set_tmds_vid_clk_div(1, clk_div_val);
// set crt_vid_mux_div
//[19] disable clk_div0
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL, 0, 19, 1);
// bit[18:16] crt_vid_mux_div source select
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL, 0, 16, 3);
hd21_set_reg_bits(CLKCTRL_VID_CLK0_DIV, 0, 0, 8);
// bit[2:0] crt_vid_mux_div div1/2/4 enable
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL, 7, 0, 3);
// cts_enc_clk div and enable
hd21_set_reg_bits(CLKCTRL_VIID_CLK0_DIV, 0, 12, 4);
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL2, 1, 3, 1);
// enc0_hdmi_tx_fe_clk div and enable
hd21_set_reg_bits(CLKCTRL_ENC0_HDMI_CLK_CTRL, 0, 20, 4);
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL2, 1, 9, 1);
// enc0_hdmi_tx_pnx_clk div and enable
hd21_set_reg_bits(CLKCTRL_ENC0_HDMI_CLK_CTRL, 0, 24, 4);
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL2, 1, 10, 1);
// enc0_hdmi_tx_pixel_clk div and enable
hd21_set_reg_bits(CLKCTRL_ENC0_HDMI_CLK_CTRL, 0, 16, 4);
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL2, 1, 5, 1);
//[19] enable clk_div0
hd21_set_reg_bits(CLKCTRL_VID_CLK0_CTRL, 1, 19, 1);
return;
}
}
static int likely_frac_rate_mode(char *m)
{
if (strstr(m, "24hz") || strstr(m, "30hz") || strstr(m, "60hz") ||
strstr(m, "120hz") || strstr(m, "240hz"))
return 1;
else
return 0;
}
static void hdmitx_set_fpll_without_dsc(struct hdmitx_dev *hdev)
{
u32 fpll_vco = 2376000;
u32 div = 1;
u32 tmp_clk = 0;
u32 pixel_od = 0;
enum hdmi_vic vic = HDMI_0_UNKNOWN;
if (!hdev && !hdev->para)
return;
vic = hdev->para->timing.vic;
tmp_clk = hdev->para->timing.pixel_freq;
if (hdev->frl_rate)
tmp_clk /= 2;
switch (hdev->para->cs) {
case HDMI_COLORSPACE_RGB:
case HDMI_COLORSPACE_YUV444:
if (hdev->para->cd == COLORDEPTH_30B) {
tmp_clk = tmp_clk * 5 / 4;
pixel_od = 1;
}
if (hdev->para->cd == COLORDEPTH_36B) {
tmp_clk = tmp_clk * 3 / 2;
pixel_od = 2;
}
if (hdev->para->cd == COLORDEPTH_48B) {
tmp_clk = tmp_clk * 2;
pixel_od = 4;
}
break;
case HDMI_COLORSPACE_YUV420:
tmp_clk /= 2;
if (hdev->para->cd == COLORDEPTH_30B) {
tmp_clk = tmp_clk * 5 / 4;
pixel_od = 1;
}
if (hdev->para->cd == COLORDEPTH_36B) {
tmp_clk = tmp_clk * 3 / 2;
pixel_od = 2;
}
if (hdev->para->cd == COLORDEPTH_48B) {
tmp_clk *= 1;
pixel_od = 4;
}
break;
case HDMI_COLORSPACE_YUV422:
default:
tmp_clk *= 1;
pixel_od = 0;
break;
}
tmp_clk *= 2; /* here is a fixed DIV2 to tmds_clk */
fpll_vco = tmp_clk;
if (fpll_vco > MAX_FPLL_VCO) {
pr_info("hdmitx21: FPLL VCO over clock %d\n", fpll_vco);
return;
}
if (check_clock_shift(vic, frac_rate) == 1) {
fpll_vco = fpll_vco - fpll_vco / 1001;
pr_info("fpll_vco %d down shift to %d\n", tmp_clk, fpll_vco);
}
if (check_clock_shift(vic, frac_rate) == 2) {
fpll_vco = fpll_vco + fpll_vco / 1000;
pr_info("fpll_vco %d up shift to %d\n", tmp_clk, fpll_vco);
}
div = 1;
do {
if (fpll_vco >= MIN_FPLL_VCO && fpll_vco < MAX_FPLL_VCO)
break;
div *= 2;
fpll_vco *= 2;
} while (div <= 64);
hdmitx_set_s5_fpll(fpll_vco, div, pixel_od);
}
static void hdmitx_set_fpll_with_dsc(struct hdmitx_dev *hdev)
{
u32 fpll_vco = 2376000;
u32 div = 1;
u32 tmp_clk = 0;
u32 pixel_od = 0;
if (!hdev && !hdev->para)
return;
/* HARD CODE, FRL8G4L 4320p60 y420 8bit, HDMI 2.1 Spec, Page 281 */
/* 594 / 4500 * (2380 + 116) */
tmp_clk = 329472 * 2;
/* TODO */
fpll_vco = tmp_clk;
if (fpll_vco > MAX_FPLL_VCO) {
pr_info("hdmitx21: FPLL VCO over clock %d\n", fpll_vco);
return;
}
if (0) { /* TODO */
fpll_vco = fpll_vco - fpll_vco / 1001;
pr_info("fpll_vco %d shift to %d\n", tmp_clk, fpll_vco);
}
div = 1;
do {
if (fpll_vco >= MIN_FPLL_VCO && fpll_vco < MAX_FPLL_VCO)
break;
div *= 2;
fpll_vco *= 2;
} while (div <= 64);
hdmitx_set_s5_fpll(fpll_vco, div, pixel_od);
}
void hdmitx_set_fpll(struct hdmitx_dev *hdev)
{
if (hdev->dsc_en)
hdmitx_set_fpll_with_dsc(hdev);
else
hdmitx_set_fpll_without_dsc(hdev);
}
void hdmitx_set_gp2pll(struct hdmitx_dev *hdev)
{
u32 gp2pll_vco = 2376000;
u32 div = 1;
u32 tmp_clk = 0;
if (!hdev && !hdev->para)
return;
tmp_clk = hdev->para->timing.pixel_freq;
if (hdev->frl_rate)
tmp_clk /= 2;
switch (hdev->para->cs) {
case HDMI_COLORSPACE_RGB:
case HDMI_COLORSPACE_YUV444:
if (hdev->para->cd == COLORDEPTH_30B)
tmp_clk = tmp_clk * 5 / 4;
if (hdev->para->cd == COLORDEPTH_36B)
tmp_clk = tmp_clk * 3 / 2;
if (hdev->para->cd == COLORDEPTH_48B)
tmp_clk = tmp_clk * 2;
break;
case HDMI_COLORSPACE_YUV420:
tmp_clk /= 2;
if (hdev->para->cd == COLORDEPTH_30B)
tmp_clk = tmp_clk * 5 / 4;
if (hdev->para->cd == COLORDEPTH_36B)
tmp_clk = tmp_clk * 3 / 2;
if (hdev->para->cd == COLORDEPTH_48B)
tmp_clk *= 1;
break;
case HDMI_COLORSPACE_YUV422:
default:
tmp_clk *= 1;
break;
}
tmp_clk *= 2; /* here is a fixed DIV2 to tmds_clk */
gp2pll_vco = tmp_clk;
if (gp2pll_vco > MAX_FPLL_VCO) {
pr_info("hdmitx21: GP2PLL VCO over clock %d\n", gp2pll_vco);
return;
}
if (0) { /* TODO */
gp2pll_vco = gp2pll_vco * 1000 / 1001;
pr_info("gp2pll_vco %d shift to %d\n", tmp_clk, gp2pll_vco);
}
div = 1;
do {
if (gp2pll_vco >= MIN_GP2PLL_VCO && gp2pll_vco < MAX_GP2PLL_VCO)
break;
div *= 2;
gp2pll_vco *= 2;
} while (div <= 16);
hdmitx_set_s5_gp2pll(gp2pll_vco, div);
}
void hdmitx_set_clkdiv(struct hdmitx_dev *hdev)
{
hdmitx_set_s5_clkdiv(hdev);
}
static void hdmitx_check_frac_rate(struct hdmitx_dev *hdev)
{
struct hdmi_format_para *para = hdev->para;
char *frac_rate_str = NULL;
frac_rate = hdev->frac_rate_policy;
frac_rate_str = env_get("frac_rate_policy");
if (frac_rate_str && (frac_rate_str[0] == '0'))
frac_rate = 0;
else if (para && para->timing.name && likely_frac_rate_mode(para->timing.name))
frac_rate = 1;
hdev->frac_rate_policy = frac_rate;
pr_info("%s: frac_rate:%d\n", __func__, frac_rate);
}
void hdmitx21_set_clk(struct hdmitx_dev *hdev)
{
hdmitx_check_frac_rate(hdev);
disable_hdmitx_s5_plls(hdev);
/* typical 3 modes: legacy tmds, FRL w/o DSC, FRL w/ DSC */
set_hdmitx_htx_pll(hdev);
if (hdev->frl_rate) {
hdmitx_set_fpll(hdev);
if (hdev->dsc_en)
hdmitx_set_gp2pll(hdev);
}
}