Re: RandR + i915 (HD 4600 / Haswell) -- Cannot switch from internal to 3x external

Chris Wilson
2015-06-17T09:13:53Z

On Wed, Jun 17, 2015 at 09:46:25AM +0100, Chris Wilson wrote:
> On Tue, Jun 16, 2015 at 10:40:17AM -0500, Nathan Schulte wrote:
> > I am running Debian Sid on a laptop machine with an Intel Haswell
> > CPU with Intel HD 4600 graphics.
> >
> > >$ Xorg -version
> > >
> > >X.Org X Server 1.17.1
> > >Release Date: 2015-02-10
> > >X Protocol Version 11, Revision 0
> > >Build Operating System: Linux 3.16.0-4-amd64 x86_64 Debian
> > >Current Operating System: Linux desmas-l 4.0.0-1-amd64 #1 SMP Debian 4.0.2-1 (2015-05-11) x86_64
> > >Kernel command line: BOOT_IMAGE=/vmlinuz-4.0.0-1-amd64 root=UUID=f5ba8b5c-63aa-4a67-a07c-dd8d3297b2d3 ro quiet
> > >Build Date: 04 May 2015 11:22:06PM
> > >xorg-server 2:1.17.1-2 (http://www.debian.org/support)
> > >Current version of pixman: 0.32.6
> > > Before reporting problems, check http://wiki.x.org
> > > to make sure that you have the latest version.
> >
> > When using RandR (xrandr, arandr), I am unable to re-configure my
> > displays from the one integrated in the machine (eDP1), to three
> > external displays (in this case, HDMI1 (DP -> DVI via DP++), HDMI2
> > (HDMI -> DVI), HDMI3 (miniDP -> DVI); the machine has three external
> > connectors). I haven't played with DP and multi-stream transport,
> > but I expect it would behave similarly (that is, annoyingly).
> >
> > >xrandr: cannot find crtc for output HDMI
> >
> > My work-around is to issue two RandR requests, with the first one
> > enabling an external display, and the second one disabling the
> > internal display and enabling the rest. This is rather annoying and
> > conflicts with some of the scripts I use to automatically configure
> > my displays based upon context and hot-plug (autorandr).
> >
> > The internal display is 1920x1080, and the three external displays
> > are identical monitors with 1920x1200 resolution. I believe this is
> > key, and has to do with the number of available PLLs (2) and
> > encoders (3) in the Intel hardware.
> >
> > I've attached an strace (xrandr.log) of the xrandr command:
> >
> > >xrandr \
> > > --output DP2 --off \
> > > --output DP1 --off \
> > > --output HDMI3 --mode 1920x1200 --pos 3120x0 --rotate left \
> > > --output HDMI2 --mode 1920x1200 --pos 1920x0 --rotate left \
> > > --output HDMI1 --primary --mode 1920x1200 --pos 0x410 --rotate normal \
> > > --output eDP1 --off \
> > > --output VGA1 --off
> >
> > Any help resolving this issue is greatly appreciated.
>
> I think this is the right fix:
>
> diff --git a/xrandr.c b/xrandr.c
> index fbfd93e..c0feac3 100644
> --- a/xrandr.c
> +++ b/xrandr.c
> @@ -3029,7 +3029,7 @@ main (int argc, char **argv)
> if (!config_output) argerr ("%s must be used after --output\n", argv[i]);
> set_name_xid (&config_output->mode, None);
> set_name_xid (&config_output->crtc, None);
> - config_output->changes |= changes_mode;
> + config_output->changes |= changes_mode | changes_crtc;
> continue;
> }
> if (!strcmp ("--fb", argv[i])) {

Though there is still this logic in check_crtc_for_output:

if (crtc->notput) { // check we can clone in our configuration
...
} else if (crtc->crtc_info->noutput) { // check against old
}

But we never ignore the old configuration when we disable all the
outputs on it. So to avoid running out of CRTC prematurely, we also need
to stop considering the old configuration like:

diff --git a/xrandr.c b/xrandr.c
index c0feac3..181c76e 100644
--- a/xrandr.c
+++ b/xrandr.c
@@ -2243,6 +2243,8 @@ static void
pick_crtcs (void)
{
output_t *output;
+ int saved_crtc_noutput[num_crtcs];
+ int n;

/*
* First try to match up newly enabled outputs with spare crtcs
@@ -2274,7 +2276,18 @@ pick_crtcs (void)
*/
for (output = all_outputs; output; output = output->next)
output->current_crtc_info = output->crtc_info;
+
+ /* Mark all CRTC as currently unused */
+ for (n = 0; n < num_crtcs; n++) {
+ saved_crtc_noutput[n] = crtcs[n].crtc_info->noutput;
+ crtcs[n].crtc_info->noutput = 0;
+ }
+
pick_crtcs_score (all_outputs);
+
+ for (n = 0; n < num_crtcs; n++)
+ crtcs[n].crtc_info->noutput = saved_crtc_noutput[n];
+
for (output = all_outputs; output; output = output->next)
{
if (output->mode_info && !output->crtc_info)