From 4eac37eb258bb9c119b9133cd102767f2dc7dbad Mon Sep 17 00:00:00 2001
From: mrwastl <mrwastl@users.sourceforge.net>
Date: Tue, 23 Aug 2011 16:11:09 +0200
Subject: enhancements for AX206dpf support (contr. by Lutz Neumann)

---
 docs/DRIVER.ax206dpf   |  59 +++++-
 glcddrivers/ax206dpf.c | 474 +++++++++++++++++++++++++++++++++++++++----------
 glcddrivers/ax206dpf.h |  34 +++-
 graphlcd.conf          |  35 ++++
 4 files changed, 498 insertions(+), 104 deletions(-)

diff --git a/docs/DRIVER.ax206dpf b/docs/DRIVER.ax206dpf
index 45c3d40..d8faf4b 100644
--- a/docs/DRIVER.ax206dpf
+++ b/docs/DRIVER.ax206dpf
@@ -24,6 +24,17 @@ If your DPF is already hacked, there is no need to install the "dpf-ax" package
 described in "Hacking your DPF". All necessary routines to access a hacked
 DPF are included in this driver.
 
+Multi-display setups
+--------------------
+This driver supports up to 4 displays. The displays can be aligned horizontal,
+vertical or tiled (See "Example multi-display setups").
+
+Hot plugging
+------------
+Hot plugging of displays is supported. The driver will add/remove displays automatically
+as they are plugged in or removed. For multi-display setups the scan for new displays will
+continue until all needed displays are detected.
+
 Non-root users
 --------------
 The driver needs write access to /dev/proc/usb. So the calling user 
@@ -57,9 +68,14 @@ Height
 
 UpsideDown
  Rotates the display output by 180 degrees. This might be useful, if
- the LCD is mounted upside-down.
+ the display is mounted upside-down.
  Possible values: 'yes', 'no'
  Default value: 'no'
+ 
+Brightness
+ Sets the brightness of the display's backlight.
+ Possible values: 0 <= x <= 100)
+ Default value: 100
 
 Portrait
  Select portrait or landscape mode.
@@ -73,6 +89,45 @@ Zoom
  Possible values: 1, 2, 3, 4
  Default value: 1
 
+The following parameters are for multi-display setups only (see "Example multi-display setups").
+
+Horizontal
+Sets the number of displays in horizontal direction.
+ Possible values if Vertical=1: 1, 2, 3, 4
+                 if Vertical=2: 1, 2
+ Default value: 1
+
+Vertical
+ Sets the number of displays in vertical direction.
+ Possible values if Horizontal=1: 1, 2, 3, 4
+                 if Horizontal=2: 1, 2
+ Default value: 1
+
+Flip
+ Rotates a single displays output by 180 degrees. This might be useful, if
+ some displays are mounted upside-down.
+ Possible values for every display : 'y', 'n'
+ Default value: 'nnnn'
+
+
+Example multi-display setups
+----------------------------
+'#' = position of USB-connector
+
+Portrait=y      Portrait=n          Portrait=n      Portrait=n          
+Horizontal=2    Horizontal=2        Horizontal=1    Horizontal=2
+Vertical=1      Vertical=1          Vertical=3      Vertical=2
+Flip=nn         Flip=ny             Flip=nyn        Flip=nyny
+
++-+  +-+        +---+  +---+        +---+           +---+  +---+
+| |  | |      # +---+  +---+#      #+---+          #+---+  +---+#
++-+  +-+          1      2            1               1      2
+ #    #                             +---+           +---+  +---+
+ 1    2                             +---+#         #+---+  +---+#
+                                      2               3      4
+                                    +---+
+                                   #+---+ 
+                                      3
 
 Hacking your DPF
 ----------------
@@ -97,7 +152,7 @@ http://geekparadise.de/2011/04/digitaler-bilderrahmen-von-pearl-als-statusdispla
 [/quote]
 
 So if you are not sure if you have the right DPF or something goes wrong:
-don't ask me ether - I'm like you only a user and not involved in the
+don't ask me either - I'm like you only a user and not involved in the
 development of the hack!
 
 A guide how to hack the Pearl display can be found here (in german):
diff --git a/glcddrivers/ax206dpf.c b/glcddrivers/ax206dpf.c
index 708622a..ce5b15d 100644
--- a/glcddrivers/ax206dpf.c
+++ b/glcddrivers/ax206dpf.c
@@ -24,17 +24,25 @@
  * This file is released under the GNU General Public License. Refer
  * to the COPYING file distributed with this package.
  *
- * (c) 2011      Lutz Neumann <superelchi AT wolke7.de>
+ * (c) 2011      Lutz Neumann <superelchi AT wolke7.net>
  *
  * HISTORY
  * 
  * v0.1 - 10 Aug 2011 - Inital release
+ * V0.2 - 20 Aug 2011 - Optimized display data transfer
+ *                      SetBrightness() implemented
+ *                      Multi-display support
+ * 
  * 
  */
 
 #include <stdio.h>
 #include <syslog.h>
 #include <cstring>
+#include <algorithm>
+#include <pthread.h>
+#include <time.h>
+#include <usb.h>
 
 #include "common.h"
 #include "config.h"
@@ -44,7 +52,24 @@
 
 namespace GLCD
 {
-static     LIBDPF::DPFContext *dpfh;
+typedef struct display_handle {
+    bool attached;
+    char address[8];
+    bool isPortrait;
+    bool rotate90;
+    bool flip;
+    int minx, maxx;
+    int miny, maxy;
+    LIBDPF::DPFContext *dpfh;
+    unsigned char * LCD;
+} DISPLAYHANDLE;
+
+static DISPLAYHANDLE *dh[MAX_DPFS];
+static	pthread_mutex_t libax_mutex;
+static  std::string flips = "";
+static time_t lastscan;
+static int lastbrightness;
+
 
 cDriverAX206DPF::cDriverAX206DPF(cDriverConfig * config)
 :   config(config)
@@ -60,11 +85,25 @@ cDriverAX206DPF::~cDriverAX206DPF()
 int cDriverAX206DPF::Init(void)
 {
     zoom = 1;
-    bool forcePortrait = false;
+    portrait = false;
+    numxdisplays = numydisplays = 1;
+    sizex = sizey = bpp = 0;
+    
+    for (unsigned int i = 0; i < MAX_DPFS; i++)
+    {
+        dh[i] = (DISPLAYHANDLE *) malloc(sizeof(DISPLAYHANDLE));
+        dh[i]->attached = false;
+        dh[i]->address[0] = 0;
+        dh[i]->dpfh = NULL;
+        dh[i]->LCD = NULL;
+    }
+
+    lastbrightness = config->brightness ? config->brightness : 100;
+    
     for (unsigned int i = 0; i < config->options.size(); i++)
     {
         if (config->options[i].name == "Portrait") {
-            forcePortrait = config->options[i].value == "yes";
+            portrait = config->options[i].value == "yes";
         }    
         else if (config->options[i].name == "Zoom") {
             int z = strtol(config->options[i].value.c_str(), (char **) NULL, 0);
@@ -72,77 +111,236 @@ int cDriverAX206DPF::Init(void)
                 zoom = z;
             else
                 syslog(LOG_ERR, "%s error: zoom %d not supported, using default (%d)!\n",config->name.c_str(), z, zoom);
+        }
+        else if (config->options[i].name == "Horizontal") {
+            int h = strtol(config->options[i].value.c_str(), (char **) NULL, 0);
+            if (h > 0 && h <= 4)
+                numxdisplays = h;
+            else
+                syslog(LOG_ERR, "%s error: Horizontal=%d not supported, using default (%d)!\n",config->name.c_str(), h, numxdisplays);
+        }
+        else if (config->options[i].name == "Vertical") {
+            int v = strtol(config->options[i].value.c_str(), (char **) NULL, 0);
+            if (v > 0 && v <= 4)
+                numydisplays = v;
+            else
+                syslog(LOG_ERR, "%s error: Vertical=%d not supported, using default (%d)!\n",config->name.c_str(), v, numydisplays);
+        }
+        else if (config->options[i].name == "Flip") {
+            flips = config->options[i].value;
+            for (unsigned int j = 0; j < flips.size(); j++)
+            {
+                if (flips[j] != 'y' && flips[j] != 'n')
+                {
+                syslog(LOG_ERR, "%s error: flips=%s - illegal character, only 'y' and 'n' supported, using default!\n",config->name.c_str(), flips.c_str());
+                flips = "";
+                break;
+                }
+            }
+        }
+    }
+
+    // See if we have too many displays
+    if (numxdisplays * numydisplays > MAX_DPFS)
+        syslog(LOG_ERR, "%s: too many displays (%dx%d). Max is %d!\n",config->name.c_str(), numxdisplays, numydisplays, MAX_DPFS);    
+    
+    // Init all displays
+    numdisplays = 0;
+   	int error = 0;
+    for (unsigned int i = 0; i < numxdisplays * numydisplays; i++)
+    {
+        error = InitSingleDisplay(i);
+        if (error < 0)
+            return -1;
+        numdisplays++;
+    }
+    
+    if (sizex == 0)
+    {
+        // no displays detected
+        sizex = (portrait) ? DEFAULT_HEIGHT : DEFAULT_WIDTH;
+        sizey = (portrait) ? DEFAULT_WIDTH : DEFAULT_HEIGHT;
+        bpp = DEFAULT_BPP;
+    }
+    
+    // setup temp transfer LCD array
+    tempLCD = (unsigned char *) malloc(sizex * sizey * bpp);
+    
+    width = sizex * numxdisplays;
+    height = sizey * numydisplays;
+    
+    if (zoom > 1)
+    {
+        height /= zoom;
+        width /= zoom;
+    }
     
+    ResetMinMax();
+
+    *oldConfig = *config;
+
+    if (numdisplays == 1)
+        syslog(LOG_INFO, "%s: AX206DPF initialized (%dx%d).\n", config->name.c_str(), width, height);
+    else
+    {
+        unsigned n = 0;
+        for (unsigned int i = 0; i < numdisplays; i++)
+            if (dh[i]->attached) n++;
+        syslog(LOG_INFO, "%s: using %d display(s) (%d online, %d offline).\n", config->name.c_str(), numdisplays, n, numdisplays - n);
+    }
+    
+    lastscan = time(NULL);
+    
+    return 0;
+}
+
+bool cDriverAX206DPF::RescanUSB()
+{
+    bool ret = false;
+    usb_find_busses();
+    if (usb_find_devices() > 0)
+    {
+        unsigned int a = 0, b = 0;
+        for (unsigned int i = 0; i < numdisplays; i++)
+        {
+            if (dh[i]->attached) a |= 0x01 << i;
+            DeInitSingleDisplay(i);
+        }
+        for (unsigned int i = 0; i < numdisplays; i++)
+        {
+            InitSingleDisplay(i);
+            if (dh[i]->attached) b |= 0x01 << i;
         }
+        ret = a != b;
     }
+    return ret;
+}
 
-    // Init display
-        
-   	int error;
+int cDriverAX206DPF::InitSingleDisplay(unsigned int di)
+{
 	char index;
-
+    
     if (config->device.length() != 4 || config->device.compare(0, 3, "dpf"))
         index = '0';
     else
         index = config->device.at(3);
     
-    char usb_device[5];
-    sprintf(usb_device, "usb%c", index);
-        
-	error = dpf_open(usb_device, &dpfh);
-	if (error < 0) {
-        syslog(LOG_INFO, "%s: can not open dpf device number '%c' (error %d).\n", config->name.c_str(), index, error);
-		return -1;
-	}
-
+    char device[5];
+    sprintf(device, "usb%c", index + di);
+    int error = dpf_open(device, &dh[di]->dpfh);
+    if (error < 0)
+    {
+        dh[di]->dpfh = NULL;
+        dh[di]->attached = false;
+        return 0;
+    }
+    dh[di]->attached = true;
+    struct usb_device *dev = usb_device(dh[di]->dpfh->dev.udev);
+    char *s1 = dev->bus->dirname;
+    char *s2 = dev->filename;
+    if (strlen(s1) > 3) s1 = (char *) "???";
+    if (strlen(s2) > 3) s2 = (char *) "???";
+    sprintf(dh[di]->address, "%s:%s", s1, s2);
+    
     // See, if we have to rotate the display
-    isPortrait = dpfh->width < dpfh->height;
-    rotate90 = isPortrait != forcePortrait;
-
-    // Set width / height from display
-    unsigned int vwidth = (!rotate90) ? dpfh->width : dpfh->height;
-    unsigned int vheight = (!rotate90) ? dpfh->height : dpfh->width;
-    width = config->width;
-    if (width <= 0 || width > (int) vwidth)
-        width = (int) vwidth;
-    height = config->height;
-    if (height <= 0 || height > (int) vheight)
-        height = (int) vheight;
-    if (zoom > 1)
+    dh[di]->isPortrait = dh[di]->dpfh->width < dh[di]->dpfh->height;
+    dh[di]->rotate90 = dh[di]->isPortrait != portrait;
+    dh[di]->flip = (!dh[di]->isPortrait && dh[di]->rotate90);    // adjust to make rotate por/land = physical por/land
+    if (flips.size() >= di + 1 && flips[di] == 'y')
+        dh[di]->flip = !dh[di]->flip;
+
+    if (sizex == 0)
     {
-        height /= zoom;
-        width /= zoom;
+        // this is the first display found
+        // Get width / height from this display (all displays have same geometry)
+        sizex = ((!dh[di]->rotate90) ? dh[di]->dpfh->width : dh[di]->dpfh->height);
+        sizey = ((!dh[di]->rotate90) ? dh[di]->dpfh->height : dh[di]->dpfh->width);
+        bpp = dh[di]->dpfh->bpp;
+    }
+    else
+    {
+        // make sure alle displays have the same geometry
+        if ((!(sizex == dh[di]->dpfh->width && sizey == dh[di]->dpfh->height) &&
+            !(sizex == dh[di]->dpfh->height && sizey == dh[di]->dpfh->width)) ||
+            bpp != (unsigned int) dh[di]->dpfh->bpp)
+        {
+        syslog(LOG_INFO, "%s: all displays must have same geometry. Display %d has not. Giving up.\n", config->name.c_str(), di);
+        return -1;
+        }
     }
+    // setup physical lcd arrays
+    dh[di]->LCD = (unsigned char *) malloc(dh[di]->dpfh->height * dh[di]->dpfh->width * dh[di]->dpfh->bpp);
+    ClearSingleDisplay(di);
 
-    // setup lcd array
-    LCD = (unsigned char *) malloc(dpfh->height * dpfh->width * dpfh->bpp);
-    ResetMinMax();
+    // Set Display Brightness
+    SetSingleDisplayBrightness(di, lastbrightness);
 
-    *oldConfig = *config;
+
+    // Reorder displays
+    bool changed = false;
+    for (unsigned int i = 0; i < MAX_DPFS - 1; i++)
+    {
+        for (unsigned int j = i + 1; j < MAX_DPFS; j++)
+            {
+            if (strcmp(dh[i]->address, dh[j]->address) < 0)
+            {
+                DISPLAYHANDLE *h = dh[i];
+                dh[i] = dh[j];
+                dh[j] = h;
+                changed = true;
+            }
+        }
+    }
+
+    //for (unsigned int i = 0; i < MAX_DPFS; i++)
+    //    fprintf(stderr, "Display %d at %s\n", i, (dh[i]->attached) ? dh[i]->address : "-none-");
+    //fprintf(stderr, "\n");
+
+    //fprintf(stderr, "Display %d at %s attached.\n", di, dh[di]->address);
+    //syslog(LOG_INFO, "%s: display %d at %s attached\n", config->name.c_str(), di, dh[di]->address);
     
-    // Set Display Brightness -- NOT WORKING!
-    //SetBrightness(config->brightness ? config->brightness : 100);
-    syslog(LOG_INFO, "%s: AX206DPF initialized (%dx%d).\n", config->name.c_str(), width, height);
     return 0;
 }
 
+void cDriverAX206DPF::DeInitSingleDisplay(unsigned int di)
+{
+    if (dh[di]->dpfh != NULL)
+        dpf_close(dh[di]->dpfh);
+    dh[di]->dpfh = NULL;
+
+    if (dh[di]->LCD != NULL)
+        free(dh[di]->LCD);
+    dh[di]->LCD = NULL;
+    
+    dh[di]->attached = false;
+    dh[di]->address[0] = 0;
+}
+
 int cDriverAX206DPF::DeInit(void)
 {
-    dpf_close(dpfh);
+    // close displays & free lcd arrays
+    for (unsigned int i = 0; i< numdisplays; i++)
+        DeInitSingleDisplay(i);
 
-    // free lcd array
-    if (LCD)
-    {
-        free(LCD);
-    }
+    if (tempLCD)
+        free(tempLCD);
 
     return 0;
 }
 
+
 void cDriverAX206DPF::ResetMinMax(void)
 {
-    miny = dpfh->height - 1;
-    maxy = 0;
+    for (unsigned int i = 0; i < numydisplays; i++)
+    {
+        if (dh[i]->attached)
+        {
+            dh[i]->minx = dh[i]->dpfh->width - 1;
+            dh[i]->maxx = 0;
+            dh[i]->miny = dh[i]->dpfh->height - 1;
+            dh[i]->maxy = 0;
+        }
+    }
 }
 
 int cDriverAX206DPF::CheckSetup(void)
@@ -171,11 +369,22 @@ int cDriverAX206DPF::CheckSetup(void)
     return 0;
 }
 
+void cDriverAX206DPF::ClearSingleDisplay(unsigned int di)
+{
+    if (dh[di]->attached)
+    {
+        memset(dh[di]->LCD, 0, dh[di]->dpfh->width * dh[di]->dpfh->height * dh[di]->dpfh->bpp);       //Black
+        dh[di]->minx = 0;
+        dh[di]->maxx = dh[di]->dpfh->width - 1;
+        dh[di]->miny = 0;
+        dh[di]->maxy = dh[di]->dpfh->height - 1;
+    }
+}
+
 void cDriverAX206DPF::Clear(void)
 {
-    memset(LCD, 0, dpfh->height * dpfh->width * dpfh->bpp);       //Black
-    miny = 0;
-    maxy = dpfh->height - 1;
+    for (unsigned int i = 0; i < numdisplays; i++)
+        ClearSingleDisplay(i);
 }
 
 #define _RGB565_0(p) \
@@ -185,76 +394,142 @@ void cDriverAX206DPF::Clear(void)
 
 void cDriverAX206DPF::SetPixel(int x, int y, uint32_t data)
 {
-    bool flip = config->upsideDown;
-    if (!isPortrait && rotate90)
-    flip = !flip;
+    bool changed = false;
     
-    if (flip)
+    if (config->upsideDown)
     {
-        // upside down orientation
+        // global upside down orientation
         x = width - 1 - x;
-        y = height - 1 - y;
+        y = height - 1 -y;
     }
-    
-    unsigned int i;
-    if (rotate90)
+
+    int sx = sizex / zoom;
+    int sy = sizey / zoom;
+    int di = (y / sy) * numxdisplays + (x / sx);
+    int lx = (x % sx) * zoom;
+    int ly = (y % sy) * zoom;
+
+    if (!dh[di]->attached)
+        return;
+        
+    if (dh[di]->flip)
+    {
+        // local upside down orientation
+        lx = sizex - 1 - lx;
+        ly = sizey - 1 - ly;
+    }
+
+    if (dh[di]->rotate90)
     {
         // wrong Orientation, rotate
-        int xn = y;
-        y = dpfh->height - 1 - x;
-        x = xn;
+        int i = ly;
+        ly = (dh[di]->dpfh->height) - 1 - lx;
+        lx = i;
     }
 
-    if (x >= ((int) dpfh->width / zoom) || y >= ((int) dpfh->height / zoom))
+    if (lx < 0 || lx >= (int) dh[di]->dpfh->width || ly < 0 || ly >= (int) dh[di]->dpfh->height)
+    {
+        syslog(LOG_INFO, "x/y out of bounds (x=%d, y=%d, di=%d, rot=%d, flip=%d, lx=%d, ly=%d)\n", x, y, di, dh[di]->rotate90, dh[di]->flip, lx, ly);
         return;
+    }
 
-    y *= zoom;
+    unsigned char c1 = _RGB565_0(data);
+    unsigned char c2 = _RGB565_1(data);
+    
     if (zoom == 1)
     {
-        i = (y * dpfh->width + x) * dpfh->bpp;
-        LCD[i]   = _RGB565_0(data);
-        LCD[i+1] = _RGB565_1(data);
+        unsigned int i = (ly * dh[di]->dpfh->width + lx) * dh[di]->dpfh->bpp;
+        if (dh[di]->LCD[i] != c1 || dh[di]->LCD[i+1] != c2)
+        {
+            dh[di]->LCD[i]   = c1;
+            dh[di]->LCD[i+1] = c2;
+            changed = true;
+        }
     }
     else
     {
         for (int dy = 0; dy < zoom; dy++)
         {
-            i = ((y + dy) * dpfh->width + (x * zoom)) * dpfh->bpp;
-            for (int dx = 0; dx < zoom * dpfh->bpp; dx += dpfh->bpp)
+            unsigned int i = ((ly + dy) * dh[di]->dpfh->width + lx) * dh[di]->dpfh->bpp;
+            for (int dx = 0; dx < zoom * dh[di]->dpfh->bpp; dx += dh[di]->dpfh->bpp)
             {
-                LCD[i+dx]   = _RGB565_0(data);
-                LCD[i+dx+1] = _RGB565_1(data);
+                if (dh[di]->LCD[i+dx] != c1 || dh[di]->LCD[i+dx+1] != c2)
+                {
+                    dh[di]->LCD[i+dx]   = c1;
+                    dh[di]->LCD[i+dx+1] = c2;
+                    changed = true;
+                }
             }
         }
     }
-    if (y * zoom < miny) miny = y;
-    if (y * zoom > maxy) maxy = y;
+
+    if (changed)
+    {
+        if (lx < dh[di]->minx) dh[di]->minx = lx;
+        if (lx > dh[di]->maxx) dh[di]->maxx = lx;
+        if (ly < dh[di]->miny) dh[di]->miny = ly;
+        if (ly > dh[di]->maxy) dh[di]->maxy = ly;
+    }
 }
 
 void cDriverAX206DPF::Refresh(bool refreshAll)
 {
+	short rect[4];
 
     if (CheckSetup() > 0)
         refreshAll = true;
-
-	short rect[4];
     
-    if (!refreshAll)
+    for (unsigned int di = 0; di < numdisplays; di++)
     {
-	rect[0] = 0; rect[1] = miny;
-	rect[2] = dpfh-> width; rect[3] = maxy + 1;
-    }
-    else
-    {
-	rect[0] = 0; rect[1] = 0;
-	rect[2] = dpfh->width; rect[3] = dpfh->height;
+        if (!dh[di]->attached)
+        {
+            time_t current = time(NULL);
+            if (current - lastscan >= USB_SCAN_INTERVALL)
+            {
+                lastscan = current;
+                if (RescanUSB())
+                    ; //return;             // something changed, wait for next refresh
+            }
+        }
+        
+        if (!dh[di]->attached)
+            continue;
+            
+        if (refreshAll)
+        {
+            dh[di]->minx = 0; dh[di]->miny = 0;
+            dh[di]->maxx = dh[di]->dpfh->width - 1; dh[di]->maxy = dh[di]->dpfh->height - 1;
+        }
+        //fprintf(stderr, "%d: (%d,%d)-(%d,%d) ", di, dh[di]->minx, dh[di]->miny, dh[di]->maxx, dh[di]->maxy);
+        if (dh[di]->minx > dh[di]->maxx || dh[di]->miny > dh[di]->maxy)
+            continue;
+
+        unsigned int cpylength = (dh[di]->maxx - dh[di]->minx + 1) * dh[di]->dpfh->bpp;
+        unsigned char *ps = dh[di]->LCD + (dh[di]->miny * dh[di]->dpfh->width + dh[di]->minx) * dh[di]->dpfh->bpp;
+        unsigned char *pd = tempLCD;
+        for (int y = dh[di]->miny; y <= dh[di]->maxy; y++)
+        {
+            memcpy(pd, ps, cpylength);
+            ps += dh[di]->dpfh->width * dh[di]->dpfh->bpp;
+            pd += cpylength;
+        }
+            
+        rect[0] = dh[di]->minx; rect[1] = dh[di]->miny; rect[2] = dh[di]->maxx + 1; rect[3] = dh[di]->maxy + 1;
+        pthread_mutex_lock(&libax_mutex);
+        int err = dpf_screen_blit(dh[di]->dpfh, tempLCD, rect);
+        pthread_mutex_unlock(&libax_mutex);
+        if (err < 0)
+        {
+            //fprintf(stderr, "Display %d detached (err=%d).\n", di, err);
+            syslog(LOG_INFO, "%s: display %d communication error (%d). Display detached\n", config->name.c_str(), di, err);
+            DeInitSingleDisplay(di);
+            RescanUSB();
+            lastscan = time(NULL);
+        }
     }
     
-    if (rect[1] > rect[3])
-        return;
-        
-	dpf_screen_blit(dpfh, &LCD[rect[1] * dpfh->width * dpfh->bpp], rect);
     ResetMinMax();
+    //fprintf(stderr, "\n");
 }
 
 GLCD::cColor cDriverAX206DPF::GetBackgroundColor(void)
@@ -262,9 +537,11 @@ GLCD::cColor cDriverAX206DPF::GetBackgroundColor(void)
     return GLCD::cColor::Black;
 }
 
-/* NOT WORKING - WILL FREEZE THE DISPLAY FROM TIME TO TIME!
-void cDriverAX206DPF::SetBrightness(unsigned int percent)
+void cDriverAX206DPF::SetSingleDisplayBrightness(unsigned int di, unsigned int percent)
 {
+    if (!dh[di]->attached)
+        return;
+        
     LIBDPF::DPFValue val;
     val.type = LIBDPF::TYPE_INTEGER;
     
@@ -275,9 +552,20 @@ void cDriverAX206DPF::SetBrightness(unsigned int percent)
         val.value.integer = 7;
     else    
         val.value.integer = (((percent * 10) + 167) * 6) / 1000;
-    dpf_setproperty(dpfh, PROPERTY_BRIGHTNESS, &val);
+    pthread_mutex_lock(&libax_mutex);
+    dpf_setproperty(dh[di]->dpfh, PROPERTY_BRIGHTNESS, &val);
+    pthread_mutex_unlock(&libax_mutex);
+}
+
+void cDriverAX206DPF::SetBrightness(unsigned int percent)
+{
+    lastbrightness = percent;
+    
+    for (unsigned int i = 0; i < numdisplays; i++)
+    {
+        SetSingleDisplayBrightness(i, percent);
+    }
 }
-*/
 
 bool cDriverAX206DPF::GetDriverFeature(const std::string & Feature, int & value)
 {
@@ -438,7 +726,7 @@ int dpf_open(const char *dev, DPFHANDLE *h)
 	//if (dpf->mode == MODE_USB) {
 		dpf->dev.udev = u;
 		error = probe(dpf);
-		fprintf(stderr, "Got LCD dimensions: %dx%d\n", dpf->width, dpf->height);
+		//fprintf(stderr, "Got LCD dimensions: %dx%d\n", dpf->width, dpf->height);
 	//} else {
 	//	dpf->dev.fd = fd;
 	//}
@@ -586,7 +874,7 @@ int check_known_device(struct usb_device *d)
 	while (dev->desc) {
 		if ((d->descriptor.idVendor == dev->vid) &&
 			(d->descriptor.idProduct == dev->pid)) { 
-				fprintf(stderr, "Found %s\n", dev->desc);
+				//fprintf(stderr, "Found %s at %s:%s\n", dev->desc, d->bus->dirname, d->filename);
 				return 1;
 		}
 		dev++;
diff --git a/glcddrivers/ax206dpf.h b/glcddrivers/ax206dpf.h
index 1446924..dc94873 100644
--- a/glcddrivers/ax206dpf.h
+++ b/glcddrivers/ax206dpf.h
@@ -23,7 +23,7 @@
  * This file is released under the GNU General Public License. Refer
  * to the COPYING file distributed with this package.
  *
- * (c) 2011      Lutz Neumann <superelchi AT wolke7.de>
+ * (c) 2011      Lutz Neumann <superelchi AT wolke7.net>
  * 
  * HISTORY
  * 
@@ -35,26 +35,42 @@
 
 #include "driver.h"
 
-
 namespace GLCD
 {
+#define MAX_DPFS 4
+
+#define DEFAULT_WIDTH  320
+#define DEFAULT_HEIGHT 240
+#define DEFAULT_BPP    2
+
+#define USB_SCAN_INTERVALL  10       // seconds between usb scans for missing displays
 
 class cDriverConfig;
 
 class cDriverAX206DPF : public cDriver
 {
 private:
-    unsigned char * LCD;
     cDriverConfig * config;
     cDriverConfig * oldConfig;
 
-    int zoom;
-    bool isPortrait;
-    bool rotate90;
-    int miny, maxy;
-
+    unsigned char * tempLCD;        // temp transfer buffer
+
+    bool portrait;                  // portrait or landscape mode
+    int zoom;                       // pixel zoom factor
+    unsigned int numdisplays;       // number of detected displays
+    unsigned int numxdisplays;      // number of displays (horizontal)
+    unsigned int numydisplays;      // number of displays (vertical)
+    unsigned int sizex;             // logical horizontal size of one display
+    unsigned int sizey;             // logical vertical size of one display
+    unsigned int bpp;               // bits per pixel
+    
     int CheckSetup();
     void ResetMinMax();
+    bool RescanUSB();
+    int InitSingleDisplay(unsigned int);
+    void DeInitSingleDisplay(unsigned int);
+    void ClearSingleDisplay(unsigned int);
+    void SetSingleDisplayBrightness(unsigned int, unsigned int);
 
 public:
     cDriverAX206DPF(cDriverConfig * config);
@@ -67,7 +83,7 @@ public:
     virtual void SetPixel(int x, int y, uint32_t data);
     virtual void Refresh(bool refreshAll = false);
     virtual GLCD::cColor GetBackgroundColor(void);
-    //virtual void SetBrightness(unsigned int);
+    virtual void SetBrightness(unsigned int);
     virtual bool GetDriverFeature  (const std::string & Feature, int & value);
 };
 
diff --git a/graphlcd.conf b/graphlcd.conf
index 027e6f4..bb5a0f5 100644
--- a/graphlcd.conf
+++ b/graphlcd.conf
@@ -575,8 +575,19 @@ Driver=dm140gink
 Driver=ax206dpf
 #Width=320
 #Height=240
+#
+# UpsideDown
+#  Rotates the display output by 180 degrees. This might be useful, if
+#  the LCD is mounted upside-down.
+#  Possible values: 'yes', 'no'
+#  Default value: 'no'
 #UpsideDown=no
 #
+# Brightness
+#  Sets the brightness of the display's backlight
+#  Possible values: 0 <= x <= 100)
+#  Default value: 100
+#Brightness=100
 #
 # Device
 #  Selects a specific display
@@ -597,3 +608,27 @@ Driver=ax206dpf
 #  Possible values: 1, 2, 3, 4
 #  Default value: 1
 #Zoom=1
+#
+# The following parameters are for multi-display setups only!
+#
+# Horizontal
+#  Sets the number of displays in horizontal direction.
+#  Possible values if Vertical=1: 1, 2, 3, 4
+#                  if Vertical=2: 1, 2
+#  Default value: 1
+#Horizontal=1
+#
+# Vertical
+#  Sets the number of displays in vertical direction.
+#  Possible values if Horizontal=1: 1, 2, 3, 4
+#                  if Horizontal=2: 1, 2
+#  Default value: 1
+#Vertical=1
+#
+# Flip
+#  Rotates a single displays output by 180 degrees. This might be useful, if
+#  some LCDs are mounted upside-down.
+#  Possible values for every display : 'y', 'n'
+#  Default value: 'nnnn'
+#Flip=nnnn
+#
-- 
cgit v1.2.3