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://people.freedesktop.org/~keithp/linux
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.