From bcfe045c75fb86c909e016b962f593c6a11b9e94 Mon Sep 17 00:00:00 2001 From: zwl Date: Mon, 10 Mar 2014 18:22:21 +0800 Subject: [PATCH] HDMI: add rk3288 hdmi PHY MPLL config --- .../rockchip/hdmi/chips/rk3288/rk3288_hdmi.c | 34 ++++- .../hdmi/chips/rk3288/rk3288_hdmi_hw.c | 124 ++++++++++++++++-- .../hdmi/chips/rk3288/rk3288_hdmi_hw.h | 96 +++++++++++++- 3 files changed, 238 insertions(+), 16 deletions(-) diff --git a/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi.c index 9c54392a1c8e..ce511d69c907 100644 --- a/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi.c +++ b/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi.c @@ -35,9 +35,28 @@ extern irqreturn_t hdmi_irq(int irq, void *priv); static struct rk3288_hdmi_device *hdmi_dev = NULL; #if defined(CONFIG_DEBUG_FS) +static const struct rk3288_hdmi_reg_table hdmi_reg_table[] = { + {IDENTIFICATION_BASE, CONFIG3_ID}, + {INTERRUPT_BASE, IH_MUTE}, + {VIDEO_SAMPLER_BASE, TX_BCBDATA1}, + {VIDEO_PACKETIZER_BASE, VP_MASK}, + {FRAME_COMPOSER_BASE, FC_DBGTMDS2}, + {HDMI_SOURCE_PHY_BASE, PHY_PLLCFGFREQ2}, + {I2C_MASTER_PHY_BASE, PHY_I2CM_SDA_HOLD}, + {AUDIO_SAMPLER_BASE, AHB_DMA_STPADDR_SET1_0}, + {MAIN_CONTROLLER_BASE, MC_SWRSTZREQ_2}, + {COLOR_SPACE_CONVERTER_BASE, CSC_SPARE_2}, + {HDCP_ENCRYPTION_ENGINE_BASE, HDCP_REVOC_LIST}, //HDCP_REVOC_LIST+5059 + {HDCP_BKSV_BASE, HDCPREG_BKSV4}, + {HDCP_AN_BASE, HDCPREG_AN7}, + {ENCRYPTED_DPK_EMBEDDED_BASE, HDCPREG_DPK6}, + {CEC_ENGINE_BASE, CEC_WKUPCTRL}, + {I2C_MASTER_BASE, I2CM_SCDC_UPDATE1}, +}; + static int rk3288_hdmi_reg_show(struct seq_file *s, void *v) { - int i = 0; + int i = 0,j = 0; u32 val = 0; seq_printf(s, "\n>>>hdmi_ctl reg"); for (i = 0; i < 16; i++) { @@ -45,12 +64,14 @@ static int rk3288_hdmi_reg_show(struct seq_file *s, void *v) } seq_printf(s, "\n-----------------------------------------------------------------"); - for(i=0; i<= I2CM_SCDC_UPDATE1; i++) { - val = hdmi_readl(hdmi_dev, i); - if(i%16==0) - seq_printf(s,"\n>>>hdmi_ctl %2x:", i); - seq_printf(s," %02x",val); + for(i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) { + for(j = hdmi_reg_table[i].reg_base; j <= hdmi_reg_table[i].reg_end; j++) { + val = hdmi_readl(hdmi_dev, j); + if((j - hdmi_reg_table[i].reg_base)%16==0) + seq_printf(s,"\n>>>hdmi_ctl %2x:", j); + seq_printf(s," %02x",val); + } } seq_printf(s, "\n-----------------------------------------------------------------\n"); @@ -158,6 +179,7 @@ static int rk3288_hdmi_probe(struct platform_device *pdev) hdmi_dev->dev = &pdev->dev; platform_set_drvdata(pdev, hdmi_dev); + mutex_init(&hdmi_dev->int_mutex); rk3288_hdmi_parse_dt(hdmi_dev); //TODO Daisen wait to add cec iomux diff --git a/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.c index b981eccef3fd..99fbc09c346c 100644 --- a/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.c +++ b/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.c @@ -2,6 +2,34 @@ #include #include "rk3288_hdmi_hw.h" + +static const struct phy_mpll_config_tab PHY_MPLL_TABLE[] = { //opmode: 0:HDMI1.4 1:HDMI2.0 +// |pixclock|pixrepet|colordepth|prepdiv|tmdsmhl|opmode|fbdiv2|fbdiv1|ref_cntrl|nctrl|propctrl|intctrl|gmpctrl| + {27000000, 0, 8, 0, 0, 0, 2, 3, 0, 3, 3, 0, 0}, + {74250000, 0, 8, 0, 0, 0, 1, 3, 0, 2, 5, 0, 1}, + {148500000, 0, 8, 0, 0, 0, 1, 1, 0, 1, 7, 0, 2}, + {297000000, 0, 8, 0, 0, 0, 1, 0, 0, 0, 7, 0, 3}, + {297000000, 0, 16, 3, 3, 1, 1, 1, 0, 0, 5, 0, 3}, + {594000000, 0, 8, 0, 3, 1, 1, 0, 0, 0, 3, 0, 3}, +}; + +const struct phy_mpll_config_tab* get_phy_mpll_tab(int pixClock, char pixRepet, char colorDepth) +{ + int i; + + if(pixClock == 0) + return NULL; + + for(i = 0; i < ARRAY_SIZE(PHY_MPLL_TABLE); i++) + { + if((PHY_MPLL_TABLE[i].pix_clock == pixClock) && (PHY_MPLL_TABLE[i].pix_repet == pixRepet) + && (PHY_MPLL_TABLE[i].color_depth == colorDepth)) + return &PHY_MPLL_TABLE[i]; + } + return NULL; +} + + //i2c master reset void rk3288_hdmi_i2cm_reset(struct rk3288_hdmi_device *hdmi_dev) { @@ -93,9 +121,77 @@ int rk3288_hdmi_read_edid(struct hdmi *hdmi_drv, int block, unsigned char *buff) return ret; } -static void rk3288_hdmi_config_phy(unsigned char vic) +static int rk3288_hdmi_write_phy(struct rk3288_hdmi_device *hdmi_dev, int reg_addr, int val) +{ + int trytime = 2, i = 0, op_status = 0; + + mutex_lock(&hdmi_dev->int_mutex); + hdmi_dev->phy_status = 0; + mutex_unlock(&hdmi_dev->int_mutex); + + while(trytime--) { + hdmi_writel(hdmi_dev, PHY_I2CM_ADDRESS, reg_addr); + hdmi_writel(hdmi_dev, PHY_I2CM_DATAO_1, (val >> 8) & 0xff); + hdmi_writel(hdmi_dev, PHY_I2CM_DATAO_0, val & 0xff); + hdmi_writel(hdmi_dev, PHY_I2CM_OPERATION, m_PHY_I2CM_WRITE); + + i = 100; + while(i--) { + mutex_lock(&hdmi_dev->int_mutex); + //op_status = hdmi_readl(hdmi_dev, PHY_I2CM_INT); + op_status = hdmi_dev->phy_status; + hdmi_dev->phy_status = 0; + mutex_unlock(&hdmi_dev->int_mutex); + if(op_status & (m_I2CMPHY_DONE | m_I2CMPHY_ERR)) { + break; + } + msleep(10); + } + + if(op_status & m_I2CMPHY_DONE) { + return 0; + } + else { + hdmi_err(hdmi_dev->dev, "[%s] operation error,trytime=%d\n", __FUNCTION__,trytime); + } + msleep(100); + } + + return -1; +} + +static void rk3288_hdmi_config_phy(struct hdmi *hdmi_drv) { - //TODO Daisen wait to add code + int stat = 0; + char color_depth = COLOR_DEPTH_24BIT_DEFAULT; + char pix_repet = NO_PIXEL_REPET; + const struct phy_mpll_config_tab *phy_mpll = NULL; + struct rk3288_hdmi_device *hdmi_dev = container_of(hdmi_drv, struct rk3288_hdmi_device, driver); + + hdmi_writel(hdmi_dev, PHY_CONF0, 0x32); + hdmi_writel(hdmi_dev, MC_PHYRSTZ, v_PHY_RSTZ(1)); + //the resolution that we used is no pixel repet,refer to the CEA-861 specification. + hdmi_writel(hdmi_dev, VP_PR_CD, v_COLOR_DEPTH(color_depth) | v_DESIRED_PR_FACTOR(pix_repet)); + msleep(5); + hdmi_writel(hdmi_dev, MC_PHYRSTZ, v_PHY_RSTZ(0)); + + //Set slave address as PHY GEN2 address + hdmi_writel(hdmi_dev, PHY_I2CM_SLAVE, PHY_GEN2_ADDR); + + //config the required PHY I2C register + phy_mpll = get_phy_mpll_tab(hdmi_drv->tmdsclk, pix_repet, color_depth); + rk3288_hdmi_write_phy(hdmi_dev, PHYTX_OPMODE_PLLCFG, v_PREP_DIV(phy_mpll->prep_div) | v_TMDS_CNTRL(phy_mpll->tmdsmhl_cntrl) | v_OPMODE(phy_mpll->opmode) | + v_FBDIV2_CNTRL(phy_mpll->fbdiv2_cntrl) | v_FBDIV1_CNTRL(phy_mpll->fbdiv1_cntrl) | v_REF_CNTRL(phy_mpll->ref_cntrl) | v_MPLL_N_CNTRL(phy_mpll->n_cntrl)); + rk3288_hdmi_write_phy(hdmi_dev, PHYTX_PLLCURRCTRL, v_MPLL_PROP_CNTRL(phy_mpll->prop_cntrl) | v_MPLL_INT_CNTRL(phy_mpll->int_cntrl)); + rk3288_hdmi_write_phy(hdmi_dev, PHYTX_PLLGMPCTRL, v_MPLL_GMP_CNTRL(phy_mpll->gmp_cntrl)); + + //power on PHY + hdmi_writel(hdmi_dev, PHY_CONF0, 0x2a); + + //check if the PHY PLL is locked + stat = hdmi_readl(hdmi_dev, PHY_STAT0); + if(stat & m_PHY_LOCK) + hdmi_writel(hdmi_dev, PHY_STAT0, v_PHY_LOCK(1)); } static void rk3288_hdmi_config_avi(struct hdmi *hdmi_drv, unsigned char vic, unsigned char output_color) @@ -271,11 +367,6 @@ int rk3288_hdmi_config_video(struct hdmi *hdmi_drv, struct hdmi_video_para *vpar //Set Data enable signal from external and set video sample input mapping hdmi_msk_reg(hdmi_dev, TX_INVID0, m_INTERNAL_DE_GEN | m_VIDEO_MAPPING, v_INTERNAL_DE_GEN(0) | v_VIDEO_MAPPING(input_color)); - if(hdmi_drv->tmdsclk > 340000000) { //used for HDMI 2.0 TX - hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_HDCP_KEEPOUT, v_FC_HDCP_KEEPOUT(1)); - hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL, m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(1)); - } - //Color space convert rk3288_hdmi_config_csc(hdmi_drv, vpara); @@ -287,6 +378,12 @@ int rk3288_hdmi_config_video(struct hdmi *hdmi_drv, struct hdmi_video_para *vpar return -ENOENT; } + hdmi_drv->tmdsclk = mode->pixclock; + if(hdmi_drv->tmdsclk > 340000000) { //used for HDMI 2.0 TX + hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_HDCP_KEEPOUT, v_FC_HDCP_KEEPOUT(1)); + hdmi_msk_reg(hdmi_dev, FC_SCRAMBLER_CTRL, m_FC_SCRAMBLE_EN, v_FC_SCRAMBLE_EN(1)); + } + hdmi_msk_reg(hdmi_dev, FC_INVIDCONF, m_FC_VSYNC_POL | m_FC_HSYNC_POL | m_FC_DE_POL | m_FC_INTERLACE_MODE, v_FC_VSYNC_POL(vsync_pol) | v_FC_HSYNC_POL(hsync_pol) | v_FC_DE_POL(de_pol) | v_FC_INTERLACE_MODE(mode->vmode)); //TODO Daisen wait to set m_FC_VBLANK value!! @@ -329,7 +426,7 @@ int rk3288_hdmi_config_video(struct hdmi *hdmi_drv, struct hdmi_video_para *vpar hdmi_dbg(hdmi_drv->dev, "[%s] sucess output DVI.\n", __FUNCTION__); } - rk3288_hdmi_config_phy(vpara->vic); + rk3288_hdmi_config_phy(hdmi_drv); rk3288_hdmi_control_output(hdmi_drv, 0); return 0; } @@ -486,16 +583,18 @@ irqreturn_t hdmi_irq(int irq, void *priv) { struct hdmi *hdmi_drv = (struct hdmi *)priv; struct rk3288_hdmi_device *hdmi_dev = container_of(hdmi_drv, struct rk3288_hdmi_device, driver); - int phy_int,i2cm_int,cec_int,hdcp_int; + int phy_int, i2cm_int, phy_i2cm_int, cec_int, hdcp_int; phy_int = hdmi_readl(hdmi_dev, IH_PHY_STAT0); i2cm_int = hdmi_readl(hdmi_dev, IH_I2CM_STAT0); + phy_i2cm_int = hdmi_readl(hdmi_dev, IH_I2CMPHY_STAT0); cec_int = hdmi_readl(hdmi_dev, IH_CEC_STAT0); hdcp_int = hdmi_readl(hdmi_dev, A_APIINTSTAT); //clear interrupt hdmi_writel(hdmi_dev, IH_PHY_STAT0, phy_int); hdmi_writel(hdmi_dev, IH_I2CM_STAT0, i2cm_int); + hdmi_writel(hdmi_dev, IH_I2CMPHY_STAT0, phy_i2cm_int); hdmi_writel(hdmi_dev, IH_CEC_STAT0, cec_int); hdmi_writel(hdmi_dev, A_APIINTCLR, hdcp_int); @@ -513,6 +612,13 @@ irqreturn_t hdmi_irq(int irq, void *priv) spin_unlock(&hdmi_drv->irq_lock); } + //PHY I2CM write or read status + if(phy_i2cm_int & (m_I2CMPHY_DONE | m_I2CMPHY_ERR)) { + mutex_lock(&hdmi_dev->int_mutex); + hdmi_dev->phy_status = phy_i2cm_int; + mutex_unlock(&hdmi_dev->int_mutex); + } + //CEC if(cec_int) { //TODO Daisen wait to modify } diff --git a/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.h index 75966b175796..74e494fe6a48 100644 --- a/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.h +++ b/drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.h @@ -639,6 +639,9 @@ enum { #define I2C_MASTER_PHY_BASE 0x3020 #define PHY_I2CM_SLAVE 0x3020 +#define PHY_GEN2_ADDR 0x69 +#define PHY_HEAC_ADDR 0x49 + #define PHY_I2CM_ADDRESS 0x3021 #define PHY_I2CM_DATAO_1 0x3022 #define PHY_I2CM_DATAO_0 0x3023 @@ -687,7 +690,7 @@ enum { #define PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030 #define PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031 #define PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032 -#define I2CM_PHY_SDA_HOLD 0x3033 +#define PHY_I2CM_SDA_HOLD 0x3033 /*Audio Sampler Registers*/ @@ -1147,6 +1150,90 @@ enum { #define I2CM_SCDC_UPDATE1 0x7e31 + +/*********************************************HDMI TX PHY Define Start*********************************************/ +#define PHYTX_OPMODE_PLLCFG 0x06 +enum { + PREP_DIV_BY_2 = 0, //16 bits + PREP_DIV_BY_15, //12 bits + PREP_DIV_BY_125, //10 bits + PREP_DIV_BY_1, //8 bits +}; +#define m_PREP_DIV (0x03 << 13) +#define v_PREP_DIV(n) (((n)&0x03) << 13) +enum { + TMDS_DIV_BY_1 = 0, + TMDS_DIV_NOT_USED, + TMDS_DIV_BY_3, + TMDS_DIV_BY_4, +}; +#define m_TMDS_CNTRL (0x03 << 11) +#define v_TMDS_CNTRL(n) (((n)&0x03) << 11) +enum OPMODE{ + OP_HDMI_14 = 0, + OP_HDMI_20, +}; +#define m_OPMODE (0x03 << 9) +#define v_OPMODE(n) (((n)&0x03) << 9) +enum { + FBDIV2_BY_1 = 1, + FBDIV2_BY_2, + FBDIV2_BY_3, + FBDIV2_BY_4, + FBDIV2_BY_5, + FBDIV2_BY_6, +}; +#define m_FBDIV2_CNTRL (0x07 << 6) +#define v_FBDIV2_CNTRL(n) (((n)&0x07) << 6) +enum { + FBDIV1_BY_1 = 0, + FBDIV1_BY_2, + FBDIV1_BY_3, + FBDIV1_BY_4, +}; +#define m_FBDIV1_CNTRL (0x03 << 4) +#define v_FBDIV1_CNTRL(n) (((n)&0x03) << 4) +enum { + REF_DIV_BY_1 = 0, + REF_DIV_BY_2, + REF_DIV_NOT_USED, + REF_DIV_BY_4, +}; +#define m_REF_CNTRL (0x03 << 2) +#define v_REF_CNTRL(n) (((n)&0x03) << 2) +#define m_MPLL_N_CNTRL (0x03 << 0) +#define v_MPLL_N_CNTRL(n) (((n)&0x03) << 0) + +#define PHYTX_PLLCURRCTRL 0x10 +#define m_MPLL_PROP_CNTRL (0x07 << 3) +#define v_MPLL_PROP_CNTRL(n) (((n)&0x07) << 3) +#define m_MPLL_INT_CNTRL (0x07 << 0) +#define v_MPLL_INT_CNTRL(n) (((n)&0x07) << 0) + +#define PHYTX_PLLGMPCTRL 0x15 +#define m_MPLL_GMP_CNTRL (0x03 << 0) +#define v_MPLL_GMP_CNTRL(n) (((n)&0x03) << 0) + +struct phy_mpll_config_tab { + u32 pix_clock; + u8 pix_repet; + u8 color_depth; + u16 prep_div; + u16 tmdsmhl_cntrl; + u16 opmode; + u32 fbdiv2_cntrl; + u16 fbdiv1_cntrl; + u16 ref_cntrl; + u16 n_cntrl; + u32 prop_cntrl; + u32 int_cntrl; + u16 gmp_cntrl; +}; + +/********************************************* HDMI TX PHY Define End *********************************************/ + + + enum{ INPUT_IIS, INPUT_SPDIF @@ -1162,6 +1249,10 @@ enum { CSC_ITU709_16_235_TO_RGB_0_255 //YCbCr 16-235 input to RGB 0-255 output according BT709 }; +struct rk3288_hdmi_reg_table { + int reg_base; + int reg_end; +}; struct rk3288_hdmi_device { int irq; @@ -1170,6 +1261,8 @@ struct rk3288_hdmi_device { int regsize_phy; int lcdc_id; int edid_status; + int phy_status; + struct mutex int_mutex; struct device *dev; struct clk *hclk; //HDMI AHP clk struct hdmi driver; @@ -1198,6 +1291,7 @@ static inline int hdmi_msk_reg(struct rk3288_hdmi_device *hdmi_dev, u16 offset, return ret; } + int rk3288_hdmi_initial(struct hdmi *hdmi_drv); void rk3288_hdmi_control_output(struct hdmi *hdmi_drv, int enable); -- 2.34.1