HDMI: add rk3288 hdmi PHY MPLL config
authorzwl <zwl@rock-chips.com>
Mon, 10 Mar 2014 10:22:21 +0000 (18:22 +0800)
committerzwl <zwl@rock-chips.com>
Mon, 10 Mar 2014 10:22:44 +0000 (18:22 +0800)
drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi.c
drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.c
drivers/video/rockchip/hdmi/chips/rk3288/rk3288_hdmi_hw.h

index 9c54392a1c8e74f394a19f19b5a82f8f4d26a35b..ce511d69c9074ba9d6c3e58f9eef4f8bf35ee234 100644 (file)
@@ -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
index b981eccef3fd0592f332be548ceb585f23ef12f3..99fbc09c346c3ce2005bb658544946727c310c43 100644 (file)
@@ -2,6 +2,34 @@
 #include <linux/interrupt.h>
 #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
        }
index 75966b175796fb5a2c9b09749b2ec7c6fb22da06..74e494fe6a48d714df71de8b44cd5a175b7d677c 100644 (file)
@@ -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);