Startup RandR Configuration

Sun, 26 Nov 2006 17:28:51 -0800

Well, RandR 1.2 work is progressing apace; I can now reconfigure the X server in some fairly dramatic ways. I now regularly use the 1600x1200 monitor at home as extra desktop space for my laptop, growing the X root window to cover both monitors.

But, what I've lost in the process is any ability to configure the system at startup time. This is rather unusual; the system is now far more flexible through the RandR protocol than through the configuration file. Getting things set right at startup time seems important, as that will avoid flashing monitors as they change modes and other possible issues.

I started the process of allowing startup-time configuration by making the RandR 1.2 code permit object creation before the Screen objects were created. This allows the driver to create the necessary RandR 1.2 structures and use those to control the configuration process. This would work, except that I'd like the same driver to work in the absence of RandR 1.2 in the core server.

Back to the drawing board.

What I'm doing now is creating some new structures that map to the RandR 1.2 structures but which are hw/xfree86 specific and which don't depend on RandR 1.2 in the core server. With the goal of eventually moving these into the hw/xfree86 portion of the server, these structures provide all of the RandR 1.2 semantics using smaller driver-specific methods. The code for this new work can then use these new data structures to configure the server at startup time, as well as on-the-fly at runtime using RandR 1.2.

The two primary data structures are the xf86CrtcRec and the xf86OutputRec:

struct _xf86Crtc {
    /**
     * Associated ScrnInfo
     */
    ScrnInfoPtr     scrn;

    /**
     * Active state of this CRTC
     *
     * Set when this CRTC is driving one or more outputs
     */
    Bool        enabled;

    /**
     * Position on screen
     *
     * Locates this CRTC within the frame buffer
     */
    int         x, y;

    /** Track whether cursor is within CRTC range  */
    Bool        cursorInRange;

    /** Track state of cursor associated with this CRTC */
    Bool        cursorShown;

    /**
     * Active mode
     *
     * This reflects the mode as set in the CRTC currently
     * It will be cleared when the VT is not active or
     * during server startup
     */
    DisplayModeRec  curMode;

    /**
     * Desired mode
     *
     * This is set to the requested mode, independent of
     * whether the VT is active. In particular, it receives
     * the startup configured mode and saves the active mode
     * on VT switch.
     */
    DisplayModeRec  desiredMode;

    /** crtc-specific functions */
    const xf86CrtcFuncsRec *funcs;

    /**
     * Driver private
     *
     * Holds driver-private information
     */
    void        *driver_private;

#ifdef RANDR_12_INTERFACE
    /**
     * RandR crtc
     *
     * When RandR 1.2 is available, this
     * points at the associated crtc object
     */
    RRCrtcPtr       randr_crtc;
#else
    void        *randr_crtc;
#endif
};


struct _xf86Output {
    /**
     * Associated ScrnInfo
     */
    ScrnInfoPtr     scrn;
    /**
     * Currently connected crtc (if any)
     *
     * If this output is not in use, this field will be NULL.
     */
    xf86CrtcPtr     crtc;
    /**
     * List of available modes on this output.
     *
     * This should be the list from get_modes(), plus perhaps additional
     * compatible modes added later.
     */
    DisplayModePtr  probed_modes;

    /** EDID monitor information */
    xf86MonPtr      MonInfo;

    /** Physical size of the currently attached output device. */
    int         mm_width, mm_height;

    /** Output name */
    char        *name;

    /** output-specific functions */
    const xf86OutputFuncsRec *funcs;

    /** driver private information */
    void        *driver_private;

#ifdef RANDR_12_INTERFACE
    /**
     * RandR 1.2 output structure.
     *
     * When RandR 1.2 is available, this points at the associated
     * RandR output structure and is created when this output is created
     */
    RROutputPtr     randr_output;
#else
    void        *randr_output;
#endif
};

The hardware is manipulated through driver-specific functions contained in the xf86CrtcFuncsRec and xf86OutputFuncsRec:

typedef struct _xf86CrtcFuncs {
   /**
    * Turns the crtc on/off, or sets intermediate power levels if available.
    *
    * Unsupported intermediate modes drop to the lower power setting.  If the
    * mode is DPMSModeOff, the crtc must be disabled, as the DPLL may be
    * disabled afterwards.
    */
   void
    (*dpms)(xf86CrtcPtr     crtc,
        int             mode);

   /**
    * Saves the crtc's state for restoration on VT switch.
    */
   void
    (*save)(xf86CrtcPtr     crtc);

   /**
    * Restore's the crtc's state at VT switch.
    */
   void
    (*restore)(xf86CrtcPtr      crtc);

    /**
     * Clean up driver-specific bits of the crtc
     */
    void
    (*destroy) (xf86CrtcPtr crtc);
} xf86CrtcFuncsRec, *xf86CrtcFuncsPtr;


typedef struct _xf86OutputFuncs {
    /**
     * Turns the output on/off, or sets intermediate power levels if available.
     *
     * Unsupported intermediate modes drop to the lower power setting.  If the
     * mode is DPMSModeOff, the output must be disabled, as the DPLL may be
     * disabled afterwards.
     */
    void
    (*dpms)(xf86OutputPtr   output,
        int         mode);

    /**
     * Saves the output's state for restoration on VT switch.
     */
    void
    (*save)(xf86OutputPtr       output);

    /**
     * Restore's the output's state at VT switch.
     */
    void
    (*restore)(xf86OutputPtr    output);

    /**
     * Callback for testing a video mode for a given output.
     *
     * This function should only check for cases where a mode can't be supported
     * on the pipe specifically, and not represent generic CRTC limitations.
     *
     * \return MODE_OK if the mode is valid, or another MODE_* otherwise.
     */
    int
    (*mode_valid)(xf86OutputPtr     output,
          DisplayModePtr    pMode);

    /**
     * Callback for setting up a video mode before any crtc/dpll changes.
     *
     * \param pMode the mode that will be set, or NULL if the mode to be set is
     * unknown (such as the restore path of VT switching).
     */
    void
    (*pre_set_mode)(xf86OutputPtr   output,
            DisplayModePtr  pMode);

    /**
     * Callback for setting up a video mode after the DPLL update but before
     * the plane is enabled.
     */
    void
    (*post_set_mode)(xf86OutputPtr  output,
             DisplayModePtr pMode);

    /**
     * Probe for a connected output, and return detect_status.
     */
    enum detect_status
    (*detect)(xf86OutputPtr output);

    /**
     * Query the device for the modes it provides.
     *
     * This function may also update MonInfo, mm_width, and mm_height.
     *
     * \return singly-linked list of modes or NULL if no modes found.
     */
    DisplayModePtr
    (*get_modes)(xf86OutputPtr  output);

    /**
     * Clean up driver-specific bits of the output
     */
    void
    (*destroy) (xf86OutputPtr   output);
} xf86OutputFuncsRec, *xf86OutputFuncsPtr;

Right now, I've just hacked up the Intel driver internals using these new structures and have left the implementation a tangled mess with driver-specific, randr-specific and other code all tied together. Obviously this is not a long-term plan, but I want to change the data structures first, then split the code apart.

At this point, I've managed to get the Intel driver to compile with the new data structures, so I'll first get it working then start cleaning up the implementation so that driver-independent code is clearly separated and performing as much of the work as possible.

Once driver-independent code is in place, I will add startup configuration to the driver-independent code. I'll probably use the existing Radeon configuration file options as much as possible, and probably accept the Intel configuration file options as well. While those do not expose the full capabilities of the RandR 1.2 extension, I suspect they're sufficient for most users.

It's a long way around to get startup configuration for the new capabilities in the Intel driver, but I'm hoping it will allow us to create a common configuration language for all of the drivers and also remove the current dependence on RandR 1.2 from the Intel driver for much of this functionality.