[AD] Linux patch: more on mode timings with fbcon

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


This patch makes the fbcon driver able to read /etc/fb.modes.  I
changed the format of the data in the Allegro config file too.
I don't mind not using the Allegro config file, apart from the
fact that the fb.modes file doesn't seem to tell the whole
story.  Maybe I should read the source to fbset. :)

The system here now is this: given a mode to set, first check
the Allegro config file for a corresponding mode definition.  If
that's not found, parse fb.modes looking for the mode.  Failing
that, attempt to tweak the current mode to get a reasonable
result -- except at the moment it's not very reasonable.

I haven't included any changes to the setup program here, partly
because if we drop the option to use the Allegro config file
then the setup program doesn't need changing anyway, and partly
because I keep changing my mind about how it should look.

BTW Shawn, I suspect `vsync' is waiting for the end of the VBI,
not its start -- on Matrox at least -- but I could be wrong.  If
this is a real bug, it's probably in your kernel patches, if the
bug is Matrox-specific.

-- 
George

diff -urN allegro-3.9.24-5/src/linux/fbcon.c allegro-3.9.24-6/src/linux/fbcon.c
--- allegro-3.9.24-5/src/linux/fbcon.c	Fri Jul 23 17:43:01 1999
+++ allegro-3.9.24-6/src/linux/fbcon.c	Sat Jul 24 06:22:44 1999
@@ -32,6 +32,7 @@
 #include <sys/ioctl.h>
 #include <linux/fb.h>
 #include <sys/mman.h>
+#include <ctype.h>
 
 
 
@@ -89,7 +90,7 @@
 
 
 
-static void update_timings(struct fb_var_screeninfo *mode);
+static int update_timings(struct fb_var_screeninfo *mode);
 
 
 /* fb_init:
@@ -229,7 +230,8 @@
       my_mode.blue.msb_right = 0;
 
       /* fill in the timings */
-      update_timings(&my_mode);
+      if (update_timings(&my_mode) != 0)
+	 continue;
 
       /* try to set the mode */
       if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &my_mode) == 0)
@@ -526,23 +528,230 @@
 
 
 
-char _fb_config_section[1024];
-int  _fb_pixclock;
-int  _fb_left_margin;
-int  _fb_right_margin;
-int  _fb_upper_margin;
-int  _fb_lower_margin;
-int  _fb_hsync_len;
-int  _fb_vsync_len;
-int  _fb_vmode;
-int  _fb_sync;
+struct timings {
+   char config_item[1024];
+   int pixclock;
+   int left_margin;
+   int right_margin;
+   int upper_margin;
+   int lower_margin;
+   int hsync_len;
+   int vsync_len;
+   int vmode;
+   int sync;
+   int xres;
+   int yres;
+} _fb_current_timings;
+
+static struct timings temp_timings;
+
+
+static int read_config_file (int w, int h);
+static int read_fbmodes_file (int w, int h);
+static int tweak_timings (int w, int h);
+
+
+/* _fb_get_timings:
+ *  Returns a pointer to a struct as above containing timings for the given
+ *  resolution, or NULL on error.
+ */
+struct timings *_fb_get_timings (int w, int h)
+{
+   /* First try the config file */
+   if (read_config_file (w, h)) return &temp_timings;
+
+   /* Failing that, try fb.modes */
+   if (read_fbmodes_file (w, h)) return &temp_timings;
+
+   /* Still no luck, so tweak the current mode instead */
+   if (tweak_timings (w, h)) return &temp_timings;
+   return NULL;
+}
+
+
+/* read_config_file:
+ *  Assigns timing settings from the config file or returns 0.
+ */
+static int read_config_file (int w, int h)
+{
+   char **argv;
+   int argc;
+
+   /* Let the setup program know what config string we read for this mode */
+   usprintf(temp_timings.config_item, get_config_text("fb_mode_%dx%d"), w, h);
+
+   /* First try the config file */
+   argv = get_config_argv (NULL, temp_timings.config_item, &argc);
+   if (argv) {
+      #define get_info(info) if (*argv) temp_timings.info = ustrtol (*argv++, NULL, 10)
+      get_info(pixclock);
+      get_info(left_margin);
+      get_info(right_margin);
+      get_info(upper_margin);
+      get_info(lower_margin);
+      get_info(hsync_len);
+      get_info(vsync_len);
+      
+      if (*argv) {
+	 if (!ustrcmp (*argv, "none"))
+	    temp_timings.vmode = FB_VMODE_NONINTERLACED;
+	 else if (!ustrcmp (*argv, "interlaced"))
+	    temp_timings.vmode = FB_VMODE_INTERLACED;
+	 else if (!ustrcmp (*argv, "doublescan"))
+	    temp_timings.vmode = FB_VMODE_DOUBLE;
+         argv++;
+      } else
+	 temp_timings.vmode = FB_VMODE_NONINTERLACED;
+
+      get_info(sync);
+      #undef get_info
+
+      temp_timings.xres = w;
+      temp_timings.yres = h;
 
+      return 1;
+   }
+   return 0;
+}
+
+
+/* helper to read the relevant parts of a line from fb.modes */
+static char *get_line (FILE *file)
+{
+   static char buffer[1024];
+   char *ch;
+   if (feof (file)) return NULL;
+   fgets (buffer, sizeof buffer, file);
+   if (!strchr (buffer, '\n')) {
+      do {
+	 fgets (buffer, sizeof buffer, file);
+      } while (!strchr (buffer, '\n'));
+      return NULL;
+   }
+   ch = strchr (buffer, '#');
+   if (ch) *ch = '\0';
+   ch = strchr (buffer, '\n');
+   if (ch) *ch = '\0';
+   ch = buffer;
+   while (isspace(*ch)) ch++;
+   return ch;
+}
+
+/* read_fbmodes_file:
+ *  Assigns timing settings from the fbmodes file or returns 0.
+ */
+static int read_fbmodes_file (int w, int h)
+{
+   char *mode_id = NULL;
+   char *geometry = NULL;
+   char *timings = NULL;
+   char *s, *t;
+   FILE *fbmodes;
+   int ret = 0;
+
+   fbmodes = fopen ("/etc/fb.modes", "r");
+   if (!fbmodes) return 0;
+
+   do {
+      s = get_line (fbmodes);
+      if (!s) break;
+      t = strchr (s, ' ');
+      if (t) *t++ = '\0';
+
+      if (!strcmp (s, "mode")) {
+	 free (mode_id);
+	 free (geometry);
+	 free (timings);
+	 mode_id = strdup (t);
+	 geometry = timings = NULL;
+      } else if (!strcmp (s, "endmode")) {
+	 if (geometry && timings) {
+	    int mw, mh;
+	    sscanf (geometry, "%d %d", &mw, &mh);
+	    if ((mw == w) && (mh == h)) {
+	       sscanf (timings, "%d %d %d %d %d %d %d",
+		  &temp_timings.pixclock,
+		  &temp_timings.left_margin,
+		  &temp_timings.right_margin,
+		  &temp_timings.upper_margin,
+		  &temp_timings.lower_margin,
+		  &temp_timings.hsync_len,
+		  &temp_timings.vsync_len
+	       );
+	       temp_timings.xres = w;
+	       temp_timings.yres = h;
+	       ret = 1;
+	       s = NULL;
+	    }
+	 }
+	 free (mode_id);
+	 free (geometry);
+	 free (timings);
+	 mode_id = geometry = timings = NULL;
+      } else if (!strcmp (s, "geometry")) {
+	 free (geometry);
+	 geometry = strdup (t);
+      } else if (!strcmp (s, "timings")) {
+	 free (timings);
+	 timings = strdup (t);
+      }
+   } while (s);
+
+   free (mode_id);
+   free (geometry);
+   free (timings);
+
+   fclose (fbmodes);
+   return ret;
+}
+
+
+/* tweak_timings:
+ *  Tweak the timings to match the mode we want to set.  Only works if
+ *  the parent is a higher resolution.
+ */
+static int tweak_timings (int w, int h)
+{
+   if ((w <= temp_timings.xres) && (h <= temp_timings.yres)) {
+      int diff = temp_timings.xres - w;
+      temp_timings.left_margin += diff/2;
+      temp_timings.right_margin += diff/2 + diff%2;
+      temp_timings.xres = w;
+
+      diff = temp_timings.yres - h;
+      temp_timings.upper_margin += diff/2;
+      temp_timings.lower_margin += diff/2 + diff%2;
+      temp_timings.yres = h;
+
+      return 1;
+   }
+   return 0;
+}
+
+
+static void set_default_timings (void)
+{
+   #define cp(x) temp_timings.x = orig_mode.x
+   cp(pixclock);
+   cp(left_margin);
+   cp(right_margin);
+   cp(upper_margin);
+   cp(lower_margin);
+   cp(hsync_len);
+   cp(vsync_len);
+   cp(vmode);
+   cp(sync);
+   cp(xres);
+   cp(yres);
+   #undef cp
+   usprintf(temp_timings.config_item, get_config_text("fb_mode_%dx%d"), orig_mode.xres, orig_mode.yres);
+}
 
 
 /* update_timings:
  *  Updates the timing section of the mode info.  Maybe we can make
  *  this algorithmic, as a backup, at some point.  For now it searches
- *  the config file for the data.
+ *  the config file or /etc/fb.modes for the data.
  *  
  *  We could make the init routine give up if the data isn't there, or 
  *  use an algorithmic guesser. 
@@ -551,44 +760,48 @@
  *  should offer quite a few options -- dotclock probing might be useful,
  *  along with the functionality of xvidtune.
  */
-static void update_timings(struct fb_var_screeninfo *mode)
+static int update_timings(struct fb_var_screeninfo *mode)
 {
-   char sec[1024], buffer[1024], *x;
+   struct timings *t;
 
-   usprintf(sec, get_config_text("fb_mode_%dx%d"), mode->xres, mode->yres);
+   set_default_timings();
+   t = _fb_get_timings (mode->xres, mode->yres);
+   if (!t) return -1;
 
-   #define get_info(info) mode->info = get_config_int(sec, uconvert_ascii(#info, buffer), mode->info)
-   get_info(pixclock);
-   get_info(left_margin);
-   get_info(right_margin);
-   get_info(upper_margin);
-   get_info(lower_margin);
-   get_info(hsync_len);
-   get_info(vsync_len);
-   #undef get_info
-
-   if ((x = get_config_string(sec, uconvert_ascii("vmode", buffer), NULL))) {
-      if (!ustrcmp (x, "none"))
-	 mode->vmode = FB_VMODE_NONINTERLACED;
-      else if (!ustrcmp (x, "interlaced"))
-	 mode->vmode = FB_VMODE_INTERLACED;
-      else if (!ustrcmp (x, "doublescan"))
-	 mode->vmode = FB_VMODE_DOUBLE;
-   }
+   /* for debugging, maybe for the setup program too */
+   memcpy (&_fb_current_timings, t, sizeof(struct timings));
 
-   /* TODO -- sync? */
+   /* update the mode struct */
+   mode->pixclock = t->pixclock;
+   mode->left_margin = t->left_margin;
+   mode->right_margin = t->right_margin;
+   mode->upper_margin = t->upper_margin;
+   mode->lower_margin = t->lower_margin;
+   mode->hsync_len = t->hsync_len;
+   mode->vsync_len = t->vsync_len;
+   mode->vmode = t->vmode;
+   mode->sync = t->sync;
 
-   /* for debugging, maybe for the setup program too */
-   ustrcpy (_fb_config_section, sec);
-   _fb_pixclock = mode->pixclock;
-   _fb_left_margin = mode->left_margin;
-   _fb_right_margin = mode->right_margin;
-   _fb_upper_margin = mode->upper_margin;
-   _fb_lower_margin = mode->lower_margin;
-   _fb_hsync_len = mode->hsync_len;
-   _fb_vsync_len = mode->vsync_len;
-   _fb_vmode = mode->vmode;
-   _fb_sync = mode->sync;
+   return 0;
+}
+
+
+/* I'm not sure whether these work or not -- my Matrox seems capable
+ * of setting whatever you ask it to. */
+
+int _fb_get_pixclock(void)
+{
+   struct fb_var_screeninfo mode;
+   if (ioctl (fbfd, FBIOGET_VSCREENINFO, &mode)) return -1;
+   return mode.pixclock;
+}
+
+void _fb_set_pixclock(int new_val)
+{
+   struct fb_var_screeninfo mode;
+   if (ioctl (fbfd, FBIOGET_VSCREENINFO, &mode)) return;
+   mode.pixclock = new_val;
+   if (ioctl (fbfd, FBIOPUT_VSCREENINFO, &mode)) return;
 }
 
 


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/