AnsweredAssumed Answered

i.MX6Q HDMI PHY PLL lock failure

Question asked by Toyn Prisk on Aug 18, 2013
Latest reply on Aug 21, 2013 by Toyn Prisk

Is there a documented workflow for programming the the i.MX6 hdmi + phy?

 

I am trying to integrate an HDMI driver into the linux-next staging imx-drm driver, and seem to have issues getting the PHY pll to lock after programming.

HDMI mode setting starts at imx_hdmi_encoder_mode_set(), but I get a failure at "PHY PLL failed to lock" everytime.

Output clockrate also seems to be incorrect - for example:

Requesting 1920x1080@60, my monitor reports 1920x1080@50.

Requesting 1920x1080@50, my monitor reports 1920x1080@30.


All testing performed on a Wandboard Quad i.MX6Q


Code snippets follow:

 

static void imx_hdmi_phy_set_power_state(struct imx_hdmi *hdmi,

                     enum imx_phy_power_state state)

{

    if (state == hdmi->phy_power_state) {

        dev_info(hdmi->dev, "requested phy power state already set\n");

    }

 

    /* POWEROFF can be called from any previous state */

    if (state == IMX_PHY_POWEROFF) {

        hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);

 

        imx_hdmi_phy_enable_tmds(hdmi, 0);

        imx_hdmi_phy_enable_power(hdmi, 0);

        imx_hdmi_phy_gen2_txpwron(hdmi, 0);

        imx_hdmi_phy_gen2_pddq(hdmi, 1);

 

        hdmi->phy_power_state = IMX_PHY_POWEROFF;

    }

 

    /* CONFIGURE should not be called from POWERON, so POWEROFF first */

    if (state == IMX_PHY_CONFIGURE) {

        if (hdmi->phy_power_state != IMX_PHY_POWEROFF) {

            dev_err(hdmi->dev, "forcing PHY power-off before config\n");

            imx_hdmi_phy_set_power_state(hdmi, IMX_PHY_POWEROFF);

        }

 

        hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);

 

        if (hdmi->heac_phy_supported)

            hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT,

                            HDMI_MC_HEACPHY_RST);

 

        hdmi->phy_power_state = IMX_PHY_CONFIGURE;

    }

 

    /* POWERON should only be called after the PHY has been configured */

    if (state == IMX_PHY_POWERON) {

        if (hdmi->phy_power_state != IMX_PHY_CONFIGURE) {

            dev_err(hdmi->dev, "attempted to enable PHY before configuring\n");

            return;

        }

        imx_hdmi_phy_enable_power(hdmi, 1);

        imx_hdmi_phy_enable_tmds(hdmi, 1);

        imx_hdmi_phy_gen2_txpwron(hdmi, 1);

        imx_hdmi_phy_gen2_pddq(hdmi, 0);

 

        hdmi->phy_power_state = IMX_PHY_POWERON;

    }

}

 

static void imx_hdmi_configure_phy(struct imx_hdmi *hdmi,

                   struct drm_display_mode *mode)

{

    /*

     * PHY configuration taken from Application Processor Reference Manual

     * Only 8-bit color depth is supported.

     */

    imx_hdmi_phy_set_power_state(hdmi, IMX_PHY_CONFIGURE);

 

    hdmi_phy_test_clear(hdmi, 1);

    hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,

                    HDMI_PHY_I2CM_SLAVE_ADDR);

    hdmi_phy_test_clear(hdmi, 0);

 

    /* a) PLL/MPLL mode of operation */

    imx_hdmi_phy_i2c_write(hdmi, 0x0000, HDMI_PHY_PLLPHBYCTRL);

    imx_hdmi_phy_i2c_write(hdmi, 0x0006, HDMI_PHY_MSM_CTRL);

 

    /* b) PLL/MPLL clock dividers and analog configuration */

    /* c) Driver edge rate control */

    if (mode->clock <= 45250) {

        imx_hdmi_phy_i2c_write(hdmi, 0x01e0, HDMI_PHY_CPCE_CTRL);

        imx_hdmi_phy_i2c_write(hdmi, 0x0000, HDMI_PHY_GMPCTRL);

    } else if (mode->clock <= 92500) {

        imx_hdmi_phy_i2c_write(hdmi, 0x0140, HDMI_PHY_CPCE_CTRL);

        imx_hdmi_phy_i2c_write(hdmi, 0x0005, HDMI_PHY_GMPCTRL);

    } else if (mode->clock <= 148500) {

        imx_hdmi_phy_i2c_write(hdmi, 0x00a0, HDMI_PHY_CPCE_CTRL);

        imx_hdmi_phy_i2c_write(hdmi, 0x000a, HDMI_PHY_GMPCTRL);

    } else {

        imx_hdmi_phy_i2c_write(hdmi, 0x00a0, HDMI_PHY_CPCE_CTRL);

        imx_hdmi_phy_i2c_write(hdmi, 0x000a, HDMI_PHY_GMPCTRL);

    }

 

    if (mode->clock <= 58400)

        imx_hdmi_phy_i2c_write(hdmi, 0x091c, HDMI_PHY_CURRCTRL);

    else if (mode->clock <= 74250)

        imx_hdmi_phy_i2c_write(hdmi, 0x06dc, HDMI_PHY_CURRCTRL);

    else if (mode->clock <= 118800)

        imx_hdmi_phy_i2c_write(hdmi, 0x091c, HDMI_PHY_CURRCTRL);

    else if (mode->clock <= 216000)

        imx_hdmi_phy_i2c_write(hdmi, 0x06dc, HDMI_PHY_CURRCTRL);

 

    /* d) Driver differential source termination */

    imx_hdmi_phy_i2c_write(hdmi, 0x0005, HDMI_PHY_TXTERM);

 

    /* e) Driver voltage level */

    /* f) Driver pre-emphasis */

    if (mode->clock <= 148500) {

        imx_hdmi_phy_i2c_write(hdmi, 0x0210, HDMI_PHY_VLEVCTRL);

        imx_hdmi_phy_i2c_write(hdmi, 0x8009, HDMI_PHY_CKSYMTXCTRL);

    } else {

        imx_hdmi_phy_i2c_write(hdmi, 0x0129, HDMI_PHY_VLEVCTRL);

        imx_hdmi_phy_i2c_write(hdmi, 0x800b, HDMI_PHY_CKSYMTXCTRL);

    }

 

    /* Clock Resistance Calibration */

    imx_hdmi_phy_i2c_write(hdmi, 0x8000, HDMI_PHY_CKCALCCTRL);

}

 

static void imx_hdmi_init_phy(struct imx_hdmi *hdmi,

                    struct drm_display_mode *mode)

{

    u8 val, msec;

    int i;

 

    for (i = 0; i < 2; i++) {

        imx_hdmi_phy_sel_data_en_pol(hdmi, 1);

        imx_hdmi_phy_sel_interface_control(hdmi, 0);

 

        if (is_colorspace_conversion(hdmi) && !hdmi->mode_data.is_dvi)

            val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH;

        else

            val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;

        hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);

 

        imx_hdmi_phy_set_power_state(hdmi, IMX_PHY_POWEROFF);

        imx_hdmi_configure_phy(hdmi, mode);

        imx_hdmi_phy_set_power_state(hdmi, IMX_PHY_POWERON);

 

        /* Wait for PHY PLL lock */

        msec = 4;

        val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;

        while (val == 0) {

            udelay(1000);

            if (msec-- == 0) {

                dev_err(hdmi->dev, "PHY PLL failed to lock\n");

                break;

            }

            val = hdmi_readb(hdmi, HDMI_PHY_STAT0) &

                            HDMI_PHY_TX_PHY_LOCK;

        }

    }

}

 

static void imx_hdmi_encoder_mode_set(struct drm_encoder *encoder,

                      struct drm_display_mode *mode,

                      struct drm_display_mode *adjusted_mode)

{

    struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);

    u32 vic;

 

    imx_hdmi_disable_overflow_interrupts(hdmi);

    imx_hdmi_clear_overflow(hdmi);

 

    vic = drm_match_cea_mode(mode);

    hdmi->mode_data.vic = vic;

 

    if (vic == 0) {

        dev_info(hdmi->dev, "Non-CEA mode specified (%d x %d @ %d\n",

            mode->hdisplay, mode->vdisplay,

            drm_mode_vrefresh(mode));

            hdmi->mode_data.is_dvi = false;

    } else {

        dev_info(hdmi->dev, "CEA mode %d specified (%d x %d @ %d)\n",

             hdmi->mode_data.vic, mode->hdisplay, mode->vdisplay,

             drm_mode_vrefresh(mode));

    }

 

    if ((vic == 2) || (vic == 3) || (vic == 6) ||

        (vic == 7) || (vic == 17) || (vic == 18) ||

        (vic == 21) || (vic == 22))

        hdmi->mode_data.colorimetry = eITU601;

    else

        hdmi->mode_data.colorimetry = eITU709;

 

    if ((vic == 10) || (vic == 11) || (vic == 12) ||

        (vic == 13) || (vic == 14) || (vic == 15) ||

        (vic == 25) || (vic == 26) || (vic == 27) ||

        (vic == 28) || (vic == 29) || (vic == 30) ||

        (vic == 35) || (vic == 36) || (vic == 37) ||

        (vic == 38))

        hdmi->mode_data.pixelrepetition_output = 1;

    else

        hdmi->mode_data.pixelrepetition_output = 0;

 

    hdmi->mode_data.pixelrepetition_input = 0;

    hdmi->mode_data.pixelrepetition_factor = 0;

    hdmi->mode_data.encoder_format_input = RGB;

    hdmi->mode_data.encoder_format_output = RGB;

    /*

     * encoder_color_depth MUST be 8 as the IPU doesn't support higher

     * color depth

     */

    hdmi->mode_data.encoder_color_depth = 8;

    hdmi->mode_data.hdcp_enable = false;

    hdmi->mode_data.aspect_16_9 = true;

    hdmi->mode_data.de_polarity_high = true;

 

    imx_hdmi_configure_av_composer(hdmi, mode);

    imx_hdmi_init_phy(hdmi, mode);

    imx_hdmi_enable_video_clocks(hdmi);

 

    if (!hdmi->mode_data.is_dvi) {

        imx_hdmi_clk_regenerator_update_pixel_clock(hdmi, mode);

        imx_hdmi_enable_audio_clock(hdmi);

        imx_hdmi_configure_avi_infoframe(hdmi);

    }

 

    imx_hdmi_video_packetize(hdmi);

    imx_hdmi_video_csc(hdmi);

    imx_hdmi_video_sample(hdmi);

    imx_hdmi_tx_hdcp_config(hdmi);

 

    imx_hdmi_clear_overflow(hdmi);

}

Outcomes