diff options
author | Alan Tull <alan.tull@freescale.com> | 2011-12-08 13:43:42 -0600 |
---|---|---|
committer | Jason Liu <r64343@freescale.com> | 2012-01-09 21:11:36 +0800 |
commit | ec1f85521053f7dde403b114d3b57dbd03502612 (patch) | |
tree | 5074a1e04c68803f5e914f19afb86981d89248b3 /drivers/mfd | |
parent | 9db2cba9f754b93938ef1c7af02b31c4250b99f1 (diff) |
ENGR00169872-2 rework hdmi initialization and hotplug sequence
This commit intends to implement the flowchart and details
documented in the HDMI Transmitter Controller User Guide
section entitled "Programming Model".
Some input is also from the Synopsys API code.
The HDMI specification requires HDMI to set itself to VGA DVI mode
before reading the EDID.
So follow this sequence when HDMI is hotplugged:
1. Hdmi connector is plugged in, HDMI video gets an interrupt.
2. Clear out video mode list. Add only VGA DVI mode to list.
3. Request VGA DVI mode (call fb_set_var())
4. HDMI video driver will get FB_EVENT_MODE_CHANGE callback and
call mxc_hdmi_setup() to set up HDMI.
5. Read the edid and add video modes from edid. Select the video
mode that is similar to the command line default.
6. Request VGA DVI mode (call fb_set_var())
7. HDMI video driver will get FB_EVENT_MODE_CHANGE callback and
do mxc_hdmi_setup().
Also included is a workaround for an overflow condition in the HDMI.
The frame composer has an arithmetic unit that gets updated every time
we write to one of the FC registers. But sometimes, depending on the
relation between the tmds and sfr clocks, it may happen that this unit
doesn't get updated, even though the registers are holding correct
values. The workaround for this is, after completing the controller
configuration, to rewrite one of the FC registers (i.e. FC_INVIDCONF)
three or four times with the same value, and then follow it up by a SW
reset to the TMDS clock domain (MC_SWRSTZ).
We clear the overflow condition as described above every time we
change video mode. Also an overflow interupt handler will clear the
overflow condition if it happens again. This overflow condition is
expected (and not a problem) when we are in DVI (non-HDMI) mode, so
we do not worry about it in that case.
Signed-off-by: Alan Tull <alan.tull@freescale.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/mxc-hdmi-core.c | 109 |
1 files changed, 94 insertions, 15 deletions
diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index 48cc57cc1b8d..5f86788750a1 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -72,10 +72,40 @@ u8 hdmi_readb(unsigned int reg) return value; } +#ifdef DEBUG +static bool overflow_lo; +static bool overflow_hi; + +bool hdmi_check_overflow(void) +{ + u8 val, lo, hi; + + val = hdmi_readb(HDMI_IH_FC_STAT2); + lo = (val & HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW) != 0; + hi = (val & HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW) != 0; + + if ((lo != overflow_lo) || (hi != overflow_hi)) { + pr_debug("%s LowPriority=%d HighPriority=%d <=======================\n", + __func__, lo, hi); + overflow_lo = lo; + overflow_hi = hi; + return true; + } + return false; +} +#else +bool hdmi_check_overflow(void) +{ + return false; +} +#endif + void hdmi_writeb(u8 value, unsigned int reg) { + hdmi_check_overflow(); pr_debug("hdmi wr: 0x%04x = 0x%02x\n", reg, value); __raw_writeb(value, hdmi_base + reg); + hdmi_check_overflow(); } void hdmi_mask_writeb(u8 data, unsigned int reg, u8 shift, u8 mask) @@ -170,6 +200,23 @@ static void initialize_hdmi_ih_mutes(void) hdmi_writeb(ih_mute, HDMI_IH_MUTE); + /* by default mask all interrupts */ + hdmi_writeb(0xff, HDMI_VP_MASK); + hdmi_writeb(0xff, HDMI_FC_MASK0); + hdmi_writeb(0xff, HDMI_FC_MASK1); + hdmi_writeb(0xff, HDMI_FC_MASK2); + hdmi_writeb(0xff, HDMI_PHY_MASK0); + hdmi_writeb(0xff, HDMI_PHY_I2CM_INT_ADDR); + hdmi_writeb(0xff, HDMI_PHY_I2CM_CTLINT_ADDR); + hdmi_writeb(0xff, HDMI_AUD_INT); + hdmi_writeb(0xff, HDMI_AUD_SPDIFINT); + hdmi_writeb(0xff, HDMI_AUD_HBR_MASK); + hdmi_writeb(0xff, HDMI_GP_MASK); + hdmi_writeb(0xff, HDMI_A_APIINTMSK); + hdmi_writeb(0xff, HDMI_CEC_MASK); + hdmi_writeb(0xff, HDMI_I2CM_INT); + hdmi_writeb(0xff, HDMI_I2CM_CTLINT); + /* Disable interrupts in the IH_MUTE_* registers */ hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT0); hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT1); @@ -190,13 +237,27 @@ static void initialize_hdmi_ih_mutes(void) static void hdmi_set_clock_regenerator_n(unsigned int value) { + u8 val; + hdmi_writeb(value & 0xff, HDMI_AUD_N1); hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2); - hdmi_writeb((value >> 16) & 0xff, HDMI_AUD_N3); + hdmi_writeb((value >> 16) & 0x0f, HDMI_AUD_N3); + + /* nshift factor = 0 */ + val = hdmi_readb(HDMI_AUD_CTS3); + val &= ~HDMI_AUD_CTS3_N_SHIFT_MASK; + hdmi_writeb(val, HDMI_AUD_CTS3); } static void hdmi_set_clock_regenerator_cts(unsigned int cts) { + u8 val; + + /* Must be set/cleared first */ + val = hdmi_readb(HDMI_AUD_CTS3); + val &= ~HDMI_AUD_CTS3_CTS_MANUAL; + hdmi_writeb(val, HDMI_AUD_CTS3); + hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1); hdmi_writeb((cts >> 8) & 0xff, HDMI_AUD_CTS2); hdmi_writeb(((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | @@ -339,6 +400,7 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, static void hdmi_get_pixel_clk(void) { struct ipu_soc *ipu; + unsigned long rate; if (pixel_clk == NULL) { ipu = ipu_get_soc(mxc_hdmi_ipu_id); @@ -349,28 +411,20 @@ static void hdmi_get_pixel_clk(void) } } - pixel_clk_rate = clk_get_rate(pixel_clk); + rate = clk_get_rate(pixel_clk); + if (rate != 0) + pixel_clk_rate = rate; } -/* - * input: audio sample rate and video pixel rate - * output: N and cts written to the HDMI regs. - */ -void hdmi_set_clk_regenerator(void) +static void hdmi_set_clk_regenerator(void) { unsigned int clk_n, clk_cts; - /* Get pixel clock from ipu */ - hdmi_get_pixel_clk(); - - pr_debug("%s: sample rate is %d ; ratio is %d ; pixel clk is %d\n", - __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate); - clk_n = hdmi_compute_n(sample_rate, pixel_clk_rate, hdmi_ratio); clk_cts = hdmi_compute_cts(sample_rate, pixel_clk_rate, hdmi_ratio); if (clk_cts == 0) { - pr_err("%s: pixel clock not supported: %d\n", + pr_debug("%s: pixel clock not supported: %d\n", __func__, (int)pixel_clk_rate); return; } @@ -378,6 +432,10 @@ void hdmi_set_clk_regenerator(void) clk_enable(isfr_clk); clk_enable(iahb_clk); + pr_debug("%s: samplerate=%d ratio=%d pixelclk=%d N=%d cts=%d\n", + __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate, + clk_n, clk_cts); + hdmi_set_clock_regenerator_n(clk_n); hdmi_set_clock_regenerator_cts(clk_cts); @@ -385,6 +443,23 @@ void hdmi_set_clk_regenerator(void) clk_disable(isfr_clk); } +/* Need to run this before phy is enabled the first time to prevent + * overflow condition in HDMI_IH_FC_STAT2 */ +void hdmi_init_clk_regenerator(void) +{ + if (pixel_clk_rate == 0) { + pixel_clk_rate = 74250000; + hdmi_set_clk_regenerator(); + } +} + +void hdmi_clk_regenerator_update_pixel_clock(void) +{ + /* Get pixel clock from ipu */ + hdmi_get_pixel_clk(); + hdmi_set_clk_regenerator(); +} + void hdmi_set_sample_rate(unsigned int rate) { sample_rate = rate; @@ -398,6 +473,10 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) struct resource *res; int ret = 0; +#ifdef DEBUG + overflow_lo = false; + overflow_hi = false; +#endif res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENOENT; @@ -411,7 +490,7 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) pixel_clk = NULL; sample_rate = 48000; - pixel_clk_rate = 74250000; + pixel_clk_rate = 0; hdmi_ratio = 100; irq_enable_cnt = 0; |