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);
}
Which BSP and board are you using? Do you apply all the BSP patches?
As I mentioned in my original message, I am using linux-next (not a bsp) and the board is a Wandboard Quad.