diff options
Diffstat (limited to 'drivers/video/tegra/dc/hdmi.c')
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 182 |
1 files changed, 137 insertions, 45 deletions
diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index 2e1df8d82641..59b9afcec0e4 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -67,6 +67,28 @@ #define HDMI_ELD_PRODUCT_CODE_INDEX 18 #define HDMI_ELD_MONITOR_NAME_INDEX 20 +/* These two values need to be cross checked in case of + addition/removal from tegra_dc_hdmi_aspect_ratios[] */ +#define TEGRA_DC_HDMI_MIN_ASPECT_RATIO_PERCENT 80 +#define TEGRA_DC_HDMI_MAX_ASPECT_RATIO_PERCENT 320 + +/* Percentage equivalent of standard aspect ratios + accurate upto two decimal digits */ +static int tegra_dc_hdmi_aspect_ratios[] = { + /* 3:2 */ 150, + /* 4:3 */ 133, + /* 4:5 */ 80, + /* 5:4 */ 125, + /* 9:5 */ 180, + /* 16:5 */ 320, + /* 16:9 */ 178, + /* 16:10 */ 160, + /* 19:10 */ 190, + /* 25:16 */ 156, + /* 64:35 */ 183, + /* 72:35 */ 206 +}; + struct tegra_dc_hdmi_data { struct tegra_dc *dc; struct tegra_edid *edid; @@ -94,6 +116,7 @@ struct tegra_dc_hdmi_data { bool clk_enabled; unsigned audio_freq; unsigned audio_source; + bool audio_inject_null; bool dvi; }; @@ -947,12 +970,16 @@ static const struct tegra_hdmi_audio_config unsigned long tegra_hdmi_readl(struct tegra_dc_hdmi_data *hdmi, unsigned long reg) { - return readl(hdmi->base + reg * 4); + unsigned long ret; + ret = readl(hdmi->base + reg * 4); + trace_printk("readl %p=%#08lx\n", hdmi->base + reg * 4, ret); + return ret; } void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi, unsigned long val, unsigned long reg) { + trace_printk("writel %p=%#08lx\n", hdmi->base + reg * 4, val); writel(val, hdmi->base + reg * 4); } @@ -1240,62 +1267,84 @@ static bool tegra_dc_reload_mode(struct fb_videomode *mode) return false; } +static bool tegra_dc_hdmi_valid_asp_ratio(const struct tegra_dc *dc, + struct fb_videomode *mode) +{ + int count = 0; + int m_aspratio = 0; + int s_aspratio = 0; + + /* To check the aspect upto two decimal digits, calculate in % */ + m_aspratio = (mode->xres*100 / mode->yres); + + if ((m_aspratio < TEGRA_DC_HDMI_MIN_ASPECT_RATIO_PERCENT) || + (m_aspratio > TEGRA_DC_HDMI_MAX_ASPECT_RATIO_PERCENT)) + return false; + + /* Check from the table of supported aspect ratios, allow + difference of 1% for second decimal digit calibration */ + for (count = 0; count < ARRAY_SIZE(tegra_dc_hdmi_aspect_ratios); + count++) { + s_aspratio = tegra_dc_hdmi_aspect_ratios[count]; + if ((m_aspratio == s_aspratio) || + (abs(m_aspratio - s_aspratio) == 1)) + return true; + } + + return false; +} + static bool tegra_dc_hdmi_mode_filter(const struct tegra_dc *dc, struct fb_videomode *mode) { - int i; - int clock_per_frame; + if (mode->vmode & FB_VMODE_INTERLACED) + return false; + /* Ignore modes with a 0 pixel clock */ if (!mode->pixclock) return false; #ifdef CONFIG_TEGRA_HDMI_74MHZ_LIMIT - if (PICOS2KHZ(mode->pixclock) > 74250) - return false; + if (PICOS2KHZ(mode->pixclock) > 74250) + return false; #endif - for (i = 0; i < ARRAY_SIZE(tegra_dc_hdmi_supported_modes); i++) { - const struct fb_videomode *supported_mode - = &tegra_dc_hdmi_supported_modes[i]; - if (tegra_dc_hdmi_mode_equal(supported_mode, mode) && - tegra_dc_hdmi_valid_pixclock(dc, supported_mode)) { - if (mode->lower_margin == 1) { - /* This might be the case for HDMI<->DVI - * where std VESA representation will not - * pass constraint V_FRONT_PORCH >= - * V_REF_TO_SYNC + 1.So reload mode in - * CVT timing standards. - */ - if (!tegra_dc_reload_mode(mode)) - return false; - } - else - memcpy(mode, supported_mode, sizeof(*mode)); + /* Check if the mode's pixel clock is more than the max rate*/ + if (!tegra_dc_hdmi_valid_pixclock(dc, mode)) + return false; - mode->flag = FB_MODE_IS_DETAILED; - clock_per_frame = tegra_dc_calc_clock_per_frame(mode); - mode->refresh = (PICOS2KHZ(mode->pixclock) * 1000) - / clock_per_frame; - return true; + /* Check if the mode's aspect ratio is supported */ + if (!tegra_dc_hdmi_valid_asp_ratio(dc, mode)) + return false; + + /* Check some of DC's constraints */ + if (mode->hsync_len > 1 && mode->vsync_len > 1 && + mode->lower_margin + mode->vsync_len + mode->upper_margin > 1 && + mode->xres >= 16 && mode->yres >= 16) { + + if (mode->lower_margin == 1) { + /* This might be the case for HDMI<->DVI + * where std VESA representation will not + * pass constraint V_FRONT_PORCH >= + * V_REF_TO_SYNC + 1.So reload mode in + * CVT timing standards. + */ + if (!tegra_dc_reload_mode(mode)) + return false; } + mode->flag = FB_MODE_IS_DETAILED; + mode->refresh = (PICOS2KHZ(mode->pixclock) * 1000) / + tegra_dc_calc_clock_per_frame(mode); + return true; } return false; } - static bool tegra_dc_hdmi_hpd(struct tegra_dc *dc) { - int sense; - int level; - - level = gpio_get_value(dc->out->hotplug_gpio); - - sense = dc->out->flags & TEGRA_DC_OUT_HOTPLUG_MASK; - - return (sense == TEGRA_DC_OUT_HOTPLUG_HIGH && level) || - (sense == TEGRA_DC_OUT_HOTPLUG_LOW && !level); + return tegra_dc_hpd(dc); } @@ -1816,7 +1865,10 @@ static int tegra_dc_hdmi_setup_audio(struct tegra_dc *dc, unsigned audio_freq, a_source = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF; #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) - tegra_hdmi_writel(hdmi,a_source | AUDIO_CNTRL0_INJECT_NULLSMPL, + if (hdmi->audio_inject_null) + a_source |= AUDIO_CNTRL0_INJECT_NULLSMPL; + + tegra_hdmi_writel(hdmi,a_source, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0); tegra_hdmi_writel(hdmi, AUDIO_CNTRL0_ERROR_TOLERANCE(6) | @@ -1919,6 +1971,31 @@ int tegra_hdmi_setup_audio_freq_source(unsigned audio_freq, unsigned audio_sourc EXPORT_SYMBOL(tegra_hdmi_setup_audio_freq_source); #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) +int tegra_hdmi_audio_null_sample_inject(bool on) +{ + struct tegra_dc_hdmi_data *hdmi = dc_hdmi; + unsigned int val = 0; + + if (!hdmi) + return -EAGAIN; + + if (hdmi->audio_inject_null != on) { + hdmi->audio_inject_null = on; + if (hdmi->clk_enabled) { + val = tegra_hdmi_readl(hdmi, + HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0); + val &= ~AUDIO_CNTRL0_INJECT_NULLSMPL; + if (on) + val |= AUDIO_CNTRL0_INJECT_NULLSMPL; + tegra_hdmi_writel(hdmi,val, + HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0); + } + } + + return 0; +} +EXPORT_SYMBOL(tegra_hdmi_audio_null_sample_inject); + int tegra_hdmi_setup_hda_presence() { struct tegra_dc_hdmi_data *hdmi = dc_hdmi; @@ -2005,6 +2082,11 @@ static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi) avi.r = HDMI_AVI_R_SAME; + if ((dc->mode.h_active == 720) && ((dc->mode.v_active == 480) || (dc->mode.v_active == 576))) + tegra_dc_writel(dc, 0x00101010, DC_DISP_BORDER_COLOR); + else + tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR); + if (dc->mode.v_active == 480) { if (dc->mode.h_active == 640) { avi.m = HDMI_AVI_M_4_3; @@ -2037,9 +2119,12 @@ static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi) (dc->mode.v_active == 2205 && dc->mode.stereo_mode)) { /* VIC for both 1080p and 1080p 3D mode */ avi.m = HDMI_AVI_M_16_9; - if (dc->mode.h_front_porch == 88) - avi.vic = 16; /* 60 Hz */ - else if (dc->mode.h_front_porch == 528) + if (dc->mode.h_front_porch == 88) { + if (dc->mode.pclk > 74250000) + avi.vic = 16; /* 60 Hz */ + else + avi.vic = 34; /* 30 Hz */ + } else if (dc->mode.h_front_porch == 528) avi.vic = 31; /* 50 Hz */ else avi.vic = 32; /* 24 Hz */ @@ -2200,10 +2285,16 @@ static void tegra_dc_hdmi_enable(struct tegra_dc *dc) VSYNC_WINDOW_ENABLE, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW); - tegra_hdmi_writel(hdmi, - (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) | - ARM_VIDEO_RANGE_LIMITED, - HDMI_NV_PDISP_INPUT_CONTROL); + if ((dc->mode.h_active == 720) && ((dc->mode.v_active == 480) || (dc->mode.v_active == 576))) + tegra_hdmi_writel(hdmi, + (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) | + ARM_VIDEO_RANGE_FULL, + HDMI_NV_PDISP_INPUT_CONTROL); + else + tegra_hdmi_writel(hdmi, + (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) | + ARM_VIDEO_RANGE_LIMITED, + HDMI_NV_PDISP_INPUT_CONTROL); clk_disable(hdmi->disp1_clk); clk_disable(hdmi->disp2_clk); @@ -2374,6 +2465,7 @@ struct tegra_dc_out_ops tegra_dc_hdmi_ops = { .detect = tegra_dc_hdmi_detect, .suspend = tegra_dc_hdmi_suspend, .resume = tegra_dc_hdmi_resume, + .mode_filter = tegra_dc_hdmi_mode_filter, }; struct tegra_dc_edid *tegra_dc_get_edid(struct tegra_dc *dc) |