This patch extends display layer configuration and the DirectFB
configuration system to support selecting display modes based
on their scan mode (interlaced/progressive) and vertical refresh
rate. Such selection is implemented for the primary layer in
the fbdev system. This is now sufficient to select any VESA
or CEA-861-C DTV mode given suitable framebuffer driver support
and entries in /etc/fb.modes. 

The current DirectFB semantics for mode selection have been
maintained, unless a more selective mode is specified
either in user configuration or programatically via 
IDirectFBDisplayLayer::SetConfiguration. This includes
the current behaviour of IDirectFB::SetVideoMode and
IDirectFB::CreateSurface for the primary surface.

The "mode=" configuration on the command line and directfbrc files
has been extended, in a compatible manner, to allow the default
mode to specify a refresh rate and scan mode. The commandline
help "--dfb:help" has been updated to show the new syntax.

Signed off by: Stephen Gallimore <stephen.gallimore@st.com>
Index: DirectFB-1.0.1/include/directfb.h
===================================================================
--- DirectFB-1.0.1.orig/include/directfb.h
+++ DirectFB-1.0.1/include/directfb.h
@@ -1801,6 +1801,15 @@ typedef enum {
 } DFBDisplayLayerBackgroundMode;
 
 /*
+ * Layer scan mode, which translates to display mode's scan type.
+ */
+typedef enum {
+  DLSM_DONTCARE               = 0,
+  DLSM_PROGRESSIVE,
+  DLSM_INTERLACED
+} DFBDisplayLayerScanMode;
+
+/*
  * Layer configuration flags
  */
 typedef enum {
@@ -1813,8 +1822,10 @@ typedef enum {
      DLCONF_OPTIONS           = 0x00000010,
      DLCONF_SOURCE            = 0x00000020,
      DLCONF_SURFACE_CAPS      = 0x00000040,
+     DLCONF_SCANMODE          = 0x00000080,
+     DLCONF_REFRESHRATE       = 0x00000100,
 
-     DLCONF_ALL               = 0x0000007F
+     DLCONF_ALL               = 0x000001FF
 } DFBDisplayLayerConfigFlags;
 
 /*
@@ -1832,6 +1843,9 @@ typedef struct {
 
      DFBSurfaceCapabilities        surface_caps;  /* Choose surface capabilities, available:
                                                      INTERLACED, SEPARATED, PREMULTIPLIED. */
+
+     DFBDisplayLayerScanMode       scanmode;      /* Choose progressive or interlaced display mode */
+     int                           refreshrate;   /* Display refresh rate in 1/100th Hz */
 } DFBDisplayLayerConfig;
 
 /*
Index: DirectFB-1.0.1/src/core/layer_context.c
===================================================================
--- DirectFB-1.0.1.orig/src/core/layer_context.c
+++ DirectFB-1.0.1/src/core/layer_context.c
@@ -541,6 +541,12 @@ dfb_layer_context_test_configuration( Co
 
                if (failed & CLRCF_SURFACE_CAPS)
                     flags |= DLCONF_SURFACE_CAPS;
+
+               if (failed & CLRCF_SCANMODE)
+                    flags |= DLCONF_SCANMODE;
+
+               if (failed & CLRCF_REFRESHRATE)
+                    flags |= DLCONF_REFRESHRATE;
           }
 
           *ret_failed = flags;
@@ -694,6 +700,12 @@ dfb_layer_context_set_configuration( Cor
      if (config->flags & DLCONF_SURFACE_CAPS)
           context->config.surface_caps = config->surface_caps;
 
+     if (config->flags & DLCONF_SCANMODE)
+          context->config.scanmode = config->scanmode;
+
+     if (config->flags & DLCONF_REFRESHRATE)
+          context->config.refreshrate = config->refreshrate;
+
      /* Update the window stack. */
      if (context->stack) {
           CoreWindowStack *stack = context->stack;
@@ -1409,6 +1421,18 @@ build_updated_config( CoreLayer         
           ret_config->surface_caps = update->surface_caps;
      }
 
+     /* Change scan mode. */
+     if (update->flags & DLCONF_SCANMODE) {
+          flags |= CLRCF_SCANMODE;
+          ret_config->scanmode = update->scanmode;
+     }
+
+     /* Change refresh rate. */
+     if (update->flags & DLCONF_REFRESHRATE) {
+          flags |= CLRCF_REFRESHRATE;
+          ret_config->refreshrate = update->refreshrate;
+     }
+
      /* Return translated flags. */
      if (ret_flags)
           *ret_flags = flags;
Index: DirectFB-1.0.1/src/core/layers.h
===================================================================
--- DirectFB-1.0.1.orig/src/core/layers.h
+++ DirectFB-1.0.1/src/core/layers.h
@@ -59,6 +59,10 @@ struct __DFB_CoreLayerRegionConfig {
      DFBRegion                 *clips;            /* clip regions */
      int                        num_clips;        /* number of clip regions */
      DFBBoolean                 positive;         /* show or cut out regions */
+
+     DFBDisplayLayerScanMode    scanmode;         /* display scan mode (progressive/interlaced)
+                                                     note this may be different to the surface caps */
+     int                        refreshrate;      /* display refresh frequency, 1/100th Hz */
 };
 
 typedef enum {
@@ -85,10 +89,13 @@ typedef enum {
 
      CLRCF_PARITY       = 0x00100000,
 
+     CLRCF_SCANMODE     = 0x01000000,
+     CLRCF_REFRESHRATE  = 0x02000000,
+
      CLRCF_SURFACE      = 0x10000000,
      CLRCF_PALETTE      = 0x20000000,
 
-     CLRCF_ALL          = 0x3013377F
+     CLRCF_ALL          = 0x3313377F
 } CoreLayerRegionConfigFlags;
 
 typedef struct {
Index: DirectFB-1.0.1/src/core/layer_region.c
===================================================================
--- DirectFB-1.0.1.orig/src/core/layer_region.c
+++ DirectFB-1.0.1/src/core/layer_region.c
@@ -607,6 +607,12 @@ dfb_layer_region_set_configuration( Core
           if (flags & CLRCF_BUFFERMODE)
                new_config.buffermode = config->buffermode;
 
+          if (flags & CLRCF_SCANMODE)
+               new_config.scanmode = config->scanmode;
+
+          if (flags & CLRCF_REFRESHRATE)
+               new_config.refreshrate = config->refreshrate;
+
           if (flags & CLRCF_OPTIONS)
                new_config.options = config->options;
 
Index: DirectFB-1.0.1/systems/fbdev/fbdev.c
===================================================================
--- DirectFB-1.0.1.orig/systems/fbdev/fbdev.c
+++ DirectFB-1.0.1/systems/fbdev/fbdev.c
@@ -232,6 +232,7 @@ static DFBResult dfb_fbdev_set_mode( Cor
                                      CoreLayerRegionConfig *config );
 static void      dfb_fbdev_var_to_mode( struct fb_var_screeninfo *var,
                                         VideoMode                *mode );
+static int       dfb_fbdev_mode_refresh_rate( VideoMode *mode );
 
 /******************************************************************************/
 
@@ -1098,10 +1099,14 @@ primaryInitLayer( CoreLayer             
 
      /* fill out the default configuration */
      config->flags      = DLCONF_WIDTH       | DLCONF_HEIGHT |
-                          DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE;
+                          DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE |
+                          DLCONF_SCANMODE    | DLCONF_REFRESHRATE;
      config->buffermode = DLBM_FRONTONLY;
      config->width      = default_mode->xres;
      config->height     = default_mode->yres;
+     /* for backwards compatibility, match any scan mode and refreshrate by default */
+     config->scanmode   = DLSM_DONTCARE;
+     config->refreshrate= 0;
      
      tmp.format     = DSPF_RGB16;
      tmp.buffermode = DLBM_FRONTONLY;
@@ -1254,18 +1259,22 @@ primaryTestRegion( CoreLayer            
 {
      VideoMode                  *videomode = NULL;
      CoreLayerRegionConfigFlags  fail = 0;
+     int wantlaced = (config->scanmode==DLSM_INTERLACED)?1:0;
 
      videomode = dfb_fbdev->shared->modes;
      while (videomode) {
+          int refreshrate = dfb_fbdev_mode_refresh_rate(videomode);
           if (videomode->xres == config->source.w  &&
-              videomode->yres == config->source.h)
+              videomode->yres == config->source.h  &&
+                (config->refreshrate == 0 || refreshrate == config->refreshrate) &&
+                (config->scanmode == DLSM_DONTCARE || videomode->laced == wantlaced))
                break;
 
           videomode = videomode->next;
      }
 
      if (!videomode || dfb_fbdev_set_mode( NULL, videomode, config ))
-          fail |= CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_FORMAT | CLRCF_BUFFERMODE;
+          fail |= CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_FORMAT | CLRCF_BUFFERMODE | CLRCF_SCANMODE | CLRCF_REFRESHRATE;
 
      if (config->options)
           fail |= CLRCF_OPTIONS;
@@ -1303,11 +1312,15 @@ primarySetRegion( CoreLayer             
      VideoMode *videomode;
      VideoMode *highest = NULL;
      FBDevShared *shared = dfb_fbdev->shared;
+     int wantlaced = (config->scanmode==DLSM_INTERLACED)?1:0;
 
      videomode = shared->modes;
      while (videomode) {
+          int refreshrate = dfb_fbdev_mode_refresh_rate(videomode);
           if (videomode->xres == config->source.w  &&
-              videomode->yres == config->source.h)
+              videomode->yres == config->source.h  &&
+              (config->refreshrate == 0 || refreshrate == config->refreshrate) &&
+              (config->scanmode == DLSM_DONTCARE || videomode->laced == wantlaced))
           {
                if (!highest || highest->priority < videomode->priority)
                     highest = videomode;
@@ -1320,7 +1333,8 @@ primarySetRegion( CoreLayer             
           return DFB_UNSUPPORTED;
 
      switch (updated & (CLRCF_BUFFERMODE | CLRCF_FORMAT | CLRCF_HEIGHT |
-                        CLRCF_SURFACE | CLRCF_WIDTH |  CLRCF_SOURCE)) {
+                        CLRCF_SURFACE | CLRCF_WIDTH |  CLRCF_SOURCE |
+                        CLRCF_SCANMODE | CLRCF_REFRESHRATE)) {
      case CLRCF_NONE:
           /* No Flags - Clear pass through */
           break;
@@ -1506,6 +1520,19 @@ static void dfb_fbdev_var_to_mode( struc
      mode->doubled       = (var->vmode & FB_VMODE_DOUBLE) ? 1 : 0;
 }
 
+static int dfb_fbdev_mode_refresh_rate( VideoMode *mode)
+{
+     unsigned int htotal = mode->left_margin+mode->xres+mode->right_margin+mode->hsync_len;
+     unsigned int vtotal = mode->upper_margin+mode->yres+mode->lower_margin+mode->vsync_len;
+     double vrate;
+
+     if (!mode->laced)
+        vtotal *= 2;
+
+     vrate = (((1E12/mode->pixclock)/htotal)/vtotal)*2*100;
+     return (int)vrate;
+}
+
 static int dfb_fbdev_compatible_format( struct fb_var_screeninfo *var,
                                         int al, int rl, int gl, int bl,
                                         int ao, int ro, int go, int bo )
Index: DirectFB-1.0.1/src/directfb.c
===================================================================
--- DirectFB-1.0.1.orig/src/directfb.c
+++ DirectFB-1.0.1/src/directfb.c
@@ -305,9 +305,11 @@ apply_configuration( IDirectFB        *d
           layer_config.buffermode = dfb_config->buffer_mode;
      
      if (dfb_config->mode.width > 0 && dfb_config->mode.height > 0) {
-          layer_config.flags |= DLCONF_WIDTH | DLCONF_HEIGHT;
-          layer_config.width  = dfb_config->mode.width;
-          layer_config.height = dfb_config->mode.height;
+          layer_config.flags      |= DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_SCANMODE | DLCONF_REFRESHRATE;
+          layer_config.width       = dfb_config->mode.width;
+          layer_config.height      = dfb_config->mode.height;
+          layer_config.scanmode    = dfb_config->mode.scanmode;
+          layer_config.refreshrate = dfb_config->mode.refreshrate;
      }
 
      if (dfb_config->mode.format != DSPF_UNKNOWN) {
@@ -325,13 +327,13 @@ apply_configuration( IDirectFB        *d
      }
 
      if (dfb_layer_context_test_configuration( context, &layer_config, &fail )) {
-          if (fail & (DLCONF_WIDTH | DLCONF_HEIGHT)) {
+          if (fail & (DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_SCANMODE | DLCONF_REFRESHRATE)) {
                D_ERROR( "DirectFB/DirectFBCreate: "
                         "Setting desktop resolution to %dx%d failed!\n"
                         "     -> Using default resolution.\n",
                         layer_config.width, layer_config.height );
                
-               layer_config.flags &= ~(DLCONF_WIDTH | DLCONF_HEIGHT);
+               layer_config.flags &= ~(DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_SCANMODE | DLCONF_REFRESHRATE);
           }
 
           if (fail & DLCONF_PIXELFORMAT) { 
Index: DirectFB-1.0.1/src/idirectfb.c
===================================================================
--- DirectFB-1.0.1.orig/src/idirectfb.c
+++ DirectFB-1.0.1/src/idirectfb.c
@@ -367,11 +367,16 @@ IDirectFB_SetVideoMode( IDirectFB    *th
                DFBResult ret;
                DFBDisplayLayerConfig config;
 
-               config.flags       = DLCONF_WIDTH | DLCONF_HEIGHT |
+               config.flags       = DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_SCANMODE | DLCONF_REFRESHRATE |
                                     DLCONF_PIXELFORMAT;
                config.width       = width;
                config.height      = height;
                config.pixelformat = format;
+               /*
+                * Preserve API behaviour, match display mode only on width and height
+                */
+               config.scanmode    = DLSM_DONTCARE;
+               config.refreshrate = 0;
 
                ret = dfb_layer_context_set_configuration( data->primary.context,
                                                           &config );
@@ -635,7 +640,7 @@ IDirectFB_CreateSurface( IDirectFB      
                     CoreLayerContext *context = data->primary.context;
 
                     config.flags |= DLCONF_BUFFERMODE | DLCONF_PIXELFORMAT |
-                                    DLCONF_WIDTH | DLCONF_HEIGHT;
+                                    DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_SCANMODE | DLCONF_REFRESHRATE;
 
                     /* Source compatibility with older programs */
                     if ((caps & DSCAPS_FLIPPING) == DSCAPS_FLIPPING)
@@ -662,6 +667,8 @@ IDirectFB_CreateSurface( IDirectFB      
                     config.pixelformat = format;
                     config.width       = width;
                     config.height      = height;
+                    config.scanmode    = dfb_config->mode.scanmode;
+                    config.refreshrate = dfb_config->mode.refreshrate;
 
                     ret = dfb_layer_context_set_configuration( context, &config );
                     if (ret) {
Index: DirectFB-1.0.1/src/misc/conf.c
===================================================================
--- DirectFB-1.0.1.orig/src/misc/conf.c
+++ DirectFB-1.0.1/src/misc/conf.c
@@ -65,7 +65,11 @@ static const char *config_usage =
      "  system=<system>                Specify the system (FBDev, SDL, etc.)\n"
      "  fbdev=<device>                 Open <device> instead of /dev/fb0\n"
      "  busid=<id>                     Specify the bus location of the graphics card (default 1:0:0)\n"
-     "  mode=<width>x<height>          Set the default resolution\n"
+     "  mode=<width>x<height>[-<vrefresh>[(i|p)]]\n"
+     "                                 Set the default resolution, vrefresh can be in\n"
+     "                                 Hz or 1/100ths Hz. E.g. :\n"
+     "                                 1280x720-60p (60Hz progressive) or\n"
+     "                                 1920x1080-5994i (59.94Hz interlaced)\n"
      "  scaled=<width>x<height>        Scale the window to this size for 'force-windowed' apps\n"
      "  depth=<pixeldepth>             Set the default pixel depth\n"
      "  pixelformat=<pixelformat>      Set the default pixel format\n"
@@ -497,11 +501,31 @@ DFBResult dfb_config_set( const char *na
      } else
      if (strcmp (name, "mode" ) == 0) {
           if (value) {
-               int width, height;
+               int width, height, refreshrate, count;
+               char laced;
 
-               if (sscanf( value, "%dx%d", &width, &height ) < 2) {
-                    D_ERROR("DirectFB/Config 'mode': Could not parse mode!\n");
-                    return DFB_INVARG;
+               count = sscanf( value, "%dx%d-%d%c", &width, &height,&refreshrate,&laced);
+               switch  (count) {
+                    case 2:
+                         dfb_config->mode.refreshrate = 0;
+                         dfb_config->mode.scanmode = DLSM_DONTCARE;
+                         break;
+                    case 3:
+                         dfb_config->mode.refreshrate = refreshrate<300?refreshrate*100:refreshrate;
+                         dfb_config->mode.scanmode = DLSM_DONTCARE;
+                         break;
+                    case 4:
+                         if(laced != 'i' && laced != 'p') {
+                              D_ERROR("DirectFB/Config 'mode': Could not parse mode!\n");
+                              return DFB_INVARG;
+                         }
+
+                         dfb_config->mode.refreshrate = refreshrate<300?refreshrate*100:refreshrate;
+                         dfb_config->mode.scanmode = (laced == 'i')?DLSM_INTERLACED:DLSM_PROGRESSIVE;
+                         break;
+                    default:
+                         D_ERROR("DirectFB/Config 'mode': Could not parse mode!\n");
+                         return DFB_INVARG;
                }
 
                dfb_config->mode.width  = width;
Index: DirectFB-1.0.1/src/misc/conf.h
===================================================================
--- DirectFB-1.0.1.orig/src/misc/conf.h
+++ DirectFB-1.0.1/src/misc/conf.h
@@ -98,10 +98,12 @@ typedef struct
                                                      windows */
 
      struct {
-          int                   width;            /* primary layer width */
-          int                   height;           /* primary layer height */
-          int                   depth;            /* primary layer depth */
-          DFBSurfacePixelFormat format;           /* primary layer format */
+          int                     width;          /* primary layer width */
+          int                     height;         /* primary layer height */
+          int                     depth;          /* primary layer depth */
+          DFBSurfacePixelFormat   format;         /* primary layer format */
+          DFBDisplayLayerScanMode scanmode;       /* primary layer display scan mode */
+          int                     refreshrate;    /* primary layer display refresh rate */
      } mode;
 
      struct {
