summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-01-13 04:28:12 +0400
committerAlejandro Mery <amery@geeks.cl>2013-01-14 14:36:06 +0400
commitfbb80feb985cfbda6482a3e353cf9cc7dbb008fb (patch)
treecfeecd8778ac96235ba4bd95d669b1565f9450e7
parent6a723af5f76d2da03478cdea3e417db1c3a7aa5b (diff)
downloadlinux-sunxi-fbb80feb985cfbda6482a3e353cf9cc7dbb008fb.tar.xz
sunxi-hdmi: Fix some modes no longer working with X
The "video:sunxi:disp:Dynamic mode switching" patch adds a check to see if the passed in pixelclock is valid when calling Fb_check_var. However that pixelclock gets there through a KHZ2PICOS followed by a PICOS2KHZ conversion, changing for example a 85500000 clock into a 85506000 clock, causing this check to fail. Note that the inaccuracy in this conversion is such, that it will fail for almost any mode, yet for some reason this seems to only cause problems for some modes. One theory I've is that a 85500000 mode causes problems with X, because X only makes the (now failing) FBIOPUT_VSCREENINFO call when KHZ2PICOS followed by PICOS2KHZ leads to a difference of more then .01 Mhz after rounding. IE xorg.log for the failed case contains: FBDEV(0): Built-in mode "current": 85.5 MHz, 47.7 kHz, 60.0 Hz FBDEV(0): Modeline "current"x0.0 85.51 1360 1424 1536 1792 ... Note the 85.51 versus 85.5. In the end it does not matter, since we simply need to deal with the inaccuracy and not make Fb_check_var fail because of it. This patch fixes this by: 1) Introducing new PICOS2HZ and HZ2PICOS inline functions (named in caps to match the naming of the PICOS2KHZ and KHZ2PICOS macros, but these cannot be macros because they need 64 bit divisions). This greatly improves accuracy since we have all clocks in HZ and converting them to Khz first just throws away a lot of precision 2) Rounding the result of PICOS2HZ to the nearest multiple of 10000 since edid clocks are always a multiple of 10000 Concretely this fixes X no longer starting on my HD ready television. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--drivers/video/sunxi/disp/dev_fb.c6
-rw-r--r--drivers/video/sunxi/disp/disp_display.h16
-rw-r--r--drivers/video/sunxi/disp/disp_hdmi.c15
-rw-r--r--drivers/video/sunxi/disp/disp_hdmi.h1
-rw-r--r--drivers/video/sunxi/disp/disp_lcd.c16
5 files changed, 43 insertions, 11 deletions
diff --git a/drivers/video/sunxi/disp/dev_fb.c b/drivers/video/sunxi/disp/dev_fb.c
index e06910a90a9c..7dcafd8ff25b 100644
--- a/drivers/video/sunxi/disp/dev_fb.c
+++ b/drivers/video/sunxi/disp/dev_fb.c
@@ -31,6 +31,7 @@
#include "dev_fb.h"
#include "disp_display.h"
#include "disp_lcd.h"
+#include "disp_hdmi.h"
fb_info_t g_fbi;
static DEFINE_MUTEX(g_fbi_mutex);
@@ -1061,8 +1062,9 @@ static int Fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
continue;
/* Check that pll is found */
- if (disp_get_pll_freq(PICOS2KHZ(var->pixclock) *
- 1000, &dummy, &dummy))
+ if (disp_get_pll_freq(
+ fb_videomode_pixclock_to_hdmi_pclk(var->pixclock),
+ &dummy, &dummy))
return -EINVAL;
}
diff --git a/drivers/video/sunxi/disp/disp_display.h b/drivers/video/sunxi/disp/disp_display.h
index 02e060594b38..12ed2d9c1f7b 100644
--- a/drivers/video/sunxi/disp/disp_display.h
+++ b/drivers/video/sunxi/disp/disp_display.h
@@ -24,11 +24,13 @@
#include "disp_layer.h"
#include "disp_scaler.h"
#include "disp_video.h"
+#include <asm/div64.h>
#ifdef CONFIG_ARCH_SUN5I
#include "disp_iep.h"
#endif
+
#define IMAGE_USED 0x00000004
#define YUV_CH_USED 0x00000010
#define HWC_USED 0x00000040
@@ -51,6 +53,20 @@
#define DE_FLICKER_USED 0x01000000
#define DE_FLICKER_REQUIRED 0x02000000
+static inline __u32 PICOS2HZ(__u32 picos)
+{
+ __u64 numerator = 1000000000000ULL + (picos / 2);
+ do_div(numerator, picos);
+ return numerator;
+}
+
+static inline __u32 HZ2PICOS(__u32 hz)
+{
+ __u64 numerator = 1000000000000ULL + (hz / 2);
+ do_div(numerator, hz);
+ return numerator;
+}
+
typedef struct {
__bool lcd_used;
diff --git a/drivers/video/sunxi/disp/disp_hdmi.c b/drivers/video/sunxi/disp/disp_hdmi.c
index 763218e55f8e..028a75b1e368 100644
--- a/drivers/video/sunxi/disp/disp_hdmi.c
+++ b/drivers/video/sunxi/disp/disp_hdmi.c
@@ -191,12 +191,25 @@ __s32 BSP_disp_hdmi_set_mode(__u32 sel, __disp_tv_mode_t mode)
return DIS_SUCCESS;
}
+__u32 fb_videomode_pixclock_to_hdmi_pclk(__u32 pixclock)
+{
+ /*
+ * The conversions loose precision, which *is* a problem, luckily
+ * all hdmi clocks are always a multiple of 10000 (the EDID base
+ * unit for a pixelclock), so round to the nearest multiple of
+ * 10000 to undo the precision loss.
+ */
+ __u32 pclk = (PICOS2HZ(pixclock) + 5000) / 10000;
+ return pclk * 10000;
+}
+
void videomode_to_video_timing(struct __disp_video_timing *video_timing,
const struct fb_videomode *mode)
{
memset(video_timing, 0, sizeof(struct __disp_video_timing));
video_timing->VIC = 511;
- video_timing->PCLK = (PICOS2KHZ(mode->pixclock) * 1000);
+ video_timing->PCLK =
+ fb_videomode_pixclock_to_hdmi_pclk(mode->pixclock);
video_timing->AVI_PR = 0;
video_timing->INPUTX = mode->xres;
video_timing->INPUTY = mode->yres;
diff --git a/drivers/video/sunxi/disp/disp_hdmi.h b/drivers/video/sunxi/disp/disp_hdmi.h
index 9378dc15e92a..c1e61e0a4104 100644
--- a/drivers/video/sunxi/disp/disp_hdmi.h
+++ b/drivers/video/sunxi/disp/disp_hdmi.h
@@ -22,5 +22,6 @@
__s32 Display_Hdmi_Init(void);
__s32 Display_Hdmi_Exit(void);
+__u32 fb_videomode_pixclock_to_hdmi_pclk(__u32 pixclock);
#endif
diff --git a/drivers/video/sunxi/disp/disp_lcd.c b/drivers/video/sunxi/disp/disp_lcd.c
index 312f783458e7..7d33af0c0876 100644
--- a/drivers/video/sunxi/disp/disp_lcd.c
+++ b/drivers/video/sunxi/disp/disp_lcd.c
@@ -1830,9 +1830,9 @@ __s32 BSP_disp_get_videomode(__u32 sel, struct fb_videomode *videomode)
} else if ((gdisp.screen[sel].status & TV_ON)) {
__disp_tv_mode_t tv_mode = gdisp.screen[sel].tv_mode;
LCDC_get_timing(sel, 1, videomode);
- videomode->pixclock = KHZ2PICOS(
- (clk_tab.tv_clk_tab[tv_mode].tve_clk /
- clk_tab.tv_clk_tab[tv_mode].pre_scale) / 1000);
+ videomode->pixclock = HZ2PICOS(
+ clk_tab.tv_clk_tab[tv_mode].tve_clk /
+ clk_tab.tv_clk_tab[tv_mode].pre_scale);
interlaced = Disp_get_screen_scan_mode(tv_mode);
} else if (gdisp.screen[sel].status & HDMI_ON) {
@@ -1843,7 +1843,7 @@ __s32 BSP_disp_get_videomode(__u32 sel, struct fb_videomode *videomode)
return DIS_FAIL;
LCDC_get_timing(sel, 1, videomode);
- videomode->pixclock = KHZ2PICOS(video_timing.PCLK / 1000);
+ videomode->pixclock = HZ2PICOS(video_timing.PCLK);
interlaced = video_timing.I;
hsync = video_timing.HSYNC;
vsync = video_timing.VSYNC;
@@ -1852,9 +1852,9 @@ __s32 BSP_disp_get_videomode(__u32 sel, struct fb_videomode *videomode)
LCDC_get_timing(sel, 1, videomode);
- videomode->pixclock = KHZ2PICOS(
- (clk_tab.vga_clk_tab[vga_mode].tve_clk /
- clk_tab.vga_clk_tab[vga_mode].pre_scale) / 1000);
+ videomode->pixclock = HZ2PICOS(
+ clk_tab.vga_clk_tab[vga_mode].tve_clk /
+ clk_tab.vga_clk_tab[vga_mode].pre_scale);
interlaced = Disp_get_screen_scan_mode(vga_mode);
} else {
DE_INF("get videomode fail because device is not output !\n");
@@ -1876,7 +1876,7 @@ __s32 BSP_disp_get_videomode(__u32 sel, struct fb_videomode *videomode)
if (!videomode->pixclock)
return DIS_SUCCESS;
- pixclock = PICOS2KHZ(videomode->pixclock) * 1000;
+ pixclock = PICOS2HZ(videomode->pixclock);
htotal = videomode->xres + videomode->right_margin +
videomode->hsync_len + videomode->left_margin;