Fixing the Sandbrige MacBook Air display initialization

We left our hero with Debian installed on the MacBook Air, but with the display getting scrambled as soon as the i915 driver loaded.

As was reported to Matthew, the problem is as simple as a lack of the right mode for the eDP panel in the machine. This mode is supposed to come from the panel EDID data, but for some reason the driver wasn't able to query the EDID data, and so it decided to try some random panel timings it dug out of the VBT tables, which are generally supposed to be used by LVDS panels. Apple helpfully stuck valid data there, but for some other panel -- one that is 1280x800 pixels instead of the 1366x768 pixel panel in the MacBook Air.

I heard rumors that some machines would get a black screen when the i915 driver loaded. I was fortunate -- my machine simply displayed a 1366x768 subset of the programmed 1280x800 mode. A bit of garbage on the right side, and a few scanlines missing at the bottom. Quite workable, especially after I ran fbset -yres 768 to keep the console in the visible portion of the screen.

DDC failure

Looking through the kernel logs, the Intel driver tries to access the EDID data and times out, as if DDC is just completely broken. This is rather unexpected; the eDP spec says that the panel is required to support DDC and provide EDID. Now, we've seen a lot of panels which don't quite live up to the rigorous eDP specifications, but it's a bit surprising from Apple, who generally do VESA stuff pretty well.

We've heard reports about panels reporting invalid EDID data, or EDID data which didn't actually match the panel (causing us to prefer the VBT data on LVDS machines). But I've not heard of an eDP panel which didn't have anything hanging off of the DDC channel.

But X works fine?

During early debugging, I happened to start X up. Much to my surprise, X came up at the native 1366x768 mode. Digging through the kernel logs after that, I discovered that EDID was successfully fetched from the eDP panel while X started up.

At this point, I knew it was all downhill -- the EDID data was present, it just wasn't getting picked up during the early part of the driver initialization when the console mode is initialized.

eDP power management

The CPU is given complete control over the power management of the eDP panel; sequencing through various power states and waiting appropriate amounts of time when things change. Given the goal of keeping power usage as low as possible, this makes a huge amount of sense.

The eDP spec is quite clear though, without power, the panel will not respond to anything over the aux channel, and that includes EDID data. The eDP panel power hardware in the Sandybridge chip has a special mode for dealing with this requirement. If the panel is not displaying data, you can supply power for the aux channel stuff by setting a magic bit in the panel power registers.

When initializing the frame buffer, the kernel driver turns off the panel completely so that it has all of the hardware in a known state (yeah, this is not optimal, but that's another bug). When X started, the panel was already running with the console mode.

Given the difference between these two states -- EDID querying with the panel off failed, while EDID querying with the panel on worked, it seemed pretty clear that the panel power wasn't getting managed correctly. So, it seemed pretty clear that the magic 'power the panel' bit wasn't getting turned on at the right times.

Getting the power turned on.

I stuck a check inside all of the aux channel communication functions to see where things were broken. This pointed out several places missing the panel power calls. This wasn't quite sufficient to get EDID data flowing. The remaining problem was that the code wasn't waiting long enough after turning the panel power on before starting the aux channel communication. A few msleep calls and huzzah! EDID at boot time and the console had the right mode.

Making it faster

However, it turns out that the driver does this a lot, and the msleeps required were fairly long -- the eDP panel wants a 500ms delay from turning the panel power off before you can turn it back on.

I fix this by simply delaying the panel power off until things were idle for a 'long' time. Now mode setting goes zipping through, and a few seconds later, the bit to force panel power on gets turned off.

Getting these bits for yourself

I've pushed out the code to my (temporary) kernel repository git:// in the fix-edp-vdd-power branch. I'd love to hear if you've tried this on either a MacBook Air or any other eDP machine from Ironlake onwards.