package/lcd4linux: add driver for the LCD of the TEW-673GRU board
authorGabor Juhos <juhosg@openwrt.org>
Wed, 8 Feb 2012 16:39:20 +0000 (16:39 +0000)
committerGabor Juhos <juhosg@openwrt.org>
Wed, 8 Feb 2012 16:39:20 +0000 (16:39 +0000)
SVN-Revision: 30381

utils/lcd4linux/Config.in
utils/lcd4linux/Makefile
utils/lcd4linux/patches/170-add-generic-spidev-driver.patch [new file with mode: 0644]
utils/lcd4linux/patches/171-allow-to-specify-drv_generic_graphic_real_clear.patch [new file with mode: 0644]
utils/lcd4linux/patches/172-add-TEW673GRU-driver.patch [new file with mode: 0644]

index 94fa254357351269a921520d425510a6921c4b1f..621a5d39b9c9e85fca7a0b68d57775e31e0f6648 100644 (file)
@@ -240,6 +240,12 @@ config LCD4LINUX_CUSTOM_DRIVER_T6963
        prompt "T6963"
        select LCD4LINUX_CUSTOM_NEEDS_libgd
 
+config LCD4LINUX_CUSTOM_DRIVER_TEW673GRU
+       bool
+       select LCD4LINUX_CUSTOM_NEEDS_libgd
+       depends on TARGET_ar71xx
+       default TARGET_ar71xx
+
 config LCD4LINUX_CUSTOM_DRIVER_Trefon
        bool
        prompt "Trefon"
index e0aa272ef45409acd650eb0c9e9ce7626147437a..52bf057f10cbdce903845cf88e87436d74371e67 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2007-2011 OpenWrt.org
+# Copyright (C) 2007-2012 OpenWrt.org
 #
 # This is free software, licensed under the GNU General Public License v2.
 # See /LICENSE for more information.
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
 PKG_NAME:=lcd4linux
 PKG_REV:=1159
 PKG_VERSION:=r$(PKG_REV)
-PKG_RELEASE:=4
+PKG_RELEASE:=5
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=https://ssl.bulix.org/svn/lcd4linux/trunk/
@@ -58,6 +58,7 @@ LCD4LINUX_DRIVERS:= \
        SimpleLCD \
        st2205 \
        T6963 \
+       $(if $(CONFIG_TARGET_ar71xx),TEW673GRU) \
        Trefon \
        USBHUB \
        USBLCD \
diff --git a/utils/lcd4linux/patches/170-add-generic-spidev-driver.patch b/utils/lcd4linux/patches/170-add-generic-spidev-driver.patch
new file mode 100644 (file)
index 0000000..5b5741b
--- /dev/null
@@ -0,0 +1,289 @@
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -71,6 +71,8 @@ drv_generic_i2c.c             \
+ drv_generic_i2c.h             \
+ drv_generic_keypad.c          \
+ drv_generic_keypad.h          \
++drv_generic_spidev.c          \
++drv_generic_spidev.h          \
+ drv_ASTUSB.c                  \
+ drv_BeckmannEgle.c            \
+ drv_BWCT.c                    \
+--- /dev/null
++++ b/drv_generic_spidev.c
+@@ -0,0 +1,89 @@
++/* $Id$
++ * $URL$
++ *
++ * generic driver helper for displays connected via SPI bus
++ *
++ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
++ *
++ * This file is part of LCD4Linux.
++ *
++ * LCD4Linux is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * LCD4Linux is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#include "config.h"
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <errno.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/ioctl.h>
++
++#include "debug.h"
++#include "qprintf.h"
++#include "cfg.h"
++#include "drv_generic_spidev.h"
++
++static char *generic_spidev_section = "";
++static char *generic_spidev_driver = "";
++static int generic_spidev_fd;
++
++int drv_generic_spidev_open(const char *section, const char *driver)
++{
++    char *spidev;
++
++    udelay_init();
++
++    generic_spidev_section = (char *) section;
++    generic_spidev_driver = (char *) driver;
++
++    spidev = cfg_get(generic_spidev_section, "Port", NULL);
++
++    info("%s: initializing SPI device %s", generic_spidev_driver, spidev);
++    generic_spidev_fd = open(spidev, O_WRONLY);
++    if (generic_spidev_fd < 0) {
++      error("%s: unable to open SPI device %s!\n", generic_spidev_driver, spidev);
++      goto exit_error;
++    }
++
++    return 0;
++
++  exit_error:
++    free(spidev);
++    return -1;
++}
++
++int drv_generic_spidev_close(void)
++{
++    close(generic_spidev_fd);
++    return 0;
++}
++
++int drv_generic_spidev_transfer(const int count, struct spi_ioc_transfer *tr)
++{
++    int ret;
++
++    ret = ioctl(generic_spidev_fd, SPI_IOC_MESSAGE(count), tr);
++    if (ret < count) {
++      error("%s: can't send SPI message! (%s)\n",
++              generic_spidev_driver, strerror(errno));
++      return -1;
++    }
++
++    return 0;
++}
+--- /dev/null
++++ b/drv_generic_spidev.h
+@@ -0,0 +1,54 @@
++/* $Id$
++ * $URL$
++ *
++ * generic driver helper for displays connected via SPI bus
++ *
++ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
++ * Copyright (C) 2012 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
++ *
++ * This file is part of LCD4Linux.
++ *
++ * LCD4Linux is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * LCD4Linux is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++/*
++ *
++ * exported fuctions:
++ *
++ * int drv_generic_spidev_open (const char *section, const char *driver)
++ *   reads 'Port' entry from config and opens
++ *   the SPI device
++ *   returns 0 if ok, -1 on failure
++ *
++ * int drv_generic_spidev_close (void)
++ *   closes SPI device
++ *   returns 0 if ok, -1 on failure
++ *
++ * void drv_generic_spidev_transfer (int count, struct spi_ioc_transfer *tr)
++ *   transfer data to/from the SPI device
++ *
++ */
++
++#ifndef _DRV_GENERIC_SPIDEV_H_
++#define _DRV_GENERIC_SPIDEV_H_
++
++#include <linux/spi/spidev.h>
++
++int drv_generic_spidev_open(const char *section, const char *driver);
++int drv_generic_spidev_close(void);
++int drv_generic_spidev_transfer(const int count, struct spi_ioc_transfer *tr);
++
++#endif /* _DRV_GENERIC_SPIDEV_H_ */
+--- a/drivers.m4
++++ b/drivers.m4
+@@ -232,9 +232,9 @@ for driver in $drivers; do
+       serdisplib)
+          SERDISPLIB=$val;
+          ;;
+-      ShuttleVFD) 
++      ShuttleVFD)
+        SHUTTLEVFD=$val
+-       ;;          
++       ;;
+       SimpleLCD)
+          SIMPLELCD=$val
+          ;;
+@@ -285,6 +285,7 @@ PARPORT="no"
+ SERIAL="no"
+ I2C="no"
+ KEYPAD="no"
++SPIDEV="no"
+ # generic libraries
+ LIBUSB="no"
+@@ -544,17 +545,17 @@ if test "$MATRIXORBITALGX" = "yes"; then
+     fi
+ fi
+-if test "$MDM166A" = "yes"; then 
+-   if test "$has_usb10" = "true"; then 
++if test "$MDM166A" = "yes"; then
++   if test "$has_usb10" = "true"; then
+       GRAPHIC="yes"
+-      DRIVERS="$DRIVERS drv_mdm166a.o" 
+-      GPIO="yes" 
++      DRIVERS="$DRIVERS drv_mdm166a.o"
++      GPIO="yes"
+       LIBUSB10="yes"
+-      AC_DEFINE(WITH_MDM166A,1,[MDM166A driver]) 
+-    else 
+-      AC_MSG_WARN(libusb-1.0/libusb.h not found: MDM166A driver disabled) 
+-    fi 
+-fi 
++      AC_DEFINE(WITH_MDM166A,1,[MDM166A driver])
++    else
++      AC_MSG_WARN(libusb-1.0/libusb.h not found: MDM166A driver disabled)
++    fi
++fi
+ if test "$MILINST" = "yes"; then
+    TEXT="yes"
+@@ -630,7 +631,7 @@ if test "$PICOLCDGRAPHIC" = "yes"; then
+    if test "$has_usb" = "true"; then
+       TEXT="yes"
+       GRAPHIC="yes"
+-      KEYPAD="yes"      
++      KEYPAD="yes"
+       GPIO="yes"
+       SERIAL="yes"
+       LIBUSB="yes"
+@@ -698,17 +699,17 @@ if test "$SERDISPLIB" = "yes"; then
+    fi
+ fi
+-if test "$SHUTTLEVFD" = "yes"; then 
+-   if test "$has_usb" = "true"; then 
+-      TEXT="yes" 
+-      GPIO="yes" 
+-      DRIVERS="$DRIVERS drv_ShuttleVFD.o" 
+-      LIBUSB="yes" 
+-      AC_DEFINE(WITH_SHUTTLEVFD,1,[ShuttleVFD driver]) 
+-    else 
+-      AC_MSG_WARN(usb.h not found: ShuttleVFD driver disabled) 
+-    fi 
+-fi 
++if test "$SHUTTLEVFD" = "yes"; then
++   if test "$has_usb" = "true"; then
++      TEXT="yes"
++      GPIO="yes"
++      DRIVERS="$DRIVERS drv_ShuttleVFD.o"
++      LIBUSB="yes"
++      AC_DEFINE(WITH_SHUTTLEVFD,1,[ShuttleVFD driver])
++    else
++      AC_MSG_WARN(usb.h not found: ShuttleVFD driver disabled)
++    fi
++fi
+ if test "$SIMPLELCD" = "yes"; then
+    TEXT="yes"
+@@ -786,7 +787,7 @@ fi
+ if test "$VNC" = "yes"; then
+    if test "$has_vncserverlib" = "true"; then
+       GRAPHIC="yes"
+-      KEYPAD="yes"      
++      KEYPAD="yes"
+       DRIVERS="$DRIVERS drv_vnc.o"
+       DRVLIBS="$DRVLIBS -L/usr/local/lib -lvncserver -lz"
+       AC_DEFINE(WITH_VNC,1,[vnc driver])
+@@ -874,6 +875,12 @@ if test "$KEYPAD" = "yes"; then
+    DRIVERS="$DRIVERS drv_generic_keypad.o"
+ fi
++# generic spidev driver
++if test "$SPIDEV" = "yes"; then
++   DRIVERS="$DRIVERS drv_generic_spidev.o"
++   AC_DEFINE(WITH_SPIDEV, 1, [SPIDEV driver])
++fi
++
+ # libusb
+ if test "$LIBUSB" = "yes"; then
+    DRVLIBS="$DRVLIBS -lusb"
+@@ -892,6 +899,6 @@ fi
+ if test "$DRIVERS" = ""; then
+    AC_MSG_ERROR([You should include at least one driver...])
+ fi
+-   
++
+ AC_SUBST(DRIVERS)
+ AC_SUBST(DRVLIBS)
+--- a/configure.in
++++ b/configure.in
+@@ -108,6 +108,9 @@ fi
+ #AC_CHECK_HEADERS(asm/io.h)
+ AC_CHECK_HEADERS([asm/io.h] [linux/parport.h linux/ppdev.h], [has_parport="true"], [has_parport="false"])
++# check for spidev
++AC_CHECK_HEADERS([linux/spi/spidev.h], [has_spidev="true"], [has_spidev="false"])
++
+ # drivers
+ sinclude(drivers.m4)
diff --git a/utils/lcd4linux/patches/171-allow-to-specify-drv_generic_graphic_real_clear.patch b/utils/lcd4linux/patches/171-allow-to-specify-drv_generic_graphic_real_clear.patch
new file mode 100644 (file)
index 0000000..5a26643
--- /dev/null
@@ -0,0 +1,45 @@
+--- a/drv_generic_graphic.c
++++ b/drv_generic_graphic.c
+@@ -24,7 +24,7 @@
+  *
+  */
+-/* 
++/*
+  *
+  * exported functions:
+  *
+@@ -98,6 +98,9 @@ static int INVERTED = 0;
+ /* must be implemented by the real driver */
+ void (*drv_generic_graphic_real_blit) () = NULL;
++/* can be implemented by the real driver */
++void (*drv_generic_graphic_real_clear) () = NULL;
++
+ /****************************************/
+ /*** generic Framebuffer stuff        ***/
+@@ -691,7 +694,10 @@ int drv_generic_graphic_clear(void)
+       for (i = 0; i < LCOLS * LROWS; i++)
+           drv_generic_graphic_FB[l][i] = NO_COL;
+-    drv_generic_graphic_blit(0, 0, LROWS, LCOLS);
++    if (drv_generic_graphic_real_clear)
++      drv_generic_graphic_real_clear(NO_COL);
++    else
++      drv_generic_graphic_blit(0, 0, LROWS, LCOLS);
+     return 0;
+ }
+--- a/drv_generic_graphic.h
++++ b/drv_generic_graphic.h
+@@ -40,6 +40,9 @@ extern RGBA NO_COL;          /* no color (comple
+ /* these functions must be implemented by the real driver */
+ extern void (*drv_generic_graphic_real_blit) (const int row, const int col, const int height, const int width);
++/* these functions can be implemented by the real driver */
++void (*drv_generic_graphic_real_clear) (const RGBA rgba);
++
+ /* helper function to get pixel color or gray value */
+ extern RGBA drv_generic_graphic_rgb(const int row, const int col);
+ extern unsigned char drv_generic_graphic_gray(const int row, const int col);
diff --git a/utils/lcd4linux/patches/172-add-TEW673GRU-driver.patch b/utils/lcd4linux/patches/172-add-TEW673GRU-driver.patch
new file mode 100644 (file)
index 0000000..40a569a
--- /dev/null
@@ -0,0 +1,553 @@
+--- a/drivers.m4
++++ b/drivers.m4
+@@ -38,7 +38,7 @@ AC_ARG_WITH(
+   [                        LW_ABP, M50530, MatrixOrbital, MatrixOrbitalGX, MilfordInstruments, MDM166A,]
+   [                        Newhaven, Noritake, NULL, Pertelian, PHAnderson,]
+   [                        PICGraphic, picoLCD, picoLCDGraphic, PNG, PPM, RouterBoard,]
+-  [                        Sample, serdisplib, ShuttleVFD, SimpleLCD, st2205, T6963,]
++  [                        Sample, serdisplib, ShuttleVFD, SimpleLCD, st2205, T6963, TEW673GRU]
+   [                        Trefon, ULA200, USBHUB, USBLCD, VNC, WincorNixdorf, X11],
+   drivers=$withval,
+   drivers=all
+@@ -104,6 +104,7 @@ for driver in $drivers; do
+        SHUTTLEVFD="yes"
+          SIMPLELCD="yes"
+          T6963="yes"
++         TEW673GRU="yes"
+          Trefon="yes"
+          ULA200="yes"
+        USBHUB="yes"
+@@ -244,6 +245,9 @@ for driver in $drivers; do
+       T6963)
+          T6963=$val
+          ;;
++      TEW673GRU)
++         TEW673GRU=$val
++         ;;
+       Trefon)
+          Trefon=$val
+          ;;
+@@ -740,6 +744,18 @@ if test "$T6963" = "yes"; then
+    fi
+ fi
++if test "$TEW673GRU" = "yes"; then
++   if test "$has_spidev" = "true"; then
++      GRAPHIC="yes"
++      TEXT="yes"
++      SPIDEV="yes"
++      DRIVERS="$DRIVERS drv_TEW673GRU.o"
++      AC_DEFINE(WITH_TEW673GRU,1,[TEW673GRU driver])
++   else
++      AC_MSG_WARN(linux/spi/spidev.h not found: TEW673GRU driver disabled)
++   fi
++fi
++
+ if test "$Trefon" = "yes"; then
+    if test "$has_usb" = "true"; then
+       TEXT="yes"
+--- a/drv.c
++++ b/drv.c
+@@ -89,6 +89,7 @@ extern DRIVER drv_serdisplib;
+ extern DRIVER drv_ShuttleVFD;
+ extern DRIVER drv_SimpleLCD;
+ extern DRIVER drv_T6963;
++extern DRIVER drv_TEW673GRU;
+ extern DRIVER drv_Trefon;
+ extern DRIVER drv_ula200;
+ extern DRIVER drv_USBHUB;
+@@ -232,6 +233,9 @@ DRIVER *Driver[] = {
+ #ifdef WITH_T6963
+     &drv_T6963,
+ #endif
++#ifdef WITH_TEW673GRU
++    &drv_TEW673GRU,
++#endif
+ #ifdef WITH_TREFON
+     &drv_Trefon,
+ #endif
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -115,6 +115,7 @@ drv_serdisplib.c              \
+ drv_ShuttleVFD.c              \
+ drv_SimpleLCD.c               \
+ drv_T6963.c                   \
++drv_TEW673GRU.c               \
+ drv_Trefon.c                  \
+ drv_ula200.c                  \
+ drv_USBHUB.c                  \
+--- /dev/null
++++ b/drv_TEW673GRU.c
+@@ -0,0 +1,457 @@
++/* $Id$
++ * $URL$
++ *
++ * TRENDnet TEW673GRU LCD4linux driver
++ *
++ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
++ *
++ * based on the Sample driver which is:
++ * Copyright (C) 2005 Michael Reinelt <michael@reinelt.co.at>
++ * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
++ *
++ * This file is part of LCD4Linux.
++ *
++ * LCD4Linux is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * LCD4Linux is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++/*
++ * exported fuctions:
++ *
++ * struct DRIVER drv_TEW673GRU
++ */
++
++#include "config.h"
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++
++#include "debug.h"
++#include "cfg.h"
++#include "qprintf.h"
++#include "udelay.h"
++#include "plugin.h"
++#include "widget.h"
++#include "widget_text.h"
++#include "widget_icon.h"
++#include "widget_bar.h"
++#include "drv.h"
++
++#include "drv_generic_text.h"
++#include "drv_generic_graphic.h"
++#include "drv_generic_spidev.h"
++
++#ifdef WITH_DMALLOC
++#include <dmalloc.h>
++#endif
++
++#define TEW673GRU_NUM_COLS    220
++#define TEW673GRU_NUM_ROWS    176
++#define TEW673GRU_BPP         2       /* bytes per pixel */
++
++#define TEW673GRU_CMD_SIZE    9
++#define TEW673GRU_NUM_ROW_BYTES (TEW673GRU_NUM_COLS * TEW673GRU_BPP)
++
++enum {
++      CMD_SHOW_STRING         = 49,
++      CMD_SHOW_IMAGE_DIR      = 52,
++      CMD_SCREEN_COLOR        = 54,
++};
++
++static char Name[] = "TEW673GRU";
++
++static char *drv_TEW673GRU_FB;
++static long FB_SIZE;
++static int MODE;
++
++static unsigned int RGBAto16(RGBA rgb)
++{
++    return (((rgb.R >> 3) << 11) | ((rgb.G >> 2) << 5) | (rgb.B >> 3));
++}
++
++static unsigned char color_msb(unsigned int color)
++{
++    return color >> 8;
++}
++
++static unsigned char color_lsb(unsigned int color)
++{
++    return color & 0xff;
++}
++
++static void drv_TEW673GRU_hw_clear(const unsigned int color)
++{
++    unsigned char cmd[TEW673GRU_CMD_SIZE];
++    struct spi_ioc_transfer tr[1];
++
++    memset(tr, '\0', sizeof(tr));
++    memset(cmd, '\0', sizeof(cmd));
++
++    cmd[0] = CMD_SCREEN_COLOR;
++    cmd[7] = color_msb(color);
++    cmd[8] = color_lsb(color);
++
++    tr[0].tx_buf = (unsigned long) cmd;
++    tr[0].len = sizeof(cmd);
++
++    drv_generic_spidev_transfer(1, tr);
++}
++
++static void drv_TEW673GRU_hw_send_row(const int row, const int col, const char *data, const int width)
++{
++    unsigned char cmd[TEW673GRU_CMD_SIZE];
++    struct spi_ioc_transfer tr[2];
++    int datasize;
++
++    memset(tr, '\0', sizeof(tr));
++    memset(cmd, '\0', sizeof(cmd));
++
++    datasize =  width * TEW673GRU_BPP;
++
++    cmd[0] = CMD_SHOW_IMAGE_DIR;
++    cmd[1] = col;
++    cmd[2] = col + width;
++    cmd[3] = row;
++    cmd[4] = row;
++    cmd[5] = datasize >> 8;
++    cmd[6] = datasize & 0xff;
++
++    tr[0].tx_buf = (unsigned long) cmd;
++    tr[0].len = sizeof(cmd);
++    tr[1].tx_buf = (unsigned long) data;
++    tr[1].len = datasize;
++
++    drv_generic_spidev_transfer(2, tr);
++}
++
++static void drv_TEW673GRU_hw_write_string(const int row, const int col, const char *data, const int datasize)
++{
++    unsigned char cmd[TEW673GRU_CMD_SIZE];
++    struct spi_ioc_transfer tr[2];
++    unsigned char len;
++
++    memset(tr, '\0', sizeof(tr));
++    memset(cmd, '\0', sizeof(cmd));
++
++    len = datasize & 0xff;
++    cmd[0] = CMD_SHOW_STRING;
++    cmd[1] = col;
++    cmd[2] = col + (XRES * len);
++    cmd[3] = row;
++    cmd[4] = row + YRES;
++    cmd[7] = 0;
++    cmd[8] = len;
++
++    tr[0].tx_buf = (unsigned long) cmd;
++    tr[0].len = sizeof(cmd);
++    tr[1].tx_buf = (unsigned long) data;
++    tr[1].len = datasize;
++
++    drv_generic_spidev_transfer(2, tr);
++}
++
++static void drv_TEW673GRU_FB_set_pixel(const int col, const unsigned int color)
++{
++    int pos;
++
++    pos = col * TEW673GRU_BPP;
++    drv_TEW673GRU_FB[pos] = color_msb(color);
++    drv_TEW673GRU_FB[pos + 1] = color_lsb(color);
++}
++
++static void drv_TEW673GRU_blit(const int row, const int col, const int height, const int width)
++{
++    int r, c;
++
++    debug("%s: update area r:%d c:%d w:%d h:%d", Name, row, col, height, width);
++
++    for (r = row; r < row + height; r++) {
++      for (c = col; c < col + width; c++) {
++          unsigned int color;
++          RGBA rgb;
++
++          rgb = drv_generic_graphic_rgb(r, c);
++          color = RGBAto16(rgb);
++            drv_TEW673GRU_FB_set_pixel(c, color);
++      }
++
++      if (width) {
++          char *data;
++
++          data = &drv_TEW673GRU_FB[col * TEW673GRU_BPP];
++          drv_TEW673GRU_hw_send_row(r, col, data, width);
++          udelay(100 + width * 50);
++      }
++    }
++}
++
++static void drv_TEW673GRU_clear(RGBA rgba)
++{
++    unsigned int color;
++
++    color = RGBAto16(rgba);
++    drv_TEW673GRU_hw_clear(color);
++}
++
++static void drv_TEW673GRU_write(const int row, const int col, const char *data, const int len)
++{
++    int i;
++
++    for (i = 0; i < len; i++) {
++      drv_TEW673GRU_hw_write_string(row * YRES, (col + i) * XRES, " ", 1);
++      udelay(10000);
++      drv_TEW673GRU_hw_write_string(row * YRES, 2 + (col + i) * XRES, " ", 1);
++      udelay(10000);
++      drv_TEW673GRU_hw_write_string(row * YRES, (col + i) * XRES, &data[i], 1);
++      udelay(10000);
++    }
++}
++
++static int drv_TEW673GRU_open(const char *section)
++{
++    int err;
++
++    err = drv_generic_spidev_open(section, Name);
++    if (err < 0)
++      return err;
++
++    return 0;
++}
++
++static int drv_TEW673GRU_close(void)
++{
++    drv_generic_spidev_close();
++    return 0;
++}
++
++static void drv_TEW673GRU_clear_screen(void)
++{
++    if (MODE) {
++      drv_generic_graphic_clear();
++    } else {
++      memset(drv_TEW673GRU_FB, ' ', FB_SIZE);
++      drv_TEW673GRU_hw_clear(0x0000);
++    }
++}
++
++static int drv_TEW673GRU_init_font(const char *section)
++{
++    char *font;
++    int ret = -1;
++
++    font = cfg_get(section, "Font", "6x8");
++    if (font == NULL) {
++      error("%s: no '%s.Font' entry from %s", Name, section, cfg_source());
++      goto out;
++    }
++
++    if (*font == '\0') {
++      error("%s: invalid '%s.Font' entry in %s", Name, section, cfg_source());
++      goto out_free;
++    }
++
++    XRES = -1;
++    YRES = -1;
++    if (sscanf(font, "%dx%d", &XRES, &YRES) != 2 ||
++      XRES < 1 ||
++      YRES < 1) {
++      error("%s: bad Font '%s' from %s", Name, font, cfg_source());
++      goto out_free;
++    }
++
++    if (XRES != 6 && YRES != 8) {
++      error("%s: bad Font '%s' from %s (only 6x8 at the moment)",
++            Name, font, cfg_source());
++      goto out_free;
++    }
++
++    error("%s: font '%s' selected", Name, font);
++
++    ret = 0;
++
++out_free:
++    free(font);
++out:
++    return ret;
++}
++
++static int drv_TEW673GRU_start(const char *section)
++{
++    int ret;
++
++    DCOLS = TEW673GRU_NUM_COLS;
++    DROWS = TEW673GRU_NUM_ROWS;
++
++    if (MODE) {
++      ret = drv_TEW673GRU_init_font(section);
++      if (ret)
++          goto err;
++
++      FB_SIZE = DCOLS * TEW673GRU_BPP;
++    } else {
++      XRES = 10;
++      YRES = 16;
++      DCOLS = DCOLS / XRES;
++      DROWS = DROWS / YRES;
++
++      FB_SIZE = DCOLS * DROWS;
++    }
++
++    if (FB_SIZE) {
++      drv_TEW673GRU_FB = malloc(FB_SIZE);
++      if (drv_TEW673GRU_FB == NULL) {
++          error("%s: framebuffer could not be allocated", Name);
++          goto err;
++      }
++    }
++
++    ret = drv_TEW673GRU_open(section);
++    if (ret < 0)
++        goto err_free;
++
++    if (MODE == 0)
++      drv_TEW673GRU_clear_screen();
++
++    return 0;
++
++ err_free:
++    if (drv_TEW673GRU_FB)
++      free(drv_TEW673GRU_FB);
++ err:
++    return -1;
++}
++
++static int drv_TEW673GRU_greet(const char *msg1, const char *msg2)
++{
++    if (MODE)
++      return drv_generic_graphic_greet(msg1, msg2);
++
++    return drv_generic_text_greet(msg1, msg2);
++}
++
++
++/****************************************/
++/***        widget callbacks          ***/
++/****************************************/
++
++
++/* using drv_generic_text_draw(W) */
++/* using drv_generic_text_icon_draw(W) */
++/* using drv_generic_text_bar_draw(W) */
++/* using drv_generic_gpio_draw(W) */
++
++
++/****************************************/
++/***        exported functions        ***/
++/****************************************/
++
++int drv_TEW673GRU_list(void)
++{
++    printf("TEW673GRU driver");
++    return 0;
++}
++
++int drv_TEW673GRU_init(const char *section, const int quiet)
++{
++    WIDGET_CLASS wc;
++    int ret;
++
++    cfg_number(section, "Mode", 0, 0, 1, &MODE);
++
++    if (MODE) {
++      drv_generic_graphic_real_blit = drv_TEW673GRU_blit;
++      drv_generic_graphic_real_clear = drv_TEW673GRU_clear;
++    } else {
++      drv_generic_text_real_write = drv_TEW673GRU_write;
++    }
++
++    ret = drv_TEW673GRU_start(section);
++    if (ret)
++      return ret;
++
++    if (MODE) {
++      ret = drv_generic_graphic_init(section, Name);
++      if (ret)
++          return ret;
++    } else {
++      ret = drv_generic_text_init(section, Name);
++      if (ret)
++          return ret;
++
++      ret = drv_generic_text_icon_init();
++      if (ret != 0)
++          return ret;
++
++      ret = drv_generic_text_bar_init(1);
++      if (ret != 0)
++          return ret;
++
++      drv_generic_text_bar_add_segment(0, 0, 255, ' ');
++      drv_generic_text_bar_add_segment(255, 255, 255, '#');
++
++      wc = Widget_Text;
++      wc.draw = drv_generic_text_draw;
++      widget_register(&wc);
++
++      wc = Widget_Icon;
++      wc.draw = drv_generic_text_icon_draw;
++      widget_register(&wc);
++
++      wc = Widget_Bar;
++      wc.draw = drv_generic_text_bar_draw;
++      widget_register(&wc);
++    }
++
++    if (!quiet) {
++      char buffer[40];
++      qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, DCOLS, DROWS);
++      drv_TEW673GRU_greet(buffer, "www.openwrt.org");
++      sleep(3);
++      drv_TEW673GRU_clear_screen();
++    }
++
++    return 0;
++}
++
++int drv_TEW673GRU_quit(const int quiet)
++{
++
++    info("%s: shutting down.", Name);
++
++    drv_TEW673GRU_clear_screen();
++
++    if (!quiet)
++      drv_TEW673GRU_greet("goodbye!", NULL);
++
++    if (MODE)
++      drv_generic_graphic_quit();
++    else
++      drv_generic_text_quit();
++
++    debug("closing connection");
++    drv_TEW673GRU_close();
++
++    return (0);
++}
++
++DRIVER drv_TEW673GRU = {
++    .name = Name,
++    .list = drv_TEW673GRU_list,
++    .init = drv_TEW673GRU_init,
++    .quit = drv_TEW673GRU_quit,
++};
+--- a/lcd4linux.conf.sample
++++ b/lcd4linux.conf.sample
+@@ -547,6 +547,12 @@ Display VNC {
+     HttpPort   '5800'
+ }
++Display TEW673GRU {
++    Driver       'TEW673GRU'
++    Font         '6x8'
++    Port         '/dev/spidev1.0'
++}
++
+ #Plugin KVV {
+ #    StationID '12_701'
+ #    Refresh 30