Wednesday, March 27, 2013

BUGFIX for wrong monitor resolution in Rockchip kernels

Finally found it!


You have:
- a 1080p capable monitor
- a 1080p capable stick + kernel (Picuntu Linux on a Measy U2C RK3066 in my case)

But when booting on Android or Picuntu Linux the resolution looks wrong (specially in Linux).

Spotting the problem:

Open up your monitor's OSD and find the Info tab, where it should say which resolution it is displaying.

If it is 1280x720 then you have just run into this bug.

Fix Procedure:

Apply the patch indicated in [1] to your kernel's /drivers/video/fbmon.c
Recompile the kernel (if Picuntu Linux, see [2])
Enjoy 1080p

The Discovery :)

I must say I was thinking the blame was for the rk30_hdmi driver, however the bug was in the Linux kernel's EDID parser itself!

[N.B.: EDID is the contents of a very small ROM inside every digital TV & PC monitor, it summarizes the available and possible resolutions]

I ran up and down through the drivers/video/rockchip/hdmi/ folder looking for the place where the monitor's non-CEA EDID was being ignored, finding the function hdmi_ouputmode_select (rk30_hdmi_lcdc.c), which produced a sink_info in the kernel log that did not include the 1080p resolution.

After a couple of days, the following call sequence was found to be causing this:

hdmi_sys_parse_edid (rk30_hdmi_edid.c) -> hdmi_edid_parse_base (rk30_hdmi_edid.c) -> fb_edid_to_monspecs (../../fbmon.c)
and back in hdmi_sys_parse_edid (rk30_hdmi_edid.c) that called hdmi_ouputmode_select (rk30_hdmi_lcdc.c).

Since the EDID video modes themselves were being parsed by fbmon.c, I decided to delve into it just to know which modes it reported back to the rk30_hdmi driver.

Defining DEBUG in fbmon.c resulted in the list of 13 valid video modes extracted from the monitor's EDID. Of these, two looked extremely unusual:

So I looked up the monitor's EDID in another tool and, sure enough, the missing modes were:

It totally looked like the reported X-Y dimensions were being modified due to some erroneous aspect ratio calculation!!!

Digging further into the fbmon.c code, I found a totally suspicious:
static int get_std_timing(unsigned char *block, struct fb_videomode *mode) {
switch (ratio) {
case 0:
         yres = xres;

So indeed there was a place modifying a reported resolution into a "square" one (like 1440x1440, or 1280x1280) due to a ratio. However this behavior is so bizarre that I was sure it had been spotted before so I Googled for "get_std_timing" and "ratio" and found the kernel patch for this bug (see [1]).

What was happening back in the rk30_hdmi is that it took 1440x1440 as the largest resolution supported by the monitor, but couldn't match it to any of its hardcoded modes, so it just discarded it.

This is one of the problems of basing a system on Linux Kernel 3.0.8 when the latest version is 3.9

Hope it helps!


1 comment:

  1. you can tell me with these changes I would be able to change the resolution to 480p?