diff options
-rw-r--r-- | drivers/mfd/mxc-hdmi-core.c | 109 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_edid.c | 19 | ||||
-rw-r--r-- | drivers/video/mxc_hdmi.c | 1024 | ||||
-rw-r--r-- | include/linux/mfd/mxc-hdmi-core.h | 7 |
4 files changed, 770 insertions, 389 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; diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index 386df9259971..2fa655d345c5 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -138,9 +138,17 @@ const struct fb_videomode mxc_cea_mode[64] = { * pixclock, since for many CEA modes, 2 frequencies are supported * e.g. 640x480 @ 60Hz or 59.94Hz */ -int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1, - const struct fb_videomode *mode2) +int mxc_edid_fb_mode_is_equal(bool use_aspect, + const struct fb_videomode *mode1, + const struct fb_videomode *mode2) { + u32 mask; + + if (use_aspect) + mask = ~0; + else + mask = ~FB_VMODE_ASPECT_MASK; + return (mode1->xres == mode2->xres && mode1->yres == mode2->yres && mode1->hsync_len == mode2->hsync_len && @@ -150,7 +158,7 @@ int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1, mode1->upper_margin == mode2->upper_margin && mode1->lower_margin == mode2->lower_margin && mode1->sync == mode2->sync && - mode1->vmode == mode2->vmode); + (mode1->vmode & mask) == (mode2->vmode & mask)); } static void get_detailed_timing(unsigned char *block, @@ -450,7 +458,7 @@ int mxc_edid_var_to_vic(struct fb_var_screeninfo *var) for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { fb_var_to_videomode(&m, var); - if (mxc_edid_fb_mode_is_equal(&m, &mxc_cea_mode[i])) + if (mxc_edid_fb_mode_is_equal(false, &m, &mxc_cea_mode[i])) break; } @@ -465,9 +473,10 @@ EXPORT_SYMBOL(mxc_edid_var_to_vic); int mxc_edid_mode_to_vic(const struct fb_videomode *mode) { int i; + bool use_aspect = (mode->vmode & FB_VMODE_ASPECT_MASK); for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { - if (mxc_edid_fb_mode_is_equal(mode, &mxc_cea_mode[i])) + if (mxc_edid_fb_mode_is_equal(use_aspect, mode, &mxc_cea_mode[i])) break; } diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 0d1641f7858f..e217a014e74f 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -62,8 +62,11 @@ #define DISPDRV_HDMI "hdmi" #define HDMI_EDID_LEN 512 -#define TRUE 1 -#define FALSE 0 +/* status codes for reading edid */ +#define HDMI_EDID_SUCCESS 0 +#define HDMI_EDID_FAIL -1 +#define HDMI_EDID_SAME -2 +#define HDMI_EDID_NO_MODES -3 #define NUM_CEA_VIDEO_MODES 64 #define DEFAULT_VIDEO_MODE 16 /* 1080P */ @@ -74,6 +77,25 @@ #define YCBCR422_8BITS 3 #define XVYCC444 4 +/* + * We follow a flowchart which is in the "Synopsys DesignWare Courses + * HDMI Transmitter Controller User Guide, 1.30a", section 3.1 + * (dwc_hdmi_tx_user.pdf) + * + * Below are notes that say "HDMI Initialization Step X" + * These correspond to the flowchart. + */ + +/* + * We are required to configure VGA mode before reading edid + * in HDMI Initialization Step B + */ +static const struct fb_videomode vga_mode = { + /* 640x480 @ 60 Hz, 31.5 kHz hsync */ + NULL, 60, 640, 480, 39721, 48, 16, 33, 10, 96, 2, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0, +}; + enum hdmi_datamap { RGB444_8B = 0x01, RGB444_10B = 0x03, @@ -94,11 +116,12 @@ enum hdmi_colorimetry { }; struct hdmi_vmode { - unsigned int mHdmiDviSel; - unsigned int mHSyncPolarity; - unsigned int mVSyncPolarity; - unsigned int mInterlaced; - unsigned int mDataEnablePolarity; + bool mDVI; + bool mHSyncPolarity; + bool mVSyncPolarity; + bool mInterlaced; + bool mDataEnablePolarity; + unsigned int mPixelClock; unsigned int mPixelRepetitionInput; unsigned int mPixelRepetitionOutput; @@ -121,7 +144,7 @@ struct mxc_hdmi { struct fb_info *fbi; struct clk *hdmi_isfr_clk; struct clk *hdmi_iahb_clk; - struct delayed_work det_work; + struct delayed_work hotplug_work; struct notifier_block nb; struct hdmi_data_info hdmi_data; @@ -129,17 +152,36 @@ struct mxc_hdmi { struct mxc_edid_cfg edid_cfg; u8 edid[HDMI_EDID_LEN]; bool fb_reg; - bool need_mode_change; bool cable_plugin; + bool dft_mode_set; + char *dft_mode_str; + int default_bpp; u8 latest_intr_stat; bool irq_enabled; spinlock_t irq_lock; + bool phy_enabled; + struct fb_videomode previous_mode; + struct fb_videomode previous_non_vga_mode; + bool requesting_vga_for_initialization; }; struct i2c_client *hdmi_i2c; extern const struct fb_videomode mxc_cea_mode[64]; +#ifdef DEBUG +static void dump_fb_videomode(struct fb_videomode *m) +{ + pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + m->refresh, m->xres, m->yres, m->pixclock, m->left_margin, + m->right_margin, m->upper_margin, m->lower_margin, + m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); +} +#else +static void dump_fb_videomode(struct fb_videomode *m) +{} +#endif + /*! * this submodule is responsible for the video data synchronization. * for example, for RGB 4:4:4 input, the data map is defined as @@ -147,7 +189,7 @@ extern const struct fb_videomode mxc_cea_mode[64]; * pin{31~24} <==> G[7:0] * pin{15~8} <==> B[7:0] */ -void hdmi_video_sample(struct mxc_hdmi *hdmi) +static void hdmi_video_sample(struct mxc_hdmi *hdmi) { int color_format = 0; u8 val; @@ -206,28 +248,27 @@ void hdmi_video_sample(struct mxc_hdmi *hdmi) static int isColorSpaceConversion(struct mxc_hdmi *hdmi) { return (hdmi->hdmi_data.enc_in_format != - hdmi->hdmi_data.enc_out_format) ? TRUE : FALSE; + hdmi->hdmi_data.enc_out_format); } static int isColorSpaceDecimation(struct mxc_hdmi *hdmi) { return ((hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) && (hdmi->hdmi_data.enc_in_format == RGB || - hdmi->hdmi_data.enc_in_format == XVYCC444)) ? TRUE : FALSE; + hdmi->hdmi_data.enc_in_format == XVYCC444)); } static int isColorSpaceInterpolation(struct mxc_hdmi *hdmi) { return ((hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) && - (hdmi->hdmi_data.enc_out_format == RGB - || hdmi->hdmi_data.enc_out_format == XVYCC444)) ? - TRUE : FALSE; + (hdmi->hdmi_data.enc_out_format == RGB + || hdmi->hdmi_data.enc_out_format == XVYCC444)); } /*! * update the color space conversion coefficients. */ -void update_csc_coeffs(struct mxc_hdmi *hdmi) +static void update_csc_coeffs(struct mxc_hdmi *hdmi) { unsigned short csc_coeff[3][4]; unsigned int csc_scale = 1; @@ -391,7 +432,7 @@ void update_csc_coeffs(struct mxc_hdmi *hdmi) hdmi_writeb(val, HDMI_CSC_SCALE); } -void hdmi_video_csc(struct mxc_hdmi *hdmi) +static void hdmi_video_csc(struct mxc_hdmi *hdmi) { int color_depth = 0; int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; @@ -430,7 +471,7 @@ void hdmi_video_csc(struct mxc_hdmi *hdmi) * for example, if input is YCC422 mode or repeater is used, * data should be repacked this module can be bypassed. */ -void hdmi_video_packetize(struct mxc_hdmi *hdmi) +static void hdmi_video_packetize(struct mxc_hdmi *hdmi) { unsigned int color_depth = 0; unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; @@ -548,11 +589,15 @@ void hdmi_video_packetize(struct mxc_hdmi *hdmi) hdmi_writeb(val, HDMI_VP_CONF); } -void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force) +#if 0 +/* Force a fixed color screen */ +static void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force) { u8 val; - if (force == TRUE) { + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + if (force) { hdmi_writeb(0x00, HDMI_FC_DBGTMDS2); /* R */ hdmi_writeb(0x00, HDMI_FC_DBGTMDS1); /* G */ hdmi_writeb(0xFF, HDMI_FC_DBGTMDS0); /* B */ @@ -568,6 +613,7 @@ void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force) hdmi_writeb(0x00, HDMI_FC_DBGTMDS0); /* B */ } } +#endif static inline void hdmi_phy_test_clear(struct mxc_hdmi *hdmi, unsigned char bit) @@ -611,21 +657,21 @@ static inline void hdmi_phy_test_dout(struct mxc_hdmi *hdmi, hdmi_writeb(bit, HDMI_PHY_TST2); } -int hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec) +static bool hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec) { unsigned char val = 0; val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3; while (val == 0) { udelay(1000); if (msec-- == 0) - return FALSE; + return false; val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3; } - return TRUE; + return true; } -int hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data, - unsigned char addr) +static void hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data, + unsigned char addr) { hdmi_writeb(0xFF, HDMI_IH_I2CMPHY_STAT0); hdmi_writeb(addr, HDMI_PHY_I2CM_ADDRESS_ADDR); @@ -636,10 +682,11 @@ int hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data, hdmi_writeb(HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, HDMI_PHY_I2CM_OPERATION_ADDR); hdmi_phy_wait_i2c_done(hdmi, 1000); - return TRUE; } -unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, unsigned char addr) +#if 0 +static unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, + unsigned char addr) { unsigned short data; unsigned char msb = 0, lsb = 0; @@ -654,31 +701,84 @@ unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, unsigned char addr) return data; } -int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data, - unsigned char addr) +static int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data, + unsigned char addr) { unsigned short val = 0; hdmi_phy_i2c_write(hdmi, data, addr); val = hdmi_phy_i2c_read(hdmi, addr); - if (val != data) - return FALSE; - return TRUE; + return (val == data); +} +#endif + +/* "Power-down enable (active low)" + * That mean that power up == 1! */ +static void mxc_hdmi_phy_enable_power(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_PDZ_OFFSET, + HDMI_PHY_CONF0_PDZ_MASK); +} + +static void mxc_hdmi_phy_enable_tmds(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_ENTMDS_OFFSET, + HDMI_PHY_CONF0_ENTMDS_MASK); } -int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, - unsigned char cRes, int cscOn, int audioOn, - int cecOn, int hdcpOn) +static void mxc_hdmi_phy_gen2_pddq(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET, + HDMI_PHY_CONF0_GEN2_PDDQ_MASK); +} + +static void mxc_hdmi_phy_gen2_txpwron(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET, + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); +} + +#if 0 +static void mxc_hdmi_phy_gen2_enhpdrxsense(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK); +} +#endif + +static void mxc_hdmi_phy_sel_data_en_pol(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_SELDATAENPOL_OFFSET, + HDMI_PHY_CONF0_SELDATAENPOL_MASK); +} + +static void mxc_hdmi_phy_sel_interface_control(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_SELDIPIF_OFFSET, + HDMI_PHY_CONF0_SELDIPIF_MASK); +} + +static int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, + unsigned char cRes, int cscOn) { u8 val; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + /* color resolution 0 is 8 bit colour depth */ if (cRes == 0) cRes = 8; if (pRep != 0) - return FALSE; + return false; else if (cRes != 8 && cRes != 12) - return FALSE; + return false; if (cscOn) val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH; @@ -687,39 +787,17 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_writeb(val, HDMI_MC_FLOWCTRL); - /* clock gate == 0 => turn on modules */ - val = hdcpOn ? HDMI_MC_CLKDIS_HDCPCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_HDCPCLK_DISABLE_DISABLE; - val |= HDMI_MC_CLKDIS_PIXELCLK_DISABLE_ENABLE; - val |= HDMI_MC_CLKDIS_TMDSCLK_DISABLE_ENABLE; - val |= (pRep > 0) ? HDMI_MC_CLKDIS_PREPCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_PREPCLK_DISABLE_DISABLE; - val |= cecOn ? HDMI_MC_CLKDIS_CECCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_CECCLK_DISABLE_DISABLE; - val |= cscOn ? HDMI_MC_CLKDIS_CSCCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_CSCCLK_DISABLE_DISABLE; - val |= audioOn ? HDMI_MC_CLKDIS_AUDCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_AUDCLK_DISABLE_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - /* gen2 tx power off */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; - val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF; - hdmi_writeb(val, HDMI_PHY_CONF0); + mxc_hdmi_phy_gen2_txpwron(0); /* gen2 pddq */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; - val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE; - hdmi_writeb(val, HDMI_PHY_CONF0); + mxc_hdmi_phy_gen2_pddq(1); /* PHY reset */ hdmi_writeb(HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); hdmi_writeb(HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); - hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT, - HDMI_MC_HEACPHY_RST); + hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); hdmi_phy_test_clear(hdmi, 1); hdmi_writeb(HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, @@ -729,7 +807,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, if (hdmi->hdmi_data.video_mode.mPixelClock < 0) { dev_dbg(&hdmi->pdev->dev, "Pixel clock (%d) must be positive\n", hdmi->hdmi_data.video_mode.mPixelClock); - return FALSE; + return false; } if (hdmi->hdmi_data.video_mode.mPixelClock <= 45250000) { @@ -748,7 +826,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 92500000) { switch (cRes) { @@ -764,7 +842,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x4142, 0x06); hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 148500000) { switch (cRes) { @@ -780,7 +858,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x40a2, 0x06); hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); default: - return FALSE; + return false; } } else { switch (cRes) { @@ -796,7 +874,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x4002, 0x06); hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); default: - return FALSE; + return false; } } @@ -812,7 +890,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 58400000) { switch (cRes) { @@ -826,7 +904,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 72000000) { switch (cRes) { @@ -840,7 +918,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 74250000) { switch (cRes) { @@ -854,7 +932,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 118800000) { switch (cRes) { @@ -868,7 +946,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 216000000) { switch (cRes) { @@ -882,13 +960,13 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); break; default: - return FALSE; + return false; } } else { dev_err(&hdmi->pdev->dev, "Pixel clock %d - unsupported by HDMI\n", hdmi->hdmi_data.video_mode.mPixelClock); - return FALSE; + return false; } hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ @@ -907,50 +985,50 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x0129, 0x0E); } - /* gen2 tx power on */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; - val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON; - hdmi_writeb(val, HDMI_PHY_CONF0); + mxc_hdmi_phy_enable_power(1); - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; - val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE; - hdmi_writeb(val, HDMI_PHY_CONF0); + /* toggle TMDS enable */ + mxc_hdmi_phy_enable_tmds(0); + mxc_hdmi_phy_enable_tmds(1); + + /* gen2 tx power on */ + mxc_hdmi_phy_gen2_txpwron(1); + mxc_hdmi_phy_gen2_pddq(0); udelay(1000); - if ((hdmi_readb(HDMI_PHY_STAT0) & 0x01) == 0) - return FALSE; + /* wait for the phy PLL to lock */ + while ((hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK) == 0) + ; + + /* Has the PHY PLL locked? */ + if ((hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK) == 0) + return false; - return TRUE; + return true; } -void hdmi_phy_init(struct mxc_hdmi *hdmi, unsigned char de) +static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) { - u8 val; + int i; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* HDMI Phy spec says to do the phy initialization sequence twice */ + for (i = 0 ; i < 2 ; i++) { + mxc_hdmi_phy_sel_data_en_pol(1); + mxc_hdmi_phy_sel_interface_control(0); + mxc_hdmi_phy_enable_tmds(0); + mxc_hdmi_phy_enable_power(0); - /* set the DE polarity */ - val = (de << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET) & - HDMI_PHY_CONF0_SELDATAENPOL_MASK; - /* set ENHPDRXSENSE to 1 */ - val |= HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE; - /* set the interface control to 0 */ - val |= (0 << HDMI_PHY_CONF0_SELDIPIF_OFFSET) & - HDMI_PHY_CONF0_SELDIPIF_MASK; - /* enable TMDS output */ - val |= (1 << HDMI_PHY_CONF0_ENTMDS_OFFSET) & - HDMI_PHY_CONF0_ENTMDS_MASK; - /* PHY power enable */ - val |= (1 << HDMI_PHY_CONF0_PDZ_OFFSET) & - HDMI_PHY_CONF0_PDZ_MASK; - hdmi_writeb(val, HDMI_PHY_CONF0); - - /* TODO: Enable CSC */ - hdmi_phy_configure(hdmi, 0, 8, FALSE, TRUE, TRUE, FALSE); + /* TODO: Enable CSC */ + hdmi_phy_configure(hdmi, 0, 8, false); + } + + hdmi->phy_enabled = true; } -void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) +static void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) { u8 de, val; @@ -976,20 +1054,6 @@ void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) hdmi_writeb(val, HDMI_A_HDCPCFG1); } -void preamble_filter_set(struct mxc_hdmi *hdmi, unsigned char value, - unsigned char channel) -{ - if (channel == 0) - hdmi_writeb(value, HDMI_FC_CH0PREAM); - else if (channel == 1) - hdmi_writeb(value, HDMI_FC_CH1PREAM); - else if (channel == 2) - hdmi_writeb(value, HDMI_FC_CH2PREAM); - else - - return; -} - static void hdmi_config_AVI(struct mxc_hdmi *hdmi) { u8 val; @@ -1117,23 +1181,21 @@ static void hdmi_config_AVI(struct mxc_hdmi *hdmi) /*! * this submodule is responsible for the video/audio data composition. */ -void hdmi_av_composer(struct mxc_hdmi *hdmi) +static void hdmi_av_composer(struct mxc_hdmi *hdmi) { - unsigned char i = 0; - u8 val; + u8 inv_val; struct fb_info *fbi = hdmi->fbi; struct fb_videomode fb_mode; struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; int hblank, vblank; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + fb_var_to_videomode(&fb_mode, &fbi->var); - vmode->mHSyncPolarity = - (fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) ? TRUE : FALSE; - vmode->mVSyncPolarity = - (fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) ? TRUE : FALSE; - vmode->mInterlaced = - (fb_mode.vmode & FB_VMODE_INTERLACED) ? TRUE : FALSE; + vmode->mHSyncPolarity = ((fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) != 0); + vmode->mVSyncPolarity = ((fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) != 0); + vmode->mInterlaced = ((fb_mode.vmode & FB_VMODE_INTERLACED) != 0); vmode->mPixelClock = (fb_mode.xres + fb_mode.left_margin + fb_mode.right_margin + fb_mode.hsync_len) * (fb_mode.yres + fb_mode.upper_margin + fb_mode.lower_margin + @@ -1142,47 +1204,52 @@ void hdmi_av_composer(struct mxc_hdmi *hdmi) dev_dbg(&hdmi->pdev->dev, "final pixclk = %d\n", vmode->mPixelClock); /* Set up HDMI_FC_INVIDCONF */ - val = ((hdmi->hdmi_data.hdcp_enable == TRUE) ? + inv_val = (hdmi->hdmi_data.hdcp_enable ? HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); - val |= ((vmode->mVSyncPolarity == TRUE) ? + + inv_val |= (vmode->mVSyncPolarity ? HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW); - val |= ((vmode->mHSyncPolarity == TRUE) ? + + inv_val |= (vmode->mHSyncPolarity ? HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW); - val |= ((vmode->mDataEnablePolarity == TRUE) ? + + inv_val |= (vmode->mDataEnablePolarity ? HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); - val |= ((vmode->mHdmiDviSel == TRUE) ? - HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE : - HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE); + if (hdmi->vic == 39) - val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; + inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; else - val |= ((vmode->mInterlaced == TRUE) ? + inv_val |= (vmode->mInterlaced ? HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH : HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW); - val |= ((vmode->mInterlaced == TRUE) ? + + inv_val |= (vmode->mInterlaced ? HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE); - hdmi_writeb(val, HDMI_FC_INVIDCONF); + + inv_val |= (vmode->mDVI ? + HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE : + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE); + + hdmi_writeb(inv_val, HDMI_FC_INVIDCONF); /* Set up horizontal active pixel region width */ - hdmi_writeb(fb_mode.xres, - HDMI_FC_INHACTV0); - hdmi_writeb(fb_mode.xres >> 8, - HDMI_FC_INHACTV1); + hdmi_writeb(fb_mode.xres >> 8, HDMI_FC_INHACTV1); + hdmi_writeb(fb_mode.xres, HDMI_FC_INHACTV0); + + /* Set up vertical blanking pixel region width */ + hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1); + hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0); /* Set up horizontal blanking pixel region width */ hblank = fb_mode.left_margin + fb_mode.right_margin + fb_mode.hsync_len; - hdmi_writeb(hblank, HDMI_FC_INHBLANK0); hdmi_writeb(hblank >> 8, HDMI_FC_INHBLANK1); - - /* Set up vertical blanking pixel region width */ - hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0); - hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1); + hdmi_writeb(hblank, HDMI_FC_INHBLANK0); /* Set up vertical blanking pixel region width */ vblank = fb_mode.upper_margin + fb_mode.lower_margin + @@ -1190,117 +1257,250 @@ void hdmi_av_composer(struct mxc_hdmi *hdmi) hdmi_writeb(vblank, HDMI_FC_INVBLANK); /* Set up HSYNC active edge delay width (in pixel clks) */ - hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0); hdmi_writeb(fb_mode.right_margin >> 8, HDMI_FC_HSYNCINDELAY1); - - /* Set up HSYNC active pulse width (in pixel clks) */ - hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0); - hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); + hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0); /* Set up VSYNC active edge delay (in pixel clks) */ hdmi_writeb(fb_mode.lower_margin, HDMI_FC_VSYNCINDELAY); + /* Set up HSYNC active pulse width (in pixel clks) */ + hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); + hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0); + /* Set up VSYNC active edge delay (in pixel clks) */ hdmi_writeb(fb_mode.vsync_len, HDMI_FC_VSYNCINWIDTH); - /* control period minimum duration */ - hdmi_writeb(12, HDMI_FC_CTRLDUR); - hdmi_writeb(32, HDMI_FC_EXCTRLDUR); - hdmi_writeb(1, HDMI_FC_EXCTRLSPAC); - - for (i = 0; i < 3; i++) - preamble_filter_set(hdmi, (i + 1) * 11, i); - - /* configure AVI InfoFrame */ - hdmi_config_AVI(hdmi); + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); } -static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi, - struct fb_info *fbi) +static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) { int ret; u8 edid_old[HDMI_EDID_LEN]; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + /* save old edid */ memcpy(edid_old, hdmi->edid, HDMI_EDID_LEN); - /* edid reading */ - ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, - hdmi->edid, &hdmi->edid_cfg, fbi); + ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, hdmi->edid, + &hdmi->edid_cfg, hdmi->fbi); if (ret < 0) - return ret; + return HDMI_EDID_FAIL; - if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) - ret = -2; - return ret; + if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) { + dev_info(&hdmi->pdev->dev, "same edid\n"); + return HDMI_EDID_SAME; + } + + if (hdmi->fbi->monspecs.modedb_len == 0) { + dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); + return HDMI_EDID_NO_MODES; + } + + return HDMI_EDID_SUCCESS; } -static void mxc_hdmi_poweron(struct mxc_hdmi *hdmi) +static void mxc_hdmi_enable_pins(struct mxc_hdmi *hdmi) { struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; - dev_dbg(&hdmi->pdev->dev, "power on\n"); + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); /* Enable pins to HDMI */ if (plat->enable_pins) plat->enable_pins(); } -static void mxc_hdmi_poweroff(struct mxc_hdmi *hdmi) +static void mxc_hdmi_disable_pins(struct mxc_hdmi *hdmi) { struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; - dev_dbg(&hdmi->pdev->dev, "power off\n"); + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); /* Disable pins to HDMI */ if (plat->disable_pins) plat->disable_pins(); } -static void mxc_hdmi_enable(struct mxc_hdmi *hdmi) +static void mxc_hdmi_phy_disable(struct mxc_hdmi *hdmi) { - u8 val; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - dev_dbg(&hdmi->pdev->dev, "hdmi enable\n"); + if (!hdmi->phy_enabled) + return; - clk_enable(hdmi->hdmi_iahb_clk); + mxc_hdmi_phy_enable_tmds(0); + mxc_hdmi_phy_enable_power(0); - /* Enable HDMI PHY - Set PDDQ=0 and TXPWRON=1 */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK | - HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); - val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE | - HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON; - hdmi_writeb(val, HDMI_PHY_CONF0); - - if (hdmi->need_mode_change && hdmi->fb_reg) { - dev_dbg(&hdmi->pdev->dev, "HDMI changing FB mode\n"); - hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE; - console_lock(); - hdmi->fbi->flags |= FBINFO_MISC_USEREVENT; - fb_set_var(hdmi->fbi, &hdmi->fbi->var); - hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT; - console_unlock(); - hdmi->need_mode_change = false; - } + hdmi->phy_enabled = false; + dev_dbg(&hdmi->pdev->dev, "%s - exit\n", __func__); +} + +/* HDMI Initialization Step B.4 */ +static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi) +{ + u8 clkdis; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* control period minimum duration */ + hdmi_writeb(12, HDMI_FC_CTRLDUR); + hdmi_writeb(32, HDMI_FC_EXCTRLDUR); + hdmi_writeb(1, HDMI_FC_EXCTRLSPAC); + + /* Set to fill TMDS data channels */ + hdmi_writeb(0x0B, HDMI_FC_CH0PREAM); + hdmi_writeb(0x16, HDMI_FC_CH1PREAM); + hdmi_writeb(0x21, HDMI_FC_CH2PREAM); + + /* Enable pixel clock and tmds data path */ + clkdis = 0x7F; + clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); + + clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); +} + +static void hdmi_enable_audio_clk(struct mxc_hdmi *hdmi) +{ + u8 clkdis; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + clkdis = hdmi_readb(HDMI_MC_CLKDIS); + clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); } -static void mxc_hdmi_disable(struct mxc_hdmi *hdmi) +/* Workaround to clear the overflow condition */ +static void mxc_hdmi_clear_overflow(void) { + int count; u8 val; - dev_dbg(&hdmi->pdev->dev, "hdmi disable\n"); + val = hdmi_readb(HDMI_FC_INVIDCONF); - /* Disable HDMI PHY - Set PDDQ=1 and TXPWRON=0 */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK | - HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); - val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE | - HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF; - hdmi_writeb(val, HDMI_PHY_CONF0); + for (count = 0 ; count < 5 ; count++) + hdmi_writeb(val, HDMI_FC_INVIDCONF); - clk_disable(hdmi->hdmi_iahb_clk); + /* TMDS software reset */ + hdmi_writeb((u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); +} + +static void hdmi_enable_overflow_interrupts(void) +{ + pr_debug("%s\n", __func__); + hdmi_writeb(0, HDMI_FC_MASK2); + hdmi_writeb(0, HDMI_IH_MUTE_FC_STAT2); +} + +static void hdmi_disable_overflow_interrupts(void) +{ + pr_debug("%s\n", __func__); + hdmi_writeb(HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, + HDMI_IH_MUTE_FC_STAT2); +} + +static void mxc_hdmi_notify_fb(struct mxc_hdmi *hdmi) +{ + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* Don't notify if we aren't registered yet */ + WARN_ON(!hdmi->fb_reg); + + /* disable the phy before ipu changes mode */ + mxc_hdmi_phy_disable(hdmi); + + /* + * Note that fb_set_var will block. During this time, + * FB_EVENT_MODE_CHANGE callback will happen. + * So by the end of this function, mxc_hdmi_setup() + * will be done. + */ + hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + hdmi->fbi->flags |= FBINFO_MISC_USEREVENT; + fb_set_var(hdmi->fbi, &hdmi->fbi->var); + hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); +} + +static void mxc_hdmi_set_mode_to_previous(struct mxc_hdmi *hdmi) +{ + const struct fb_videomode *mode; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + mode = fb_find_nearest_mode(&hdmi->previous_non_vga_mode, + &hdmi->fbi->modelist); + if (mode) { + fb_videomode_to_var(&hdmi->fbi->var, mode); + mxc_hdmi_notify_fb(hdmi); + } else + pr_err("%s: could not find mode in modelist\n", __func__); +} + +static void mxc_hdmi_edid_rebuild_modelist(struct mxc_hdmi *hdmi) +{ + int i; + const struct fb_videomode *mode; + struct fb_videomode m; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + console_lock(); + + fb_destroy_modelist(&hdmi->fbi->modelist); + fb_add_videomode(&vga_mode, &hdmi->fbi->modelist); + + for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { + /* + * We might check here if mode is supported by HDMI. + * We do not currently support interlaced modes + */ + if (!(hdmi->fbi->monspecs.modedb[i].vmode & + FB_VMODE_INTERLACED)) { + dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); + dev_dbg(&hdmi->pdev->dev, + "xres = %d, yres = %d, freq = %d\n", + hdmi->fbi->monspecs.modedb[i].xres, + hdmi->fbi->monspecs.modedb[i].yres, + hdmi->fbi->monspecs.modedb[i].refresh); + + fb_add_videomode(&hdmi->fbi->monspecs.modedb[i], + &hdmi->fbi->modelist); + } + } + + console_unlock(); + + /* Set the default mode only once. */ + if (!hdmi->dft_mode_set) { + dev_dbg(&hdmi->pdev->dev, "%s: setting to default=%s bpp=%d\n", + __func__, hdmi->dft_mode_str, hdmi->default_bpp); + + fb_find_mode(&hdmi->fbi->var, hdmi->fbi, + hdmi->dft_mode_str, NULL, 0, NULL, + hdmi->default_bpp); + + hdmi->dft_mode_set = true; + } else + mxc_hdmi_set_mode_to_previous(hdmi); + + fb_var_to_videomode(&m, &hdmi->fbi->var); + dump_fb_videomode(&m); + mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + if (mode) { + fb_videomode_to_var(&hdmi->fbi->var, mode); + dump_fb_videomode((struct fb_videomode *)mode); + mxc_hdmi_notify_fb(hdmi); + } else + pr_err("%s: could not find mode in modelist\n", __func__); } static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) @@ -1308,89 +1508,103 @@ static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) u32 i; const struct fb_videomode *mode; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* If not EDID data read, set up default modelist */ + dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); + + /* Set the default mode only once. */ + if (!hdmi->dft_mode_set) { + dev_dbg(&hdmi->pdev->dev, "%s: setting to default=%s bpp=%d\n", + __func__, hdmi->dft_mode_str, hdmi->default_bpp); + + fb_find_mode(&hdmi->fbi->var, hdmi->fbi, + hdmi->dft_mode_str, NULL, 0, NULL, + hdmi->default_bpp); + + hdmi->dft_mode_set = true; + } else { + fb_videomode_to_var(&hdmi->fbi->var, &hdmi->previous_non_vga_mode); + } + + console_lock(); + fb_destroy_modelist(&hdmi->fbi->modelist); for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { mode = &mxc_cea_mode[i]; if ((mode->xres == hdmi->fbi->var.xres) && - (mode->yres == hdmi->fbi->var.yres) && - !(mode->vmode & FB_VMODE_INTERLACED)) - fb_add_videomode(mode, &hdmi->fbi->modelist); + (mode->yres == hdmi->fbi->var.yres) && + !(mode->vmode & FB_VMODE_INTERLACED)) + fb_add_videomode(mode, &hdmi->fbi->modelist); } + + console_unlock(); + + mxc_hdmi_notify_fb(hdmi); } -static int mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) +static void mxc_hdmi_set_mode_to_vga_dvi(struct mxc_hdmi *hdmi) { - int ret; - struct fb_videomode m; - const struct fb_videomode *mode; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - dev_dbg(&hdmi->pdev->dev, "cable connected\n"); + hdmi_disable_overflow_interrupts(); - hdmi->cable_plugin = true; + fb_videomode_to_var(&hdmi->fbi->var, &vga_mode); - /* edid read */ - ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi); - if (ret == -2) - dev_info(&hdmi->pdev->dev, "same edid\n"); - else if (hdmi->fbi->monspecs.modedb_len > 0) { - int i; + hdmi->requesting_vga_for_initialization = true; + mxc_hdmi_notify_fb(hdmi); + hdmi->requesting_vga_for_initialization = false; +} - fb_destroy_modelist(&hdmi->fbi->modelist); +static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) +{ + int edid_status; - for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { - /* - * We might check here if mode is supported by HDMI. - * We do not currently support interlaced modes - */ - if (!(hdmi->fbi->monspecs.modedb[i].vmode - & FB_VMODE_INTERLACED)) { - dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); - dev_dbg(&hdmi->pdev->dev, - "xres = %d, yres = %d, freq = %d\n", - hdmi->fbi->monspecs.modedb[i].xres, - hdmi->fbi->monspecs.modedb[i].yres, - hdmi->fbi->monspecs.modedb[i].refresh); - fb_add_videomode(&hdmi->fbi->monspecs.modedb[i], - &hdmi->fbi->modelist); - } - } + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - if (mode) - fb_videomode_to_var(&hdmi->fbi->var, mode); + hdmi->cable_plugin = true; - hdmi->need_mode_change = mode ? true : false; - } else { - /* If not EDID data readed, setup default modelist */ - dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); - mxc_hdmi_default_modelist(hdmi); + /* HDMI Initialization Step B */ + mxc_hdmi_set_mode_to_vga_dvi(hdmi); - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - if (mode) - fb_videomode_to_var(&hdmi->fbi->var, mode); + /* HDMI Initialization Step C */ + edid_status = mxc_hdmi_read_edid(hdmi); - hdmi->need_mode_change = mode ? true : false; - } + /* HDMI Initialization Steps D, E, F */ + switch (edid_status) { + case HDMI_EDID_SUCCESS: + mxc_hdmi_edid_rebuild_modelist(hdmi); + break; + case HDMI_EDID_SAME: + mxc_hdmi_set_mode_to_previous(hdmi); + break; - return 0; + case HDMI_EDID_NO_MODES: + case HDMI_EDID_FAIL: + default: + mxc_hdmi_default_modelist(hdmi); + break; + } + + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); } static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) { + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + hdmi_disable_overflow_interrupts(); + hdmi->cable_plugin = false; } -static void det_worker(struct work_struct *work) +static void hotplug_worker(struct work_struct *work) { struct delayed_work *delay_work = to_delayed_work(work); struct mxc_hdmi *hdmi = - container_of(delay_work, struct mxc_hdmi, det_work); + container_of(delay_work, struct mxc_hdmi, hotplug_work); u32 phy_int_stat, phy_int_pol, phy_int_mask; u8 val; bool hdmi_disable = false; @@ -1398,16 +1612,17 @@ static void det_worker(struct work_struct *work) unsigned long flags; if (!hdmi->irq_enabled) { + /* Enable clock long enough to do a few register accesses */ clk_enable(hdmi->hdmi_iahb_clk); - /* Capture status - used in det_worker ISR */ + /* Capture status - used in hotplug_worker ISR */ phy_int_stat = hdmi_readb(HDMI_IH_PHY_STAT0); if ((phy_int_stat & HDMI_IH_PHY_STAT0_HPD) == 0) { clk_disable(hdmi->hdmi_iahb_clk); return; /* No interrupts to handle */ } - dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); + dev_dbg(&hdmi->pdev->dev, "\nHotplug interrupt received\n"); /* Unmask interrupts until handled */ val = hdmi_readb(HDMI_PHY_MASK0); @@ -1433,15 +1648,22 @@ static void det_worker(struct work_struct *work) if (phy_int_stat & HDMI_IH_PHY_STAT0_HPD) { /* cable connection changes */ if (phy_int_pol & HDMI_PHY_HPD) { + /* + * Plugin event = assume that iahb clock was disabled. + */ dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n"); + + clk_enable(hdmi->hdmi_iahb_clk); mxc_hdmi_cable_connected(hdmi); - mxc_hdmi_enable(hdmi); /* Make HPD intr active low to capture unplug event */ val = hdmi_readb(HDMI_PHY_POL0); val &= ~HDMI_PHY_HPD; hdmi_writeb(val, HDMI_PHY_POL0); } else if (!(phy_int_pol & HDMI_PHY_HPD)) { + /* + * Plugout event = assume that iahb clock was enabled. + */ dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n"); mxc_hdmi_cable_disconnected(hdmi); hdmi_disable = true; @@ -1463,8 +1685,14 @@ static void det_worker(struct work_struct *work) phy_int_mask &= ~HDMI_PHY_HPD; hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0); - if (hdmi_disable) - mxc_hdmi_disable(hdmi); + if (hdmi_readb(HDMI_FC_INT2) & HDMI_FC_INT2_OVERFLOW_MASK) + mxc_hdmi_clear_overflow(); + + /* We keep the iahb clock enabled only if we are plugged in. */ + if (hdmi_disable) { + mxc_hdmi_phy_disable(hdmi); + clk_disable(hdmi->hdmi_iahb_clk); + } spin_unlock_irqrestore(&hdmi->irq_lock, flags); } @@ -1488,7 +1716,19 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) */ ret = hdmi_irq_disable(irq); if (ret == IRQ_DISABLE_FAIL) { - /* Capture status - used in det_worker ISR */ + if (hdmi_readb(HDMI_FC_INT2) & HDMI_FC_INT2_OVERFLOW_MASK) { + mxc_hdmi_clear_overflow(); + + /* clear irq status */ + hdmi_writeb(HDMI_IH_FC_STAT2_OVERFLOW_MASK, + HDMI_IH_FC_STAT2); + } + + /* + * We could not disable the irq. Probably the audio driver + * has enabled it. That also means that iahb clk is enabled. + */ + /* Capture status - used in hotplug_worker ISR */ intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0); if ((intr_stat & HDMI_IH_PHY_STAT0_HPD) == 0) { hdmi_irq_enable(irq); @@ -1510,31 +1750,59 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) } else hdmi->irq_enabled = false; - schedule_delayed_work(&(hdmi->det_work), msecs_to_jiffies(20)); + schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20)); spin_unlock_irqrestore(&hdmi->irq_lock, flags); return IRQ_HANDLED; } -static int mxc_hdmi_setup(struct mxc_hdmi *hdmi) +static void mxc_hdmi_setup(struct mxc_hdmi *hdmi) { struct fb_videomode m; const struct fb_videomode *edid_mode; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + fb_var_to_videomode(&m, &hdmi->fbi->var); - if (!list_empty(&hdmi->fbi->modelist)) { - edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + dump_fb_videomode(&m); - hdmi->vic = mxc_edid_mode_to_vic(edid_mode); - } else - hdmi->vic = 0; + /* Exit the setup if we are already set to this video mode */ + if (fb_mode_is_equal(&hdmi->previous_mode, &m)) { + dev_dbg(&hdmi->pdev->dev, + "%s video mode did not change.\n", __func__); + mxc_hdmi_phy_init(hdmi); + return; + } + dev_dbg(&hdmi->pdev->dev, "%s - video mode changed\n", __func__); + + hdmi_disable_overflow_interrupts(); + + /* Save mode as 'previous_mode' so that we can know if mode changed. */ + memcpy(&hdmi->previous_mode, &m, sizeof(struct fb_videomode)); + + hdmi->vic = 0; + if (!hdmi->requesting_vga_for_initialization) { + /* Save mode if this isn't the result of requesting + * vga default. */ + memcpy(&hdmi->previous_non_vga_mode, &m, + sizeof(struct fb_videomode)); + + if (!list_empty(&hdmi->fbi->modelist)) { + edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + pr_debug("edid mode "); + dump_fb_videomode((struct fb_videomode *)edid_mode); + hdmi->vic = mxc_edid_mode_to_vic(edid_mode); + } + } if (hdmi->vic == 0) { dev_dbg(&hdmi->pdev->dev, "Non-CEA mode used in HDMI\n"); - hdmi->hdmi_data.video_mode.mHdmiDviSel = FALSE; - } else - hdmi->hdmi_data.video_mode.mHdmiDviSel = TRUE; + hdmi->hdmi_data.video_mode.mDVI = true; + } else { + dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic); + hdmi->hdmi_data.video_mode.mDVI = false; + } if ((hdmi->vic == 6) || (hdmi->vic == 7) || (hdmi->vic == 21) || (hdmi->vic == 22) || @@ -1572,19 +1840,78 @@ static int mxc_hdmi_setup(struct mxc_hdmi *hdmi) hdmi->hdmi_data.enc_color_depth = 8; hdmi->hdmi_data.pix_repet_factor = 0; hdmi->hdmi_data.hdcp_enable = 0; - hdmi->hdmi_data.video_mode.mDataEnablePolarity = TRUE; + hdmi->hdmi_data.video_mode.mDataEnablePolarity = true; - hdmi_video_force_output(hdmi, TRUE); + /* HDMI Initialization Step B.1 */ hdmi_av_composer(hdmi); + + /* HDMI Initializateion Step B.2 */ + mxc_hdmi_phy_init(hdmi); + + /* HDMI Initialization Step B.3 */ + mxc_hdmi_enable_video_path(hdmi); + + /* not for DVI mode */ + if (hdmi->hdmi_data.video_mode.mDVI) + dev_dbg(&hdmi->pdev->dev, "%s DVI mode\n", __func__); + else { + dev_dbg(&hdmi->pdev->dev, "%s CEA mode\n", __func__); + + /* HDMI Initialization Step E - Configure audio */ + hdmi_clk_regenerator_update_pixel_clock(); + hdmi_enable_audio_clk(hdmi); + + /* HDMI Initialization Step F - Configure AVI InfoFrame */ + hdmi_config_AVI(hdmi); + } + hdmi_video_packetize(hdmi); hdmi_video_csc(hdmi); hdmi_video_sample(hdmi); hdmi_tx_hdcp_config(hdmi); - hdmi_phy_init(hdmi, TRUE); - hdmi_video_force_output(hdmi, FALSE); - hdmi_set_clk_regenerator(); - return 0; + mxc_hdmi_clear_overflow(); + if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mDVI) + hdmi_enable_overflow_interrupts(); + + dev_dbg(&hdmi->pdev->dev, "%s exit\n\n", __func__); +} + +/* Wait until we are registered to enable interrupts */ +static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi) +{ + unsigned long flags; + + if (hdmi->fb_reg) + return; + + spin_lock_irqsave(&hdmi->irq_lock, flags); + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + clk_enable(hdmi->hdmi_iahb_clk); + + hdmi_writeb(HDMI_PHY_I2CM_INT_ADDR_DONE_POL, + HDMI_PHY_I2CM_INT_ADDR); + + hdmi_writeb(HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, + HDMI_PHY_I2CM_CTLINT_ADDR); + + /* enable cable hot plug irq */ + hdmi_writeb((u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + /* Unmute interrupts */ + hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + + hdmi->fb_reg = true; + + clk_disable(hdmi->hdmi_iahb_clk); + + spin_unlock_irqrestore(&hdmi->irq_lock, flags); } static int mxc_hdmi_fb_event(struct notifier_block *nb, @@ -1598,44 +1925,61 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, switch (val) { case FB_EVENT_FB_REGISTERED: - hdmi->fb_reg = true; + dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_REGISTERED\n"); + mxc_hdmi_fb_registered(hdmi); break; + case FB_EVENT_FB_UNREGISTERED: + dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_UNREGISTERED\n"); hdmi->fb_reg = false; break; + case FB_EVENT_MODE_CHANGE: - mxc_hdmi_setup(hdmi); + dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_MODE_CHANGE\n"); + if (hdmi->fb_reg) + mxc_hdmi_setup(hdmi); break; + case FB_EVENT_BLANK: - if (*((int *)event->data) == FB_BLANK_UNBLANK) - mxc_hdmi_poweron(hdmi); - else - mxc_hdmi_poweroff(hdmi); + if (*((int *)event->data) == FB_BLANK_UNBLANK) { + dev_dbg(&hdmi->pdev->dev, + "event=FB_EVENT_BLANK - UNBLANK\n"); + mxc_hdmi_enable_pins(hdmi); + } else { + dev_dbg(&hdmi->pdev->dev, + "event=FB_EVENT_BLANK - BLANK\n"); + mxc_hdmi_disable_pins(hdmi); + } break; } return 0; } +/* HDMI Initialization Step A */ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, - struct mxc_dispdrv_setting *setting) + struct mxc_dispdrv_setting *setting) { int ret = 0; struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; int irq = platform_get_irq(hdmi->pdev, 0); - bool found = false; - u8 val; - const struct fb_videomode *mode; - struct fb_videomode m; - struct fb_var_screeninfo var; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); if (!plat || irq < 0) return -ENODEV; + hdmi->dft_mode_set = false; + setting->dev_id = mxc_hdmi_ipu_id; setting->disp_id = mxc_hdmi_disp_id; setting->if_fmt = IPU_PIX_FMT_RGB24; + hdmi->dft_mode_str = setting->dft_mode_str; + hdmi->default_bpp = setting->default_bpp; + dev_dbg(&hdmi->pdev->dev, "%s - default mode %s bpp=%d\n", + __func__, hdmi->dft_mode_str, hdmi->default_bpp); + hdmi->fbi = setting->fbi; /* Claim HDMI pins */ @@ -1689,84 +2033,26 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, hdmi_readb(HDMI_PRODUCT_ID0), hdmi_readb(HDMI_PRODUCT_ID1)); - INIT_LIST_HEAD(&hdmi->fbi->modelist); - - /* try to read edid */ - ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi); - if (ret < 0) { - /* If not EDID data readed, setup default modelist */ - dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); - mxc_hdmi_default_modelist(hdmi); + /* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator + * N and cts values before enabling phy */ + hdmi_init_clk_regenerator(); - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - - fb_videomode_to_var(&hdmi->fbi->var, mode); - hdmi->need_mode_change = true; - } else if (hdmi->fbi->monspecs.modedb_len > 0) { - int i; - - for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { - /* - * We might check here if mode is supported by HDMI. - * Also, we do not currently support interlaced modes - */ - fb_videomode_to_var(&var, &hdmi->fbi->monspecs.modedb[i]); - if (!(hdmi->fbi->monspecs.modedb[i].vmode - & FB_VMODE_INTERLACED)) { - dev_dbg(&hdmi->pdev->dev, "Adding mode %d:", i); - dev_dbg(&hdmi->pdev->dev, - "xres = %d, yres = %d, freq = %d\n", - hdmi->fbi->monspecs.modedb[i].xres, - hdmi->fbi->monspecs.modedb[i].yres, - hdmi->fbi->monspecs.modedb[i].refresh); - fb_add_videomode( - &hdmi->fbi->monspecs.modedb[i], - &hdmi->fbi->modelist); - } - } - - fb_find_mode(&hdmi->fbi->var, hdmi->fbi, - setting->dft_mode_str, NULL, 0, NULL, - setting->default_bpp); - - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - fb_videomode_to_var(&hdmi->fbi->var, mode); - found = 1; - - hdmi->need_mode_change = true; - } - - if (!found) { - ret = fb_find_mode(&hdmi->fbi->var, hdmi->fbi, - setting->dft_mode_str, NULL, 0, NULL, - setting->default_bpp); - if (!ret) { - ret = -EINVAL; - goto efindmode; - } - } + INIT_LIST_HEAD(&hdmi->fbi->modelist); spin_lock_init(&hdmi->irq_lock); - INIT_DELAYED_WORK(&(hdmi->det_work), det_worker); + fb_add_videomode(&vga_mode, &hdmi->fbi->modelist); + fb_videomode_to_var(&hdmi->fbi->var, &vga_mode); + + INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_worker); /* Configure registers related to HDMI interrupt * generation before registering IRQ. */ hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0); - /* enable cable hot plug irq */ - val = ~HDMI_PHY_HPD; - hdmi_writeb(val, HDMI_PHY_MASK0); - /* Clear Hotplug interrupts */ hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); - hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); - hdmi->nb.notifier_call = mxc_hdmi_fb_event; ret = fb_register_client(&hdmi->nb); if (ret < 0) @@ -1774,8 +2060,6 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, memset(&hdmi->hdmi_data, 0, sizeof(struct hdmi_data_info)); - mxc_hdmi_setup(hdmi); - /* Disable IAHB clock while waiting for hotplug interrupt. * ISFR clock must remain enabled for hotplug to work. */ clk_disable(hdmi->hdmi_iahb_clk); @@ -1791,11 +2075,12 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, goto ereqirq; } + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); + return ret; efbclient: free_irq(irq, hdmi); -efindmode: ereqirq: clk_disable(hdmi->hdmi_iahb_clk); erate2: @@ -1807,6 +2092,8 @@ erate1: egetclk1: plat->put_pins(); egetpins: + dev_dbg(&hdmi->pdev->dev, "%s error exit\n", __func__); + return ret; } @@ -1815,9 +2102,11 @@ static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_handle *disp) struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + fb_unregister_client(&hdmi->nb); - mxc_hdmi_poweroff(hdmi); + mxc_hdmi_disable_pins(hdmi); clk_disable(hdmi->hdmi_isfr_clk); clk_put(hdmi->hdmi_isfr_clk); @@ -1922,7 +2211,6 @@ static void __exit mxc_hdmi_exit(void) } module_exit(mxc_hdmi_exit); - static int __devinit mxc_hdmi_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h index f449946a50f6..8c6ce456ddad 100644 --- a/include/linux/mfd/mxc-hdmi-core.h +++ b/include/linux/mfd/mxc-hdmi-core.h @@ -22,16 +22,21 @@ #define IRQ_DISABLE_SUCCEED 0 #define IRQ_DISABLE_FAIL 1 +bool hdmi_check_overflow(void); + u8 hdmi_readb(unsigned int reg); void hdmi_writeb(u8 value, unsigned int reg); void hdmi_mask_writeb(u8 data, unsigned int addr, u8 shift, u8 mask); unsigned int hdmi_read4(unsigned int reg); void hdmi_write4(unsigned int value, unsigned int reg); + void hdmi_irq_init(void); void hdmi_irq_enable(int irq); unsigned int hdmi_irq_disable(int irq); + void hdmi_set_sample_rate(unsigned int rate); -void hdmi_set_clk_regenerator(void); +void hdmi_init_clk_regenerator(void); +void hdmi_clk_regenerator_update_pixel_clock(void); extern int mxc_hdmi_ipu_id; extern int mxc_hdmi_disp_id; |