linux-openmoko-2.6.34: add rebased gdrm and qtmoko patches, use gta02_drm_defconfig...
authorMartin Jansa <Martin.Jansa@gmail.com>
Wed, 19 May 2010 16:53:47 +0000 (18:53 +0200)
committerMartin Jansa <Martin.Jansa@gmail.com>
Tue, 25 May 2010 07:09:24 +0000 (09:09 +0200)
Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
15 files changed:
recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0002-Glamo-DRM-and-KMS-driver.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0002-usbhost.patch.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0003-Work-on-Glamo-core-for-DRM.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0003-ar6000_delay.patch.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0004-JBT6k74-work-for-KMS.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0004-save_regs.patch.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0005-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0006-Fix-dynamic-command-queue-allocation.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0007-Debug-statements-for-testing.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0008-Fix-claim-of-2D-register-resource.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/0009-Use-unlocked_ioctl-rather-than-ioctl.patch [new file with mode: 0644]
recipes/linux/linux-openmoko-2.6.34/gta02-defconfig-update-for-2.6.34.patch [deleted file]
recipes/linux/linux-openmoko-2.6.34_git.bb

diff --git a/recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch b/recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch
new file mode 100644 (file)
index 0000000..56197a2
--- /dev/null
@@ -0,0 +1,458 @@
+From 69d87612a1b545e6d4cf9fc93117be86f871f7d2 Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Sat, 22 May 2010 18:59:58 +0200
+Subject: [PATCH 01/13] DRM for platform devices
+
+This modifies the DRM core in a small number of places to allow platform
+devices to be used for direct rendering, alongside PCI devices.
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/gpu/drm/Kconfig     |    2 +-
+ drivers/gpu/drm/drm_bufs.c  |    2 +-
+ drivers/gpu/drm/drm_drv.c   |   27 ++++++++++
+ drivers/gpu/drm/drm_info.c  |   27 ++++++++--
+ drivers/gpu/drm/drm_ioctl.c |  118 ++++++++++++++++++++++++++++++-------------
+ drivers/gpu/drm/drm_stub.c  |   76 +++++++++++++++++++++++++++-
+ drivers/gpu/drm/drm_sysfs.c |    6 ++-
+ include/drm/drmP.h          |   13 +++++
+ 8 files changed, 224 insertions(+), 47 deletions(-)
+
+diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
+index 305c590..7244cef 100644
+--- a/drivers/gpu/drm/Kconfig
++++ b/drivers/gpu/drm/Kconfig
+@@ -6,7 +6,7 @@
+ #
+ menuconfig DRM
+       tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
+-      depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU
++      depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU
+       select I2C
+       select I2C_ALGOBIT
+       help
+diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
+index f7ba82e..30ce982 100644
+--- a/drivers/gpu/drm/drm_bufs.c
++++ b/drivers/gpu/drm/drm_bufs.c
+@@ -189,7 +189,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
+       switch (map->type) {
+       case _DRM_REGISTERS:
+       case _DRM_FRAME_BUFFER:
+-#if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__)
++#if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__arm__)
+               if (map->offset + (map->size-1) < map->offset ||
+                   map->offset < virt_to_phys(high_memory)) {
+                       kfree(map);
+diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
+index 4a66201..769d12b 100644
+--- a/drivers/gpu/drm/drm_drv.c
++++ b/drivers/gpu/drm/drm_drv.c
+@@ -250,6 +250,7 @@ int drm_lastclose(struct drm_device * dev)
+  */
+ int drm_init(struct drm_driver *driver)
+ {
++#ifdef CONFIG_PCI
+       struct pci_dev *pdev = NULL;
+       const struct pci_device_id *pid;
+       int i;
+@@ -283,11 +284,37 @@ int drm_init(struct drm_driver *driver)
+                       drm_get_dev(pdev, pid, driver);
+               }
+       }
++#endif
+       return 0;
+ }
+ EXPORT_SYMBOL(drm_init);
++/**
++ * Call this to associate a drm_driver with a platform_device.
++ *
++ * \return zero on success or a negative number on failure.
++ *
++ * This is a replacement for drm_init(), but for platform drivers.
++ * In this case, the caller must provide the matching platform_device
++ *
++ * since there is no physical bus to scan through.
++ *
++ * \sa drm_init
++ *
++ */
++int drm_platform_init(struct drm_driver *driver, struct platform_device *pdev,
++                    void *priv)
++{
++      DRM_DEBUG("\n");
++
++      INIT_LIST_HEAD(&driver->device_list);
++
++      return drm_get_platform_dev(pdev, driver, priv);
++}
++
++EXPORT_SYMBOL(drm_platform_init);
++
+ void drm_exit(struct drm_driver *driver)
+ {
+       struct drm_device *dev, *tmp;
+diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
+index f0f6c6b..838c2ee 100644
+--- a/drivers/gpu/drm/drm_info.c
++++ b/drivers/gpu/drm/drm_info.c
+@@ -52,12 +52,28 @@ int drm_name_info(struct seq_file *m, void *data)
+               return 0;
+       if (master->unique) {
+-              seq_printf(m, "%s %s %s\n",
+-                         dev->driver->pci_driver.name,
+-                         pci_name(dev->pdev), master->unique);
++
++              if (drm_core_is_platform(dev)) {
++                      seq_printf(m, "%s %s %s\n",
++                                 dev->driver->name,
++                                 dev_name(&dev->platform_dev->dev),
++                                 master->unique);
++              } else {
++                      seq_printf(m, "%s %s %s\n",
++                                 dev->driver->pci_driver.name,
++                                 pci_name(dev->pdev), master->unique);
++              }
++
+       } else {
+-              seq_printf(m, "%s %s\n", dev->driver->pci_driver.name,
+-                         pci_name(dev->pdev));
++
++              if (drm_core_is_platform(dev)) {
++                      seq_printf(m, "%s %s\n", dev->driver->name,
++                                 dev_name(&dev->platform_dev->dev));
++              } else {
++                      seq_printf(m, "%s %s\n", dev->driver->pci_driver.name,
++                                 pci_name(dev->pdev));
++              }
++
+       }
+       return 0;
+@@ -325,4 +341,3 @@ int drm_vma_info(struct seq_file *m, void *data)
+ }
+ #endif
+-
+diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
+index 9b9ff46..133ef29 100644
+--- a/drivers/gpu/drm/drm_ioctl.c
++++ b/drivers/gpu/drm/drm_ioctl.c
+@@ -83,7 +83,6 @@ int drm_setunique(struct drm_device *dev, void *data,
+ {
+       struct drm_unique *u = data;
+       struct drm_master *master = file_priv->master;
+-      int domain, bus, slot, func, ret;
+       if (master->unique_len || master->unique)
+               return -EBUSY;
+@@ -101,28 +100,46 @@ int drm_setunique(struct drm_device *dev, void *data,
+       master->unique[master->unique_len] = '\0';
+-      dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) +
+-                             strlen(master->unique) + 2, GFP_KERNEL);
+-      if (!dev->devname)
+-              return -ENOMEM;
++      if ( !drm_core_is_platform(dev) ) {
+-      sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
+-              master->unique);
++              int domain, bus, slot, func, ret;
+-      /* Return error if the busid submitted doesn't match the device's actual
+-       * busid.
+-       */
+-      ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
+-      if (ret != 3)
+-              return -EINVAL;
+-      domain = bus >> 8;
+-      bus &= 0xff;
++              /* PCI device */
++              dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) +
++                                     strlen(master->unique) + 2, GFP_KERNEL);
++              if (!dev->devname)
++                      return -ENOMEM;
+-      if ((domain != drm_get_pci_domain(dev)) ||
+-          (bus != dev->pdev->bus->number) ||
+-          (slot != PCI_SLOT(dev->pdev->devfn)) ||
+-          (func != PCI_FUNC(dev->pdev->devfn)))
+-              return -EINVAL;
++              sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
++                      master->unique);
++
++              /* Return error if the busid submitted doesn't match the
++               * device's actual busid.
++               */
++              ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
++              if (ret != 3)
++                      return -EINVAL;
++              domain = bus >> 8;
++              bus &= 0xff;
++
++              if ((domain != drm_get_pci_domain(dev)) ||
++                  (bus != dev->pdev->bus->number) ||
++                  (slot != PCI_SLOT(dev->pdev->devfn)) ||
++                  (func != PCI_FUNC(dev->pdev->devfn)))
++                      return -EINVAL;
++
++      } else {
++
++              /* Platform device */
++              dev->devname = kmalloc(strlen(dev->driver->name) +
++                                     strlen(master->unique) + 2, GFP_KERNEL);
++              if (!dev->devname)
++                      return -ENOMEM;
++
++              sprintf(dev->devname, "%s@%s", dev->driver->name,
++                      master->unique);
++
++      }
+       return 0;
+ }
+@@ -141,23 +158,52 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
+       if (master->unique == NULL)
+               return -ENOMEM;
+-      len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d",
+-                     drm_get_pci_domain(dev),
+-                     dev->pdev->bus->number,
+-                     PCI_SLOT(dev->pdev->devfn),
+-                     PCI_FUNC(dev->pdev->devfn));
+-      if (len >= master->unique_len)
+-              DRM_ERROR("buffer overflow");
+-      else
+-              master->unique_len = len;
+-
+-      dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) +
+-                             master->unique_len + 2, GFP_KERNEL);
+-      if (dev->devname == NULL)
+-              return -ENOMEM;
++      if ( !drm_core_is_platform(dev) ) {
++
++              /* PCI device */
+-      sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
+-              master->unique);
++              len = snprintf(master->unique, master->unique_len,
++                             "pci:%04x:%02x:%02x.%d",
++                             drm_get_pci_domain(dev),
++                             dev->pdev->bus->number,
++                             PCI_SLOT(dev->pdev->devfn),
++                             PCI_FUNC(dev->pdev->devfn));
++              if (len >= master->unique_len)
++                      DRM_ERROR("buffer overflow");
++              else
++                      master->unique_len = len;
++
++              dev->devname = kmalloc(strlen(dev->driver->pci_driver.name) +
++                                     master->unique_len + 2, GFP_KERNEL);
++              if (dev->devname == NULL)
++                      return -ENOMEM;
++
++              sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
++                      master->unique);
++
++      } else {
++
++              /* Platform device */
++
++              int len;
++
++              len = snprintf(master->unique, master->unique_len,
++                              "platform:%s", dev->platform_dev->name);
++
++              if (len >= master->unique_len)
++                      DRM_ERROR("buffer overflow");
++              else
++                      master->unique_len = len;
++
++              dev->devname = kmalloc(strlen(dev->driver->name)
++                                      + master->unique_len + 2, GFP_KERNEL);
++              if (dev->devname == NULL)
++                      return -ENOMEM;
++
++              sprintf(dev->devname, "%s@%s", dev->driver->name,
++                      master->unique);
++
++      }
+       return 0;
+ }
+diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
+index a0c365f..7e7eba6 100644
+--- a/drivers/gpu/drm/drm_stub.c
++++ b/drivers/gpu/drm/drm_stub.c
+@@ -246,8 +246,10 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
+       idr_init(&dev->drw_idr);
+       dev->pdev = pdev;
+-      dev->pci_device = pdev->device;
+-      dev->pci_vendor = pdev->vendor;
++      if (pdev) {
++              dev->pci_device = pdev->device;
++              dev->pci_vendor = pdev->vendor;
++      }
+ #ifdef __alpha__
+       dev->hose = pdev->sysdata;
+@@ -465,6 +467,76 @@ err_g1:
+ EXPORT_SYMBOL(drm_get_dev);
+ /**
++ *
++ * Register a platform device as a DRM device
++ *
++ * \param pdev - platform device structure
++ * \param driver - the matching drm_driver structure
++ * \return zero on success or a negative number on failure.
++ *
++ * Attempt to gets inter module "drm" information. If we are first
++ * then register the character device and inter module information.
++ * Try and register, if we fail to register, backout previous work.
++ *
++ * \sa drm_get_dev
++ */
++int drm_get_platform_dev(struct platform_device *pdev,
++                         struct drm_driver *driver, void *priv)
++{
++      struct drm_device *dev;
++      int ret;
++      DRM_DEBUG("\n");
++
++      dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++      if (!dev)
++              return -ENOMEM;
++      dev->dev_private = priv;
++
++      if ((ret = drm_fill_in_dev(dev, NULL, NULL, driver))) {
++              printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
++              goto err_g1;
++      }
++      dev->platform_dev = pdev;
++
++      if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++              ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
++              if (ret)
++                      goto err_g2;
++      }
++
++      if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
++              goto err_g3;
++
++      if (dev->driver->load) {
++              ret = dev->driver->load(dev, 0);
++              if (ret)
++                      goto err_g3;
++      }
++
++        /* setup the grouping for the legacy output */
++      if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++              ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
++              if (ret)
++                      goto err_g3;
++      }
++
++      list_add_tail(&dev->driver_item, &driver->device_list);
++
++      DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
++      driver->name, driver->major, driver->minor, driver->patchlevel,
++      driver->date, dev->primary->index);
++
++      return 0;
++
++err_g3:
++      drm_put_minor(&dev->primary);
++err_g2:
++err_g1:
++      kfree(dev);
++      return ret;
++}
++
++/**
+  * Put a secondary minor number.
+  *
+  * \param sec_minor - structure to be released
+diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
+index 25bbd30..947e731 100644
+--- a/drivers/gpu/drm/drm_sysfs.c
++++ b/drivers/gpu/drm/drm_sysfs.c
+@@ -488,7 +488,11 @@ int drm_sysfs_device_add(struct drm_minor *minor)
+       int err;
+       char *minor_str;
+-      minor->kdev.parent = &minor->dev->pdev->dev;
++      if (minor->dev->pdev) {
++              minor->kdev.parent = &minor->dev->pdev->dev;
++      } else {
++              minor->kdev.parent = &minor->dev->platform_dev->dev;
++      }
+       minor->kdev.class = drm_class;
+       minor->kdev.release = drm_sysfs_device_release;
+       minor->kdev.devt = minor->device;
+diff --git a/include/drm/drmP.h b/include/drm/drmP.h
+index 2f3b3a0..43894ec 100644
+--- a/include/drm/drmP.h
++++ b/include/drm/drmP.h
+@@ -56,6 +56,7 @@
+ #include <linux/cdev.h>
+ #include <linux/mutex.h>
+ #include <linux/slab.h>
++#include <linux/platform_device.h>
+ #if defined(__alpha__) || defined(__powerpc__)
+ #include <asm/pgtable.h>      /* For pte_wrprotect */
+ #endif
+@@ -144,6 +145,7 @@ extern void drm_ut_debug_printk(unsigned int request_level,
+ #define DRIVER_IRQ_VBL2    0x800
+ #define DRIVER_GEM         0x1000
+ #define DRIVER_MODESET     0x2000
++#define DRIVER_IS_PLATFORM 0x4000
+ /***********************************************************************/
+ /** \name Begin the DRM... */
+@@ -1014,6 +1016,7 @@ struct drm_device {
+       wait_queue_head_t buf_writers;  /**< Processes waiting to ctx switch */
+       struct drm_agp_head *agp;       /**< AGP data */
++      struct platform_device *platform_dev;  /**< platform device structure */
+       struct pci_dev *pdev;           /**< PCI device structure */
+       int pci_vendor;                 /**< PCI vendor id */
+@@ -1124,12 +1127,20 @@ static inline int drm_mtrr_del(int handle, unsigned long offset,
+ }
+ #endif
++static inline int drm_core_is_platform(struct drm_device *dev)
++{
++      return drm_core_check_feature(dev, DRIVER_IS_PLATFORM);
++}
++
+ /******************************************************************/
+ /** \name Internal function definitions */
+ /*@{*/
+                               /* Driver support (drm_drv.h) */
+ extern int drm_init(struct drm_driver *driver);
++extern int drm_platform_init(struct drm_driver *driver,
++                           struct platform_device *pdev,
++                           void *dev_private);
+ extern void drm_exit(struct drm_driver *driver);
+ extern long drm_ioctl(struct file *filp,
+                     unsigned int cmd, unsigned long arg);
+@@ -1350,6 +1361,8 @@ extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+                               struct drm_file *file_priv);
+ struct drm_master *drm_master_create(struct drm_minor *minor);
+ extern struct drm_master *drm_master_get(struct drm_master *master);
++extern int drm_get_platform_dev(struct platform_device *pdev,
++                              struct drm_driver *driver, void *priv);
+ extern void drm_master_put(struct drm_master **master);
+ extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
+                      struct drm_driver *driver);
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch b/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch
new file mode 100644 (file)
index 0000000..1b6964a
--- /dev/null
@@ -0,0 +1,1759 @@
+From b4c5d1702abcaef7d8af5d3dc8934b2184ba6be0 Mon Sep 17 00:00:00 2001
+From: Radek Polak <psonek2@seznam.cz>
+Date: Fri, 9 Apr 2010 09:15:40 +0200
+Subject: [PATCH 1/4] accels.patch
+
+adds support for accelerometers. You will need include/linux/lis302dl.h and
+drivers/input/misc/lis302dl.c from andy-tracking. The patch needs
+spi_bitbang_transfer_sync() and bitbang_work() to be ported correctly (some
+fixes from original 2.6.32 are missing).
+
+Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
+---
+ arch/arm/mach-s3c2410/include/mach/spi-gpio.h |    3 +-
+ arch/arm/mach-s3c2440/mach-gta02.c            |  157 ++++
+ drivers/input/misc/Kconfig                    |    9 +
+ drivers/input/misc/Makefile                   |    1 +
+ drivers/input/misc/lis302dl.c                 |  952 +++++++++++++++++++++++++
+ drivers/spi/spi_bitbang.c                     |  231 ++++---
+ drivers/spi/spi_s3c24xx_gpio.c                |    7 +-
+ include/linux/lis302dl.h                      |  152 ++++
+ include/linux/spi/spi.h                       |   30 +
+ include/linux/spi/spi_bitbang.h               |    5 +
+ 10 files changed, 1433 insertions(+), 114 deletions(-)
+ create mode 100644 drivers/input/misc/lis302dl.c
+ create mode 100644 include/linux/lis302dl.h
+
+diff --git a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h
+index dcef228..8eedc9c 100644
+--- a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h
++++ b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h
+@@ -21,7 +21,8 @@ struct s3c2410_spigpio_info {
+       int                      num_chipselect;
+       int                      bus_num;
+-      void (*chip_select)(struct s3c2410_spigpio_info *spi, int cs);
++      int                      non_blocking_transfer;
++      void (*chip_select)(struct s3c2410_spigpio_info *spi, int csid, int cs);
+ };
+diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
+index e8ac76b..795b9f4 100644
+--- a/arch/arm/mach-s3c2440/mach-gta02.c
++++ b/arch/arm/mach-s3c2440/mach-gta02.c
+@@ -62,6 +62,7 @@
+ #include <linux/input.h>
+ #include <linux/gpio_keys.h>
++#include <linux/lis302dl.h>
+ #include <linux/leds.h>
+ #include <linux/leds_pwm.h>
+@@ -111,6 +112,22 @@
+ #include <linux/glamofb.h>
+ #include <linux/mfd/glamo.h>
++#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
++
++#define S3C2410_GPIO_BANKD   (32*3)
++#define S3C2410_GPIO_BANKG   (32*6)
++
++#define S3C2410_GPG5          S3C2410_GPIONO(S3C2410_GPIO_BANKG, 5)
++#define S3C2410_GPG6          S3C2410_GPIONO(S3C2410_GPIO_BANKG, 6)
++#define S3C2410_GPG7          S3C2410_GPIONO(S3C2410_GPIO_BANKG, 7)
++#define S3C2410_GPD12         S3C2410_GPIONO(S3C2410_GPIO_BANKD, 12)
++#define S3C2410_GPD13         S3C2410_GPIONO(S3C2410_GPIO_BANKD, 13)
++
++#define        BITBANG_CS_ACTIVE       1       /* normally nCS, active low */
++#define        BITBANG_CS_INACTIVE     0
++
++#define S3C_SYSTEM_REV_ATAG GTA02v6_SYSTEM_REV
++
+ static struct pcf50633 *gta02_pcf;
+ /*
+@@ -322,6 +339,60 @@ const static struct jbt6k74_platform_data jbt6k74_pdata = {
+       .gpio_reset = GTA02_GPIO_GLAMO(4),
+ };
++/*----------- SPI: Accelerometers attached to SPI of s3c244x ----------------- */
++
++void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume)
++{
++      struct lis302dl_platform_data *pdata = lis->pdata;
++
++      if (!resume) {
++               /*
++               * we don't want to power them with a high level
++               * because GSENSOR_3V3 is not up during suspend
++               */
++              s3c2410_gpio_setpin(pdata->pin_chip_select, 0);
++              s3c2410_gpio_setpin(pdata->pin_clk, 0);
++              s3c2410_gpio_setpin(pdata->pin_mosi, 0);
++              /* misnomer: it is a pullDOWN in 2442 */
++              s3c2410_gpio_pullup(pdata->pin_miso, 1);
++              return;
++      }
++
++      /* back to normal */
++      s3c2410_gpio_setpin(pdata->pin_chip_select, 1);
++      s3c2410_gpio_setpin(pdata->pin_clk, 1);
++      /* misnomer: it is a pullDOWN in 2442 */
++      s3c2410_gpio_pullup(pdata->pin_miso, 0);
++
++      s3c2410_gpio_cfgpin(pdata->pin_chip_select, S3C2410_GPIO_OUTPUT);
++      s3c2410_gpio_cfgpin(pdata->pin_clk, S3C2410_GPIO_OUTPUT);
++      s3c2410_gpio_cfgpin(pdata->pin_mosi, S3C2410_GPIO_OUTPUT);
++      s3c2410_gpio_cfgpin(pdata->pin_miso, S3C2410_GPIO_INPUT);
++
++}
++
++struct lis302dl_platform_data lis302_pdata_top = {
++              .name           = "lis302-1 (top)",
++              .pin_chip_select= S3C2410_GPD12,
++              .pin_clk        = S3C2410_GPG7,
++              .pin_mosi       = S3C2410_GPG6,
++              .pin_miso       = S3C2410_GPG5,
++              .interrupt      = GTA02_IRQ_GSENSOR_1,
++              .open_drain     = 1, /* altered at runtime by PCB rev */
++              .lis302dl_suspend_io = gta02_lis302dl_suspend_io,
++};
++
++struct lis302dl_platform_data lis302_pdata_bottom = {
++              .name           = "lis302-2 (bottom)",
++              .pin_chip_select= S3C2410_GPD13,
++              .pin_clk        = S3C2410_GPG7,
++              .pin_mosi       = S3C2410_GPG6,
++              .pin_miso       = S3C2410_GPG5,
++              .interrupt      = GTA02_IRQ_GSENSOR_2,
++              .open_drain     = 1, /* altered at runtime by PCB rev */
++              .lis302dl_suspend_io = gta02_lis302dl_suspend_io,
++};
++
+ static struct spi_board_info gta02_spi_board_info[] = {
+       {
+               .modalias       = "jbt6k74",
+@@ -332,6 +403,81 @@ static struct spi_board_info gta02_spi_board_info[] = {
+               .bus_num        = 2,
+               .chip_select = 0
+       },
++      {
++              .modalias       = "lis302dl",
++              /* platform_data */
++              .platform_data  = &lis302_pdata_top,
++              /* controller_data */
++              /* irq */
++              .max_speed_hz   = 100 * 1000,
++              .bus_num        = 3,
++              .chip_select    = 0,
++      },
++      {
++              .modalias       = "lis302dl",
++              /* platform_data */
++              .platform_data  = &lis302_pdata_bottom,
++              /* controller_data */
++              /* irq */
++              .max_speed_hz   = 100 * 1000,
++              .bus_num        = 3,
++              .chip_select    = 1,
++      },
++};
++
++static void gta02_lis302_chip_select(struct s3c2410_spigpio_info *info, int csid, int cs)
++{
++
++      /*
++       * Huh... "quirk"... CS on this device is not really "CS" like you can
++       * expect.
++       *
++       * When it is 0 it selects SPI interface mode.
++       * When it is 1 it selects I2C interface mode.
++       *
++       * Because we have 2 devices on one interface we have to make sure
++       * that the "disabled" device (actually in I2C mode) don't think we're
++       * talking to it.
++       *
++       * When we talk to the "enabled" device, the "disabled" device sees
++       * the clocks as I2C clocks, creating havoc.
++       *
++       * I2C sees MOSI going LOW while CLK HIGH as a START action, thus we
++       * must ensure this is never issued.
++       */
++
++      int cs_gpio, other_cs_gpio;
++
++      cs_gpio = csid ? S3C2410_GPD13 : S3C2410_GPD12;
++      other_cs_gpio = (1 - csid) ? S3C2410_GPD13 : S3C2410_GPD12;
++
++
++      if (cs == BITBANG_CS_ACTIVE) {
++              s3c2410_gpio_setpin(other_cs_gpio, 1);
++              s3c2410_gpio_setpin(cs_gpio, 1);
++              s3c2410_gpio_setpin(info->pin_clk, 1);
++              s3c2410_gpio_setpin(cs_gpio, 0);
++      } else {
++              s3c2410_gpio_setpin(cs_gpio, 1);
++              s3c2410_gpio_setpin(other_cs_gpio, 1);
++      }
++}
++
++static struct s3c2410_spigpio_info gta02_spigpio_cfg = {
++      .pin_clk        = S3C2410_GPG7,
++      .pin_mosi       = S3C2410_GPG6,
++      .pin_miso       = S3C2410_GPG5,
++      .bus_num        = 3,
++      .num_chipselect = 2,
++      .chip_select    = gta02_lis302_chip_select,
++      .non_blocking_transfer = 1,
++};
++
++static struct platform_device gta02_spi_gpio_dev = {
++      .name           = "spi_s3c24xx_gpio",
++      .dev = {
++              .platform_data = &gta02_spigpio_cfg,
++      },
+ };
+  
+ static struct resource gta02_glamo_resources[] = {
+@@ -1091,6 +1237,7 @@ static struct platform_device *gta02_devices[] __initdata = {
+       &gta02_pm_bt_dev,
+       &gta02_pm_wlan_dev,
+       &gta02_glamo_dev,
++      &gta02_spi_gpio_dev,
+       &s3c_device_adc,
+       &s3c_device_ts,
+ };
+@@ -1302,6 +1449,16 @@ static void __init gta02_machine_init(void)
+       /* Set the panic callback to make AUX LED blink at ~5Hz. */
+       panic_blink = gta02_panic_blink;
++      switch (S3C_SYSTEM_REV_ATAG) {
++              case GTA02v6_SYSTEM_REV:
++              /* we need push-pull interrupt from motion sensors */
++              lis302_pdata_top.open_drain = 0;
++              lis302_pdata_bottom.open_drain = 0;
++              break;
++      default:
++              break;
++      }
++
+       bus_register_notifier(&platform_bus_type, &gta02_device_register_notifier);
+       bus_register_notifier(&spi_bus_type, &gta02_device_register_notifier);
+diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
+index 23140a3..d8d0932 100644
+--- a/drivers/input/misc/Kconfig
++++ b/drivers/input/misc/Kconfig
+@@ -340,4 +340,13 @@ config INPUT_PCAP
+         To compile this driver as a module, choose M here: the
+         module will be called pcap_keys.
++config INPUT_LIS302DL
++      tristate "STmicro LIS302DL 3-axis accelerometer"
++      depends on SPI_MASTER
++      help
++        SPI driver for the STmicro LIS302DL 3-axis accelerometer.
++
++        The userspece interface is a 3-axis (X/Y/Z) relative movement
++        Linux input device, reporting REL_[XYZ] events.
++
+ endif
+diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
+index 7e95a5d..5b810db 100644
+--- a/drivers/input/misc/Makefile
++++ b/drivers/input/misc/Makefile
+@@ -32,4 +32,5 @@ obj-$(CONFIG_INPUT_WINBOND_CIR)              += winbond-cir.o
+ obj-$(CONFIG_INPUT_WISTRON_BTNS)      += wistron_btns.o
+ obj-$(CONFIG_INPUT_WM831X_ON)         += wm831x-on.o
+ obj-$(CONFIG_INPUT_YEALINK)           += yealink.o
++obj-$(CONFIG_INPUT_LIS302DL)          += lis302dl.o
+diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
+new file mode 100644
+index 0000000..d345bfb
+--- /dev/null
++++ b/drivers/input/misc/lis302dl.c
+@@ -0,0 +1,952 @@
++/* Linux kernel driver for the ST LIS302D 3-axis accelerometer
++ *
++ * Copyright (C) 2007-2008 by Openmoko, Inc.
++ * Author: Harald Welte <laforge@openmoko.org>
++ *         converted to private bitbang by:
++ *         Andy Green <andy@openmoko.com>
++ *         ability to set acceleration threshold added by:
++ *         Simon Kagstrom <simon.kagstrom@gmail.com>
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ * TODO
++ *    * statistics for overflow events
++ *    * configuration interface (sysfs) for
++ *            * enable/disable x/y/z axis data ready
++ *            * enable/disable resume from freee fall / click
++ *            * free fall / click parameters
++ *            * high pass filter parameters
++ */
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/sysfs.h>
++#include <linux/spi/spi.h>
++
++#include <linux/lis302dl.h>
++
++/* Utility functions */
++static u8 __reg_read(struct lis302dl_info *lis, u8 reg)
++{
++      struct spi_message msg;
++      struct spi_transfer t;
++      u8 data[2] = {0xc0 | reg};
++      int rc;
++
++      spi_message_init(&msg);
++      memset(&t, 0, sizeof t);
++      t.len = 2;
++      spi_message_add_tail(&t, &msg);
++      t.tx_buf = &data[0];
++      t.rx_buf = &data[0];
++
++      /* Should complete without blocking */
++      rc = spi_non_blocking_transfer(lis->spi, &msg);
++      if (rc < 0) {
++              dev_err(lis->dev, "Error reading register\n");
++              return rc;
++      }
++
++      return data[1];
++}
++
++static void __reg_write(struct lis302dl_info *lis, u8 reg, u8 val)
++{     
++      struct spi_message msg;
++      struct spi_transfer t;
++      u8 data[2] = {reg, val};
++
++      spi_message_init(&msg);
++      memset(&t, 0, sizeof t);
++      t.len = 2;
++      spi_message_add_tail(&t, &msg);
++      t.tx_buf = &data[0];
++      t.rx_buf = &data[0];
++
++      /* Completes without blocking */
++      if (spi_non_blocking_transfer(lis->spi, &msg) < 0)
++              dev_err(lis->dev, "Error writing register\n");
++}
++
++static void __reg_set_bit_mask(struct lis302dl_info *lis, u8 reg, u8 mask,
++              u8 val)
++{
++      u_int8_t tmp;
++
++      val &= mask;
++
++      tmp = __reg_read(lis, reg);
++      tmp &= ~mask;
++      tmp |= val;
++      __reg_write(lis, reg, tmp);
++}
++
++static int __ms_to_duration(struct lis302dl_info *lis, int ms)
++{
++      /* If we have 400 ms sampling rate, the stepping is 2.5 ms,
++       * on 100 ms the stepping is 10ms */
++      if (lis->flags & LIS302DL_F_DR)
++              return min((ms * 10) / 25, 637);
++
++      return min(ms / 10, 2550);
++}
++
++static int __duration_to_ms(struct lis302dl_info *lis, int duration)
++{
++      if (lis->flags & LIS302DL_F_DR)
++              return (duration * 25) / 10;
++
++      return duration * 10;
++}
++
++static u8 __mg_to_threshold(struct lis302dl_info *lis, int mg)
++{
++      /* If FS is set each bit is 71mg, otherwise 18mg. The THS register
++       * has 7 bits for the threshold value */
++      if (lis->flags & LIS302DL_F_FS)
++              return min(mg / 71, 127);
++
++      return min(mg / 18, 127);
++}
++
++static int __threshold_to_mg(struct lis302dl_info *lis, u8 threshold)
++{
++      if (lis->flags & LIS302DL_F_FS)
++              return threshold * 71;
++
++      return threshold * 18;
++}
++
++/* interrupt handling related */
++
++enum lis302dl_intmode {
++      LIS302DL_INTMODE_GND            = 0x00,
++      LIS302DL_INTMODE_FF_WU_1        = 0x01,
++      LIS302DL_INTMODE_FF_WU_2        = 0x02,
++      LIS302DL_INTMODE_FF_WU_12       = 0x03,
++      LIS302DL_INTMODE_DATA_READY     = 0x04,
++      LIS302DL_INTMODE_CLICK          = 0x07,
++};
++
++static void __lis302dl_int_mode(struct device *dev, int int_pin,
++                            enum lis302dl_intmode mode)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++
++      switch (int_pin) {
++      case 1:
++              __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode);
++              break;
++      case 2:
++              __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3);
++              break;
++      default:
++              BUG();
++      }
++}
++
++static void __enable_wakeup(struct lis302dl_info *lis)
++{
++      __reg_write(lis, LIS302DL_REG_CTRL1, 0);
++
++      /* First zero to get to a known state */
++      __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, LIS302DL_FFWUCFG_XHIE |
++                      LIS302DL_FFWUCFG_YHIE | LIS302DL_FFWUCFG_ZHIE |
++                      LIS302DL_FFWUCFG_LIR);
++      __reg_write(lis, LIS302DL_REG_FF_WU_THS_1,
++                      __mg_to_threshold(lis, lis->wakeup.threshold));
++      __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1,
++                      __ms_to_duration(lis, lis->wakeup.duration));
++
++      /* Route the interrupt for wakeup */
++      __lis302dl_int_mode(lis->dev, 1,
++                      LIS302DL_INTMODE_FF_WU_1);
++
++      __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET);
++      __reg_read(lis, LIS302DL_REG_OUT_X);
++      __reg_read(lis, LIS302DL_REG_OUT_Y);
++      __reg_read(lis, LIS302DL_REG_OUT_Z);
++      __reg_read(lis, LIS302DL_REG_STATUS);
++      __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
++      __reg_read(lis, LIS302DL_REG_FF_WU_SRC_2);
++      __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | 7);
++}
++
++static void __enable_data_collection(struct lis302dl_info *lis)
++{
++      u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen |
++                       LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen;
++
++      /* make sure we're powered up and generate data ready */
++      __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1);
++
++      /* If the threshold is zero, let the device generated an interrupt
++       * on each datum */
++      if (lis->threshold == 0) {
++              __reg_write(lis, LIS302DL_REG_CTRL2, 0);
++              __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_DATA_READY);
++              __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_DATA_READY);
++      } else {
++              __reg_write(lis, LIS302DL_REG_CTRL2,
++                              LIS302DL_CTRL2_HPFF1);
++              __reg_write(lis, LIS302DL_REG_FF_WU_THS_1,
++                              __mg_to_threshold(lis, lis->threshold));
++              __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1,
++                              __ms_to_duration(lis, lis->duration));
++
++              /* Clear the HP filter "starting point" */
++              __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET);
++              __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1,
++                              LIS302DL_FFWUCFG_XHIE | LIS302DL_FFWUCFG_YHIE |
++                              LIS302DL_FFWUCFG_ZHIE | LIS302DL_FFWUCFG_LIR);
++              __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_FF_WU_12);
++              __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_FF_WU_12);
++      }
++}
++
++#if 0
++static void _report_btn_single(struct input_dev *inp, int btn)
++{
++      input_report_key(inp, btn, 1);
++      input_sync(inp);
++      input_report_key(inp, btn, 0);
++}
++
++static void _report_btn_double(struct input_dev *inp, int btn)
++{
++      input_report_key(inp, btn, 1);
++      input_sync(inp);
++      input_report_key(inp, btn, 0);
++      input_sync(inp);
++      input_report_key(inp, btn, 1);
++      input_sync(inp);
++      input_report_key(inp, btn, 0);
++}
++#endif
++
++
++static void lis302dl_bitbang_read_sample(struct lis302dl_info *lis)
++{
++      u8 data[(LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS) + 2] = {0xC0 | LIS302DL_REG_STATUS};
++      u8 *read = data + 1;
++      unsigned long flags;
++      int mg_per_sample = __threshold_to_mg(lis, 1);
++      struct spi_message msg;
++      struct spi_transfer t;
++
++      spi_message_init(&msg);
++      memset(&t, 0, sizeof t);
++      t.len = sizeof(data);
++      spi_message_add_tail(&t, &msg);
++      t.tx_buf = &data[0];
++      t.rx_buf = &data[0];
++
++      /* grab the set of register containing status and XYZ data */
++
++      local_irq_save(flags);
++
++      /* Should complete without blocking */
++      if (spi_non_blocking_transfer(lis->spi, &msg) < 0) 
++              dev_err(lis->dev, "Error reading registers\n");
++
++      local_irq_restore(flags);
++
++      /*
++       * at the minute the test below fails 50% of the time due to
++       * a problem with level interrupts causing ISRs to get called twice.
++       * This is a workaround for that, but actually this test is still
++       * valid and the information can be used for overrrun stats.
++       */
++
++      /* has any kind of overrun been observed by the lis302dl? */
++      if (read[0] & (LIS302DL_STATUS_XOR |
++                     LIS302DL_STATUS_YOR |
++                     LIS302DL_STATUS_ZOR))
++              lis->overruns++;
++
++      /* we have a valid sample set? */
++      if (read[0] & LIS302DL_STATUS_XYZDA) {
++              input_report_abs(lis->input_dev, ABS_X, mg_per_sample *
++                          (s8)read[LIS302DL_REG_OUT_X - LIS302DL_REG_STATUS]);
++              input_report_abs(lis->input_dev, ABS_Y, mg_per_sample *
++                          (s8)read[LIS302DL_REG_OUT_Y - LIS302DL_REG_STATUS]);
++              input_report_abs(lis->input_dev, ABS_Z, mg_per_sample *
++                          (s8)read[LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS]);
++
++              input_sync(lis->input_dev);
++      }
++
++      if (lis->threshold)
++              /* acknowledge the wakeup source */
++              __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
++}
++
++static irqreturn_t lis302dl_interrupt(int irq, void *_lis)
++{
++      struct lis302dl_info *lis = _lis;
++
++      lis302dl_bitbang_read_sample(lis);
++      return IRQ_HANDLED;
++}
++
++/* sysfs */
++
++static ssize_t show_overruns(struct device *dev, struct device_attribute *attr,
++                       char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++
++      return sprintf(buf, "%u\n", lis->overruns);
++}
++
++static DEVICE_ATTR(overruns, S_IRUGO, show_overruns, NULL);
++
++static ssize_t show_rate(struct device *dev, struct device_attribute *attr,
++                       char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      u8 ctrl1;
++      unsigned long flags;
++
++      local_irq_save(flags);
++      ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1);
++      local_irq_restore(flags);
++
++      return sprintf(buf, "%d\n", ctrl1 & LIS302DL_CTRL1_DR ? 400 : 100);
++}
++
++static ssize_t set_rate(struct device *dev, struct device_attribute *attr,
++                      const char *buf, size_t count)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      if (!strcmp(buf, "400\n")) {
++              __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR,
++                               LIS302DL_CTRL1_DR);
++              lis->flags |= LIS302DL_F_DR;
++      } else {
++              __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR,
++                              0);
++              lis->flags &= ~LIS302DL_F_DR;
++      }
++      local_irq_restore(flags);
++
++      return count;
++}
++
++static DEVICE_ATTR(sample_rate, S_IRUGO | S_IWUSR, show_rate, set_rate);
++
++static ssize_t show_scale(struct device *dev, struct device_attribute *attr,
++                        char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      u_int8_t ctrl1;
++      unsigned long flags;
++
++      local_irq_save(flags);
++      ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1);
++      local_irq_restore(flags);
++
++      return sprintf(buf, "%s\n", ctrl1 & LIS302DL_CTRL1_FS ? "9.2" : "2.3");
++}
++
++static ssize_t set_scale(struct device *dev, struct device_attribute *attr,
++                       const char *buf, size_t count)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      if (!strcmp(buf, "9.2\n")) {
++              __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS,
++                               LIS302DL_CTRL1_FS);
++              lis->flags |= LIS302DL_F_FS;
++      } else {
++              __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS,
++                              0);
++              lis->flags &= ~LIS302DL_F_FS;
++      }
++
++      if (lis->flags & LIS302DL_F_INPUT_OPEN)
++              __enable_data_collection(lis);
++
++      local_irq_restore(flags);
++
++      return count;
++}
++
++static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale);
++
++static ssize_t show_threshold(struct device *dev, struct device_attribute *attr,
++               char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++
++      /* Display the device view of the threshold setting */
++      return sprintf(buf, "%d\n", __threshold_to_mg(lis,
++                      __mg_to_threshold(lis, lis->threshold)));
++}
++
++static ssize_t set_threshold(struct device *dev, struct device_attribute *attr,
++               const char *buf, size_t count)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      unsigned int val;
++
++      if (sscanf(buf, "%u\n", &val) != 1)
++              return -EINVAL;
++      /* 8g is the maximum if FS is 1 */
++      if (val > 8000)
++              return -ERANGE;
++
++      /* Set the threshold and write it out if the device is used */
++      lis->threshold = val;
++
++      if (lis->flags & LIS302DL_F_INPUT_OPEN) {
++              unsigned long flags;
++
++              local_irq_save(flags);
++              __enable_data_collection(lis);
++              local_irq_restore(flags);
++      }
++
++      return count;
++}
++
++static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR, show_threshold, set_threshold);
++
++static ssize_t show_duration(struct device *dev, struct device_attribute *attr,
++               char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++
++      return sprintf(buf, "%d\n", __duration_to_ms(lis,
++                      __ms_to_duration(lis, lis->duration)));
++}
++
++static ssize_t set_duration(struct device *dev, struct device_attribute *attr,
++               const char *buf, size_t count)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      unsigned int val;
++
++      if (sscanf(buf, "%u\n", &val) != 1)
++              return -EINVAL;
++      if (val > 2550)
++              return -ERANGE;
++
++      lis->duration = val;
++      if (lis->flags & LIS302DL_F_INPUT_OPEN)
++              __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1,
++                              __ms_to_duration(lis, lis->duration));
++
++      return count;
++}
++
++static DEVICE_ATTR(duration, S_IRUGO | S_IWUSR, show_duration, set_duration);
++
++static ssize_t lis302dl_dump(struct device *dev, struct device_attribute *attr,
++                                                                    char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      int n = 0;
++      u8 reg[0x40];
++      char *end = buf;
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      for (n = 0; n < sizeof(reg); n++)
++              reg[n] = __reg_read(lis, n);
++
++      local_irq_restore(flags);
++
++      for (n = 0; n < sizeof(reg); n += 16) {
++              hex_dump_to_buffer(reg + n, 16, 16, 1, end, 128, 0);
++              end += strlen(end);
++              *end++ = '\n';
++              *end++ = '\0';
++      }
++
++      return end - buf;
++}
++static DEVICE_ATTR(dump, S_IRUGO, lis302dl_dump, NULL);
++
++/* Configure freefall/wakeup interrupts */
++static ssize_t set_wakeup_threshold(struct device *dev,
++              struct device_attribute *attr, const char *buf, size_t count)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      unsigned int threshold;
++
++      if (sscanf(buf, "%u\n", &threshold) != 1)
++              return -EINVAL;
++
++      if (threshold > 8000)
++              return -ERANGE;
++
++      /* Zero turns the feature off */
++      if (threshold == 0) {
++              if (lis->flags & LIS302DL_F_IRQ_WAKE) {
++                      disable_irq_wake(lis->pdata->interrupt);
++                      lis->flags &= ~LIS302DL_F_IRQ_WAKE;
++              }
++
++              return count;
++      }
++
++      lis->wakeup.threshold = threshold;
++
++      if (!(lis->flags & LIS302DL_F_IRQ_WAKE)) {
++              enable_irq_wake(lis->pdata->interrupt);
++              lis->flags |= LIS302DL_F_IRQ_WAKE;
++      }
++
++      return count;
++}
++
++static ssize_t show_wakeup_threshold(struct device *dev,
++              struct device_attribute *attr, char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++
++      /* All events off? */
++      if (lis->wakeup.threshold == 0)
++              return sprintf(buf, "off\n");
++
++      return sprintf(buf, "%u\n", lis->wakeup.threshold);
++}
++
++static DEVICE_ATTR(wakeup_threshold, S_IRUGO | S_IWUSR, show_wakeup_threshold,
++              set_wakeup_threshold);
++
++static ssize_t set_wakeup_duration(struct device *dev,
++              struct device_attribute *attr, const char *buf, size_t count)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++      unsigned int duration;
++
++      if (sscanf(buf, "%u\n", &duration) != 1)
++              return -EINVAL;
++
++      if (duration > 2550)
++              return -ERANGE;
++
++      lis->wakeup.duration = duration;
++
++      return count;
++}
++
++static ssize_t show_wakeup_duration(struct device *dev,
++              struct device_attribute *attr, char *buf)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(dev);
++
++      return sprintf(buf, "%u\n", lis->wakeup.duration);
++}
++
++static DEVICE_ATTR(wakeup_duration, S_IRUGO | S_IWUSR, show_wakeup_duration,
++              set_wakeup_duration);
++
++static struct attribute *lis302dl_sysfs_entries[] = {
++      &dev_attr_sample_rate.attr,
++      &dev_attr_full_scale.attr,
++      &dev_attr_threshold.attr,
++      &dev_attr_duration.attr,
++      &dev_attr_dump.attr,
++      &dev_attr_wakeup_threshold.attr,
++      &dev_attr_wakeup_duration.attr,
++      &dev_attr_overruns.attr,
++      NULL
++};
++
++static struct attribute_group lis302dl_attr_group = {
++      .name   = NULL,
++      .attrs  = lis302dl_sysfs_entries,
++};
++
++/* input device handling and driver core interaction */
++
++static int lis302dl_input_open(struct input_dev *inp)
++{
++      struct lis302dl_info *lis = input_get_drvdata(inp);
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      __enable_data_collection(lis);
++      lis->flags |= LIS302DL_F_INPUT_OPEN;
++
++      local_irq_restore(flags);
++
++      return 0;
++}
++
++static void lis302dl_input_close(struct input_dev *inp)
++{
++      struct lis302dl_info *lis = input_get_drvdata(inp);
++      u_int8_t ctrl1 = LIS302DL_CTRL1_Xen | LIS302DL_CTRL1_Yen |
++                       LIS302DL_CTRL1_Zen;
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      /* since the input core already serializes access and makes sure we
++       * only see close() for the close of the last user, we can safely
++       * disable the data ready events */
++      __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, 0x00);
++      lis->flags &= ~LIS302DL_F_INPUT_OPEN;
++
++      /* however, don't power down the whole device if still needed */
++      if (!(lis->flags & LIS302DL_F_WUP_FF ||
++            lis->flags & LIS302DL_F_WUP_CLICK)) {
++              __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD,
++                               0x00);
++      }
++      local_irq_restore(flags);
++}
++
++/* get the device to reload its coefficients from EEPROM and wait for it
++ * to complete
++ */
++
++static int __lis302dl_reset_device(struct lis302dl_info *lis)
++{
++      int timeout = 10;
++
++      __reg_write(lis, LIS302DL_REG_CTRL2,
++                      LIS302DL_CTRL2_BOOT | LIS302DL_CTRL2_FDS);
++
++      while ((__reg_read(lis, LIS302DL_REG_CTRL2)
++                      & LIS302DL_CTRL2_BOOT) && (timeout--))
++              mdelay(1);
++
++      return !!(timeout < 0);
++}
++
++static int __devinit lis302dl_probe(struct spi_device *spi)
++{
++      int rc;
++      struct lis302dl_info *lis;
++      u_int8_t wai;
++      unsigned long flags;
++      struct lis302dl_platform_data *pdata = spi->dev.platform_data;
++
++      spi->mode = SPI_MODE_3;
++      rc = spi_setup(spi);
++      if (rc < 0) {
++              dev_err(&spi->dev, "spi_setup failed\n");
++              return rc;
++      }
++
++      lis = kzalloc(sizeof(*lis), GFP_KERNEL);
++      if (!lis)
++              return -ENOMEM;
++
++      lis->dev = &spi->dev;
++      lis->spi = spi;
++
++      dev_set_drvdata(lis->dev, lis);
++
++      lis->pdata = pdata;
++
++      rc = sysfs_create_group(&lis->dev->kobj, &lis302dl_attr_group);
++      if (rc) {
++              dev_err(lis->dev, "error creating sysfs group\n");
++              goto bail_free_lis;
++      }
++
++      /* initialize input layer details */
++      lis->input_dev = input_allocate_device();
++      if (!lis->input_dev) {
++              dev_err(lis->dev, "Unable to allocate input device\n");
++              goto bail_sysfs;
++      }
++
++      input_set_drvdata(lis->input_dev, lis);
++      lis->input_dev->name = pdata->name;
++       /* SPI Bus not defined as a valid bus for input subsystem*/
++      lis->input_dev->id.bustype = BUS_I2C; /* lie about it */
++      lis->input_dev->open = lis302dl_input_open;
++      lis->input_dev->close = lis302dl_input_close;
++
++      rc = input_register_device(lis->input_dev);
++      if (rc) {
++              dev_err(lis->dev, "error %d registering input device\n", rc);
++              goto bail_inp_dev;
++      }
++
++      local_irq_save(flags);
++      /* Configure our IO */
++      (lis->pdata->lis302dl_suspend_io)(lis, 1);
++
++      wai = __reg_read(lis, LIS302DL_REG_WHO_AM_I);
++      if (wai != LIS302DL_WHO_AM_I_MAGIC) {
++              dev_err(lis->dev, "unknown who_am_i signature 0x%02x\n", wai);
++              dev_set_drvdata(lis->dev, NULL);
++              rc = -ENODEV;
++              local_irq_restore(flags);
++              goto bail_inp_reg;
++      }
++
++      set_bit(EV_ABS, lis->input_dev->evbit);
++      input_set_abs_params(lis->input_dev, ABS_X, 0, 0, 0, 0);
++      input_set_abs_params(lis->input_dev, ABS_Y, 0, 0, 0, 0);
++      input_set_abs_params(lis->input_dev, ABS_Z, 0, 0, 0, 0);
++      
++
++      lis->threshold = 0;
++      lis->duration = 0;
++      memset(&lis->wakeup, 0, sizeof(lis->wakeup));
++
++      if (__lis302dl_reset_device(lis))
++              dev_err(lis->dev, "device BOOT reload failed\n");
++
++      /* force us powered */
++      __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD |
++                      LIS302DL_CTRL1_Xen |
++                      LIS302DL_CTRL1_Yen |
++                      LIS302DL_CTRL1_Zen);
++      mdelay(1);
++
++      __reg_write(lis, LIS302DL_REG_CTRL2, 0);
++      __reg_write(lis, LIS302DL_REG_CTRL3,
++                      LIS302DL_CTRL3_PP_OD | LIS302DL_CTRL3_IHL);
++      __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, 0x0);
++      __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, 0x00);
++      __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, 0x0);
++
++      /* start off in powered down mode; we power up when someone opens us */
++      __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_Xen |
++                      LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen);
++
++      if (pdata->open_drain)
++              /* switch interrupt to open collector, active-low */
++              __reg_write(lis, LIS302DL_REG_CTRL3,
++                              LIS302DL_CTRL3_PP_OD | LIS302DL_CTRL3_IHL);
++      else
++              /* push-pull, active-low */
++              __reg_write(lis, LIS302DL_REG_CTRL3, LIS302DL_CTRL3_IHL);
++
++      __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_GND);
++      __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_GND);
++
++      __reg_read(lis, LIS302DL_REG_STATUS);
++      __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
++      __reg_read(lis, LIS302DL_REG_FF_WU_SRC_2);
++      __reg_read(lis, LIS302DL_REG_CLICK_SRC);
++      local_irq_restore(flags);
++
++      dev_info(lis->dev, "Found %s\n", pdata->name);
++
++      lis->pdata = pdata;
++
++      set_irq_handler(lis->pdata->interrupt, handle_level_irq);
++
++      rc = request_irq(lis->pdata->interrupt, lis302dl_interrupt,
++                       IRQF_TRIGGER_LOW, "lis302dl", lis);
++      
++      if (rc < 0) {
++              dev_err(lis->dev, "error requesting IRQ %d\n",
++                      lis->pdata->interrupt);
++              goto bail_inp_reg;
++      }
++      return 0;
++
++bail_inp_reg:
++      input_unregister_device(lis->input_dev);
++bail_inp_dev:
++      input_free_device(lis->input_dev);
++bail_sysfs:
++      sysfs_remove_group(&lis->dev->kobj, &lis302dl_attr_group);
++bail_free_lis:
++      kfree(lis);
++      return rc;
++}
++
++static int __devexit lis302dl_remove(struct spi_device *spi)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
++      unsigned long flags;
++
++      /* Disable interrupts */
++      if (lis->flags & LIS302DL_F_IRQ_WAKE)
++              disable_irq_wake(lis->pdata->interrupt);
++      free_irq(lis->pdata->interrupt, lis);
++
++      /* Reset and power down the device */
++      local_irq_save(flags);
++      __reg_write(lis, LIS302DL_REG_CTRL3, 0x00);
++      __reg_write(lis, LIS302DL_REG_CTRL2, 0x00);
++      __reg_write(lis, LIS302DL_REG_CTRL1, 0x00);
++      local_irq_restore(flags);
++
++      /* Cleanup resources */
++      sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group);
++      input_unregister_device(lis->input_dev);
++      if (lis->input_dev)
++              input_free_device(lis->input_dev);
++      dev_set_drvdata(lis->dev, NULL);
++      kfree(lis);
++
++      return 0;
++}
++
++#ifdef CONFIG_PM
++
++static u8 regs_to_save[] = {
++      LIS302DL_REG_CTRL2,
++      LIS302DL_REG_CTRL3,
++      LIS302DL_REG_FF_WU_CFG_1,
++      LIS302DL_REG_FF_WU_THS_1,
++      LIS302DL_REG_FF_WU_DURATION_1,
++      LIS302DL_REG_FF_WU_CFG_2,
++      LIS302DL_REG_FF_WU_THS_2,
++      LIS302DL_REG_FF_WU_DURATION_2,
++      LIS302DL_REG_CLICK_CFG,
++      LIS302DL_REG_CLICK_THSY_X,
++      LIS302DL_REG_CLICK_THSZ,
++      LIS302DL_REG_CLICK_TIME_LIMIT,
++      LIS302DL_REG_CLICK_LATENCY,
++      LIS302DL_REG_CLICK_WINDOW,
++      LIS302DL_REG_CTRL1,
++};
++
++static int lis302dl_suspend(struct spi_device *spi, pm_message_t state)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
++      unsigned long flags;
++      u_int8_t tmp;
++      int n;
++
++      /* determine if we want to wake up from the accel. */
++      if (lis->flags & LIS302DL_F_WUP_CLICK)
++              return 0;
++
++      disable_irq(lis->pdata->interrupt);
++      local_irq_save(flags);
++
++      /*
++       * When we share SPI over multiple sensors, there is a race here
++       * that one or more sensors will lose.  In that case, the shared
++       * SPI bus GPIO will be in sleep mode and partially pulled down.  So
++       * we explicitly put our IO into "wake" mode here before the final
++       * traffic to the sensor.
++       */
++      (lis->pdata->lis302dl_suspend_io)(lis, 1);
++
++      /* save registers */
++      for (n = 0; n < ARRAY_SIZE(regs_to_save); n++)
++              lis->regs[regs_to_save[n]] =
++                      __reg_read(lis, regs_to_save[n]);
++
++      /* power down or enable wakeup */
++
++      if (lis->wakeup.threshold == 0) {
++              tmp = __reg_read(lis, LIS302DL_REG_CTRL1);
++              tmp &= ~LIS302DL_CTRL1_PD;
++              __reg_write(lis, LIS302DL_REG_CTRL1, tmp);
++      } else
++              __enable_wakeup(lis);
++
++      /* place our IO to the device in sleep-compatible states */
++      (lis->pdata->lis302dl_suspend_io)(lis, 0);
++
++      local_irq_restore(flags);
++
++      return 0;
++}
++
++static int lis302dl_resume(struct spi_device *spi)
++{
++      struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
++      unsigned long flags;
++      int n;
++
++      if (lis->flags & LIS302DL_F_WUP_CLICK)
++              return 0;
++
++      local_irq_save(flags);
++
++      /* get our IO to the device back in operational states */
++      (lis->pdata->lis302dl_suspend_io)(lis, 1);
++
++      /* resume from powerdown first! */
++      __reg_write(lis, LIS302DL_REG_CTRL1,
++                      LIS302DL_CTRL1_PD |
++                      LIS302DL_CTRL1_Xen |
++                      LIS302DL_CTRL1_Yen |
++                      LIS302DL_CTRL1_Zen);
++      mdelay(1);
++
++      if (__lis302dl_reset_device(lis))
++              dev_err(&spi->dev, "device BOOT reload failed\n");
++
++      /* restore registers after resume */
++      for (n = 0; n < ARRAY_SIZE(regs_to_save); n++)
++              __reg_write(lis, regs_to_save[n], lis->regs[regs_to_save[n]]);
++
++      /* if someone had us open, reset the non-wake threshold stuff */
++      if (lis->flags & LIS302DL_F_INPUT_OPEN)
++              __enable_data_collection(lis);
++
++      local_irq_restore(flags);
++      enable_irq(lis->pdata->interrupt);
++
++      return 0;
++}
++#else
++#define lis302dl_suspend      NULL
++#define lis302dl_resume               NULL
++#endif
++
++static struct spi_driver lis302dl_spi_driver = {
++      .driver = {
++              .name = "lis302dl",
++              .owner = THIS_MODULE,
++      },
++
++      .probe  = lis302dl_probe,       
++      .remove = __devexit_p(lis302dl_remove),
++      .suspend = lis302dl_suspend,
++      .resume  = lis302dl_resume,
++};
++
++static int __devinit lis302dl_init(void)
++{
++      return spi_register_driver(&lis302dl_spi_driver);
++}
++
++static void __exit lis302dl_exit(void)
++{
++      spi_unregister_driver(&lis302dl_spi_driver);
++}
++
++MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
++MODULE_LICENSE("GPL");
++
++module_init(lis302dl_init);
++module_exit(lis302dl_exit);
+diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c
+index 5265330..24c61a6 100644
+--- a/drivers/spi/spi_bitbang.c
++++ b/drivers/spi/spi_bitbang.c
+@@ -254,134 +254,139 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
+  * Drivers can provide word-at-a-time i/o primitives, or provide
+  * transfer-at-a-time ones to leverage dma or fifo hardware.
+  */
+-static void bitbang_work(struct work_struct *work)
++/* Synchronous non blocking transfer */
++int
++spi_bitbang_transfer_sync(struct spi_device *spi, struct spi_message *m)
+ {
+-      struct spi_bitbang      *bitbang =
+-              container_of(work, struct spi_bitbang, work);
+-      unsigned long           flags;
+-      int                     do_setup = -1;
+-      int                     (*setup_transfer)(struct spi_device *,
+-                                      struct spi_transfer *);
++      struct spi_bitbang *bitbang = spi_master_get_devdata(spi->master);
++      struct spi_transfer *t;
++      unsigned long flags;
++      int cs_change = 1;
++      int status;
++      int nsecs;
++      int (*setup_transfer)(struct spi_device *, struct spi_transfer *);
++
++      /* FIXME this is made-up ... the correct value is known to
++       * word-at-a-time bitbang code, and presumably chipselect()
++       * should enforce these requirements too?
++       */
++      nsecs = 100;
++      cs_change = 1;
++      status = 0;
++      setup_transfer = NULL;
++
++      local_irq_save(flags);
++      list_for_each_entry (t, &m->transfers, transfer_list) {
++              /* override or restore speed and wordsize */
++              if (t->speed_hz || t->bits_per_word) {
++                      setup_transfer = bitbang->setup_transfer;
++                      if (!setup_transfer) {
++                              status = -ENOPROTOOPT;
++                              break;
++                      }
++              }
++              if (setup_transfer) {
++                      status = setup_transfer(spi, t);
++                      if (status < 0)
++                              break;
++              }
+-      setup_transfer = bitbang->setup_transfer;
++              /* set up default clock polarity, and activate chip;
++               * this implicitly updates clock and spi modes as
++               * previously recorded for this device via setup().
++               * (and also deselects any other chip that might be
++               * selected ...)
++               */
+-      spin_lock_irqsave(&bitbang->lock, flags);
+-      bitbang->busy = 1;
+-      while (!list_empty(&bitbang->queue)) {
+-              struct spi_message      *m;
+-              struct spi_device       *spi;
+-              unsigned                nsecs;
+-              struct spi_transfer     *t = NULL;
+-              unsigned                tmp;
+-              unsigned                cs_change;
+-              int                     status;
++              if (cs_change) {
++                      bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
++                      ndelay(nsecs);
++              }
+-              m = container_of(bitbang->queue.next, struct spi_message,
+-                              queue);
+-              list_del_init(&m->queue);
+-              spin_unlock_irqrestore(&bitbang->lock, flags);
++              cs_change = t->cs_change;
++              if (!t->tx_buf && !t->rx_buf && t->len) {
++                      status = -EINVAL;
++                      break;
++              }
+-              /* FIXME this is made-up ... the correct value is known to
+-               * word-at-a-time bitbang code, and presumably chipselect()
+-               * should enforce these requirements too?
++              /* transfer data.  the lower level code handles any
++               * new dma mappings it needs. our caller always gave
++               * us dma-safe buffers.
+                */
+-              nsecs = 100;
++              if (t->len) {
++                      /* REVISIT dma API still needs a designated
++                       * DMA_ADDR_INVALID; ~0 might be better.
++                       */
++                      if (!m->is_dma_mapped)
++                              t->rx_dma = t->tx_dma = 0;
++                      status = bitbang->txrx_bufs(spi, t);
++              }
+-              spi = m->spi;
+-              tmp = 0;
+-              cs_change = 1;
++              if (status > 0)
++                      m->actual_length += status;
++              if (status != t->len) {
++                      /* always report some kind of error */
++                      if (status >= 0)
++                              status = -EREMOTEIO;
++                      break;
++              }
+               status = 0;
++                      /* protocol tweaks before next transfer */
++              if (t->delay_usecs)
++                      udelay(t->delay_usecs);
++                      if (!cs_change)
++                      continue;
++              if (t->transfer_list.next == &m->transfers)
++                      break;
++                      /* sometimes a short mid-message deselect of the chip
++               * may be needed to terminate a mode or command
++               */
++              ndelay(nsecs);
++              bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
++              ndelay(nsecs);
++      }
+-              list_for_each_entry (t, &m->transfers, transfer_list) {
+-
+-                      /* override speed or wordsize? */
+-                      if (t->speed_hz || t->bits_per_word)
+-                              do_setup = 1;
+-
+-                      /* init (-1) or override (1) transfer params */
+-                      if (do_setup != 0) {
+-                              if (!setup_transfer) {
+-                                      status = -ENOPROTOOPT;
+-                                      break;
+-                              }
+-                              status = setup_transfer(spi, t);
+-                              if (status < 0)
+-                                      break;
+-                      }
++      m->status = status;
++      if (m->complete)
++              m->complete(m->context);
+-                      /* set up default clock polarity, and activate chip;
+-                       * this implicitly updates clock and spi modes as
+-                       * previously recorded for this device via setup().
+-                       * (and also deselects any other chip that might be
+-                       * selected ...)
+-                       */
+-                      if (cs_change) {
+-                              bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
+-                              ndelay(nsecs);
+-                      }
+-                      cs_change = t->cs_change;
+-                      if (!t->tx_buf && !t->rx_buf && t->len) {
+-                              status = -EINVAL;
+-                              break;
+-                      }
++      /* restore speed and wordsize */
++      if (setup_transfer)
++              setup_transfer(spi, NULL);
+-                      /* transfer data.  the lower level code handles any
+-                       * new dma mappings it needs. our caller always gave
+-                       * us dma-safe buffers.
+-                       */
+-                      if (t->len) {
+-                              /* REVISIT dma API still needs a designated
+-                               * DMA_ADDR_INVALID; ~0 might be better.
+-                               */
+-                              if (!m->is_dma_mapped)
+-                                      t->rx_dma = t->tx_dma = 0;
+-                              status = bitbang->txrx_bufs(spi, t);
+-                      }
+-                      if (status > 0)
+-                              m->actual_length += status;
+-                      if (status != t->len) {
+-                              /* always report some kind of error */
+-                              if (status >= 0)
+-                                      status = -EREMOTEIO;
+-                              break;
+-                      }
+-                      status = 0;
+-
+-                      /* protocol tweaks before next transfer */
+-                      if (t->delay_usecs)
+-                              udelay(t->delay_usecs);
++      /* normally deactivate chipselect ... unless no error and
++       * cs_change has hinted that the next message will probably
++       * be for this chip too.
++       */
++      if (!(status == 0 && cs_change)) {
++              ndelay(nsecs);
++              bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
++              ndelay(nsecs);
++      }
+-                      if (!cs_change)
+-                              continue;
+-                      if (t->transfer_list.next == &m->transfers)
+-                              break;
++      local_irq_restore(flags);
+-                      /* sometimes a short mid-message deselect of the chip
+-                       * may be needed to terminate a mode or command
+-                       */
+-                      ndelay(nsecs);
+-                      bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+-                      ndelay(nsecs);
+-              }
++      return status;
++}
++EXPORT_SYMBOL_GPL(spi_bitbang_transfer_sync);
+-              m->status = status;
+-              m->complete(m->context);
++static void bitbang_work(struct work_struct *work)
++{
++      struct spi_bitbang      *bitbang =
++              container_of(work, struct spi_bitbang, work);
++      unsigned long           flags;
+-              /* restore speed and wordsize if it was overridden */
+-              if (do_setup == 1)
+-                      setup_transfer(spi, NULL);
+-              do_setup = 0;
++      spin_lock_irqsave(&bitbang->lock, flags);
++      bitbang->busy = 1;
++      while (!list_empty(&bitbang->queue)) {
++              struct spi_message      *m;
+-              /* normally deactivate chipselect ... unless no error and
+-               * cs_change has hinted that the next message will probably
+-               * be for this chip too.
+-               */
+-              if (!(status == 0 && cs_change)) {
+-                      ndelay(nsecs);
+-                      bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+-                      ndelay(nsecs);
+-              }
++              m = container_of(bitbang->queue.next, struct spi_message,
++                              queue);
++              list_del_init(&m->queue);
++              spin_unlock_irqrestore(&bitbang->lock, flags);
++              spi_bitbang_transfer_sync(m->spi, m);
+               spin_lock_irqsave(&bitbang->lock, flags);
+       }
+       bitbang->busy = 0;
+@@ -456,6 +461,10 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
+       if (!bitbang->master->transfer)
+               bitbang->master->transfer = spi_bitbang_transfer;
++
++      if (!bitbang->master->transfer_sync && bitbang->non_blocking_transfer)
++              bitbang->master->transfer_sync = spi_bitbang_transfer_sync;
++
+       if (!bitbang->txrx_bufs) {
+               bitbang->use_dma = 0;
+               bitbang->txrx_bufs = spi_bitbang_bufs;
+diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c
+index bbf9371..5685b78 100644
+--- a/drivers/spi/spi_s3c24xx_gpio.c
++++ b/drivers/spi/spi_s3c24xx_gpio.c
+@@ -92,7 +92,7 @@ static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value)
+       struct s3c2410_spigpio *sg = spidev_to_sg(dev);
+       if (sg->info && sg->info->chip_select)
+-              (sg->info->chip_select)(sg->info, value);
++              (sg->info->chip_select)(sg->info, dev->chip_select, value);
+ }
+ static int s3c2410_spigpio_probe(struct platform_device *dev)
+@@ -113,14 +113,17 @@ static int s3c2410_spigpio_probe(struct platform_device *dev)
+       platform_set_drvdata(dev, sp);
+-      /* copy in the plkatform data */
++      /* copy in the platform data */
+       info = sp->info = dev->dev.platform_data;
++      master->num_chipselect = info->num_chipselect;
++
+       /* setup spi bitbang adaptor */
+       sp->bitbang.master = spi_master_get(master);
+       sp->bitbang.master->bus_num = info->bus_num;
+       sp->bitbang.master->num_chipselect = info->num_chipselect;
+       sp->bitbang.chipselect = s3c2410_spigpio_chipselect;
++      sp->bitbang.non_blocking_transfer = info->non_blocking_transfer;
+       sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0;
+       sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1;
+diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h
+new file mode 100644
+index 0000000..0c1fc30
+--- /dev/null
++++ b/include/linux/lis302dl.h
+@@ -0,0 +1,152 @@
++#ifndef _LINUX_LIS302DL_H
++#define _LINUX_LIS302DL_H
++
++#include <linux/types.h>
++#include <linux/spi/spi.h>
++#include <linux/input.h>
++#include <linux/workqueue.h>
++
++struct lis302dl_info;
++
++struct lis302dl_platform_data {
++      char *name;
++      unsigned long pin_chip_select;
++      unsigned long pin_clk;
++      unsigned long pin_mosi;
++      unsigned long pin_miso;
++      int open_drain;
++      int interrupt;
++      void (*lis302dl_suspend_io)(struct lis302dl_info *, int resuming);
++};
++
++struct lis302dl_info {
++      struct lis302dl_platform_data *pdata;
++      struct device *dev;
++      struct input_dev *input_dev;
++      unsigned int flags;
++      unsigned int threshold;
++      unsigned int duration;
++      u32 overruns;
++      struct {
++              unsigned int threshold; /* mg */
++              unsigned int duration;  /* ms */
++      } wakeup;
++
++      struct spi_device *spi;
++      u_int8_t regs[0x40];
++};
++
++enum lis302dl_reg {
++      LIS302DL_REG_WHO_AM_I           = 0x0f,
++      LIS302DL_REG_CTRL1              = 0x20,
++      LIS302DL_REG_CTRL2              = 0x21,
++      LIS302DL_REG_CTRL3              = 0x22,
++      LIS302DL_REG_HP_FILTER_RESET    = 0x23,
++      LIS302DL_REG_STATUS             = 0x27,
++      LIS302DL_REG_OUT_X              = 0x29,
++      LIS302DL_REG_OUT_Y              = 0x2b,
++      LIS302DL_REG_OUT_Z              = 0x2d,
++      LIS302DL_REG_FF_WU_CFG_1        = 0x30,
++      LIS302DL_REG_FF_WU_SRC_1        = 0x31,
++      LIS302DL_REG_FF_WU_THS_1        = 0x32,
++      LIS302DL_REG_FF_WU_DURATION_1   = 0x33,
++      LIS302DL_REG_FF_WU_CFG_2        = 0x34,
++      LIS302DL_REG_FF_WU_SRC_2        = 0x35,
++      LIS302DL_REG_FF_WU_THS_2        = 0x36,
++      LIS302DL_REG_FF_WU_DURATION_2   = 0x37,
++      LIS302DL_REG_CLICK_CFG          = 0x38,
++      LIS302DL_REG_CLICK_SRC          = 0x39,
++      LIS302DL_REG_CLICK_THSY_X       = 0x3b,
++      LIS302DL_REG_CLICK_THSZ         = 0x3c,
++      LIS302DL_REG_CLICK_TIME_LIMIT   = 0x3d,
++      LIS302DL_REG_CLICK_LATENCY      = 0x3e,
++      LIS302DL_REG_CLICK_WINDOW       = 0x3f,
++};
++
++enum lis302dl_reg_ctrl1 {
++      LIS302DL_CTRL1_Xen              = 0x01,
++      LIS302DL_CTRL1_Yen              = 0x02,
++      LIS302DL_CTRL1_Zen              = 0x04,
++      LIS302DL_CTRL1_STM              = 0x08,
++      LIS302DL_CTRL1_STP              = 0x10,
++      LIS302DL_CTRL1_FS               = 0x20,
++      LIS302DL_CTRL1_PD               = 0x40,
++      LIS302DL_CTRL1_DR               = 0x80,
++};
++
++enum lis302dl_reg_ctrl2 {
++      LIS302DL_CTRL2_HPC1             = 0x01,
++      LIS302DL_CTRL2_HPC2             = 0x02,
++      LIS302DL_CTRL2_HPFF1            = 0x04,
++      LIS302DL_CTRL2_HPFF2            = 0x08,
++      LIS302DL_CTRL2_FDS              = 0x10,
++      LIS302DL_CTRL2_BOOT             = 0x40,
++      LIS302DL_CTRL2_SIM              = 0x80,
++};
++enum lis302dl_reg_ctrl3 {
++      LIS302DL_CTRL3_PP_OD            = 0x40,
++      LIS302DL_CTRL3_IHL              = 0x80,
++};
++
++enum lis302dl_reg_status {
++      LIS302DL_STATUS_XDA             = 0x01,
++      LIS302DL_STATUS_YDA             = 0x02,
++      LIS302DL_STATUS_ZDA             = 0x04,
++      LIS302DL_STATUS_XYZDA           = 0x08,
++      LIS302DL_STATUS_XOR             = 0x10,
++      LIS302DL_STATUS_YOR             = 0x20,
++      LIS302DL_STATUS_ZOR             = 0x40,
++      LIS302DL_STATUS_XYZOR           = 0x80,
++};
++
++/* Wakeup/freefall interrupt defs */
++enum lis302dl_reg_ffwucfg {
++      LIS302DL_FFWUCFG_XLIE           = 0x01,
++      LIS302DL_FFWUCFG_XHIE           = 0x02,
++      LIS302DL_FFWUCFG_YLIE           = 0x04,
++      LIS302DL_FFWUCFG_YHIE           = 0x08,
++      LIS302DL_FFWUCFG_ZLIE           = 0x10,
++      LIS302DL_FFWUCFG_ZHIE           = 0x20,
++      LIS302DL_FFWUCFG_LIR            = 0x40,
++      LIS302DL_FFWUCFG_AOI            = 0x80,
++};
++
++enum lis302dl_reg_ffwuths {
++      LIS302DL_FFWUTHS_DCRM           = 0x80,
++};
++
++enum lis302dl_reg_ffwusrc {
++      LIS302DL_FFWUSRC_XL             = 0x01,
++      LIS302DL_FFWUSRC_XH             = 0x02,
++      LIS302DL_FFWUSRC_YL             = 0x04,
++      LIS302DL_FFWUSRC_YH             = 0x08,
++      LIS302DL_FFWUSRC_ZL             = 0x10,
++      LIS302DL_FFWUSRC_ZH             = 0x20,
++      LIS302DL_FFWUSRC_IA             = 0x40,
++};
++
++enum lis302dl_reg_cloik_src {
++      LIS302DL_CLICKSRC_SINGLE_X      = 0x01,
++      LIS302DL_CLICKSRC_DOUBLE_X      = 0x02,
++      LIS302DL_CLICKSRC_SINGLE_Y      = 0x04,
++      LIS302DL_CLICKSRC_DOUBLE_Y      = 0x08,
++      LIS302DL_CLICKSRC_SINGLE_Z      = 0x10,
++      LIS302DL_CLICKSRC_DOUBLE_Z      = 0x20,
++      LIS302DL_CLICKSRC_IA            = 0x40,
++};
++
++#define LIS302DL_WHO_AM_I_MAGIC               0x3b
++
++#define LIS302DL_F_WUP_FF_1           0x0001  /* wake up from free fall */
++#define LIS302DL_F_WUP_FF_2           0x0002
++#define LIS302DL_F_WUP_FF             0x0003
++#define LIS302DL_F_WUP_CLICK  0x0004
++#define LIS302DL_F_POWER              0x0010
++#define LIS302DL_F_FS                 0x0020  /* ADC full scale */
++#define LIS302DL_F_INPUT_OPEN         0x0040  /* Set if input device is opened */
++#define LIS302DL_F_IRQ_WAKE   0x0080  /* IRQ is setup in wake mode */
++#define LIS302DL_F_DR                 0x0100  /* Data rate, 400Hz/100Hz */
++
++
++#endif /* _LINUX_LIS302DL_H */
++
+diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
+index af56071..83ad05d 100644
+--- a/include/linux/spi/spi.h
++++ b/include/linux/spi/spi.h
+@@ -292,6 +292,13 @@ struct spi_master {
+       int                     (*transfer)(struct spi_device *spi,
+                                               struct spi_message *mesg);
++      /*
++       * Synchronous non blocking transfer function. Should guarantee
++       * data availability when it returns
++       */
++      int                     (*transfer_sync)(struct spi_device *spi,
++                                              struct spi_message *mesg);
++
+       /* called on release() to free memory provided by spi_master */
+       void                    (*cleanup)(struct spi_device *spi);
+ };
+@@ -543,6 +550,29 @@ static inline void spi_message_free(struct spi_message *m)
+ extern int spi_setup(struct spi_device *spi);
+ extern int spi_async(struct spi_device *spi, struct spi_message *message);
++/**
++ * spi_non_blocking_transfer - Synchronous, non blocking transfer
++ * @spi: device with which data will be exchanged
++ * @message: describes the data transfers with optional completion handlers
++ * Context: any (irqs may be blocked, etc)
++ *
++ * Data is guaranteed to be written or read when this function returns.
++ *
++ * Note : This may not be supported by all spi masters.
++ */
++
++static inline int
++spi_non_blocking_transfer(struct spi_device *spi, struct spi_message *message)
++{
++      if (unlikely(!spi->master->transfer_sync)) {
++              dev_err(&spi->master->dev,
++                              "non-blocking transfers not supported\n");
++              return -EIO;
++      }
++
++      return spi->master->transfer_sync(spi, message);
++}
++
+ /*---------------------------------------------------------------------------*/
+ /* All these synchronous SPI transfer routines are utilities layered
+diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h
+index 3274c50..6dc9b8b 100644
+--- a/include/linux/spi/spi_bitbang.h
++++ b/include/linux/spi/spi_bitbang.h
+@@ -31,6 +31,9 @@ struct spi_bitbang {
+       u8                      use_dma;
+       u8                      flags;          /* extra spi->mode support */
++      /* Support for synchronous non blocking transfers */
++      int                     non_blocking_transfer;
++
+       struct spi_master       *master;
+       /* setup_transfer() changes clock and/or wordsize to match settings
+@@ -62,6 +65,8 @@ extern void spi_bitbang_cleanup(struct spi_device *spi);
+ extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m);
+ extern int spi_bitbang_setup_transfer(struct spi_device *spi,
+                                     struct spi_transfer *t);
++extern int spi_bitbang_transfer_sync(struct spi_device *spi,
++                                    struct spi_message *m);
+ /* start or stop queue processing */
+ extern int spi_bitbang_start(struct spi_bitbang *spi);
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0002-Glamo-DRM-and-KMS-driver.patch b/recipes/linux/linux-openmoko-2.6.34/0002-Glamo-DRM-and-KMS-driver.patch
new file mode 100644 (file)
index 0000000..14dca73
--- /dev/null
@@ -0,0 +1,3869 @@
+From 4fe52e62cc86fc303cbc0f9a3c5081e38f95b950 Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Sat, 22 May 2010 19:01:00 +0200
+Subject: [PATCH 02/13] Glamo DRM and KMS driver
+
+This adds the Glamo DRM and KMS driver, but note that modifications are needed
+elsewhere to support it.
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
+---
+ drivers/mfd/glamo/Kconfig             |   16 +
+ drivers/mfd/glamo/Makefile            |    5 +-
+ drivers/mfd/glamo/glamo-buffer.c      |  374 +++++++++++++
+ drivers/mfd/glamo/glamo-buffer.h      |   60 ++
+ drivers/mfd/glamo/glamo-cmdq.c        |  555 +++++++++++++++++++
+ drivers/mfd/glamo/glamo-cmdq.h        |   49 ++
+ drivers/mfd/glamo/glamo-display.c     |  975 +++++++++++++++++++++++++++++++++
+ drivers/mfd/glamo/glamo-display.h     |   41 ++
+ drivers/mfd/glamo/glamo-drm-drv.c     |  423 ++++++++++++++
+ drivers/mfd/glamo/glamo-drm-private.h |  166 ++++++
+ drivers/mfd/glamo/glamo-fence.c       |  330 +++++++++++
+ drivers/mfd/glamo/glamo-fence.h       |   34 ++
+ drivers/mfd/glamo/glamo-kms-fb.c      |  493 +++++++++++++++++
+ drivers/mfd/glamo/glamo-kms-fb.h      |   41 ++
+ include/drm/Kbuild                    |    1 +
+ include/drm/glamo_drm.h               |  153 +++++
+ 16 files changed, 3715 insertions(+), 1 deletions(-)
+ create mode 100644 drivers/mfd/glamo/glamo-buffer.c
+ create mode 100644 drivers/mfd/glamo/glamo-buffer.h
+ create mode 100644 drivers/mfd/glamo/glamo-cmdq.c
+ create mode 100644 drivers/mfd/glamo/glamo-cmdq.h
+ create mode 100644 drivers/mfd/glamo/glamo-display.c
+ create mode 100644 drivers/mfd/glamo/glamo-display.h
+ create mode 100644 drivers/mfd/glamo/glamo-drm-drv.c
+ create mode 100644 drivers/mfd/glamo/glamo-drm-private.h
+ create mode 100644 drivers/mfd/glamo/glamo-fence.c
+ create mode 100644 drivers/mfd/glamo/glamo-fence.h
+ create mode 100644 drivers/mfd/glamo/glamo-kms-fb.c
+ create mode 100644 drivers/mfd/glamo/glamo-kms-fb.h
+ create mode 100644 include/drm/glamo_drm.h
+
+diff --git a/drivers/mfd/glamo/Kconfig b/drivers/mfd/glamo/Kconfig
+index 3aa4831..a12ebf6 100644
+--- a/drivers/mfd/glamo/Kconfig
++++ b/drivers/mfd/glamo/Kconfig
+@@ -40,3 +40,19 @@ config MFD_GLAMO_MCI
+         neo1973 GTA-02.
+         If unsure, say N.
++
++config MFD_GLAMO_DRM
++      tristate "Glamo direct rendering and kernel modesetting support"
++      depends on MFD_GLAMO && DRM
++      select FB_CFB_FILLRECT
++      select FB_CFB_COPYAREA
++      select FB_CFB_IMAGEBLIT
++      select DRM_KMS_HELPER
++      help
++        Direct Rendering Manager interface for the S-Media Glamo chip, as
++        used in Openmoko FreeRunner (GTA02).
++
++        This DRM driver includes kernel modesetting (KMS) support.  As such,
++        do not select MFD_GLAMO_FB above if you choose to enable this option.
++
++        If unsure, say N.
+diff --git a/drivers/mfd/glamo/Makefile b/drivers/mfd/glamo/Makefile
+index ebf26f7..d5ebf8f 100644
+--- a/drivers/mfd/glamo/Makefile
++++ b/drivers/mfd/glamo/Makefile
+@@ -1,5 +1,5 @@
+ #
+-# Makefile for the Smedia Glamo framebuffer driver
++# Makefile for the Smedia Glamo driver(s)
+ #
+ obj-$(CONFIG_MFD_GLAMO)                       += glamo-core.o
+@@ -8,4 +8,7 @@ obj-$(CONFIG_MFD_GLAMO_SPI)            += glamo-spi.o
+ obj-$(CONFIG_MFD_GLAMO_FB)            += glamo-fb.o
+ obj-$(CONFIG_MFD_GLAMO_MCI)           += glamo-mci.o
++obj-$(CONFIG_MFD_GLAMO_DRM)           += glamo-drm.o
++glamo-drm-objs        :=  glamo-drm-drv.o glamo-cmdq.o glamo-buffer.o \
++                    glamo-display.o glamo-kms-fb.o glamo-fence.o
+diff --git a/drivers/mfd/glamo/glamo-buffer.c b/drivers/mfd/glamo/glamo-buffer.c
+new file mode 100644
+index 0000000..82ea0c0
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-buffer.c
+@@ -0,0 +1,374 @@
++/*
++ * SMedia Glamo 336x/337x memory management
++ *
++ * Copyright (c) 2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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, see <http://www.gnu.org/licenses/>.
++ *
++ *
++ * Memory mapping functions based on i915_gem.c, to which the following
++ * notice applies:
++ *
++ * Copyright Â© 2008 Intel Corporation
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
++ * IN THE SOFTWARE.
++ *
++ * Authors:
++ *    Eric Anholt <eric@anholt.net>
++ */
++
++
++#include <drm/drmP.h>
++#include <drm/glamo_drm.h>
++
++#include "glamo-drm-private.h"
++#include "glamo-cmdq.h"       /* For glamo_cmdq_blank() */
++
++
++struct drm_gem_object *glamo_gem_object_alloc(struct drm_device *dev, int size,
++                                              int alignment)
++{
++      struct drm_gem_object *obj;
++      struct glamodrm_handle *gdrm;
++      struct drm_glamo_gem_object *gobj;
++
++      gdrm = dev->dev_private;
++
++      size = roundup(size, PAGE_SIZE);
++
++      obj = drm_gem_object_alloc(dev, size);
++      if (obj == NULL) return NULL;
++
++      /* See glamodrm_gem_init_object() below */
++      gobj = obj->driver_private;
++
++      /* Allocate memory for this object in VRAM */
++      gobj->block = drm_mm_search_free(gdrm->mmgr, size, alignment, 1);
++      if (!gobj->block) {
++              goto fail;
++      }
++      gobj->block = drm_mm_get_block(gobj->block, size, alignment);
++      if (!gobj->block) {
++              goto fail;
++      }
++
++      /* Arrange for the contents to be set to zero */
++      glamo_cmdq_blank(gdrm, obj);
++
++      return obj;
++
++fail:
++      mutex_lock(&dev->struct_mutex);
++      drm_gem_object_unreference(obj);
++      mutex_unlock(&dev->struct_mutex);
++      printk(KERN_INFO "[glamo-drm] Failed to allocate object\n");
++
++      return NULL;
++}
++
++
++int glamo_ioctl_gem_create(struct drm_device *dev, void *data,
++                         struct drm_file *file_priv)
++{
++      struct drm_glamo_gem_create *args = data;
++      struct drm_gem_object *obj;
++      int handle, ret, alignment, size;
++
++      /* Alignment must be a non-zero multiple of 2 */
++      alignment = args->alignment;
++      if ( alignment < 2 ) alignment = 2;
++      if ( alignment % 2 ) alignment *= 2;
++
++      /* Size must be similarly sanitised */
++      size = args->size;
++      if ( size < 2 ) size = 2;
++      if ( size % 2 ) size += 1;
++
++      /* Create an object */
++      obj = glamo_gem_object_alloc(dev, size, alignment);
++      if ( obj == NULL ) return -ENOMEM;
++
++      /* Create a handle for it */
++      ret = drm_gem_handle_create(file_priv, obj, &handle);
++      mutex_lock(&dev->struct_mutex);
++      drm_gem_object_handle_unreference(obj);
++      mutex_unlock(&dev->struct_mutex);
++      if (ret) goto fail;
++
++      /* Return */
++      args->handle = handle;
++      return 0;
++
++fail:
++      mutex_lock(&dev->struct_mutex);
++      drm_gem_object_unreference(obj);
++      mutex_unlock(&dev->struct_mutex);
++      printk(KERN_INFO "[glamo-drm] Failed to allocate object\n");
++      return ret;
++}
++
++
++int glamodrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
++{
++      struct drm_gem_object *obj = vma->vm_private_data;
++      struct drm_device *dev = obj->dev;
++      struct drm_glamo_gem_object *gobj = obj->driver_private;
++      struct glamodrm_handle *gdrm = dev->dev_private;
++      pgoff_t page_offset;
++      unsigned long pfn;
++      int ret = 0;
++
++      /* We don't use vmf->pgoff since that has the fake offset */
++      page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
++                     PAGE_SHIFT;
++
++      mutex_lock(&dev->struct_mutex);
++      pfn = ((gdrm->vram->start + GLAMO_OFFSET_FB + gobj->block->start)
++             >> PAGE_SHIFT) + page_offset;
++      ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
++      mutex_unlock(&dev->struct_mutex);
++
++      switch (ret) {
++      case -ENOMEM:
++      case -EAGAIN:
++              return VM_FAULT_OOM;
++      case -EFAULT:
++      case -EBUSY:
++              DRM_ERROR("can't insert pfn??  fault or busy...\n");
++              return VM_FAULT_SIGBUS;
++      default:
++              return VM_FAULT_NOPAGE;
++      }
++}
++
++
++static int glamo_gem_create_mmap_offset(struct drm_gem_object *obj)
++{
++      struct drm_device *dev = obj->dev;
++      struct drm_gem_mm *mm = dev->mm_private;
++      struct drm_glamo_gem_object *gobj = obj->driver_private;
++      struct drm_map_list *list;
++      struct drm_local_map *map;
++      int ret = 0;
++
++      /* Set the object up for mmap'ing */
++      list = &obj->map_list;
++      list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
++      if (!list->map)
++              return -ENOMEM;
++
++      map = list->map;
++      map->type = _DRM_GEM;
++      map->size = obj->size;
++      map->handle = obj;
++
++      /* Get a DRM GEM mmap offset allocated... */
++      list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
++                                                  obj->size / PAGE_SIZE, 0, 0);
++      if (!list->file_offset_node) {
++              DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
++              ret = -ENOMEM;
++              goto out_free_list;
++      }
++
++      list->file_offset_node = drm_mm_get_block(list->file_offset_node,
++                                                obj->size / PAGE_SIZE, 0);
++      if (!list->file_offset_node) {
++              ret = -ENOMEM;
++              goto out_free_list;
++      }
++
++      list->hash.key = list->file_offset_node->start;
++      if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) {
++              DRM_ERROR("failed to add to map hash\n");
++              goto out_free_mm;
++      }
++
++      /* By now we should be all set, any drm_mmap request on the offset
++       * below will get to our mmap & fault handler */
++      gobj->mmap_offset = ((uint64_t) list->hash.key) << PAGE_SHIFT;
++
++      return 0;
++
++out_free_mm:
++      drm_mm_put_block(list->file_offset_node);
++out_free_list:
++      kfree(list->map);
++
++      return ret;
++}
++
++
++int glamo_ioctl_gem_mmap(struct drm_device *dev, void *data,
++                       struct drm_file *file_priv)
++{
++      struct drm_glamo_gem_mmap *args = data;
++      struct drm_gem_object *obj;
++      struct drm_glamo_gem_object *gobj;
++      int ret;
++
++      obj = drm_gem_object_lookup(dev, file_priv, args->handle);
++      if (obj == NULL)
++              return -EBADF;
++
++      mutex_lock(&dev->struct_mutex);
++
++      gobj = obj->driver_private;
++      if (!gobj->mmap_offset) {
++              ret = glamo_gem_create_mmap_offset(obj);
++              if (ret) {
++                      printk(KERN_CRIT "Couldn't create mmap offset\n");
++                      drm_gem_object_unreference(obj);
++                      mutex_unlock(&dev->struct_mutex);
++                      return ret;
++              }
++      }
++
++      args->offset = gobj->mmap_offset;
++
++      drm_gem_object_unreference(obj);
++      mutex_unlock(&dev->struct_mutex);
++
++      return 0;
++}
++
++
++int glamo_ioctl_gem_pin(struct drm_device *dev, void *data,
++                      struct drm_file *file_priv)
++{
++      printk(KERN_INFO "glamo_ioctl_gem_pin\n");
++      return 0;
++}
++
++
++int glamo_ioctl_gem_unpin(struct drm_device *dev, void *data,
++                        struct drm_file *file_priv)
++{
++      printk(KERN_INFO "glamo_ioctl_gem_unpin\n");
++      return 0;
++}
++
++
++int glamo_ioctl_gem_pread(struct drm_device *dev, void *data,
++                        struct drm_file *file_priv)
++{
++      printk(KERN_INFO "glamo_ioctl_gem_pread\n");
++      return 0;
++}
++
++
++int glamo_ioctl_gem_pwrite(struct drm_device *dev, void *data,
++                         struct drm_file *file_priv)
++{
++      printk(KERN_INFO "glamo_ioctl_gem_pwrite\n");
++      return 0;
++}
++
++
++int glamodrm_gem_init_object(struct drm_gem_object *obj)
++{
++      struct drm_glamo_gem_object *gobj;
++
++      /* Allocate a private structure */
++      gobj = kzalloc(sizeof(*gobj), GFP_KERNEL);
++      if (!gobj) return -ENOMEM;
++
++      obj->driver_private = gobj;
++      gobj->obj = obj;
++
++      return 0;
++}
++
++
++void glamodrm_gem_free_object(struct drm_gem_object *obj)
++{
++      struct drm_glamo_gem_object *gobj;
++      struct drm_map_list *list;
++      struct drm_device *dev;
++      struct drm_gem_mm *mm;
++      struct drm_local_map *map;
++
++      dev = obj->dev;
++      mm = dev->mm_private;
++      gobj = obj->driver_private;
++
++      /* Free the VRAM */
++      if ( gobj->block != NULL ) {
++              drm_mm_put_block(gobj->block);
++      }
++
++      /* Release mappings */
++      list = &obj->map_list;
++      drm_ht_remove_item(&mm->offset_hash, &list->hash);
++      if (list->file_offset_node) {
++              drm_mm_put_block(list->file_offset_node);
++              list->file_offset_node = NULL;
++      }
++      map = list->map;
++      if (map) {
++              kfree(map);
++              list->map = NULL;
++      }
++
++      /* Free the private structure */
++      kfree(obj->driver_private);
++}
++
++
++/* Memory management initialisation */
++int glamo_buffer_init(struct glamodrm_handle *gdrm)
++{
++      gdrm->mmgr = kzalloc(sizeof(struct drm_mm), GFP_KERNEL);
++      drm_mm_init(gdrm->mmgr, 0, gdrm->vram_size);
++
++      /* Reserve a scratch buffer.  We do this outside the protections
++       * of the other GEM code.  To do this safely, the allocation must
++       * be a multiple of PAGE_SIZE. */
++      gdrm->scratch = drm_mm_search_free(gdrm->mmgr, PAGE_SIZE, 4, 1);
++      if ( gdrm->scratch ) {
++              gdrm->scratch = drm_mm_get_block(gdrm->scratch, PAGE_SIZE, 4);
++      }
++      if ( !gdrm->scratch ) {
++              printk(KERN_WARNING "[glamo-drm] Couldn't allocate"
++                                  " scratch buffer!\n");
++      }
++
++      return 0;
++}
++
++
++/* Memory management finalisation */
++int glamo_buffer_final(struct glamodrm_handle *gdrm)
++{
++      drm_mm_takedown(gdrm->mmgr);
++      kfree(gdrm->mmgr);
++      return 0;
++}
+diff --git a/drivers/mfd/glamo/glamo-buffer.h b/drivers/mfd/glamo/glamo-buffer.h
+new file mode 100644
+index 0000000..41f18fd
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-buffer.h
+@@ -0,0 +1,60 @@
++/*
++ * SMedia Glamo 336x/337x memory management
++ *
++ * Copyright (c) 2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ */
++
++#ifndef __GLAMO_BUFFER_H
++#define __GLAMO_BUFFER_H
++
++#include <drm/drmP.h>
++
++#include "glamo-drm-private.h"
++
++extern int glamo_buffer_init(struct glamodrm_handle *gdrm);
++extern int glamo_buffer_final(struct glamodrm_handle *gdrm);
++
++extern int glamodrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
++
++extern int glamodrm_gem_init_object(struct drm_gem_object *obj);
++
++extern void glamodrm_gem_free_object(struct drm_gem_object *obj);
++
++extern struct drm_gem_object *glamo_gem_object_alloc(struct drm_device *dev,
++                                                     int size, int alignment);
++
++extern int glamo_ioctl_gem_create(struct drm_device *dev, void *data,
++                                struct drm_file *file_priv);
++
++extern int glamo_ioctl_gem_mmap(struct drm_device *dev, void *data,
++                              struct drm_file *file_priv);
++
++extern int glamo_ioctl_gem_pin(struct drm_device *dev, void *data,
++                             struct drm_file *file_priv);
++
++extern int glamo_ioctl_gem_unpin(struct drm_device *dev, void *data,
++                               struct drm_file *file_priv);
++
++extern int glamo_ioctl_gem_pread(struct drm_device *dev, void *data,
++                               struct drm_file *file_priv);
++
++extern int glamo_ioctl_gem_pwrite(struct drm_device *dev, void *data,
++                                struct drm_file *file_priv);
++
++#endif /* __GLAMO_BUFFER_H */
+diff --git a/drivers/mfd/glamo/glamo-cmdq.c b/drivers/mfd/glamo/glamo-cmdq.c
+new file mode 100644
+index 0000000..442963c
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-cmdq.c
+@@ -0,0 +1,555 @@
++/*
++ * SMedia Glamo 336x/337x command queue handling
++ *
++ * Copyright (C) 2008-2009 Thomas White <taw@bitwiz.org.uk>
++ * Copyright (C) 2009 Andreas Pokorny <andreas.pokorny@gmail.com>
++ * Based on xf86-video-glamo (see below for details)
++ *
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ * Command queue handling functions based on those from xf86-video-glamo, to
++ * which the following licence applies:
++ *
++ * Copyright  2007 OpenMoko, Inc.
++ * Copyright Â© 2009 Lars-Peter Clausen <lars@metafoo.de>
++ *
++ * This driver is based on Xati,
++ * Copyright  2004 Eric Anholt
++ *
++ * Permission to use, copy, modify, distribute, and sell this software and its
++ * documentation for any purpose is hereby granted without fee, provided that
++ * the above copyright notice appear in all copies and that both that copyright
++ * notice and this permission notice appear in supporting documentation, and
++ * that the name of the copyright holders not be used in advertising or
++ * publicity pertaining to distribution of the software without specific,
++ * written prior permission.  The copyright holders make no representations
++ * about the suitability of this software for any purpose.  It is provided "as
++ * is" without express or implied warranty.
++ *
++ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
++ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
++ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
++ * OF THIS SOFTWARE.
++ */
++
++
++#include <drm/drmP.h>
++#include <drm/glamo_drm.h>
++
++#include "glamo-core.h"
++#include "glamo-drm-private.h"
++#include "glamo-regs.h"
++
++
++#define GLAMO_CMDQ_SIZE (128 * 1024)    /* 128k ring buffer */
++
++
++static inline void reg_write(struct glamodrm_handle *gdrm,
++                             u_int16_t reg, u_int16_t val)
++{
++      iowrite16(val, gdrm->reg_base + reg);
++}
++
++
++static inline u16 reg_read(struct glamodrm_handle *gdrm, u_int16_t reg)
++{
++      return ioread16(gdrm->reg_base + reg);
++}
++
++
++static u32 glamo_get_read(struct glamodrm_handle *gdrm)
++{
++      /* we could turn off clock here */
++      u32 ring_read = reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRL);
++      ring_read |= (reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRH) & 0x7) << 16;
++
++      return ring_read;
++}
++
++
++static u32 glamo_get_write(struct glamodrm_handle *gdrm)
++{
++      u32 ring_write = reg_read(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL);
++      ring_write |= (reg_read(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH) & 0x7) << 16;
++
++      return ring_write;
++}
++
++
++/* Add commands to the ring buffer */
++int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr,
++                      unsigned int count)
++{
++      size_t ring_write, ring_read;
++      size_t new_ring_write;
++
++      if ( count >= GLAMO_CMDQ_SIZE ) {
++              printk(KERN_WARNING "[glamo-drm] CmdQ submission too large\n");
++              return -EINVAL;
++      }
++
++      down(&gdrm->add_to_ring);
++
++      ring_write = glamo_get_write(gdrm);
++
++      /* Calculate where we'll end up */
++      new_ring_write = (ring_write + count) % GLAMO_CMDQ_SIZE;
++
++      /* Wait until there is enough space to queue the cmd buffer */
++      if (new_ring_write > ring_write) {
++              /* Loop while the read pointer is between the old and new
++               * positions */
++              do {
++                      ring_read = glamo_get_read(gdrm);
++              } while (ring_read > ring_write && ring_read < new_ring_write);
++      } else {
++              /* Same, but kind of inside-out */
++              do {
++                      ring_read = glamo_get_read(gdrm);
++              } while (ring_read > ring_write || ring_read < new_ring_write);
++      }
++
++      /* Are we about to wrap around? */
++      if (ring_write >= new_ring_write) {
++
++              u32 rest_size;
++
++              /* Wrap around */
++              rest_size = GLAMO_CMDQ_SIZE - ring_write; /* Space left */
++
++              /* Write from current position to end */
++              memcpy_toio(gdrm->cmdq_base+ring_write, addr, rest_size);
++
++              /* Write from start */
++              memcpy_toio(gdrm->cmdq_base, addr+(rest_size>>1),
++                          count - rest_size);
++
++              /* ring_write being 0 will result in a deadlock because the
++               * cmdq read will never stop. To avoid such an behaviour insert
++               * an empty instruction. */
++              if (new_ring_write == 0) {
++                      iowrite16(0x0000, gdrm->cmdq_base);
++                      iowrite16(0x0000, gdrm->cmdq_base + 2);
++                      new_ring_write = 4;
++              }
++
++      } else {
++
++              memcpy_toio(gdrm->cmdq_base+ring_write, addr, count);
++
++      }
++
++      reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH,
++                      (new_ring_write >> 16) & 0x7f);
++      reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL,
++                      new_ring_write & 0xffff);
++
++      if ( !(reg_read(gdrm, GLAMO_REG_CMDQ_STATUS) & 1<<3)  ) {
++              printk(KERN_ERR "[glamo-drm] CmdQ decode failure.\n");
++      }
++
++      up(&gdrm->add_to_ring);
++
++      return 0;
++}
++
++
++/* Return true for a legal sequence of commands, otherwise false */
++static int glamo_sanitize_buffer(u16 *cmds, unsigned int count)
++{
++      /* XXX FIXME TODO: Implementation... */
++      return 1;
++}
++
++
++/* Substitute the real addresses in VRAM for any required buffer objects */
++static int glamo_do_relocation(struct glamodrm_handle *gdrm,
++                               drm_glamo_cmd_buffer_t *cbuf, u16 *cmds,
++                               struct drm_device *dev,
++                               struct drm_file *file_priv)
++{
++      u32 *handles;
++      int *offsets;
++      int nobjs =  cbuf->nobjs;
++      int i;
++
++      if ( nobjs > 32 ) return -EINVAL;       /* Get real... */
++
++      handles = kmalloc(nobjs*sizeof(u32), GFP_KERNEL);
++      if ( handles == NULL ) return -1;
++      if ( copy_from_user(handles, cbuf->objs, nobjs*sizeof(u32)) )
++              return -1;
++
++      offsets = kmalloc(nobjs*sizeof(int), GFP_KERNEL);
++      if ( offsets == NULL ) return -1;
++      if ( copy_from_user(offsets, cbuf->obj_pos, nobjs*sizeof(int)) )
++              return -1;
++
++      for ( i=0; i<nobjs; i++ ) {
++
++              u32 handle = handles[i];
++              int offset = offsets[i];
++              struct drm_gem_object *obj;
++              struct drm_glamo_gem_object *gobj;
++              u32 addr;
++              u16 addr_low, addr_high;
++
++              if ( offset > cbuf->bufsz ) {
++                      printk(KERN_WARNING "[glamo-drm] Offset out of range"
++                                          " for this relocation!\n");
++                      goto fail;
++              }
++
++              obj = drm_gem_object_lookup(dev, file_priv, handle);
++              if ( obj == NULL ) return -1;
++
++              /* Unref the object now, or it'll never get freed.
++               * This should really happen after the GPU has finished
++               * the commands which are about to be submitted. */
++              drm_gem_object_unreference(obj);
++
++              gobj = obj->driver_private;
++              if ( gobj == NULL ) {
++                      printk(KERN_WARNING "[glamo-drm] This object has no"
++                                          " private data!\n");
++                      goto fail;
++              }
++
++              addr = GLAMO_OFFSET_FB + gobj->block->start;
++              addr_low = addr & 0xffff;
++              addr_high = (addr >> 16) & 0x7f;
++
++              /* FIXME: Should really check that the register is a
++               * valid one for this relocation. */
++
++              *(cmds+(offset/2)+1) = addr_low;
++              *(cmds+(offset/2)+3) = addr_high;
++
++      }
++
++      kfree(handles);
++      kfree(offsets);
++      return 0;
++
++fail:
++      kfree(handles);
++      kfree(offsets);
++      return -1;
++}
++
++
++/* This is DRM_IOCTL_GLAMO_CMDBUF */
++int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data,
++                       struct drm_file *file_priv)
++{
++      int ret = 0;
++      struct glamodrm_handle *gdrm;
++      unsigned int count;
++      drm_glamo_cmd_buffer_t *cbuf = data;
++      u16 *cmds;
++
++      gdrm = dev->dev_private;
++
++      count = cbuf->bufsz;
++
++      if ( count > PAGE_SIZE ) return -EINVAL;
++
++      cmds = kmalloc(count, GFP_KERNEL);
++      if ( cmds == NULL ) return -ENOMEM;
++      if ( copy_from_user(cmds, cbuf->buf, count) )   {
++              printk(KERN_WARNING "[glamo-drm] copy from user failed\n");
++              ret = -EINVAL;
++              goto cleanup;
++      }
++
++      /* Check the buffer isn't going to tell Glamo to enact naughtiness */
++      if ( !glamo_sanitize_buffer(cmds, count) ) {
++              printk(KERN_WARNING "[glamo-drm] sanitize buffer failed\n");
++              ret = -EINVAL;
++              goto cleanup;
++      }
++
++      /* Perform relocation, if necessary */
++      if ( cbuf->nobjs ) {
++              if ( glamo_do_relocation(gdrm, cbuf, cmds, dev, file_priv) )
++              {
++                      printk(KERN_WARNING "[glamo-drm] Relocation failed\n");
++                      ret = -EINVAL;
++                      goto cleanup;
++              }
++      }
++
++      glamo_add_to_ring(gdrm, cmds, count);
++
++cleanup:
++      kfree(cmds);
++
++      return ret;
++}
++
++
++/* Return true for a legal sequence of commands, otherwise false */
++static int glamo_sanitize_burst(u16 base, u16 *cmds, unsigned int count)
++{
++      /* XXX FIXME TODO: Implementation... */
++      return 1;
++}
++
++
++static int glamo_relocate_burst(struct glamodrm_handle *gdrm,
++                                drm_glamo_cmd_burst_t *cbuf, u16 *data,
++                                struct drm_device *dev,
++                                struct drm_file *file_priv)
++{
++      u32 *handles;
++      int *offsets;
++      int nobjs =  cbuf->nobjs;
++      int i;
++
++      if ( nobjs > 32 ) return -EINVAL;       /* Get real... */
++
++      handles = kmalloc(nobjs*sizeof(u32), GFP_KERNEL);
++      if ( handles == NULL ) return -1;
++      if ( copy_from_user(handles, cbuf->objs, nobjs*sizeof(u32)) )
++              return -1;
++
++      offsets = kmalloc(nobjs*sizeof(int), GFP_KERNEL);
++      if ( offsets == NULL ) return -1;
++      if ( copy_from_user(offsets, cbuf->obj_pos, nobjs*sizeof(int)) )
++              return -1;
++
++      for ( i=0; i<nobjs; i++ ) {
++
++              u32 handle = handles[i];
++              int offset = offsets[i];
++              struct drm_gem_object *obj;
++              struct drm_glamo_gem_object *gobj;
++              u32 addr;
++              u16 addr_low, addr_high;
++
++              if ( offset > cbuf->bufsz ) {
++                      printk(KERN_WARNING "[glamo-drm] Offset out of range"
++                                          " for this relocation!\n");
++                      goto fail;
++              }
++
++              obj = drm_gem_object_lookup(dev, file_priv, handle);
++              if ( obj == NULL ) return -1;
++
++              /* Unref the object now, or it'll never get freed.
++               * FIXME: This should really happen after the GPU has
++               * finished executing these commands. */
++              drm_gem_object_unreference(obj);
++
++              gobj = obj->driver_private;
++              if ( gobj == NULL ) {
++                      printk(KERN_WARNING "[glamo-drm] This object has no"
++                                          " private data!\n");
++                      goto fail;
++              }
++
++              addr = GLAMO_OFFSET_FB + gobj->block->start;
++              addr_low = addr & 0xffff;
++              addr_high = (addr >> 16) & 0x7f;
++
++              /* FIXME: Should really check that the register is a
++               * valid one for this relocation. */
++
++              *(data+(offset/2)+0) = addr_low;
++              *(data+(offset/2)+1) = addr_high;
++
++      }
++
++      kfree(handles);
++      kfree(offsets);
++      return 0;
++
++fail:
++      kfree(handles);
++      kfree(offsets);
++      return -1;
++}
++
++
++/* This is DRM_IOCTL_GLAMO_CMDBURST */
++int glamo_ioctl_cmdburst(struct drm_device *dev, void *data,
++                         struct drm_file *file_priv)
++{
++      int ret = 0;
++      struct glamodrm_handle *gdrm;
++      drm_glamo_cmd_burst_t *cbuf = data;
++      u16 *burst;
++      size_t burst_size;
++      size_t data_size;
++
++      gdrm = dev->dev_private;
++
++      data_size = cbuf->bufsz;
++      if ( data_size % 4 ) data_size += 2;
++      if ( data_size % 4 ) return -EINVAL;
++      burst_size = data_size + 4;  /* Add space for header */
++      if ( burst_size > PAGE_SIZE ) return -EINVAL;
++
++      burst = kmalloc(burst_size, GFP_KERNEL);
++      if ( burst == NULL ) return -ENOMEM;
++
++      /* Get data from userspace */
++      if ( copy_from_user(burst+2, cbuf->data, cbuf->bufsz) )         {
++              printk(KERN_WARNING "[glamo-drm] copy from user failed\n");
++              ret = -EINVAL;
++              goto cleanup;
++      }
++
++      /* Sanitise */
++      if ( !glamo_sanitize_burst(cbuf->base, burst+2, cbuf->bufsz) ) {
++              printk(KERN_WARNING "[glamo-drm] sanitize buffer failed\n");
++              ret = -EINVAL;
++              goto cleanup;
++      }
++
++      /* Relocate */
++      if ( cbuf->nobjs ) {
++              if ( glamo_relocate_burst(gdrm, cbuf, burst+2, dev, file_priv) )
++              {
++                      printk(KERN_WARNING "[glamo-drm] Relocation failed\n");
++                      ret = -EINVAL;
++                      goto cleanup;
++              }
++      }
++
++      /* Add burst header */
++      burst[0] = 1<<15 | cbuf->base;
++      burst[1] = data_size / 2;  /* -> 2-byte words */
++      if ( burst[1] & 0x01 ) {
++              printk(KERN_WARNING "[glamo-drm] Burst not aligned!\n");
++              goto cleanup;
++      }
++
++      /* Zero-pad if necessary */
++      if ( data_size % 4 ) {
++              burst[burst_size-1] = 0x0000;
++      }
++
++      /* Add to command queue */
++      glamo_add_to_ring(gdrm, burst, burst_size);
++
++cleanup:
++      kfree(burst);
++
++      return ret;
++}
++
++
++int glamo_cmdq_setup(struct glamodrm_handle *gdrm)
++{
++      unsigned int i;
++
++      init_MUTEX(&gdrm->add_to_ring);
++
++      /* Enable 2D and 3D */
++      glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_2D);
++      glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_2D);
++
++      /* Start by zeroing the command queue memory */
++      for ( i=0; i<GLAMO_CMDQ_SIZE; i+=2 ) {
++              iowrite16(0x0000, gdrm->cmdq_base+i);
++      }
++
++      glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);
++      glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);
++
++      /* Set up command queue location */
++      reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRL,
++                                      gdrm->cmdq_offs & 0xffff);
++      reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRH,
++                                      (gdrm->cmdq_offs >> 16) & 0x7f);
++
++      /* Length of command queue in 1k blocks, minus one */
++      reg_write(gdrm, GLAMO_REG_CMDQ_LEN, (GLAMO_CMDQ_SIZE >> 10)-1);
++      reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH, 0);
++      reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL, 0);
++      reg_write(gdrm, GLAMO_REG_CMDQ_CONTROL,
++                                       1 << 12 |   /* Turbo flip (?) */
++                                       5 << 8  |   /* no interrupt */
++                                       8 << 4);    /* HQ threshold */
++
++      return 0;
++}
++
++
++int glamo_cmdq_init(struct glamodrm_handle *gdrm)
++{
++      struct drm_gem_object *obj;
++      struct drm_glamo_gem_object *gobj;
++      int ret = 0;
++
++      obj = glamo_gem_object_alloc(dev, GLAMO_CMDQ_SIZE, 4);
++      if ( !obj ) {
++              printk(KERN_ERR "[glamo-drm] Failed to allocate CmdQ\n");
++              ret = -ENOMEM;
++              goto out;
++      }
++      gobj = fbo->driver_private;
++      gdrm->cmdq_offs = GLAMO_OFFSET_FB + gobj->block->start;
++      gdrm->cmdq_base = ioremap(gdrm->vram->start + offs, GLAMO_CMDQ_SIZE);
++
++      /* Set up registers */
++      glamo_cmdq_setup(gdrm);
++
++out:
++      return ret;
++}
++
++
++int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm)
++{
++      return 0;
++}
++
++
++void glamo_cmdq_suspend(struct glamodrm_handle *gdrm)
++{
++      /* Placeholder... */
++}
++
++
++void glamo_cmdq_resume(struct glamodrm_handle *gdrm)
++{
++      glamo_cmdq_setup(gdrm);
++}
++
++
++/* Initialise an object's contents to zero.
++ * This is in glamo-cmdq.c in the hope that we can accelerate it later. */
++void glamo_cmdq_blank(struct glamodrm_handle *gdrm, struct drm_gem_object *obj)
++{
++      char __iomem *cookie;
++      struct drm_glamo_gem_object *gobj;
++      int i;
++
++      gobj = obj->driver_private;
++
++      cookie = ioremap(gdrm->vram->start + gobj->block->start, obj->size);
++      for ( i=0; i<obj->size; i+=2 ) {
++              iowrite16(0, cookie+i);
++      }
++      iounmap(cookie);
++}
+diff --git a/drivers/mfd/glamo/glamo-cmdq.h b/drivers/mfd/glamo/glamo-cmdq.h
+new file mode 100644
+index 0000000..510d195
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-cmdq.h
+@@ -0,0 +1,49 @@
++/* Smedia Glamo 336x/337x command queue handling
++ *
++ * Copyright (c) 2008-2009 Thomas White <taw@bitwiz.org.uk>
++ * Copyright (c) 2009 Andreas Pokorny <andreas.pokorny@gmail.com>
++ * Based on xf86-video-glamo
++ * Copyright  2007 OpenMoko, Inc.
++ * Copyright Â© 2009 Lars-Peter Clausen <lars@metafoo.de>
++ *
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#ifndef __GLAMO_CMDQ_H
++#define __GLAMO_CMDQ_H
++
++#include <drm/drmP.h>
++
++#include "glamo-drm-private.h"
++
++extern int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data,
++                            struct drm_file *file_priv);
++extern int glamo_ioctl_cmdburst(struct drm_device *dev, void *data,
++                              struct drm_file *file_priv);
++extern void glamo_cmdq_blank(struct glamodrm_handle *gdrm,
++                             struct drm_gem_object *obj);
++
++extern int glamo_cmdq_init(struct glamodrm_handle *gdrm);
++extern int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm);
++extern void glamo_cmdq_suspend(struct glamodrm_handle *gdrm);
++extern void glamo_cmdq_resume(struct glamodrm_handle *gdrm);
++
++extern int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr,
++                             unsigned int count);
++
++#endif /* __GLAMO_CMDQ_H */
+diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c
+new file mode 100644
+index 0000000..75ad734
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-display.c
+@@ -0,0 +1,975 @@
++/*
++ * SMedia Glamo 336x/337x display
++ *
++ * Copyright (C) 2008-2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc.
++ * Author: Harald Welte <laforge@openmoko.org>
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ *
++ * Based on intel_display.c and intel_crt.c from drivers/gpu/drm/i915
++ *  to which the following licence applies:
++ *
++ * Copyright Â© 2006-2007 Intel Corporation
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ *
++ * Authors:
++ *    Eric Anholt <eric@anholt.net>
++ *
++ */
++
++#include <drm/drmP.h>
++#include <drm/glamo_drm.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_crtc.h>
++#include <linux/glamofb.h>
++#include <linux/jbt6k74.h>
++
++#include "glamo-core.h"
++#include "glamo-drm-private.h"
++#include "glamo-regs.h"
++#include "glamo-kms-fb.h"
++#include "glamo-display.h"
++
++
++#define GLAMO_LCD_WIDTH_MASK 0x03FF
++#define GLAMO_LCD_HEIGHT_MASK 0x03FF
++#define GLAMO_LCD_PITCH_MASK 0x07FE
++#define GLAMO_LCD_HV_TOTAL_MASK 0x03FF
++#define GLAMO_LCD_HV_RETR_START_MASK 0x03FF
++#define GLAMO_LCD_HV_RETR_END_MASK 0x03FF
++#define GLAMO_LCD_HV_RETR_DISP_START_MASK 0x03FF
++#define GLAMO_LCD_HV_RETR_DISP_END_MASK 0x03FF
++
++
++struct glamofb_par {
++      struct drm_device *dev;
++      struct drm_display_mode *our_mode;
++      struct glamo_framebuffer *glamo_fb;
++      int crtc_count;
++      /* crtc currently bound to this */
++      uint32_t crtc_ids[2];
++};
++
++
++static int reg_read_lcd(struct glamodrm_handle *gdrm, u_int16_t reg)
++{
++      int i = 0;
++
++      for (i = 0; i != 2; i++)
++              nop();
++
++      return ioread16(gdrm->lcd_base + reg);
++}
++
++
++static void reg_write_lcd(struct glamodrm_handle *gdrm,
++                          u_int16_t reg, u_int16_t val)
++{
++      int i = 0;
++
++      for (i = 0; i != 2; i++)
++              nop();
++
++      iowrite16(val, gdrm->lcd_base + reg);
++}
++
++
++static void reg_set_bit_mask_lcd(struct glamodrm_handle *gdrm,
++                                 u_int16_t reg, u_int16_t mask,
++                                 u_int16_t val)
++{
++      u_int16_t tmp;
++
++      val &= mask;
++
++      tmp = reg_read_lcd(gdrm, reg);
++      tmp &= ~mask;
++      tmp |= val;
++      reg_write_lcd(gdrm, reg, tmp);
++}
++
++
++/* Note that this has nothing at all to do with the engine command queue
++ * in glamo-cmdq.c */
++static inline int glamo_lcd_cmdq_empty(struct glamodrm_handle *gdrm)
++{
++      /* DGCMdQempty -- 1 == command queue is empty */
++      return reg_read_lcd(gdrm, GLAMO_REG_LCD_STATUS1) & (1 << 15);
++}
++
++
++/* call holding gfb->lock_cmd  when locking, until you unlock */
++int glamo_lcd_cmd_mode(struct glamodrm_handle *gdrm, int on)
++{
++      int timeout = 2000000;
++
++      dev_dbg(gdrm->dev, "glamofb_cmd_mode(on=%d)\n", on);
++      if (on) {
++
++              while ((!glamo_lcd_cmdq_empty(gdrm)) && (timeout--))
++                      /* yield() */;
++              if (timeout < 0) {
++                      printk(KERN_ERR "*************"
++                                      " LCD command queue never got empty "
++                                      "*************\n");
++                      return -EIO;
++              }
++
++              /* display the entire frame then switch to command */
++              reg_write_lcd(gdrm, GLAMO_REG_LCD_COMMAND1,
++                        GLAMO_LCD_CMD_TYPE_DISP |
++                        GLAMO_LCD_CMD_DATA_FIRE_VSYNC);
++
++              /* wait until lcd idle */
++              timeout = 2000000;
++              while ((!reg_read_lcd(gdrm, GLAMO_REG_LCD_STATUS2) & (1 << 12))
++                        && (timeout--))
++                      /* yield() */;
++              if (timeout < 0) {
++                      printk(KERN_ERR"*************"
++                                     " LCD never idle "
++                                     "*************\n");
++                      return -EIO;
++              }
++
++              mdelay(100);
++
++      } else {
++              /* RGB interface needs vsync/hsync */
++              int mode;
++              mode = reg_read_lcd(gdrm, GLAMO_REG_LCD_MODE3);
++              if ( mode & GLAMO_LCD_MODE3_RGB)
++                      reg_write_lcd(gdrm, GLAMO_REG_LCD_COMMAND1,
++                                GLAMO_LCD_CMD_TYPE_DISP |
++                                GLAMO_LCD_CMD_DATA_DISP_SYNC);
++
++              reg_write_lcd(gdrm, GLAMO_REG_LCD_COMMAND1,
++                        GLAMO_LCD_CMD_TYPE_DISP |
++                        GLAMO_LCD_CMD_DATA_DISP_FIRE);
++      }
++
++      return 0;
++}
++
++
++static struct glamo_script lcd_init_script[] = {
++      { GLAMO_REG_LCD_MODE1, 0x0020 },
++      /* no display rotation, no hardware cursor, no dither, no gamma,
++       * no retrace flip, vsync low-active, hsync low active,
++       * no TVCLK, no partial display, hw dest color from fb,
++       * no partial display mode, LCD1, software flip,  */
++      { GLAMO_REG_LCD_MODE2, 0x9020 },
++        /* video flip, no ptr, no ptr, dhclk off,
++         * normal mode,  no cpuif,
++         * res, serial msb first, single fb, no fr ctrl,
++         * cpu if bits all zero, no crc
++         * 0000 0000 0010  0000 */
++      { GLAMO_REG_LCD_MODE3, 0x0b40 },
++        /* src data rgb565, res, 18bit rgb666
++         * 000 01 011 0100 0000 */
++      { GLAMO_REG_LCD_POLARITY, 0x440c },
++        /* DE high active, no cpu/lcd if, cs0 force low, a0 low active,
++         * np cpu if, 9bit serial data, sclk rising edge latch data
++         * 01 00 0 100 0 000 01 0 0 */
++      /* The following values assume 640*480@16bpp */
++      /* FIXME: fb0 has not yet been allocated! */
++      { GLAMO_REG_LCD_A_BASE1, PAGE_SIZE }, /* display A base address 15:0 */
++      { GLAMO_REG_LCD_A_BASE2, 0x0000 }, /* display A base address 22:16 */
++      { GLAMO_REG_LCD_B_BASE1, 0x6000 }, /* display B base address 15:0 */
++      { GLAMO_REG_LCD_B_BASE2, 0x0009 }, /* display B base address 22:16 */
++      { GLAMO_REG_LCD_CURSOR_BASE1, 0xC000 }, /* cursor base address 15:0 */
++      { GLAMO_REG_LCD_CURSOR_BASE2, 0x0012 }, /* cursor base address 22:16 */
++      { GLAMO_REG_LCD_COMMAND2, 0x0000 }, /* display page A */
++};
++
++
++static int glamo_run_lcd_script(struct glamodrm_handle *gdrm,
++                                struct glamo_script *script, int len)
++{
++      int i;
++
++      for (i = 0; i < len; i++) {
++              struct glamo_script *line = &script[i];
++
++              if (line->reg == 0xffff)
++                      return 0;
++              else if (line->reg == 0xfffe)
++                      msleep(line->val);
++              else
++                      reg_write_lcd(gdrm, script[i].reg, script[i].val);
++      }
++
++      return 0;
++}
++
++
++static bool glamo_crtc_mode_fixup(struct drm_crtc *crtc,
++                                  struct drm_display_mode *mode,
++                                  struct drm_display_mode *adjusted_mode)
++{
++      return true;
++}
++
++
++static int glamo_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
++                                    struct drm_framebuffer *old_fb)
++{
++      struct glamodrm_handle *gdrm;
++      struct glamo_crtc *gcrtc;
++      struct glamo_framebuffer *gfb;
++      struct drm_gem_object *obj;
++      struct drm_glamo_gem_object *gobj;
++      u32 addr;
++      u16 addr_low, addr_high;
++
++      if (!crtc->fb) {
++              DRM_DEBUG("No FB bound\n");
++              return -EINVAL;
++      }
++
++      /* Dig out our handle */
++      gcrtc = to_glamo_crtc(crtc);
++      gdrm = gcrtc->gdrm;     /* Here it is! */
++
++      if ( !gcrtc->pixel_clock_on ) {
++              printk(KERN_WARNING "[glamo-drm] Display is off - "
++                                  "enabling it before setting base.\n");
++              glamo_lcd_power(gdrm, 1);
++      }
++
++      gfb = to_glamo_framebuffer(crtc->fb);
++      obj = gfb->obj;
++      gobj = obj->driver_private;
++
++      addr = GLAMO_OFFSET_FB + gobj->block->start;
++      addr_low = addr & 0xffff;
++      addr_high = ((addr >> 16) & 0x7f) | 0x4000;
++
++      glamo_lcd_cmd_mode(gdrm, 1);
++      reg_write_lcd(gdrm, GLAMO_REG_LCD_A_BASE1, addr_low);
++      reg_write_lcd(gdrm, GLAMO_REG_LCD_A_BASE2, addr_high);
++      glamo_lcd_cmd_mode(gdrm, 0);
++
++      return 0;
++}
++
++
++static int glamo_crtc_mode_set(struct drm_crtc *crtc,
++                               struct drm_display_mode *mode,
++                               struct drm_display_mode *adjusted_mode,
++                               int x, int y,
++                               struct drm_framebuffer *old_fb)
++{
++      struct glamodrm_handle *gdrm;
++      struct glamo_crtc *gcrtc;
++      int retr_start, retr_end, disp_start, disp_end;
++      int rot;
++
++      /* Dig out our handle */
++      gcrtc = to_glamo_crtc(crtc);
++      gdrm = gcrtc->gdrm;     /* Here it is! */
++
++      if ( !gcrtc->pixel_clock_on ) {
++              printk(KERN_WARNING "[glamo-drm] Display is off - "
++                                  "enabling it before setting mode.\n");
++              glamo_lcd_power(gdrm, 1);
++              msleep(500);
++      }
++
++      /* Rotate? */
++      if ( (mode->hdisplay == 640) && (mode->vdisplay == 480) ) {
++              rot = GLAMO_LCD_ROT_MODE_90;
++      } else if ( (mode->hdisplay == 480) && (mode->vdisplay == 640) ) {
++              rot = GLAMO_LCD_ROT_MODE_0;
++      } else if ( (mode->hdisplay == 320) && (mode->vdisplay == 240) ) {
++              rot = GLAMO_LCD_ROT_MODE_90;
++      } else if ( (mode->hdisplay == 240) && (mode->vdisplay == 320) ) {
++              rot = GLAMO_LCD_ROT_MODE_0;
++      } else {
++              printk(KERN_WARNING "[glamo-drm] Couldn't choose rotation.\n");
++              rot = GLAMO_LCD_ROT_MODE_0;
++      }
++
++      glamo_lcd_cmd_mode(gdrm, 1);
++
++      /* Set dimensions */
++      if ( rot == GLAMO_LCD_ROT_MODE_0 ) {
++
++              glamo_engine_reclock(gdrm->glamo_core, GLAMO_ENGINE_LCD,
++                                   mode->clock);
++
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_WIDTH,
++                                   GLAMO_LCD_WIDTH_MASK, mode->hdisplay);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HEIGHT,
++                                   GLAMO_LCD_HEIGHT_MASK, mode->vdisplay);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_PITCH,
++                                   GLAMO_LCD_PITCH_MASK, mode->hdisplay*2);
++
++              /* Set rotation */
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_WIDTH,
++                                   GLAMO_LCD_ROT_MODE_MASK, rot);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_MODE1,
++                                   GLAMO_LCD_MODE1_ROTATE_EN,
++                                   (rot != GLAMO_LCD_ROT_MODE_0) ?
++                                     GLAMO_LCD_MODE1_ROTATE_EN : 0);
++
++              /* Convert "X modeline timings" into "Glamo timings" */
++              retr_start = 0;
++              retr_end = retr_start + mode->hsync_end - mode->hsync_start;
++              disp_start = mode->htotal - mode->hsync_start;
++              disp_end = disp_start + mode->hdisplay;
++
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_TOTAL,
++                                   GLAMO_LCD_HV_TOTAL_MASK, mode->htotal);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_START,
++                                   GLAMO_LCD_HV_RETR_START_MASK, retr_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_END,
++                                   GLAMO_LCD_HV_RETR_END_MASK, retr_end);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_START,
++                                   GLAMO_LCD_HV_RETR_DISP_START_MASK, disp_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_END,
++                                   GLAMO_LCD_HV_RETR_DISP_END_MASK, disp_end);
++
++              /* The same in the vertical direction */
++              retr_start = 0;
++              retr_end = retr_start + mode->vsync_end - mode->vsync_start;
++              disp_start = mode->vtotal - mode->vsync_start;
++              disp_end = disp_start + mode->vdisplay;
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_TOTAL,
++                                   GLAMO_LCD_HV_TOTAL_MASK, mode->vtotal);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_START,
++                                   GLAMO_LCD_HV_RETR_START_MASK, retr_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_END,
++                                   GLAMO_LCD_HV_RETR_END_MASK, retr_end);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_START,
++                                   GLAMO_LCD_HV_RETR_DISP_START_MASK,
++                                   disp_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_END,
++                                   GLAMO_LCD_HV_RETR_DISP_END_MASK, disp_end);
++
++      } else {
++
++              glamo_engine_reclock(gdrm->glamo_core, GLAMO_ENGINE_LCD,
++                                   mode->clock/2);
++
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_WIDTH,
++                                   GLAMO_LCD_WIDTH_MASK, mode->vdisplay);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HEIGHT,
++                                   GLAMO_LCD_HEIGHT_MASK, mode->hdisplay);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_PITCH,
++                                   GLAMO_LCD_PITCH_MASK, mode->hdisplay*2);
++
++              /* Set rotation */
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_WIDTH,
++                                   GLAMO_LCD_ROT_MODE_MASK, rot);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_MODE1,
++                                   GLAMO_LCD_MODE1_ROTATE_EN,
++                                   (rot != GLAMO_LCD_ROT_MODE_0) ?
++                                     GLAMO_LCD_MODE1_ROTATE_EN : 0);
++
++              /* Apply "vertical" numbers to the horizontal registers */
++              retr_start = 0;
++              retr_end = retr_start + mode->vsync_end - mode->vsync_start;
++              disp_start = mode->vtotal - mode->vsync_start;
++              disp_end = disp_start + mode->vdisplay;
++
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_TOTAL,
++                                   GLAMO_LCD_HV_TOTAL_MASK, mode->vtotal);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_START,
++                                   GLAMO_LCD_HV_RETR_START_MASK, retr_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_END,
++                                   GLAMO_LCD_HV_RETR_END_MASK, retr_end);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_START,
++                                   GLAMO_LCD_HV_RETR_DISP_START_MASK,
++                                   disp_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_END,
++                                   GLAMO_LCD_HV_RETR_DISP_END_MASK, disp_end);
++
++              /* Apply "horizontal" numbers to the vertical registers */
++              retr_start = 0;
++              retr_end = retr_start + mode->hsync_end - mode->hsync_start;
++              disp_start = mode->htotal - mode->hsync_start;
++              disp_end = disp_start + mode->hdisplay;
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_TOTAL,
++                                   GLAMO_LCD_HV_TOTAL_MASK, mode->htotal);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_START,
++                                   GLAMO_LCD_HV_RETR_START_MASK, retr_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_END,
++                                   GLAMO_LCD_HV_RETR_END_MASK, retr_end);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_START,
++                                   GLAMO_LCD_HV_RETR_DISP_START_MASK,
++                                   disp_start);
++              reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_END,
++                                   GLAMO_LCD_HV_RETR_DISP_END_MASK, disp_end);
++
++      }
++
++      gdrm->saved_clock = mode->clock;
++
++      glamo_lcd_cmd_mode(gdrm, 0);
++
++      glamo_crtc_mode_set_base(crtc, 0, 0, old_fb);
++
++      if ( mode->hdisplay == 240 ) {
++              jbt6k74_finish_resolutionchange(JBT_RESOLUTION_QVGA);
++      } else {
++              jbt6k74_finish_resolutionchange(JBT_RESOLUTION_VGA);
++      }
++
++      gcrtc->current_mode = *mode;
++      gcrtc->current_mode_set = 1;
++      gcrtc->current_fb = old_fb;
++
++      return 0;
++}
++
++
++/* This is not the right place to switch power on/off, because the helper
++ * stuff ends up calling this before/after setting the mode.  We can't
++ * set modes with the display off (although backlight off would be OK) */
++static void glamo_crtc_dpms(struct drm_crtc *crtc, int mode)
++{
++      /* do nothing */
++}
++
++
++static void glamo_crtc_prepare(struct drm_crtc *crtc)
++{
++}
++
++
++static void glamo_crtc_commit(struct drm_crtc *crtc)
++{
++}
++
++
++static int glamo_crtc_cursor_set(struct drm_crtc *crtc,
++                                 struct drm_file *file_priv,
++                                 uint32_t handle,
++                                 uint32_t width, uint32_t height)
++{
++      return 0;
++}
++
++
++static int glamo_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
++{
++      return 0;
++}
++
++
++static void glamo_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
++                                 u16 *blue, uint32_t size)
++{
++}
++
++
++static void glamo_crtc_destroy(struct drm_crtc *crtc)
++{
++      struct glamo_crtc *glamo_crtc = to_glamo_crtc(crtc);
++      drm_crtc_cleanup(crtc);
++      kfree(glamo_crtc);
++}
++
++
++static enum drm_connector_status
++glamo_connector_detect(struct drm_connector *connector)
++{
++      /* One hopes it hasn't been de-soldered... */
++      return connector_status_connected;
++}
++
++
++static void glamo_connector_destroy(struct drm_connector *connector)
++{
++      drm_sysfs_connector_remove(connector);
++      drm_connector_cleanup(connector);
++      kfree(connector);
++}
++
++
++static int glamo_connector_get_modes(struct drm_connector *connector)
++{
++      struct glamo_fb_platform_data *fb_info;
++      struct glamo_output *goutput = to_glamo_output(connector);
++      struct glamodrm_handle *gdrm = goutput->gdrm;
++      int i;
++
++      /* Dig out the record which will tell us about the hardware */
++      fb_info = gdrm->glamo_core->pdata->fb_data;
++
++      for ( i=0; i<fb_info->num_modes; i++ ) {
++
++              struct drm_display_mode *mode;
++
++              mode = drm_mode_create(connector->dev);
++              if ( !mode ) continue;
++
++              mode->type = DRM_MODE_TYPE_DEFAULT | DRM_MODE_TYPE_PREFERRED;
++
++              /* Convert framebuffer timings into KMS timings.
++               * First:  ps -> kHz */
++              mode->clock = 1000000000UL / fb_info->modes[i].pixclock;
++              mode->clock *= 1000; /* then kHz -> Hz */
++              mode->hdisplay = fb_info->modes[i].xres;
++              mode->hsync_start = fb_info->modes[i].right_margin
++                                   + mode->hdisplay;
++              mode->hsync_end = mode->hsync_start
++                                   + fb_info->modes[i].hsync_len;
++              mode->htotal = mode->hsync_end + fb_info->modes[i].left_margin;
++              mode->hskew = 0;
++
++              mode->vdisplay = fb_info->modes[i].yres;
++              mode->vsync_start = fb_info->modes[i].lower_margin
++                                   + mode->vdisplay;
++              mode->vsync_end = mode->vsync_start
++                                 + fb_info->modes[i].vsync_len;
++              mode->vtotal = mode->vsync_end + fb_info->modes[i].upper_margin;
++              mode->vscan = 0;
++
++              /* Physical size */
++              mode->width_mm = fb_info->width;
++              mode->height_mm = fb_info->height;
++
++              drm_mode_set_name(mode);
++              drm_mode_probed_add(connector, mode);
++
++      }
++
++      return fb_info->num_modes;
++}
++
++
++static int glamo_connector_set_property(struct drm_connector *connector,
++                                struct drm_property *property,
++                                uint64_t value)
++{
++      return 0;
++}
++
++
++static int glamo_connector_mode_valid(struct drm_connector *connector,
++                                      struct drm_display_mode *mode)
++{
++      if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
++              return MODE_NO_DBLESCAN;
++
++      return MODE_OK;
++}
++
++
++struct drm_encoder *
++glamo_connector_best_encoder(struct drm_connector *connector)
++{
++      struct glamo_output *glamo_output = to_glamo_output(connector);
++      return &glamo_output->enc;
++}
++
++
++static void glamo_encoder_dpms(struct drm_encoder *encoder, int mode)
++{
++}
++
++
++static bool glamo_encoder_mode_fixup(struct drm_encoder *encoder,
++                                 struct drm_display_mode *mode,
++                                 struct drm_display_mode *adjusted_mode)
++{
++      if ( mode->clock == 0 ) return false;
++      return true;
++}
++
++
++void glamo_encoder_prepare(struct drm_encoder *encoder)
++{
++}
++
++
++void glamo_encoder_commit(struct drm_encoder *encoder)
++{
++}
++
++
++static void glamo_encoder_mode_set(struct drm_encoder *encoder,
++                               struct drm_display_mode *mode,
++                               struct drm_display_mode *adjusted_mode)
++{
++}
++
++
++static void glamo_encoder_destroy(struct drm_encoder *encoder)
++{
++      drm_encoder_cleanup(encoder);
++}
++
++
++static void glamo_framebuffer_destroy(struct drm_framebuffer *fb)
++{
++      struct glamo_framebuffer *glamo_fb = to_glamo_framebuffer(fb);
++      struct drm_device *dev = fb->dev;
++
++      drm_framebuffer_cleanup(fb);
++      mutex_lock(&dev->struct_mutex);
++      drm_gem_object_unreference(glamo_fb->obj);
++      mutex_unlock(&dev->struct_mutex);
++
++      kfree(glamo_fb);
++}
++
++static int glamo_framebuffer_create_handle(struct drm_framebuffer *fb,
++                                              struct drm_file *file_priv,
++                                              unsigned int *handle)
++{
++      struct glamo_framebuffer *glamo_fb = to_glamo_framebuffer(fb);
++      struct drm_gem_object *object = glamo_fb->obj;
++
++      return drm_gem_handle_create(file_priv, object, handle);
++}
++
++
++static const struct drm_framebuffer_funcs glamo_fb_funcs = {
++      .destroy = glamo_framebuffer_destroy,
++      .create_handle = glamo_framebuffer_create_handle,
++};
++
++
++int glamo_framebuffer_create(struct drm_device *dev,
++                           struct drm_mode_fb_cmd *mode_cmd,
++                           struct drm_framebuffer **fb,
++                           struct drm_gem_object *obj)
++{
++      struct glamo_framebuffer *glamo_fb;
++      int ret;
++
++      glamo_fb = kzalloc(sizeof(*glamo_fb), GFP_KERNEL);
++      if (!glamo_fb)
++              return -ENOMEM;
++
++      ret = drm_framebuffer_init(dev, &glamo_fb->base, &glamo_fb_funcs);
++      if (ret) {
++              DRM_ERROR("framebuffer init failed %d\n", ret);
++              return ret;
++      }
++
++      drm_helper_mode_fill_fb_struct(&glamo_fb->base, mode_cmd);
++
++      glamo_fb->obj = obj;
++
++      *fb = &glamo_fb->base;
++
++      return 0;
++}
++
++
++static struct drm_framebuffer *
++glamo_user_framebuffer_create(struct drm_device *dev,
++                            struct drm_file *filp,
++                            struct drm_mode_fb_cmd *mode_cmd)
++{
++      struct drm_gem_object *obj;
++      struct drm_framebuffer *fb;
++      int ret;
++
++      obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle);
++      if (!obj)
++              return NULL;
++
++      ret = glamo_framebuffer_create(dev, mode_cmd, &fb, obj);
++      if (ret) {
++              drm_gem_object_unreference(obj);
++              return NULL;
++      }
++
++      return fb;
++}
++
++
++int glamo_fbchanged(struct drm_device *dev)
++{
++      return 0;
++}
++
++
++/* CRTC functions */
++static const struct drm_crtc_funcs glamo_crtc_funcs = {
++      .cursor_set = glamo_crtc_cursor_set,
++      .cursor_move = glamo_crtc_cursor_move,
++      .gamma_set = glamo_crtc_gamma_set,
++      .set_config = drm_crtc_helper_set_config,
++      .destroy = glamo_crtc_destroy,
++};
++
++
++/* CRTC helper functions */
++static const struct drm_crtc_helper_funcs glamo_crtc_helper_funcs = {
++      .dpms = glamo_crtc_dpms,
++      .mode_fixup = glamo_crtc_mode_fixup,
++      .mode_set = glamo_crtc_mode_set,
++      .mode_set_base = glamo_crtc_mode_set_base,
++      .prepare = glamo_crtc_prepare,
++      .commit = glamo_crtc_commit,
++};
++
++
++/* Connector functions */
++static const struct drm_connector_funcs glamo_connector_funcs = {
++      .detect = glamo_connector_detect,
++      .fill_modes = drm_helper_probe_single_connector_modes,
++      .destroy = glamo_connector_destroy,
++      .set_property = glamo_connector_set_property,
++};
++
++
++/* Connector helper functions */
++static const struct drm_connector_helper_funcs glamo_connector_helper_funcs = {
++      .mode_valid = glamo_connector_mode_valid,
++      .get_modes = glamo_connector_get_modes,
++      .best_encoder = glamo_connector_best_encoder,
++};
++
++
++/* Encoder functions */
++static const struct drm_encoder_funcs glamo_encoder_funcs = {
++      .destroy = glamo_encoder_destroy,
++};
++
++
++/* Encoder helper functions */
++static const struct drm_encoder_helper_funcs glamo_encoder_helper_funcs = {
++      .dpms = glamo_encoder_dpms,
++      .mode_fixup = glamo_encoder_mode_fixup,
++      .prepare = glamo_encoder_prepare,
++      .commit = glamo_encoder_commit,
++      .mode_set = glamo_encoder_mode_set,
++};
++
++
++/* Mode functions */
++static const struct drm_mode_config_funcs glamo_mode_funcs = {
++      .fb_create = glamo_user_framebuffer_create,
++      .fb_changed = glamo_fbchanged
++};
++
++
++static struct drm_mode_set kernelfb_mode;
++
++
++/* Restore's the kernel's fbcon mode, used for panic path */
++void glamo_display_restore(void)
++{
++      drm_crtc_helper_set_config(&kernelfb_mode);
++}
++
++
++static int glamo_display_panic(struct notifier_block *n, unsigned long ununsed,
++                               void *panic_str)
++{
++      DRM_ERROR("panic occurred, switching back to text console\n");
++
++      glamo_display_restore();
++      return 0;
++}
++
++
++static struct notifier_block paniced = {
++      .notifier_call = glamo_display_panic,
++};
++
++
++int glamo_display_init(struct drm_device *dev)
++{
++      struct glamodrm_handle *gdrm;
++      struct glamo_crtc *glamo_crtc;
++      struct glamo_output *glamo_output;
++      struct drm_connector *connector;
++      struct glamo_framebuffer *glamo_fb;
++      struct fb_info *info;
++      struct glamofb_par *par;
++      struct drm_mode_set *modeset;
++
++      gdrm = dev->dev_private;
++
++      /* Initial setup of the LCD controller */
++      glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_LCD);
++      glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_LCD);
++
++      glamo_run_lcd_script(gdrm, lcd_init_script,
++                                 ARRAY_SIZE(lcd_init_script));
++
++      drm_mode_config_init(dev);
++
++      dev->mode_config.min_width = 240;
++      dev->mode_config.min_height = 240;
++      dev->mode_config.max_width = 640;
++      dev->mode_config.max_height = 640;
++
++      dev->mode_config.funcs = (void *)&glamo_mode_funcs;
++
++      /* Initialise our CRTC object.
++       * Only one connector per CRTC.  We know this: it's kind of soldered. */
++      glamo_crtc = kzalloc(sizeof(struct glamo_crtc)
++                         + sizeof(struct drm_connector *), GFP_KERNEL);
++      if (glamo_crtc == NULL) return 1;
++      glamo_crtc->gdrm = gdrm;
++      gdrm->crtc = (struct drm_crtc *)glamo_crtc;
++      glamo_crtc->pixel_clock_on = 1;
++      glamo_crtc->blank_mode = DRM_MODE_DPMS_OFF;
++      drm_crtc_init(dev, &glamo_crtc->base, &glamo_crtc_funcs);
++      drm_crtc_helper_add(&glamo_crtc->base, &glamo_crtc_helper_funcs);
++
++      glamo_crtc->mode_set.crtc = &glamo_crtc->base;
++      glamo_crtc->mode_set.connectors =
++                                    (struct drm_connector **)(glamo_crtc + 1);
++      glamo_crtc->mode_set.num_connectors = 0;
++
++      /* Create our "output" object: consists of an output and an encoder */
++      glamo_output = kzalloc(sizeof(struct glamo_output), GFP_KERNEL);
++      if (glamo_output == NULL) return 1;
++      connector = &glamo_output->base;
++      glamo_output->gdrm = gdrm;
++
++      /* Initialise the connector */
++      drm_connector_init(dev, connector, &glamo_connector_funcs,
++                         DRM_MODE_CONNECTOR_LVDS);
++      drm_sysfs_connector_add(connector);
++      connector->interlace_allowed = 0;
++      connector->doublescan_allowed = 0;
++
++      /* Initialise the encoder */
++      drm_encoder_init(dev, &glamo_output->enc, &glamo_encoder_funcs,
++                       DRM_MODE_ENCODER_DAC);
++      glamo_output->enc.possible_crtcs = 1 << 0;
++      drm_mode_connector_attach_encoder(&glamo_output->base,
++                                        &glamo_output->enc);
++
++      drm_encoder_helper_add(&glamo_output->enc, &glamo_encoder_helper_funcs);
++      drm_connector_helper_add(connector, &glamo_connector_helper_funcs);
++
++      drm_helper_initial_config(dev);
++
++      if (list_empty(&dev->mode_config.fb_kernel_list)) {
++              int ret, cols, cols_g;
++              cols_g = reg_read_lcd(gdrm, GLAMO_REG_LCD_MODE3) & 0xc000;
++              switch ( cols_g ) {
++              case GLAMO_LCD_SRC_RGB565 :
++                      cols = GLAMO_FB_RGB565; break;
++              case GLAMO_LCD_SRC_ARGB1555 :
++                      cols = GLAMO_FB_ARGB1555; break;
++              case GLAMO_LCD_SRC_ARGB4444 :
++                      cols = GLAMO_FB_ARGB4444; break;
++              default :
++                      printk(KERN_WARNING "Unrecognised LCD colour mode\n");
++                      cols = GLAMO_FB_RGB565; break; /* Take a guess */
++              }
++              ret = glamofb_create(dev, 480, 640, 480, 640, cols, &glamo_fb);
++              if (ret) return -EINVAL;
++      }
++
++      info = glamo_fb->base.fbdev;
++      par = info->par;
++
++      modeset = &glamo_crtc->mode_set;
++      modeset->fb = &glamo_fb->base;
++      modeset->connectors[0] = connector;
++
++      par->crtc_ids[0] = glamo_crtc->base.base.id;
++
++      modeset->num_connectors = 1;
++      modeset->mode = modeset->crtc->desired_mode;
++
++      par->crtc_count = 1;
++
++      if (register_framebuffer(info) < 0)
++              return -EINVAL;
++
++      printk(KERN_INFO "[glamo-drm] fb%d: %s frame buffer device\n",
++             info->node, info->fix.id);
++
++      /* Switch back to kernel console on panic */
++      kernelfb_mode = *modeset;
++      atomic_notifier_chain_register(&panic_notifier_list, &paniced);
++      printk(KERN_INFO "[glamo-drm] Registered panic notifier\n");
++
++      return 0;
++}
++
++
++void glamo_lcd_power(struct glamodrm_handle *gdrm, int mode)
++{
++      struct drm_crtc *crtc = gdrm->crtc;
++      struct glamo_crtc *gcrtc = to_glamo_crtc(crtc);
++
++      if ( mode ) {
++              glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_LCD);
++              gcrtc->pixel_clock_on = 1;
++              jbt6k74_setpower(JBT_POWER_MODE_NORMAL);
++              if ( gcrtc->current_mode_set ) {
++                      glamo_crtc_mode_set(crtc, &gcrtc->current_mode,
++                                          &gcrtc->current_mode, 0, 0,
++                                          gcrtc->current_fb);
++              }
++      } else {
++              jbt6k74_setpower(JBT_POWER_MODE_OFF);
++              glamo_engine_suspend(gdrm->glamo_core, GLAMO_ENGINE_LCD);
++              gcrtc->pixel_clock_on = 0;
++      }
++}
++
++
++void glamo_display_suspend(struct glamodrm_handle *gdrm)
++{
++      /* do nothing */
++}
++
++
++void glamo_display_resume(struct glamodrm_handle *gdrm)
++{
++      struct drm_crtc *crtc = gdrm->crtc;
++      struct glamo_crtc *gcrtc = to_glamo_crtc(crtc);
++
++      glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_LCD);
++      glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_LCD);
++      glamo_run_lcd_script(gdrm, lcd_init_script,
++                                 ARRAY_SIZE(lcd_init_script));
++
++      if ( gcrtc->current_mode_set ) {
++              glamo_crtc_mode_set(crtc, &gcrtc->current_mode,
++                                  &gcrtc->current_mode, 0, 0,
++                                  gcrtc->current_fb);
++      }
++}
+diff --git a/drivers/mfd/glamo/glamo-display.h b/drivers/mfd/glamo/glamo-display.h
+new file mode 100644
+index 0000000..728bba5
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-display.h
+@@ -0,0 +1,41 @@
++/* Smedia Glamo 336x/337x Display
++ *
++ * Copyright (c) 2008-2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#ifndef __GLAMO_DISPLAY_H
++#define __GLAMO_DISPLAY_H
++
++#include <drm/drmP.h>
++#include "glamo-drm-private.h"
++
++extern int glamo_display_init(struct drm_device *dev);
++
++extern int glamo_framebuffer_create(struct drm_device *dev,
++                                    struct drm_mode_fb_cmd *mode_cmd,
++                                    struct drm_framebuffer **fb,
++                                    struct drm_gem_object *obj);
++
++extern void glamo_display_suspend(struct glamodrm_handle *gdrm);
++extern void glamo_display_resume(struct glamodrm_handle *gdrm);
++
++extern void glamo_lcd_power(struct glamodrm_handle *gdrm, int mode);
++
++#endif /* __GLAMO_DISPLAY_H */
+diff --git a/drivers/mfd/glamo/glamo-drm-drv.c b/drivers/mfd/glamo/glamo-drm-drv.c
+new file mode 100644
+index 0000000..774eaff
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-drm-drv.c
+@@ -0,0 +1,423 @@
++/* Smedia Glamo 336x/337x Graphics Driver
++ *
++ * Copyright (C) 2009 Openmoko, Inc. Jorge Luis Zapata <turran@openmoko.com>
++ * Copyright (C) 2008-2009 Thomas White <taw@bitwiz.org.uk>
++ * Copyright (C) 2009 Andreas Pokorny <andreas.pokorny@gmail.com>
++ *
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <drm/drmP.h>
++#include <drm/glamo_drm.h>
++#include <linux/glamofb.h>
++
++#include "glamo-core.h"
++#include "glamo-cmdq.h"
++#include "glamo-buffer.h"
++#include "glamo-drm-private.h"
++#include "glamo-display.h"
++#include "glamo-kms-fb.h"
++#include "glamo-fence.h"
++
++#define DRIVER_AUTHOR           "Openmoko, Inc."
++#define DRIVER_NAME             "glamo-drm"
++#define DRIVER_DESC             "SMedia Glamo 3362"
++#define DRIVER_DATE             "20090614"
++
++
++static int glamo_ioctl_swap(struct drm_device *dev, void *data,
++                          struct drm_file *file_priv)
++{
++      printk(KERN_INFO "glamo_ioctl_swap\n");
++      return 0;
++}
++
++
++static int glamo_ioctl_gem_info(struct drm_device *dev, void *data,
++                              struct drm_file *file_priv)
++{
++      printk(KERN_INFO "glamo_ioctl_gem_info\n");
++      return 0;
++}
++
++
++struct drm_ioctl_desc glamo_ioctls[] = {
++      DRM_IOCTL_DEF(DRM_GLAMO_CMDBUF, glamo_ioctl_cmdbuf, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_SWAP, glamo_ioctl_swap, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_CMDBURST, glamo_ioctl_cmdburst, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_INFO, glamo_ioctl_gem_info, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_CREATE, glamo_ioctl_gem_create, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_MMAP, glamo_ioctl_gem_mmap, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_PIN, glamo_ioctl_gem_pin, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_UNPIN, glamo_ioctl_gem_unpin, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_PREAD, glamo_ioctl_gem_pread, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_PWRITE, glamo_ioctl_gem_pwrite, DRM_AUTH),
++      DRM_IOCTL_DEF(DRM_GLAMO_GEM_WAIT_RENDERING,
++                    glamo_ioctl_wait_rendering, DRM_AUTH),
++};
++
++
++static int glamodrm_firstopen(struct drm_device *dev)
++{
++      DRM_DEBUG("\n");
++      return 0;
++}
++
++
++static int glamodrm_open(struct drm_device *dev, struct drm_file *fh)
++{
++      DRM_DEBUG("\n");
++      return 0;
++}
++
++
++static void glamodrm_preclose(struct drm_device *dev, struct drm_file *fh)
++{
++      DRM_DEBUG("\n");
++}
++
++static void glamodrm_postclose(struct drm_device *dev, struct drm_file *fh)
++{
++      DRM_DEBUG("\n");
++}
++
++
++static void glamodrm_lastclose(struct drm_device *dev)
++{
++      DRM_DEBUG("\n");
++}
++
++
++static int glamodrm_master_create(struct drm_device *dev,
++                                struct drm_master *master)
++{
++      DRM_DEBUG("\n");
++
++        return 0;
++}
++
++
++static void glamodrm_master_destroy(struct drm_device *dev,
++                                  struct drm_master *master)
++{
++      DRM_DEBUG("\n");
++}
++
++
++static int glamodrm_load(struct drm_device *dev, unsigned long flags)
++{
++      struct glamodrm_handle *gdrm;
++      gdrm = dev->dev_private;
++
++      glamo_buffer_init(gdrm);
++      glamo_cmdq_init(gdrm);
++      glamo_fence_init(gdrm);
++      glamo_display_init(dev);
++
++      return 0;
++}
++
++
++static int glamodrm_unload(struct drm_device *dev)
++{
++      struct glamodrm_handle *gdrm;
++
++      gdrm = dev->dev_private;
++
++      glamo_engine_disable(gdrm->glamo_core, GLAMO_ENGINE_2D);
++      glamo_engine_disable(gdrm->glamo_core, GLAMO_ENGINE_3D);
++      glamo_buffer_final(gdrm);
++      glamo_fence_shutdown(gdrm);
++
++      return 0;
++}
++
++
++static struct vm_operations_struct glamodrm_gem_vm_ops = {
++      .fault = glamodrm_gem_fault,
++      .open = drm_gem_vm_open,
++      .close = drm_gem_vm_close,
++};
++
++static struct drm_driver glamodrm_drm_driver = {
++      .driver_features = DRIVER_IS_PLATFORM | DRIVER_GEM | DRIVER_MODESET,
++      .firstopen = glamodrm_firstopen,
++      .load = glamodrm_load,
++      .unload = glamodrm_unload,
++      .open = glamodrm_open,
++      .preclose = glamodrm_preclose,
++      .postclose = glamodrm_postclose,
++      .lastclose = glamodrm_lastclose,
++      .reclaim_buffers = drm_core_reclaim_buffers,
++      .get_map_ofs = drm_core_get_map_ofs,
++      .get_reg_ofs = drm_core_get_reg_ofs,
++      .master_create = glamodrm_master_create,
++      .master_destroy = glamodrm_master_destroy,
++      .gem_init_object = glamodrm_gem_init_object,
++      .gem_free_object = glamodrm_gem_free_object,
++      .gem_vm_ops = &glamodrm_gem_vm_ops,
++      .ioctls = glamo_ioctls,
++      .fops = {
++              .owner = THIS_MODULE,
++              .open = drm_open,
++              .release = drm_release,
++              .ioctl = drm_ioctl,
++              .mmap = drm_gem_mmap,
++              .poll = drm_poll,
++              .fasync = drm_fasync,
++      },
++      .major = 0,
++      .minor = 1,
++      .patchlevel = 0,
++      .name = DRIVER_NAME,
++      .desc = DRIVER_DESC,
++      .date = DRIVER_DATE,
++};
++
++
++static int glamodrm_probe(struct platform_device *pdev)
++{
++      int rc;
++      struct glamodrm_handle *gdrm;
++      struct glamo_core *core = dev_get_drvdata(pdev->dev.parent);
++
++      printk(KERN_INFO "[glamo-drm] SMedia Glamo Direct Rendering Support\n");
++
++      gdrm = kzalloc(sizeof(*gdrm), GFP_KERNEL);
++      if ( !gdrm )
++              return -ENOMEM;
++      platform_set_drvdata(pdev, gdrm);
++      gdrm->glamo_core = core;
++      gdrm->dev = &pdev->dev;
++
++      /* Find the command queue registers */
++      gdrm->reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++                                               "glamo-cmdq-regs");
++      if ( !gdrm->reg ) {
++              dev_err(&pdev->dev, "Unable to find cmdq registers.\n");
++              rc = -ENOENT;
++              goto out_free;
++      }
++      gdrm->reg = request_mem_region(gdrm->reg->start,
++                                        resource_size(gdrm->reg), pdev->name);
++      if ( !gdrm->reg ) {
++              dev_err(&pdev->dev, "failed to request MMIO region\n");
++              rc = -ENOENT;
++              goto out_free;
++      }
++      gdrm->reg_base = ioremap_nocache(gdrm->reg->start,
++                                       resource_size(gdrm->reg));
++      if ( !gdrm->reg_base ) {
++              dev_err(&pdev->dev, "failed to ioremap() MMIO registers\n");
++              rc = -ENOENT;
++              goto out_release_regs;
++      }
++
++      /* Find the VRAM */
++      gdrm->vram = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++                                                "glamo-fb-mem");
++      if ( !gdrm->vram ) {
++              dev_err(&pdev->dev, "Unable to find VRAM.\n");
++              rc = -ENOENT;
++              goto out_unmap_cmdq;
++      }
++      gdrm->vram = request_mem_region(gdrm->vram->start,
++                                      resource_size(gdrm->vram), pdev->name);
++      if ( !gdrm->vram ) {
++              dev_err(&pdev->dev, "failed to request VRAM region\n");
++              rc = -ENOENT;
++              goto out_unmap_cmdq;
++      }
++
++      /* Find the LCD controller */
++      gdrm->lcd_regs = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++                                                    "glamo-fb-regs");
++      if ( !gdrm->lcd_regs ) {
++              dev_err(&pdev->dev, "Unable to find LCD registers.\n");
++              rc = -ENOENT;
++              goto out_release_vram;
++      }
++      gdrm->lcd_regs = request_mem_region(gdrm->lcd_regs->start,
++                                          resource_size(gdrm->lcd_regs),
++                                          pdev->name);
++      if ( !gdrm->lcd_regs ) {
++              dev_err(&pdev->dev, "failed to request LCD registers\n");
++              rc = -ENOENT;
++              goto out_release_vram;
++      }
++      gdrm->lcd_base = ioremap_nocache(gdrm->lcd_regs->start,
++                                       resource_size(gdrm->lcd_regs));
++      if ( !gdrm->lcd_base ) {
++              dev_err(&pdev->dev, "failed to ioremap() LCD registers\n");
++              rc = -ENOENT;
++              goto out_release_lcd;
++      }
++
++      /* Find the 2D engine */
++      gdrm->twod_regs = platform_get_resource(pdev, IORESOURCE_MEM, 4);
++      if ( !gdrm->twod_regs ) {
++              dev_err(&pdev->dev, "Unable to find 2D registers.\n");
++              rc = -ENOENT;
++              goto out_unmap_lcd;
++      }
++      gdrm->twod_regs = request_mem_region(gdrm->twod_regs->start,
++                                           resource_size(gdrm->twod_regs),
++                                           pdev->name);
++      if ( !gdrm->twod_regs ) {
++              dev_err(&pdev->dev, "failed to request 2D registers\n");
++              rc = -ENOENT;
++              goto out_unmap_lcd;
++      }
++      gdrm->twod_base = ioremap(gdrm->twod_regs->start,
++                                resource_size(gdrm->twod_regs));
++      if ( !gdrm->twod_base ) {
++              dev_err(&pdev->dev, "failed to ioremap() 2D registers\n");
++              rc = -ENOENT;
++              goto out_release_2d;
++      }
++
++      /* Hook up IRQ handle for fence processing */
++      gdrm->twod_irq = platform_get_irq_byname(pdev, "glamo-2d-irq");
++
++      gdrm->vram_size = GLAMO_FB_SIZE;
++      printk(KERN_INFO "[glamo-drm] %lli bytes of VRAM\n",
++                       (long long int)gdrm->vram_size);
++
++      /* Initialise DRM */
++      drm_platform_init(&glamodrm_drm_driver, pdev, (void *)gdrm);
++
++      return 0;
++
++out_release_2d:
++      release_mem_region(gdrm->twod_regs->start,
++                         resource_size(gdrm->twod_regs));
++out_unmap_lcd:
++      iounmap(gdrm->lcd_base);
++out_release_lcd:
++      release_mem_region(gdrm->lcd_regs->start,
++                         resource_size(gdrm->lcd_regs));
++out_release_vram:
++      release_mem_region(gdrm->vram->start, resource_size(gdrm->vram));
++out_unmap_cmdq:
++      iounmap(gdrm->cmdq_base);
++out_release_cmdq:
++      release_mem_region(gdrm->cmdq->start, resource_size(gdrm->cmdq));
++out_unmap_regs:
++      iounmap(gdrm->reg_base);
++out_release_regs:
++      release_mem_region(gdrm->reg->start, resource_size(gdrm->reg));
++out_free:
++      kfree(gdrm);
++      dev_set_drvdata(&pdev->dev, NULL);
++      return rc;
++}
++
++
++static int glamodrm_remove(struct platform_device *pdev)
++{
++      struct glamodrm_handle *gdrm = platform_get_drvdata(pdev);
++
++      glamo_buffer_final(gdrm);
++      glamo_cmdq_shutdown(gdrm);
++
++      drm_exit(&glamodrm_drm_driver);
++
++      platform_set_drvdata(pdev, NULL);
++
++      /* Release registers */
++      iounmap(gdrm->reg_base);
++      release_mem_region(gdrm->reg->start, resource_size(gdrm->reg));
++
++      /* Release VRAM */
++      release_mem_region(gdrm->vram->start, resource_size(gdrm->vram));
++
++      /* Release command queue */
++      iounmap(gdrm->cmdq_base);
++      release_mem_region(gdrm->cmdq->start, resource_size(gdrm->cmdq));
++
++      /* Release 2D engine  */
++      iounmap(gdrm->twod_base);
++      release_mem_region(gdrm->twod_regs->start,
++                         resource_size(gdrm->twod_regs));
++
++      kfree(gdrm);
++
++      return 0;
++}
++
++
++static int glamodrm_suspend(struct platform_device *pdev, pm_message_t state)
++{
++      struct glamodrm_handle *gdrm = platform_get_drvdata(pdev);
++
++      glamo_kmsfb_suspend(gdrm);
++      glamo_display_suspend(gdrm);
++      glamo_cmdq_suspend(gdrm);
++
++      /* glamo_core.c will suspend the engines for us */
++
++      return 0;
++}
++
++
++static int glamodrm_resume(struct platform_device *pdev)
++{
++      struct glamodrm_handle *gdrm = platform_get_drvdata(pdev);
++
++      glamo_cmdq_resume(gdrm);
++      glamo_display_resume(gdrm);
++      glamo_kmsfb_resume(gdrm);
++
++      return 0;
++}
++
++
++static struct platform_driver glamodrm_driver = {
++      .probe          = glamodrm_probe,
++      .remove         = glamodrm_remove,
++      .suspend        = glamodrm_suspend,
++      .resume         = glamodrm_resume,
++      .driver         = {
++              .name   = "glamo-fb",
++              .owner  = THIS_MODULE,
++      },
++};
++
++
++static int __devinit glamodrm_init(void)
++{
++      glamodrm_drm_driver.num_ioctls = DRM_ARRAY_SIZE(glamo_ioctls);
++      return platform_driver_register(&glamodrm_driver);
++}
++
++
++static void __exit glamodrm_exit(void)
++{
++      platform_driver_unregister(&glamodrm_driver);
++}
++
++
++module_init(glamodrm_init);
++module_exit(glamodrm_exit);
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/mfd/glamo/glamo-drm-private.h b/drivers/mfd/glamo/glamo-drm-private.h
+new file mode 100644
+index 0000000..75f2757
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-drm-private.h
+@@ -0,0 +1,166 @@
++/* Smedia Glamo 336x/337x DRM private bits
++ *
++ * Copyright (C) 2008-2009 Thomas White <taw@bitwiz.org.uk>
++ * Copyright (C) 2009 Andreas Pokorny <andreas.pokorny@gmail.com>
++ * Based on xf86-video-glamo
++ * Copyright  2007 OpenMoko, Inc.
++ * Copyright Â© 2009 Lars-Peter Clausen <lars@metafoo.de>
++ *
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#ifndef __GLAMO_DRMPRIV_H
++#define __GLAMO_DRMPRIV_H
++
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/semaphore.h>
++#include <linux/spinlock.h>
++#include <linux/wait.h>
++
++#include "glamo-core.h"
++
++
++/* Memory to allocate for the framebuffer.
++ * The rest is reserved for the DRM memory manager */
++#define GLAMO_FRAMEBUFFER_ALLOCATION (2*480*640)
++
++
++struct glamodrm_handle {
++
++      /* This device */
++      struct device *dev;
++
++      /* The parent device handle */
++      struct glamo_core *glamo_core;
++
++      /* Framebuffer handle for the console (i.e. /dev/fb0) */
++      struct fb_info *fb;
++
++      /* Command queue registers */
++      struct resource *reg;
++      char __iomem *reg_base;
++
++      /* VRAM region */
++      struct resource *vram;
++
++      /* Command queue region */
++      char __iomem *cmdq_base;
++      unsigned long cmdq_offs;
++
++      /* LCD controller registers */
++      struct resource *lcd_regs;
++      char __iomem *lcd_base;
++
++      /* 2D engine registers and IRQ */
++      struct resource *twod_regs;
++      char __iomem *twod_base;
++      unsigned int twod_irq;
++
++      ssize_t vram_size;
++
++      /* Memory management */
++      struct drm_mm *mmgr;
++
++      /* semaphore against concurrent ioctl */
++      struct semaphore add_to_ring;
++
++      /* Saved state */
++      u_int16_t saved_clock;
++      u_int16_t saved_width;
++      u_int16_t saved_height;
++      u_int16_t saved_pitch;
++      u_int16_t saved_htotal;
++      u_int16_t saved_hrtrst;
++      u_int16_t saved_hrtren;
++      u_int16_t saved_hdspst;
++      u_int16_t saved_hdspen;
++      u_int16_t saved_vtotal;
++      u_int16_t saved_vrtrst;
++      u_int16_t saved_vrtren;
++      u_int16_t saved_vdspst;
++      u_int16_t saved_vdspen;
++
++      /* Fencing */
++      atomic_t curr_seq;              /* The last used stamp number */
++      struct list_head fence_list;    /* List of active fences */
++      rwlock_t fence_list_lock;       /* Lock to protect fence_list */
++      wait_queue_head_t fence_queue;  /* Waitqueue */
++      struct tasklet_struct fence_tl; /* Tasklet for fence IRQ */
++
++      /* A scratch block */
++      struct drm_mm_node *scratch;
++
++      /* We only have one */
++      struct drm_crtc *crtc;
++};
++
++
++/* Private data.  This is where we keep our memory management bits */
++struct drm_glamo_gem_object {
++      struct drm_gem_object *obj;     /* The GEM object this refers to */
++      struct drm_mm_node *block;      /* Block handle for drm_mm */
++      uint64_t mmap_offset;
++};
++
++
++struct glamo_crtc {
++
++      struct drm_crtc base;
++      struct glamodrm_handle *gdrm;
++      /* a mode_set for fbdev users on this crtc */
++      struct drm_mode_set mode_set;
++      int blank_mode;
++
++      int pixel_clock_on;
++
++      int current_mode_set;
++      struct drm_display_mode current_mode;
++      struct drm_framebuffer *current_fb;
++};
++
++
++struct glamo_framebuffer {
++      struct drm_framebuffer base;
++      struct drm_gem_object *obj;
++};
++
++
++struct glamo_output {
++      struct drm_connector base;
++      struct drm_encoder enc;
++      struct glamodrm_handle *gdrm;
++};
++
++
++/* Colour mode for KMS framebuffer */
++enum {
++      GLAMO_FB_RGB565,
++      GLAMO_FB_ARGB1555,
++      GLAMO_FB_ARGB4444
++};
++
++
++#define to_glamo_crtc(x) container_of(x, struct glamo_crtc, base)
++#define to_glamo_output(x) container_of(x, struct glamo_output, base)
++#define enc_to_glamo_output(x) container_of(x, struct glamo_output, enc)
++#define to_glamo_framebuffer(x) container_of(x, struct glamo_framebuffer, base)
++
++
++#endif /* __GLAMO_DRMPRIV_H */
+diff --git a/drivers/mfd/glamo/glamo-fence.c b/drivers/mfd/glamo/glamo-fence.c
+new file mode 100644
+index 0000000..9119675
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-fence.c
+@@ -0,0 +1,330 @@
++/*
++ * SMedia Glamo 336x/337x fence objects
++ *
++ * Copyright (c) 2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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, see <http://www.gnu.org/licenses/>.
++ *
++ *
++ * Loosely based on radeon_fence.c, to which the following notice applies:
++ *
++ * Copyright 2009 Jerome Glisse.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ */
++/*
++ * Authors:
++ *    Jerome Glisse <glisse@freedesktop.org>
++ *    Dave Airlie
++ */
++
++
++#include <drm/drmP.h>
++#include <drm/glamo_drm.h>
++#include <linux/kernel.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <linux/lockdep.h>
++
++#include "glamo-drm-private.h"
++#include "glamo-regs.h"
++#include "glamo-core.h"
++#include "glamo-cmdq.h"
++
++
++static struct lock_class_key glamo_fence_lock_key;
++
++
++struct glamo_fence
++{
++      struct list_head list;
++      uint16_t               seq;       /* Wait for at least this ID */
++      int                    signalled; /* Non-zero when fence has passed */
++      struct glamodrm_handle *gdrm;
++};
++
++
++static void glamo_fence_emit(struct glamo_fence *fence)
++{
++      u16 fring[6];
++
++      fring[0] = 0x8000 | GLAMO_REG_2D_ID1;
++      fring[1] = 3;
++      fence->seq = atomic_inc_return(&fence->gdrm->curr_seq);
++      if ( fence->seq > 1<<14 ) {
++              atomic_set(&fence->gdrm->curr_seq, 0);
++              fence->seq = atomic_inc_return(&fence->gdrm->curr_seq);
++      }
++      fring[2] = 1<<15 | fence->seq;
++      fring[3] = 0;  /* Unused */
++      fring[4] = 0;  /* Unused */
++      fring[5] = 0;  /* Padding */
++
++      glamo_add_to_ring(fence->gdrm, fring, 12);
++}
++
++
++static void glamo_fence_enable(struct glamodrm_handle *gdrm)
++{
++      enable_irq( GLAMO_IRQ_2D);
++}
++
++
++static inline u16 reg_read_2d(struct glamodrm_handle *gdrm, u_int16_t reg)
++{
++      /* For command queue, the address is given relative to
++       * the overall base of Glamo.  This isn't the case here. */
++      return ioread16(gdrm->twod_base + reg-GLAMO_REGOFS_2D);
++}
++
++
++static inline u16 reg_read_cmdq(struct glamodrm_handle *gdrm, u_int16_t reg)
++{
++      return ioread16(gdrm->reg_base + reg);
++}
++
++
++static void glamo_cmdq_wait(struct glamodrm_handle *gdrm,
++                            enum glamo_engine engine)
++{
++      u16 mask, val, status;
++      int i;
++
++      switch (engine)
++      {
++              case GLAMO_ENGINE_ALL:
++                      mask = 1 << 2;
++                      val  = mask;
++                      break;
++              default:
++                      return;
++      }
++
++      for ( i=0; i<1000; i++ ) {
++              status = reg_read_cmdq(gdrm, GLAMO_REG_CMDQ_STATUS);
++              if ((status & mask) == val) break;
++              mdelay(1);
++      }
++      if ( i == 1000 ) {
++              size_t ring_read;
++              printk(KERN_WARNING "[glamo-drm] CmdQ timeout!\n");
++              printk(KERN_WARNING "[glamo-drm] status = %x\n", status);
++              ring_read = reg_read_cmdq(gdrm, GLAMO_REG_CMDQ_READ_ADDRL);
++              ring_read |= ((reg_read_cmdq(gdrm, GLAMO_REG_CMDQ_READ_ADDRH)
++                              & 0x7) << 16);
++              printk(KERN_INFO "[glamo-drm] ring_read now 0x%x\n",
++                               ring_read);
++      }
++}
++
++
++static irqreturn_t glamo_fence_irq_handler(int irq, void *data)
++{
++      struct glamodrm_handle *gdrm = data;
++      if (!gdrm) return IRQ_NONE;
++      tasklet_schedule(&gdrm->fence_tl);
++      return IRQ_HANDLED;
++}
++
++
++/* This is nasty.  I'm sorry. */
++static void glamo_fence_debodge(struct glamodrm_handle *gdrm)
++{
++      struct list_head *tmp;
++
++      printk(KERN_ERR "[glamo-drm] Attempting to recover...\n");
++
++      glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL);
++      glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_2D);
++
++      read_lock(&gdrm->fence_list_lock);
++      list_for_each(tmp, &gdrm->fence_list) {
++
++              struct glamo_fence *fence;
++
++              fence = list_entry(tmp, struct glamo_fence, list);
++
++              if ( fence->signalled != 1 ) {
++                      printk(KERN_ERR "[glamo-drm] Fence seq#%i was not"
++                                      " signalled\n", fence->seq);
++              }
++              fence->signalled = 1;
++
++      }
++      read_unlock(&gdrm->fence_list_lock);
++
++      wake_up_all(&gdrm->fence_queue);
++}
++
++
++static void glamo_fence_tl(unsigned long data)
++{
++      struct glamodrm_handle *gdrm = (struct glamodrm_handle *)data;
++      int wake = 0;
++      u16 seq;
++      struct list_head *tmp;
++
++      seq = reg_read_2d(gdrm, GLAMO_REG_2D_ID1) & 0x7fff;
++
++      read_lock(&gdrm->fence_list_lock);
++      list_for_each(tmp, &gdrm->fence_list) {
++
++              struct glamo_fence *fence;
++
++              fence = list_entry(tmp, struct glamo_fence, list);
++              if ( seq >= fence->seq ) {
++                      fence->signalled = 1;
++                      wake = 1;
++              }
++
++      }
++      read_unlock(&gdrm->fence_list_lock);
++
++      if ( wake ) wake_up_all(&gdrm->fence_queue);
++}
++
++
++static struct glamo_fence *glamo_fence_new(struct glamodrm_handle *gdrm)
++{
++      struct glamo_fence *fence;
++      unsigned long irq_flags;
++
++      fence = kmalloc(sizeof(*fence), GFP_KERNEL);
++      fence->signalled = 0;
++      fence->gdrm = gdrm;
++
++      /* Add to list */
++      write_lock_irqsave(&gdrm->fence_list_lock, irq_flags);
++      list_add(&fence->list, &gdrm->fence_list);
++      write_unlock_irqrestore(&gdrm->fence_list_lock, irq_flags);
++
++      return fence;
++}
++
++
++static struct glamo_fence *glamo_fence_destroy(struct glamo_fence *fence)
++{
++      unsigned long irq_flags;
++      struct glamodrm_handle *gdrm = fence->gdrm;
++
++      /* Remove from list */
++      write_lock_irqsave(&gdrm->fence_list_lock, irq_flags);
++      list_del(&fence->list);
++      write_unlock_irqrestore(&gdrm->fence_list_lock, irq_flags);
++
++      kfree(fence);
++
++      return fence;
++}
++
++
++int glamo_ioctl_wait_rendering(struct drm_device *dev, void *data,
++                               struct drm_file *file_priv)
++{
++      struct glamodrm_handle *gdrm;
++      struct drm_glamo_gem_wait_rendering *args = data;
++      struct glamo_fence *fence;
++      int r;
++
++      gdrm = dev->dev_private;
++
++      if ( !args->have_handle ) {
++              glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL);
++              return 0;
++      }
++
++      fence = glamo_fence_new(gdrm);
++      if ( fence == NULL ) {
++              printk(KERN_WARNING "[glamo-drm] Couldn't allocate fence -"
++                                  " falling back to busy wait.\n");
++              glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL);
++              return 0;
++      }
++
++      glamo_fence_emit(fence);
++
++      /* Wait... */
++      r = wait_event_interruptible_timeout(gdrm->fence_queue,
++                                           fence->signalled, HZ);
++      if ( r == 0 ) {
++              printk(KERN_ERR "[glamo-drm] Timeout!\n");
++              glamo_fence_debodge(gdrm);
++      }
++
++      glamo_fence_destroy(fence);
++
++      return 0;
++}
++
++
++void glamo_fence_init(struct glamodrm_handle *gdrm)
++{
++      unsigned long irq_flags;
++      int r;
++
++      if ( gdrm->twod_irq == 0 ) {
++              printk(KERN_ERR "[glamo-drm] Don't know which IRQ to use!\n");
++              return;
++      }
++
++      gdrm->fence_list_lock = __RW_LOCK_UNLOCKED(gdrm->fence_list_lock);
++      lockdep_set_class(&gdrm->fence_list_lock, &glamo_fence_lock_key);
++      init_waitqueue_head(&gdrm->fence_queue);
++
++      atomic_set(&gdrm->curr_seq, 0);
++
++      write_lock_irqsave(&gdrm->fence_list_lock, irq_flags);
++      INIT_LIST_HEAD(&gdrm->fence_list);
++      write_unlock_irqrestore(&gdrm->fence_list_lock, irq_flags);
++
++      tasklet_init(&gdrm->fence_tl, glamo_fence_tl, (unsigned long)gdrm);
++
++      r = request_irq(gdrm->twod_irq, glamo_fence_irq_handler,
++                      IRQF_SHARED, "glamo-fence", gdrm);
++      if ( r ) {
++              printk(KERN_ERR "[glamo-drm] Failed to register irq.\n");
++              return;
++      }
++
++      glamo_fence_enable(gdrm);
++}
++
++
++void glamo_fence_shutdown(struct glamodrm_handle *gdrm)
++{
++      free_irq(gdrm->twod_irq, gdrm);
++      wake_up_all(&gdrm->fence_queue);
++      tasklet_kill(&gdrm->fence_tl);
++}
+diff --git a/drivers/mfd/glamo/glamo-fence.h b/drivers/mfd/glamo/glamo-fence.h
+new file mode 100644
+index 0000000..deda995
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-fence.h
+@@ -0,0 +1,34 @@
++/*
++ * SMedia Glamo 336x/337x fence objects
++ *
++ * Copyright (c) 2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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, see <http://www.gnu.org/licenses/>.
++ *
++ */
++
++#ifndef __GLAMO_FENCE_H
++#define __GLAMO_FENCE_H
++
++#include <drm/drmP.h>
++
++#include "glamo-drm-private.h"
++
++extern void glamo_fence_init(struct glamodrm_handle *gdrm);
++extern void glamo_fence_shutdown(struct glamodrm_handle *gdrm);
++
++extern int glamo_ioctl_wait_rendering(struct drm_device *dev, void *data,
++                                      struct drm_file *file_priv);
++
++#endif /* __GLAMO_FENCE_H */
+diff --git a/drivers/mfd/glamo/glamo-kms-fb.c b/drivers/mfd/glamo/glamo-kms-fb.c
+new file mode 100644
+index 0000000..d76dd24
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-kms-fb.c
+@@ -0,0 +1,493 @@
++/*
++ * SMedia Glamo 336x/337x KMS Framebuffer
++ *
++ * Copyright (C) 2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc.
++ * Author: Harald Welte <laforge@openmoko.org>
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ *
++ * Based on intel_fb.c from drivers/gpu/drm/i915
++ *  to which the following licence applies:
++ *
++ * Copyright Â© 2006-2007 Intel Corporation
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ *
++ * Authors:
++ *    Eric Anholt <eric@anholt.net>
++ *
++ */
++
++
++#include <drm/drmP.h>
++#include <drm/glamo_drm.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_crtc.h>
++
++#include "glamo-core.h"
++#include "glamo-drm-private.h"
++#include "glamo-display.h"
++#include "glamo-buffer.h"
++
++
++struct glamofb_par {
++      struct drm_device *dev;
++      struct drm_display_mode *our_mode;
++      struct glamo_framebuffer *glamo_fb;
++      int crtc_count;
++      /* crtc currently bound to this */
++      uint32_t crtc_ids[2];
++};
++
++
++static int glamofb_setcolreg(unsigned regno, unsigned red, unsigned green,
++                      unsigned blue, unsigned transp,
++                      struct fb_info *info)
++{
++      struct glamofb_par *par = info->par;
++      struct drm_device *dev = par->dev;
++      struct drm_crtc *crtc;
++      int i;
++
++      list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++              struct glamo_crtc *glamo_crtc = to_glamo_crtc(crtc);
++              struct drm_mode_set *modeset = &glamo_crtc->mode_set;
++              struct drm_framebuffer *fb = modeset->fb;
++
++              for (i = 0; i < par->crtc_count; i++)
++                      if (crtc->base.id == par->crtc_ids[i])
++                              break;
++
++              if (i == par->crtc_count)
++                      continue;
++
++
++              if (regno > 255)
++                      return 1;
++
++              if (regno < 16) {
++                      switch (fb->depth) {
++                      case 15:
++                              fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
++                                      ((green & 0xf800) >>  6) |
++                                      ((blue & 0xf800) >> 11);
++                              break;
++                      case 16:
++                              fb->pseudo_palette[regno] = (red & 0xf800) |
++                                      ((green & 0xfc00) >>  5) |
++                                      ((blue  & 0xf800) >> 11);
++                              break;
++                      case 24:
++                      case 32:
++                              fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
++                                      (green & 0xff00) |
++                                      ((blue  & 0xff00) >> 8);
++                              break;
++                      }
++              }
++      }
++      return 0;
++}
++
++static int glamofb_check_var(struct fb_var_screeninfo *var,
++                      struct fb_info *info)
++{
++      struct glamofb_par *par = info->par;
++      struct glamo_framebuffer *glamo_fb = par->glamo_fb;
++      struct drm_framebuffer *fb = &glamo_fb->base;
++      int depth;
++
++      /* Need to resize the fb object !!! */
++      if (var->xres > fb->width || var->yres > fb->height) {
++              DRM_ERROR("Cannot resize framebuffer object (%dx%d > %dx%d)\n",
++                        var->xres,var->yres,fb->width,fb->height);
++              DRM_ERROR("Need resizing code.\n");
++              return -EINVAL;
++      }
++
++      switch (var->bits_per_pixel) {
++      case 16:
++              depth = (var->green.length == 6) ? 16 : 15;
++              break;
++      case 32:
++              depth = (var->transp.length > 0) ? 32 : 24;
++              break;
++      default:
++              depth = var->bits_per_pixel;
++              break;
++      }
++
++      switch (depth) {
++      case 16:
++              var->red.offset = 11;
++              var->green.offset = 5;
++              var->blue.offset = 0;
++              var->red.length = 5;
++              var->green.length = 6;
++              var->blue.length = 5;
++              var->transp.length = 0;
++              var->transp.offset = 0;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++/* this will let fbcon do the mode init */
++/* FIXME: take mode config lock? */
++static int glamofb_set_par(struct fb_info *info)
++{
++      struct glamofb_par *par = info->par;
++      struct drm_device *dev = par->dev;
++      struct fb_var_screeninfo *var = &info->var;
++      int i;
++      struct drm_crtc *crtc;
++      int ret;
++
++      DRM_DEBUG("%d %d\n", var->xres, var->pixclock);
++
++      if (var->pixclock != -1) {
++              DRM_ERROR("Warning: userspace gave me a pixel clock value (%i)"
++                        "- I'm ignoring it.\n", var->pixclock);
++      }
++
++      list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++              struct glamo_crtc *glamo_crtc = to_glamo_crtc(crtc);
++
++              for (i = 0; i < par->crtc_count; i++)
++                      if (crtc->base.id == par->crtc_ids[i])
++                              break;
++
++              if (i == par->crtc_count)
++                      continue;
++
++              if (crtc->fb == glamo_crtc->mode_set.fb) {
++                      mutex_lock(&dev->mode_config.mutex);
++                      ret = crtc->funcs->set_config(&glamo_crtc->mode_set);
++                      mutex_unlock(&dev->mode_config.mutex);
++                      if (ret)
++                              return ret;
++              }
++      }
++      return 0;
++}
++
++static int glamofb_pan_display(struct fb_var_screeninfo *var,
++                              struct fb_info *info)
++{
++      struct glamofb_par *par = info->par;
++      struct drm_device *dev = par->dev;
++      struct drm_mode_set *modeset;
++      struct drm_crtc *crtc;
++      struct glamo_crtc *glamo_crtc;
++      int ret = 0;
++      int i;
++
++      list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++              for (i = 0; i < par->crtc_count; i++)
++                      if (crtc->base.id == par->crtc_ids[i])
++                              break;
++
++              if (i == par->crtc_count)
++                      continue;
++
++              glamo_crtc = to_glamo_crtc(crtc);
++              modeset = &glamo_crtc->mode_set;
++
++              modeset->x = var->xoffset;
++              modeset->y = var->yoffset;
++
++              if (modeset->num_connectors) {
++                      mutex_lock(&dev->mode_config.mutex);
++                      ret = crtc->funcs->set_config(modeset);
++                      mutex_unlock(&dev->mode_config.mutex);
++                      if (!ret) {
++                              info->var.xoffset = var->xoffset;
++                              info->var.yoffset = var->yoffset;
++                      }
++              }
++      }
++
++      return ret;
++}
++
++static void glamofb_on(struct fb_info *info)
++{
++      struct glamofb_par *par = info->par;
++      struct drm_device *dev = par->dev;
++      struct glamodrm_handle *gdrm = dev->dev_private;
++
++      gdrm = dev->dev_private;
++
++      glamo_lcd_power(gdrm, 1);
++}
++
++static void glamofb_off(struct fb_info *info, int dpms_mode)
++{
++      struct glamofb_par *par = info->par;
++      struct drm_device *dev = par->dev;
++      struct glamodrm_handle *gdrm = dev->dev_private;
++
++      glamo_lcd_power(gdrm, 0);
++}
++
++static int glamofb_blank(int blank, struct fb_info *info)
++{
++      switch (blank) {
++      case FB_BLANK_UNBLANK:
++              glamofb_on(info);
++              break;
++      case FB_BLANK_NORMAL:
++              glamofb_off(info, DRM_MODE_DPMS_STANDBY);
++              break;
++      case FB_BLANK_HSYNC_SUSPEND:
++              glamofb_off(info, DRM_MODE_DPMS_STANDBY);
++              break;
++      case FB_BLANK_VSYNC_SUSPEND:
++              glamofb_off(info, DRM_MODE_DPMS_SUSPEND);
++              break;
++      case FB_BLANK_POWERDOWN:
++              glamofb_off(info, DRM_MODE_DPMS_OFF);
++              break;
++      }
++      return 0;
++}
++
++static struct fb_ops glamofb_ops = {
++      .owner = THIS_MODULE,
++      .fb_check_var = glamofb_check_var,
++      .fb_set_par = glamofb_set_par,
++      .fb_setcolreg = glamofb_setcolreg,
++      .fb_fillrect = cfb_fillrect,
++      .fb_copyarea = cfb_copyarea,
++      .fb_imageblit = cfb_imageblit,
++      .fb_pan_display = glamofb_pan_display,
++      .fb_blank = glamofb_blank,
++};
++
++
++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
++
++
++/* Here, we create a GEM object of the correct size, and then turn it into
++ * /dev/fbX so that the kernel can put a console on it. */
++int glamofb_create(struct drm_device *dev, uint32_t fb_width,
++                   uint32_t fb_height, uint32_t surface_width,
++                   uint32_t surface_height, int colour_mode,
++                   struct glamo_framebuffer **glamo_fb_p)
++{
++      struct fb_info *info;
++      struct glamofb_par *par;
++      struct drm_framebuffer *fb;
++      struct glamo_framebuffer *glamo_fb;
++      struct drm_mode_fb_cmd mode_cmd;
++      struct drm_gem_object *fbo = NULL;
++      struct drm_glamo_gem_object *gobj;
++      struct device *device = &dev->platform_dev->dev;
++      struct glamodrm_handle *gdrm;
++      int size, ret;
++      unsigned long offs;
++
++      gdrm = dev->dev_private;
++
++      mode_cmd.width = surface_width;
++      mode_cmd.height = surface_height;
++
++      mode_cmd.bpp = 16;
++      mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64);
++      mode_cmd.depth = 16;
++
++      size = mode_cmd.pitch * mode_cmd.height;
++      size = ALIGN(size, PAGE_SIZE);
++      if ( size > GLAMO_FRAMEBUFFER_ALLOCATION ) {
++              printk(KERN_ERR "[glamo-drm] Not enough memory for fb\n");
++              ret = -ENOMEM;
++              goto out;
++      }
++      fbo = glamo_gem_object_alloc(dev, GLAMO_FRAMEBUFFER_ALLOCATION, 2);
++      if (!fbo) {
++              printk(KERN_ERR "[glamo-drm] Failed to allocate framebuffer\n");
++              ret = -ENOMEM;
++              goto out;
++      }
++      gobj = fbo->driver_private;
++
++      mutex_lock(&dev->struct_mutex);
++
++      ret = glamo_framebuffer_create(dev, &mode_cmd, &fb, fbo);
++      if (ret) {
++              DRM_ERROR("failed to allocate fb.\n");
++              goto out_unref;
++      }
++
++      list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
++
++      glamo_fb = to_glamo_framebuffer(fb);
++      *glamo_fb_p = glamo_fb;
++
++      info = framebuffer_alloc(sizeof(struct glamofb_par), device);
++      if (!info) {
++              ret = -ENOMEM;
++              goto out_unref;
++      }
++
++      par = info->par;
++
++      strcpy(info->fix.id, "glamodrmfb");
++      info->fix.type = FB_TYPE_PACKED_PIXELS;
++      info->fix.visual = FB_VISUAL_TRUECOLOR;
++      info->fix.type_aux = 0;
++      info->fix.xpanstep = 1; /* doing it in hw */
++      info->fix.ypanstep = 1; /* doing it in hw */
++      info->fix.ywrapstep = 0;
++      info->fix.accel = FB_ACCEL_GLAMO;
++      info->fix.type_aux = 0;
++      info->flags = FBINFO_DEFAULT;
++
++      info->fbops = &glamofb_ops;
++
++      info->fix.line_length = fb->pitch;
++
++      info->flags = FBINFO_DEFAULT;
++
++      offs = gobj->block->start;
++      info->screen_base = ioremap(gdrm->vram->start + offs + GLAMO_OFFSET_FB,
++                                  GLAMO_FRAMEBUFFER_ALLOCATION);
++      if (!info->screen_base) {
++              printk(KERN_ERR "[glamo-drm] Couldn't map framebuffer!\n");
++              ret = -ENOSPC;
++              goto out_unref;
++      }
++      info->fix.smem_start = (unsigned long)gdrm->vram->start + offs;
++      info->fix.smem_len = size;
++      info->screen_size = size;
++
++      info->pseudo_palette = fb->pseudo_palette;
++      info->var.xres_virtual = fb->width;
++      info->var.yres_virtual = fb->height;
++      info->var.bits_per_pixel = fb->bits_per_pixel;
++      info->var.xoffset = 0;
++      info->var.yoffset = 0;
++      info->var.activate = FB_ACTIVATE_NOW;
++      info->var.height = -1;
++      info->var.width = -1;
++      info->var.xres = fb_width;
++      info->var.yres = fb_height;
++
++      info->fix.mmio_start = 0;
++      info->fix.mmio_len = 0;
++
++      info->pixmap.size = 64*1024;
++      info->pixmap.buf_align = 8;
++      info->pixmap.access_align = 32;
++      info->pixmap.flags = FB_PIXMAP_SYSTEM;
++      info->pixmap.scan_align = 1;
++
++      switch (fb->depth) {
++      case 16:
++              switch ( colour_mode ) {
++              case GLAMO_FB_RGB565:
++                      info->var.red.offset    = 11;
++                      info->var.green.offset  = 5;
++                      info->var.blue.offset   = 0;
++                      info->var.red.length    = 5;
++                      info->var.green.length  = 6;
++                      info->var.blue.length   = 5;
++                      info->var.transp.length = 0;
++                      break;
++              case GLAMO_FB_ARGB1555:
++                      info->var.transp.offset = 15;
++                      info->var.red.offset    = 10;
++                      info->var.green.offset  = 5;
++                      info->var.blue.offset   = 0;
++                      info->var.transp.length = 1;
++                      info->var.red.length    = 5;
++                      info->var.green.length  = 5;
++                      info->var.blue.length   = 5;
++                      break;
++              case GLAMO_FB_ARGB4444:
++                      info->var.transp.offset = 12;
++                      info->var.red.offset    = 8;
++                      info->var.green.offset  = 4;
++                      info->var.blue.offset   = 0;
++                      info->var.transp.length = 4;
++                      info->var.red.length    = 4;
++                      info->var.green.length  = 4;
++                      info->var.blue.length   = 4;
++                      break;
++              }
++              break;
++      case 24:
++      case 32:
++      default:
++              /* The Smedia Glamo doesn't support anything but 16bit color */
++              printk(KERN_ERR "[glamo-drm] Only 16bpp is supported.\n");
++              return -EINVAL;
++      }
++
++      fb->fbdev = info;
++      par->glamo_fb = glamo_fb;
++      par->dev = dev;
++      gdrm->fb = info;
++
++      info->var.pixclock = -1;
++
++      printk(KERN_INFO "[glamo-drm] Allocated %dx%d fb: bo %p\n",
++             glamo_fb->base.width, glamo_fb->base.height, fbo);
++      mutex_unlock(&dev->struct_mutex);
++      return 0;
++
++out_unref:
++      drm_gem_object_unreference(fbo);
++      mutex_unlock(&dev->struct_mutex);
++out:
++      return ret;
++}
++
++
++void glamo_kmsfb_suspend(struct glamodrm_handle *gdrm)
++{
++      fb_set_suspend(gdrm->fb, 1);
++}
++
++
++void glamo_kmsfb_resume(struct glamodrm_handle *gdrm)
++{
++      fb_set_suspend(gdrm->fb, 0);
++}
+diff --git a/drivers/mfd/glamo/glamo-kms-fb.h b/drivers/mfd/glamo/glamo-kms-fb.h
+new file mode 100644
+index 0000000..1960e76
+--- /dev/null
++++ b/drivers/mfd/glamo/glamo-kms-fb.h
+@@ -0,0 +1,41 @@
++/*
++ * SMedia Glamo 336x/337x KMS framebuffer
++ *
++ * Copyright (C) 2009 Thomas White <taw@bitwiz.org.uk>
++ *
++ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc.
++ * Author: Harald Welte <laforge@openmoko.org>
++ * All rights reserved.
++ *
++ * This program 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 of
++ * the License, or (at your option) any later version.
++ *
++ * This program 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., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ */
++
++#ifndef __GLAMO_KMS_FB_H
++#define __GLAMO_KMS_FB_H
++
++#include <drm/drmP.h>
++#include "glamo-drm-private.h"
++
++extern int glamofb_create(struct drm_device *dev, uint32_t fb_width,
++                          uint32_t fb_height, uint32_t surface_width,
++                          uint32_t surface_height, int colour_mode,
++                          struct glamo_framebuffer **glamo_fb_p);
++
++extern void glamo_kmsfb_suspend(struct glamodrm_handle *gdrm);
++extern void glamo_kmsfb_resume(struct glamodrm_handle *gdrm);
++
++#endif /* __GLAMO_KMS_FB_H */
+diff --git a/include/drm/Kbuild b/include/drm/Kbuild
+index bd3a1c2..a8f06eb 100644
+--- a/include/drm/Kbuild
++++ b/include/drm/Kbuild
+@@ -10,3 +10,4 @@ unifdef-y += savage_drm.h
+ unifdef-y += vmwgfx_drm.h
+ unifdef-y += via_drm.h
+ unifdef-y += nouveau_drm.h
++unifdef-y += glamo_drm.h
+diff --git a/include/drm/glamo_drm.h b/include/drm/glamo_drm.h
+new file mode 100644
+index 0000000..4c194dc
+--- /dev/null
++++ b/include/drm/glamo_drm.h
+@@ -0,0 +1,153 @@
++/* glamo_drm.h -- Public header for the Glamo driver
++ *
++ * Copyright 2009 Thomas White
++ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
++ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
++ * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas.
++ * All rights reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
++ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ *
++ * Authors:
++ *    Thomas White <taw@bitwiz.org.uk>
++ *    Kevin E. Martin <martin@valinux.com>
++ *    Gareth Hughes <gareth@valinux.com>
++ *    Keith Whitwell <keith@tungstengraphics.com>
++ */
++
++#ifndef __GLAMO_DRM_H__
++#define __GLAMO_DRM_H__
++
++#include "drm.h"
++
++#define GLAMO_GEM_DOMAIN_VRAM (0x1)
++
++/* Glamo specific ioctls */
++#define DRM_GLAMO_CMDBUF     0x01
++#define DRM_GLAMO_SWAP       0x02
++#define DRM_GLAMO_CMDBURST   0x03
++
++#define DRM_GLAMO_GEM_INFO     0x1c
++#define DRM_GLAMO_GEM_CREATE   0x1d
++#define DRM_GLAMO_GEM_MMAP     0x1e
++#define DRM_GLAMO_GEM_PIN      0x1f
++#define DRM_GLAMO_GEM_UNPIN    0x20
++#define DRM_GLAMO_GEM_PREAD    0x21
++#define DRM_GLAMO_GEM_PWRITE   0x22
++#define DRM_GLAMO_GEM_WAIT_RENDERING 0x24
++
++#define DRM_IOCTL_GLAMO_CMDBUF     DRM_IOW(DRM_COMMAND_BASE + DRM_GLAMO_CMDBUF, drm_glamo_cmd_buffer_t)
++#define DRM_IOCTL_GLAMO_SWAP       DRM_IO(DRM_COMMAND_BASE + DRM_GLAMO_SWAP)
++#define DRM_IOCTL_GLAMO_CMDBURST     DRM_IOW(DRM_COMMAND_BASE + DRM_GLAMO_CMDBURST, drm_glamo_cmd_burst_t)
++
++#define DRM_IOCTL_GLAMO_GEM_INFO   DRM_IOWR(DRM_COMMAND_BASE + DRM_GLAMO_GEM_INFO, struct drm_glamo_gem_info)
++#define DRM_IOCTL_GLAMO_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_GLAMO_GEM_CREATE, struct drm_glamo_gem_create)
++#define DRM_IOCTL_GLAMO_GEM_MMAP   DRM_IOWR(DRM_COMMAND_BASE + DRM_GLAMO_GEM_MMAP, struct drm_glamo_gem_mmap)
++#define DRM_IOCTL_GLAMO_GEM_PIN    DRM_IOWR(DRM_COMMAND_BASE + DRM_GLAMO_GEM_PIN, struct drm_glamo_gem_pin)
++#define DRM_IOCTL_GLAMO_GEM_UNPIN  DRM_IOWR(DRM_COMMAND_BASE + DRM_GLAMO_GEM_UNPIN, struct drm_glamo_gem_unpin)
++#define DRM_IOCTL_GLAMO_GEM_PREAD  DRM_IOWR(DRM_COMMAND_BASE + DRM_GLAMO_GEM_PREAD, struct drm_glamo_gem_pread)
++#define DRM_IOCTL_GLAMO_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_GLAMO_GEM_PWRITE, struct drm_glamo_gem_pwrite)
++#define DRM_IOCTL_GLAMO_GEM_WAIT_RENDERING DRM_IOW(DRM_COMMAND_BASE + DRM_GLAMO_GEM_WAIT_RENDERING, struct drm_glamo_gem_wait_rendering)
++
++
++/* Simple command submission - a list of 16-bit address-data pairs */
++typedef struct drm_glamo_cmd_buffer {
++      unsigned int bufsz;     /* Size of buffer, in bytes */
++      char __user *buf;       /* Buffer of stuff to go onto the ring buffer */
++      unsigned int *obj_pos;  /* Offsets (in bytes) at which to put objs */
++      uint32_t *objs;         /* List of buffer object (handles) to use */
++      unsigned int nobjs;     /* Number of objects referenced */
++      int nbox;
++      struct drm_clip_rect __user *boxes;
++} drm_glamo_cmd_buffer_t;
++
++
++/* Burst command submission - base address and data:
++ *  - Data can be 32-bit (more easily)
++ *  - Easier for the kernel to validate */
++typedef struct drm_glamo_cmd_burst {
++      uint16_t base;          /* Base address (command) */
++      int bufsz;              /* Size of data, in bytes */
++      uint16_t *data;         /* Pointer to data */
++      unsigned int *obj_pos;  /* Offsets (in bytes) at which to put objs */
++      uint32_t *objs;         /* List of buffer object (handles) to use */
++      unsigned int nobjs;     /* Number of objects referenced */
++} drm_glamo_cmd_burst_t;
++
++struct drm_glamo_gem_info {
++      uint64_t vram_start;
++      uint64_t vram_size;
++};
++
++struct drm_glamo_gem_create {
++      uint64_t size;
++      uint64_t alignment;
++      uint32_t handle;
++      uint32_t initial_domain; // to allow VRAM to be created
++      uint32_t no_backing_store;
++};
++
++struct drm_glamo_gem_mmap {
++      uint32_t handle;        /* Handle goes in... */
++      uint64_t offset;        /* ...offset comes out */
++};
++
++struct drm_glamo_gem_wait_rendering {
++      uint32_t handle;
++      int have_handle;
++};
++
++struct drm_glamo_gem_pin {
++      uint32_t handle;
++      uint32_t pin_domain;
++      uint64_t alignment;
++      uint64_t offset;
++};
++
++struct drm_glamo_gem_unpin {
++      uint32_t handle;
++      uint32_t pad;
++};
++
++struct drm_glamo_gem_pread {
++      /** Handle for the object being read. */
++      uint32_t handle;
++      uint32_t pad;
++      /** Offset into the object to read from */
++      uint64_t offset;
++      /** Length of data to read */
++      uint64_t size;
++      /** Pointer to write the data into. */
++      uint64_t data_ptr;      /* void *, but pointers are not 32/64 compatible */
++};
++
++struct drm_glamo_gem_pwrite {
++      /** Handle for the object being written to. */
++      uint32_t handle;
++      uint32_t pad;
++      /** Offset into the object to write to */
++      uint64_t offset;
++      /** Length of data to write */
++      uint64_t size;
++      /** Pointer to read the data from. */
++      uint64_t data_ptr;      /* void *, but pointers are not 32/64 compatible */
++};
++
++#endif
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0002-usbhost.patch.patch b/recipes/linux/linux-openmoko-2.6.34/0002-usbhost.patch.patch
new file mode 100644 (file)
index 0000000..ace1650
--- /dev/null
@@ -0,0 +1,414 @@
+From cbc005e5e709407bae373d006d06a4cf51fd3062 Mon Sep 17 00:00:00 2001
+From: Radek Polak <psonek2@seznam.cz>
+Date: Fri, 9 Apr 2010 09:17:28 +0200
+Subject: [PATCH 2/4] usbhost.patch
+
+adds support for usb host mode and adds usb host pm sysfs node. I just copied
+and modified how modem is powered up and backported changes in ohci-s3c2410.c.
+No idea if this is the right approach, but it works - my usb keyboard is now
+working after:
+
+echo 1 > /sys/devices/platform/s3c2440-i2c/i2c-0/0-0073/pcf50633-gpio/reg-
+fixed-voltage.2/gta02-pm-usbhost.0/power_on
+echo host > /sys/devices/platform/s3c2410-ohci/usb_mode
+
+Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
+---
+ arch/arm/mach-s3c2440/Makefile           |    1 +
+ arch/arm/mach-s3c2440/gta02-pm-usbhost.c |  174 ++++++++++++++++++++++++++++++
+ arch/arm/mach-s3c2440/mach-gta02.c       |   48 ++++++++-
+ drivers/usb/host/ohci-s3c2410.c          |   48 ++++++++
+ 4 files changed, 270 insertions(+), 1 deletions(-)
+ create mode 100644 arch/arm/mach-s3c2440/gta02-pm-usbhost.c
+
+diff --git a/arch/arm/mach-s3c2440/Makefile b/arch/arm/mach-s3c2440/Makefile
+index 6ce800d..450cff9 100644
+--- a/arch/arm/mach-s3c2440/Makefile
++++ b/arch/arm/mach-s3c2440/Makefile
+@@ -37,6 +37,7 @@ obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o \
+       gta02-pm-bt.o \
+       gta02-pm-gps.o \
+       gta02-pm-gsm.o \
++      gta02-pm-usbhost.o \
+       gta02-pm-wlan.o \
+       gta02-fiq.o \
+diff --git a/arch/arm/mach-s3c2440/gta02-pm-usbhost.c b/arch/arm/mach-s3c2440/gta02-pm-usbhost.c
+new file mode 100644
+index 0000000..233340a
+--- /dev/null
++++ b/arch/arm/mach-s3c2440/gta02-pm-usbhost.c
+@@ -0,0 +1,174 @@
++/*
++ * USBHOST Management code for the Openmoko Freerunner GSM Phone
++ *
++ * (C) 2007 by Openmoko Inc.
++ * Author: Harald Welte <laforge@openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/console.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/regulator/consumer.h>
++
++#include <mach/gpio.h>
++#include <asm/mach-types.h>
++
++#include <mach/hardware.h>
++
++#include <mach/gta02.h>
++#include <mach/regs-gpio.h>
++#include <mach/regs-gpioj.h>
++
++static struct regulator *gta02_usbhost_regulator;
++
++static ssize_t usbhost_read(struct device *dev, struct device_attribute *attr,
++                      char *buf)
++{
++      if (!strcmp(attr->attr.name, "power_on")) {
++              if (regulator_is_enabled(gta02_usbhost_regulator))
++                      goto out_1;
++      }
++
++      return strlcpy(buf, "0\n", 3);
++out_1:
++      return strlcpy(buf, "1\n", 3);
++}
++
++static void usbhost_on_off(struct device *dev, int on)
++{
++
++      on = !!on;
++
++      if (on == regulator_is_enabled(gta02_usbhost_regulator))
++              return;
++
++      if (!on) {
++              regulator_disable(gta02_usbhost_regulator);
++              return;
++      }
++
++      regulator_enable(gta02_usbhost_regulator);
++}
++
++static ssize_t usbhost_write(struct device *dev, struct device_attribute *attr,
++                       const char *buf, size_t count)
++{
++      unsigned long on = simple_strtoul(buf, NULL, 10);
++
++      if (!strcmp(attr->attr.name, "power_on")) {
++              usbhost_on_off(dev, on);
++
++              return count;
++      }
++
++      return count;
++}
++
++static DEVICE_ATTR(power_on, 0644, usbhost_read, usbhost_write);
++
++#ifdef CONFIG_PM
++
++static int gta02_usbhost_suspend(struct device *dev)
++{
++      return 0;
++}
++
++static int gta02_usbhost_suspend_late(struct device *dev)
++{
++      return 0;
++}
++
++static int gta02_usbhost_resume(struct device *dev)
++{
++      return 0;
++}
++
++static struct dev_pm_ops gta02_usbhost_pm_ops = {
++      .suspend        = gta02_usbhost_suspend,
++      .suspend_noirq  = gta02_usbhost_suspend_late,
++      .resume         = gta02_usbhost_resume,
++};
++
++#define GTA02_USBHOST_PM_OPS (&gta02_usbhost_pm_ops)
++
++#else
++#define GTA02_USBHOST_PM_OPS NULL
++#endif /* CONFIG_PM */
++
++static struct attribute *gta02_usbhost_sysfs_entries[] = {
++      &dev_attr_power_on.attr,
++      NULL
++};
++
++static struct attribute_group gta02_usbhost_attr_group = {
++      .name   = NULL,
++      .attrs  = gta02_usbhost_sysfs_entries,
++};
++
++static int __init gta02_usbhost_probe(struct platform_device *pdev)
++{
++      int ret;
++
++      gta02_usbhost_regulator = regulator_get_exclusive(&pdev->dev, "USBHOST");
++
++      if (IS_ERR(gta02_usbhost_regulator)) {
++              ret = PTR_ERR(gta02_usbhost_regulator);
++              dev_err(&pdev->dev, "Failed to get regulator: %d\n", ret);
++              return ret;
++      }
++
++      ret = sysfs_create_group(&pdev->dev.kobj, &gta02_usbhost_attr_group);
++      if (ret) {
++              dev_err(&pdev->dev, "Failed to create sysfs entries: %d\n", ret);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int gta02_usbhost_remove(struct platform_device *pdev)
++{
++      usbhost_on_off(&pdev->dev, 0);
++
++      sysfs_remove_group(&pdev->dev.kobj, &gta02_usbhost_attr_group);
++      regulator_put(gta02_usbhost_regulator);
++
++      return 0;
++}
++
++static struct platform_driver gta02_usbhost_driver = {
++      .probe          = gta02_usbhost_probe,
++      .remove         = gta02_usbhost_remove,
++      .driver         = {
++              .name   = "gta02-pm-usbhost",
++              .pm     = GTA02_USBHOST_PM_OPS,
++      },
++};
++
++static int __devinit gta02_usbhost_init(void)
++{
++      return platform_driver_register(&gta02_usbhost_driver);
++}
++module_init(gta02_usbhost_init);
++
++static void gta02_usbhost_exit(void)
++{
++      platform_driver_unregister(&gta02_usbhost_driver);
++}
++module_exit(gta02_usbhost_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
++MODULE_DESCRIPTION("Openmoko Freerunner USBHOST Power Management");
+diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
+index 795b9f4..e7d0b82 100644
+--- a/arch/arm/mach-s3c2440/mach-gta02.c
++++ b/arch/arm/mach-s3c2440/mach-gta02.c
+@@ -202,6 +202,10 @@ static struct platform_device gta02_pm_gsm_dev = {
+       .name = "gta02-pm-gsm",
+ };
++static struct platform_device gta02_pm_usbhost_dev = {
++      .name = "gta02-pm-usbhost",
++};
++
+ static struct platform_device gta02_pm_wlan_dev = {
+       .name = "gta02-pm-wlan",
+ };
+@@ -211,6 +215,11 @@ static struct regulator_consumer_supply gsm_supply_consumer = {
+       .supply = "GSM",
+ };
++static struct regulator_consumer_supply usbhost_supply_consumer = {
++      .dev = &gta02_pm_usbhost_dev.dev,
++      .supply = "USBHOST",
++};
++
+ static struct regulator_init_data gsm_supply_init_data = {
+       .constraints = {
+               .min_uV = 3700000,
+@@ -222,6 +231,17 @@ static struct regulator_init_data gsm_supply_init_data = {
+       .consumer_supplies = &gsm_supply_consumer,
+ };
++static struct regulator_init_data usbhost_supply_init_data = {
++      .constraints = {
++              .min_uV = 3700000,
++              .max_uV = 3700000,
++              .valid_modes_mask = REGULATOR_MODE_NORMAL,
++              .valid_ops_mask = REGULATOR_CHANGE_STATUS,
++      },
++      .num_consumer_supplies = 1,
++      .consumer_supplies = &usbhost_supply_consumer,
++};
++
+ static struct fixed_voltage_config gsm_supply_config = {
+       .supply_name = "GSM",
+       .microvolts = 3700000,
+@@ -230,6 +250,14 @@ static struct fixed_voltage_config gsm_supply_config = {
+       .init_data = &gsm_supply_init_data,
+ };
++static struct fixed_voltage_config usbhost_supply_config = {
++      .supply_name = "USBHOST",
++      .microvolts = 3700000,
++      .gpio = GTA02_GPIO_PCF(PCF50633_GPO),
++      .enable_high = 1,
++      .init_data = &usbhost_supply_init_data,
++};
++
+ static struct platform_device gta02_gsm_supply_device = {
+       .name = "reg-fixed-voltage",
+       .id = 1,
+@@ -238,6 +266,14 @@ static struct platform_device gta02_gsm_supply_device = {
+       },
+ };
++static struct platform_device gta02_usbhost_supply_device = {
++      .name = "reg-fixed-voltage",
++      .id = 2,
++      .dev = {
++              .platform_data = &usbhost_supply_config,
++      },
++};
++
+ /*
+  * we crank down SD Card clock dynamically when GPS is powered
+  */
+@@ -1287,12 +1323,17 @@ struct gta02_device_children {
+ static struct platform_device* gta02_pcf50633_gpio_children[] = {
+       &gta02_gsm_supply_device,
++      &gta02_usbhost_supply_device,
+ };
+ static struct platform_device* gta02_gsm_supply_children[] = {
+       &gta02_pm_gsm_dev,
+ };
++static struct platform_device* gta02_usbhost_supply_children[] = {
++      &gta02_pm_usbhost_dev,
++};
++
+ static struct platform_device* gta02_hdq_children[] = {
+       &bq27000_battery_device,
+ };
+@@ -1301,7 +1342,7 @@ static struct platform_device* gta02_hdq_children[] = {
+ static struct gta02_device_children gta02_device_children[] = {
+       {
+               .dev_name = "pcf50633-gpio",
+-              .num_children = 1,
++              .num_children = 2,
+               .children = gta02_pcf50633_gpio_children,
+       },
+       {
+@@ -1310,6 +1351,11 @@ static struct gta02_device_children gta02_device_children[] = {
+               .children = gta02_gsm_supply_children,
+       },
+       {
++              .dev_name = "reg-fixed-voltage.2",
++              .num_children = 1,
++              .children = gta02_usbhost_supply_children,
++      },
++      {
+               .dev_name = "spi2.0",
+               .probed_callback = gta02_jbt6k74_probe_completed,
+       },
+diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
+index a68af2d..02bd7b0 100644
+--- a/drivers/usb/host/ohci-s3c2410.c
++++ b/drivers/usb/host/ohci-s3c2410.c
+@@ -22,6 +22,10 @@
+ #include <linux/platform_device.h>
+ #include <linux/clk.h>
+ #include <plat/usb-control.h>
++#include <mach/hardware.h>
++#include <mach/gpio-fns.h>
++#include <mach/regs-gpio.h>
++#include <mach/gta02.h>
+ #define valid_port(idx) ((idx) == 1 || (idx) == 2)
+@@ -306,6 +310,42 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
+       local_irq_restore(flags);
+ }
++/* switching of USB pads */
++static ssize_t show_usb_mode(struct device *dev, struct device_attribute *attr,
++                           char *buf)
++{
++      if (__raw_readl(S3C24XX_MISCCR) & S3C2410_MISCCR_USBHOST)
++              return sprintf(buf, "host\n");
++
++      return sprintf(buf, "device\n");
++}
++
++static ssize_t set_usb_mode(struct device *dev, struct device_attribute *attr,
++                          const char *buf, size_t count)
++{
++      if (!strncmp(buf, "host", 4)) {
++              printk("s3c2410: changing usb to host\n");
++              s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST,
++                                    S3C2410_MISCCR_USBHOST);
++              /* FIXME:
++               * - call machine-specific disable-pullup function i
++               * - enable +Vbus (if hardware supports it)
++               */
++              s3c2410_gpio_setpin(GTA02_GPIO_USB_PULLUP, 0);
++      } else if (!strncmp(buf, "device", 6)) {
++              printk("s3c2410: changing usb to device\n");
++              s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, 0);
++              s3c2410_gpio_setpin(GTA02_GPIO_USB_PULLUP, 1);
++      } else {
++              printk("s3c2410: unknown mode\n");
++              return -EINVAL;
++      }
++
++      return count;
++}
++
++static DEVICE_ATTR(usb_mode, S_IRUGO | S_IWUSR, show_usb_mode, set_usb_mode);
++
+ /* may be called without controller electrically present */
+ /* may be called with controller, bus, and devices active */
+@@ -323,6 +363,7 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
+ static void
+ usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev)
+ {
++      device_remove_file(&dev->dev, &dev_attr_usb_mode);
+       usb_remove_hcd(hcd);
+       s3c2410_stop_hc(dev);
+       iounmap(hcd->regs);
+@@ -390,8 +431,15 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
+       if (retval != 0)
+               goto err_ioremap;
++      retval = device_create_file(&dev->dev, &dev_attr_usb_mode);
++      if (retval != 0)
++              goto err_hcd;
++
+       return 0;
++ err_hcd:
++      usb_remove_hcd(hcd);
++
+  err_ioremap:
+       s3c2410_stop_hc(dev);
+       iounmap(hcd->regs);
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0003-Work-on-Glamo-core-for-DRM.patch b/recipes/linux/linux-openmoko-2.6.34/0003-Work-on-Glamo-core-for-DRM.patch
new file mode 100644 (file)
index 0000000..c9afc89
--- /dev/null
@@ -0,0 +1,162 @@
+From 0c38c5d7bcce1b62534e76419ee04eea0fe50128 Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Tue, 17 Nov 2009 23:45:29 +0100
+Subject: [PATCH 03/13] Work on Glamo-core for DRM
+
+This adds modifications to the core of the Glamo driver to expose functionality
+to support DRM and KMS.
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/mfd/glamo/glamo-core.c |   27 +++++++++++++++++++++------
+ drivers/mfd/glamo/glamo-core.h |   32 ++++++++++++++++++++++----------
+ drivers/mfd/glamo/glamo-regs.h |   24 ++++++++++++++++++++++++
+ include/linux/mfd/glamo.h      |    7 ++-----
+ 4 files changed, 69 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c
+index 23073fe..f894c91 100644
+--- a/drivers/mfd/glamo/glamo-core.c
++++ b/drivers/mfd/glamo/glamo-core.c
+@@ -222,10 +222,25 @@ static struct resource glamo_fb_resources[] = {
+               .flags  = IORESOURCE_MEM,
+       }, {
+               .name   = "glamo-fb-mem",
+-              .start  = GLAMO_OFFSET_FB,
+-              .end    = GLAMO_OFFSET_FB + GLAMO_FB_SIZE - 1,
++              .start  = GLAMO_MEM_BASE + GLAMO_OFFSET_FB,
++              .end    = GLAMO_MEM_BASE + GLAMO_OFFSET_FB + GLAMO_FB_SIZE - 1,
+               .flags  = IORESOURCE_MEM,
+-      },
++      }, {
++              .name   = "glamo-cmdq-regs",
++              .start  = GLAMO_REGOFS_CMDQUEUE,
++              .end    = GLAMO_REGOFS_RISC - 1,
++              .flags  = IORESOURCE_MEM,
++      }, {
++              .name   = "glamo-2d-regs",
++              .start  = GLAMO_REGOFS_2D,
++              .end    = GLAMO_REGOFS_3D- 1,
++              .flags  = IORESOURCE_MEM,
++      }, {
++              .name   = "glamo-2d-irq",
++              .start  = GLAMO_IRQ_2D,
++              .end    = GLAMO_IRQ_2D,
++              .flags  = IORESOURCE_IRQ,
++      }
+ };
+ static struct resource glamo_mmc_resources[] = {
+@@ -236,9 +251,9 @@ static struct resource glamo_mmc_resources[] = {
+               .flags  = IORESOURCE_MEM
+       }, {
+               .name   = "glamo-mmc-mem",
+-              .start  = GLAMO_OFFSET_FB + GLAMO_FB_SIZE,
+-              .end    = GLAMO_OFFSET_FB + GLAMO_FB_SIZE +
+-                                GLAMO_MMC_BUFFER_SIZE - 1,
++              .start  = GLAMO_MEM_BASE + GLAMO_OFFSET_MMC,
++              .end    = GLAMO_MEM_BASE + GLAMO_OFFSET_MMC
++                                       + GLAMO_MMC_BUFFER_SIZE - 1,
+               .flags  = IORESOURCE_MEM
+       }, {
+               .start  = GLAMO_IRQ_MMC,
+diff --git a/drivers/mfd/glamo/glamo-core.h b/drivers/mfd/glamo/glamo-core.h
+index 17017b0..0adba96 100644
+--- a/drivers/mfd/glamo/glamo-core.h
++++ b/drivers/mfd/glamo/glamo-core.h
+@@ -3,18 +3,30 @@
+ #include <linux/mfd/glamo.h>
++/* Amount of Glamo memory */
++#define GLAMO_INTERNAL_RAM_SIZE 0x800000
++
++/* Arbitrarily determined amount for the hardware cursor */
++#define GLAMO_CURSOR_SIZE (4096)
++#define GLAMO_MMC_BUFFER_SIZE (64 * 1024) /* 64k MMC buffer */
++/* Remaining memory will be used for 2D and 3D graphics */
++#define GLAMO_FB_SIZE (GLAMO_INTERNAL_RAM_SIZE     \
++                       - GLAMO_CURSOR_SIZE       \
++                       - GLAMO_MMC_BUFFER_SIZE)
++/* A 640x480, 16bpp, double-buffered framebuffer */
++#if (GLAMO_FB_SIZE < (640 * 480 * 4)) /* == 0x12c000 */
++#error Not enough Glamo VRAM for framebuffer!
++#endif
++
+ /* for the time being, we put the on-screen framebuffer into the lowest
+  * VRAM space.  This should make the code easily compatible with the various
+- * 2MB/4MB/8MB variants of the Smedia chips */
+-#define GLAMO_OFFSET_VRAM     0x800000
+-#define GLAMO_OFFSET_FB       (GLAMO_OFFSET_VRAM)
+-
+-/* we only allocate the minimum possible size for the framebuffer to make
+- * sure we have sufficient memory for other functions of the chip */
+-/*#define GLAMO_FB_SIZE       (640*480*4)     *//* == 0x12c000 */
+-#define GLAMO_INTERNAL_RAM_SIZE 0x800000
+-#define GLAMO_MMC_BUFFER_SIZE (64 * 1024)
+-#define GLAMO_FB_SIZE (GLAMO_INTERNAL_RAM_SIZE - GLAMO_MMC_BUFFER_SIZE)
++ * 2MB/4MB/8MB variants of the Smedia chips
++ * glamo-fb.c assumes FB comes first, followed by cursor, so DON'T MOVE THEM
++ * (see glamo_regs[] in glamo-fb.c for more information) */
++#define GLAMO_MEM_BASE                (0x800000)
++#define GLAMO_OFFSET_FB               (0x000000)
++#define GLAMO_OFFSET_CURSOR   (GLAMO_OFFSET_FB + GLAMO_FB_SIZE)
++#define GLAMO_OFFSET_MMC      (GLAMO_OFFSET_CURSOR + GLAMO_CURSOR_SIZE)
+ enum glamo_pll {
+     GLAMO_PLL1,
+diff --git a/drivers/mfd/glamo/glamo-regs.h b/drivers/mfd/glamo/glamo-regs.h
+index 59848e1..8b2fd47 100644
+--- a/drivers/mfd/glamo/glamo-regs.h
++++ b/drivers/mfd/glamo/glamo-regs.h
+@@ -627,4 +627,28 @@ enum glamo_core_revisions {
+       GLAMO_CORE_REV_A3               = 0x0003,
+ };
++enum glamo_register_cq {
++      GLAMO_REG_CMDQ_BASE_ADDRL     = 0x00,
++      GLAMO_REG_CMDQ_BASE_ADDRH     = 0x02,
++      GLAMO_REG_CMDQ_LEN            = 0x04,
++      GLAMO_REG_CMDQ_WRITE_ADDRL    = 0x06,
++      GLAMO_REG_CMDQ_WRITE_ADDRH    = 0x08,
++      GLAMO_REG_CMDQ_FLIP           = 0x0a,
++      GLAMO_REG_CMDQ_CONTROL        = 0x0c,
++      GLAMO_REG_CMDQ_READ_ADDRL     = 0x0e,
++      GLAMO_REG_CMDQ_READ_ADDRH     = 0x10,
++      GLAMO_REG_CMDQ_STATUS         = 0x12,
++};
++
++#define REG_2D(x)             (GLAMO_REGOFS_2D+(x))
++
++enum glamo_register_2d {
++      GLAMO_REG_2D_DST_X              = REG_2D(0x0a),
++      GLAMO_REG_2D_COMMAND1           = REG_2D(0x3a),
++      GLAMO_REG_2D_STATUS             = REG_2D(0x42),
++      GLAMO_REG_2D_ID1                = REG_2D(0x44),
++      GLAMO_REG_2D_ID2                = REG_2D(0x46),
++      GLAMO_REG_2D_ID3                = REG_2D(0x48),
++};
++
+ #endif /* _GLAMO_REGS_H */
+diff --git a/include/linux/mfd/glamo.h b/include/linux/mfd/glamo.h
+index 529d4f0..ea91a06 100644
+--- a/include/linux/mfd/glamo.h
++++ b/include/linux/mfd/glamo.h
+@@ -41,12 +41,9 @@ enum glamo_engine {
+       GLAMO_ENGINE_RISC = 11,
+       GLAMO_ENGINE_MICROP1_MPEG_ENC = 12,
+       GLAMO_ENGINE_MICROP1_MPEG_DEC = 13,
+-#if 0
+-      GLAMO_ENGINE_H264_DEC = 14,
+-      GLAMO_ENGINE_RISC1 = 15,
+-      GLAMO_ENGINE_SPI = 16,
+-#endif
+       __NUM_GLAMO_ENGINES
+ };
++#define GLAMO_ENGINE_ALL (__NUM_GLAMO_ENGINES)
++
+ #endif
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0003-ar6000_delay.patch.patch b/recipes/linux/linux-openmoko-2.6.34/0003-ar6000_delay.patch.patch
new file mode 100644 (file)
index 0000000..8241a1f
--- /dev/null
@@ -0,0 +1,29 @@
+From 3287722e641d6e21cb0e2a8fa13058467a5319e3 Mon Sep 17 00:00:00 2001
+From: Radek Polak <psonek2@seznam.cz>
+Date: Fri, 9 Apr 2010 09:18:02 +0200
+Subject: [PATCH 3/4] ar6000_delay.patch
+
+patch from https://docs.openmoko.org/trac/ticket/2327 - wifi is working good
+(100% until now) for me with this patch.
+
+Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
+---
+ drivers/ar6000/hif/hif2.c |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/ar6000/hif/hif2.c b/drivers/ar6000/hif/hif2.c
+index 386d96e..90178d0 100644
+--- a/drivers/ar6000/hif/hif2.c
++++ b/drivers/ar6000/hif/hif2.c
+@@ -517,6 +517,8 @@ static int ar6000_do_activate(struct hif_device *hif)
+               goto out_func_ready;
+       }
++      mdelay (10);
++
+       ret = htcCallbacks.deviceInsertedHandler(hif);
+       if (ret == A_OK)
+               return 0;
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0004-JBT6k74-work-for-KMS.patch b/recipes/linux/linux-openmoko-2.6.34/0004-JBT6k74-work-for-KMS.patch
new file mode 100644 (file)
index 0000000..692896f
--- /dev/null
@@ -0,0 +1,540 @@
+From 2042106d96a13c2a15f1425fe9133257b1e0fbed Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Sat, 21 Nov 2009 21:42:16 +0100
+Subject: [PATCH 04/13] JBT6k74 work for KMS
+
+This simplifies the JBT6k74 driver, and adds hooks for the Glamo driver to cooperate
+more closely with it.
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/video/backlight/jbt6k74.c |  252 ++++++++++++++++++------------------
+ include/linux/jbt6k74.h           |   17 +++-
+ 2 files changed, 142 insertions(+), 127 deletions(-)
+
+diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c
+index 8450904..91651e7 100644
+--- a/drivers/video/backlight/jbt6k74.c
++++ b/drivers/video/backlight/jbt6k74.c
+@@ -101,20 +101,10 @@ enum jbt_register {
+       JBT_REG_HCLOCK_QVGA             = 0xed,
+ };
+-enum jbt_resolution {
+-      JBT_RESOLUTION_VGA,
+-      JBT_RESOLUTION_QVGA,
+-};
+-
+-enum jbt_power_mode {
+-      JBT_POWER_MODE_DEEP_STANDBY,
+-      JBT_POWER_MODE_SLEEP,
+-      JBT_POWER_MODE_NORMAL,
+-};
+ static const char *jbt_power_mode_names[] = {
+-      [JBT_POWER_MODE_DEEP_STANDBY]   = "deep-standby",
+-      [JBT_POWER_MODE_SLEEP]          = "sleep",
++      [JBT_POWER_MODE_OFF]            = "off",
++      [JBT_POWER_MODE_STANDBY]        = "standby",
+       [JBT_POWER_MODE_NORMAL]         = "normal",
+ };
+@@ -123,6 +113,7 @@ static const char *jbt_resolution_names[] = {
+       [JBT_RESOLUTION_QVGA] = "qvga",
+ };
++
+ struct jbt_info {
+       struct mutex lock;              /* protects this structure */
+       enum jbt_resolution resolution;
+@@ -141,6 +132,8 @@ struct jbt_info {
+       uint16_t reg_cache[0xEE];
+ };
++struct jbt_info *jbt_global;
++
+ #define JBT_COMMAND   0x000
+ #define JBT_DATA      0x100
+@@ -156,6 +149,8 @@ static int jbt_reg_write_nodata(struct jbt_info *jbt, uint8_t reg)
+       else
+               dev_err(&jbt->spi->dev, "Write failed: %d\n", ret);
++      mdelay(1);
++
+       return ret;
+ }
+@@ -173,6 +168,8 @@ static int jbt_reg_write(struct jbt_info *jbt, uint8_t reg, uint8_t data)
+       else
+               dev_err(&jbt->spi->dev, "Write failed: %d\n", ret);
++      mdelay(1);
++
+       return ret;
+ }
+@@ -191,6 +188,8 @@ static int jbt_reg_write16(struct jbt_info *jbt, uint8_t reg, uint16_t data)
+       else
+               dev_err(&jbt->spi->dev, "Write failed: %d\n", ret);
++      mdelay(1);
++
+       return ret;
+ }
+@@ -218,7 +217,7 @@ static int jbt_init_regs(struct jbt_info *jbt)
+        * to avoid red / blue flicker
+        */
+       ret |= jbt_reg_write(jbt, JBT_REG_ASW_SLEW, 0x00 | (1 << 5));
+-      ret |= jbt_reg_write(jbt, JBT_REG_DUMMY_DISPLAY, 0x00);
++      ret |= jbt_reg_write(jbt, JBT_REG_DUMMY_DISPLAY, 0xff);
+       ret |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_A, 0x11);
+       ret |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_B, 0x11);
+@@ -260,14 +259,18 @@ static int jbt_init_regs(struct jbt_info *jbt)
+       return ret ? -EIO : 0;
+ }
+-static int jbt_standby_to_sleep(struct jbt_info *jbt)
++
++static int jbt_off_to_normal(struct jbt_info *jbt)
+ {
+       int ret;
++
+       struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data;
+       gpio_set_value_cansleep(pdata->gpio_reset, 1);
+       ret = regulator_bulk_enable(ARRAY_SIZE(jbt->supplies), jbt->supplies);
++      mdelay(120);
++
+       /* three times command zero */
+       ret |= jbt_reg_write_nodata(jbt, 0x00);
+       mdelay(1);
+@@ -279,18 +282,11 @@ static int jbt_standby_to_sleep(struct jbt_info *jbt)
+       /* deep standby out */
+       ret |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x11);
+       mdelay(1);
+-      ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x28);
++      ret |= jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x28);
+       /* (re)initialize register set */
+       ret |= jbt_init_regs(jbt);
+-      return ret ? -EIO : 0;
+-}
+-
+-static int jbt_sleep_to_normal(struct jbt_info *jbt)
+-{
+-      int ret;
+-
+       /* Make sure we are 120 ms after SLEEP_OUT */
+       if (time_before(jiffies, jbt->next_sleep))
+               mdelay(jiffies_to_msecs(jbt->next_sleep - jiffies));
+@@ -320,6 +316,7 @@ static int jbt_sleep_to_normal(struct jbt_info *jbt)
+       /* Sleep mode off */
+       ret |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT);
++
+       jbt->next_sleep = jiffies + msecs_to_jiffies(120);
+       /* Allow the booster and display controller to restart stably */
+@@ -328,45 +325,68 @@ static int jbt_sleep_to_normal(struct jbt_info *jbt)
+       return ret ? -EIO : 0;
+ }
+-static int jbt_normal_to_sleep(struct jbt_info *jbt)
++static int jbt_normal_to_off(struct jbt_info *jbt)
+ {
+       int ret;
++      struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data;
+-      /* Make sure we are 120 ms after SLEEP_OUT */
+-      while (time_before(jiffies, jbt->next_sleep))
+-              cpu_relax();
++      /* Pull the plug! */
++      ret = regulator_bulk_disable(ARRAY_SIZE(jbt->supplies),
++                                   jbt->supplies);
++      if (!ret)
++              gpio_set_value_cansleep(pdata->gpio_reset, 0);
++
++      return ret ? -EIO : 0;
++}
++
++
++static int jbt_normal_to_standby(struct jbt_info *jbt)
++{
++      int ret;
++
++      if ( jbt->power_mode != JBT_POWER_MODE_NORMAL ) return 0;
++
++      /* Make sure we are 120 ms after SLEEP_{IN,OUT} */
++      while (time_before(jiffies, jbt->next_sleep)) cpu_relax();
++
++      /* Sleep mode on */
+       ret = jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF);
+       ret |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0x8000 | 1 << 3);
++
+       ret |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_IN);
+-      jbt->next_sleep = jiffies + msecs_to_jiffies(120);
++      jbt->next_sleep = jiffies + msecs_to_jiffies(150);
+-      /* Allow the internal circuits to stop automatically */
+-      mdelay(5);
++      jbt->power_mode = JBT_POWER_MODE_STANDBY;
+       return ret ? -EIO : 0;
+ }
+-static int jbt_sleep_to_standby(struct jbt_info *jbt)
++
++static int jbt_standby_to_normal(struct jbt_info *jbt)
+ {
+       int ret;
+-      struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data;
+-      ret = jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x00);
++      if ( jbt->power_mode != JBT_POWER_MODE_STANDBY ) return 0;
+-      if (!ret)
+-              ret = regulator_bulk_disable(ARRAY_SIZE(jbt->supplies), jbt->supplies);
++      /* Make sure we are 120 ms after SLEEP_{IN,OUT} */
++      while (time_before(jiffies, jbt->next_sleep)) cpu_relax();
+-      if (!ret)
+-              gpio_set_value_cansleep(pdata->gpio_reset, 0);
++      ret = jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT);
++      jbt->next_sleep = jiffies + msecs_to_jiffies(150);
+-      return ret;
++      ret |= jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_ON);
++      ret |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0xdff9);
++
++      jbt->power_mode = JBT_POWER_MODE_NORMAL;
++
++      return ret ? -EIO : 0;
+ }
++
+ static int jbt6k74_enter_power_mode(struct jbt_info *jbt,
+                                       enum jbt_power_mode new_mode)
+ {
+-      struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data;
+       int ret = -EINVAL;
+       dev_dbg(&jbt->spi->dev, "entering (old_state=%s, new_state=%s)\n",
+@@ -375,49 +395,17 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt,
+       mutex_lock(&jbt->lock);
+-      if (jbt->suspended) {
+-              switch (new_mode) {
+-              case JBT_POWER_MODE_DEEP_STANDBY:
+-              case JBT_POWER_MODE_SLEEP:
+-              case JBT_POWER_MODE_NORMAL:
+-                      ret = 0;
+-                      jbt->suspend_mode = new_mode;
+-                      break;
+-              default:
+-                      break;
+-              }
+-      } else if (new_mode == JBT_POWER_MODE_NORMAL &&
+-                      pdata->enable_pixel_clock) {
+-              pdata->enable_pixel_clock(&jbt->spi->dev, 1);
+-      }
+-
+       switch (jbt->power_mode) {
+-      case JBT_POWER_MODE_DEEP_STANDBY:
++      case JBT_POWER_MODE_OFF:
+               switch (new_mode) {
+-              case JBT_POWER_MODE_DEEP_STANDBY:
++              case JBT_POWER_MODE_OFF:
+                       ret = 0;
+                       break;
+-              case JBT_POWER_MODE_SLEEP:
+-                      ret = jbt_standby_to_sleep(jbt);
+-                      break;
+               case JBT_POWER_MODE_NORMAL:
+-                      /* first transition into sleep */
+-                      ret = jbt_standby_to_sleep(jbt);
+-                      /* then transition into normal */
+-                      ret |= jbt_sleep_to_normal(jbt);
++                      ret = jbt_off_to_normal(jbt);
+                       break;
+-              }
+-              break;
+-      case JBT_POWER_MODE_SLEEP:
+-              switch (new_mode) {
+-              case JBT_POWER_MODE_SLEEP:
+-                      ret = 0;
+-                      break;
+-              case JBT_POWER_MODE_DEEP_STANDBY:
+-                      ret = jbt_sleep_to_standby(jbt);
+-                      break;
+-              case JBT_POWER_MODE_NORMAL:
+-                      ret = jbt_sleep_to_normal(jbt);
++              case JBT_POWER_MODE_STANDBY:
++                      ret = -EINVAL;
+                       break;
+               }
+               break;
+@@ -426,25 +414,23 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt,
+               case JBT_POWER_MODE_NORMAL:
+                       ret = 0;
+                       break;
+-              case JBT_POWER_MODE_DEEP_STANDBY:
+-                      /* first transition into sleep */
+-                      ret = jbt_normal_to_sleep(jbt);
+-                      /* then transition into deep standby */
+-                      ret |= jbt_sleep_to_standby(jbt);
++              case JBT_POWER_MODE_OFF:
++                      ret = jbt_normal_to_off(jbt);
+                       break;
+-              case JBT_POWER_MODE_SLEEP:
+-                      ret = jbt_normal_to_sleep(jbt);
++              case JBT_POWER_MODE_STANDBY:
++                      ret = -EINVAL;
+                       break;
+               }
++              break;
++      case JBT_POWER_MODE_STANDBY:
++              ret = -EINVAL;
++              break;
+       }
+       if (ret == 0) {
+               jbt->power_mode = new_mode;
+-              if (new_mode != JBT_POWER_MODE_NORMAL &&
+-                      pdata->enable_pixel_clock)
+-                      pdata->enable_pixel_clock(&jbt->spi->dev, 0);
+       } else {
+-              dev_err(&jbt->spi->dev, "Failed enter state '%s': %d\n",
++              dev_err(&jbt->spi->dev, "Failed to enter state '%s': %d\n",
+                               jbt_power_mode_names[new_mode], ret);
+       }
+@@ -456,28 +442,46 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt,
+ static int jbt6k74_set_resolution(struct jbt_info *jbt,
+                                       enum jbt_resolution new_resolution)
+ {
++      int old_resolution;
+       int ret = 0;
+-      enum jbt_resolution old_resolution;
++
++      if ( !jbt ) return -1;
+       mutex_lock(&jbt->lock);
+-      if (jbt->resolution == new_resolution)
+-              goto out_unlock;
++      if ( jbt->resolution == new_resolution ) goto out_unlock;
++      if ( jbt->power_mode == JBT_POWER_MODE_OFF ) goto out_unlock;
+       old_resolution = jbt->resolution;
+       jbt->resolution = new_resolution;
+-      if (jbt->power_mode == JBT_POWER_MODE_NORMAL) {
++      if ( jbt->power_mode == JBT_POWER_MODE_NORMAL ) {
++
++              ret = jbt_normal_to_standby(jbt);
++
++              mdelay(25);
+-              /* first transition into sleep */
+-              ret = jbt_normal_to_sleep(jbt);
+-              ret |= jbt_sleep_to_normal(jbt);
++              if (jbt->resolution == JBT_RESOLUTION_VGA) {
++                      /* Quad mode off */
++                      ret |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x00);
++                      ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x80);
++              } else {
++                      /* Quad mode on */
++                      ret |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x22);
++                      ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x81);
++              }
++
++              mdelay(25);
++
++              ret |= jbt_standby_to_normal(jbt);
+               if (ret) {
+                       jbt->resolution = old_resolution;
+-                      dev_err(&jbt->spi->dev, "Failed to set resolution '%s')\n",
++                      dev_err(&jbt->spi->dev,
++                              "Failed to set resolution '%s')\n",
+                               jbt_resolution_names[new_resolution]);
+               }
++
+       }
+ out_unlock:
+@@ -589,7 +593,7 @@ static ssize_t reset_write(struct device *dev, struct device_attribute *attr,
+       mutex_unlock(&jbt->lock);
+-      jbt->power_mode = JBT_POWER_MODE_DEEP_STANDBY;
++      jbt->power_mode = JBT_POWER_MODE_OFF;
+       jbt6k74_enter_power_mode(jbt, old_power_mode);
+       return count;
+@@ -616,27 +620,6 @@ static struct attribute_group jbt_attr_group = {
+       .attrs  = jbt_sysfs_entries,
+ };
+-/* FIXME: This in an ugly hack to delay display blanking.
+-  When the jbt is in sleep mode it displays an all white screen and thus one
+-  will a see a short flash.
+-  By delaying the blanking we will give the backlight a chance to turn off and
+-  thus avoid getting the flash */
+-static void jbt_blank_worker(struct work_struct *work)
+-{
+-      struct jbt_info *jbt  = container_of(work, struct jbt_info,
+-                                              blank_work.work);
+-
+-      switch (jbt->blank_mode) {
+-      case FB_BLANK_NORMAL:
+-              jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_SLEEP);
+-              break;
+-      case FB_BLANK_POWERDOWN:
+-              jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_DEEP_STANDBY);
+-              break;
+-      default:
+-              break;
+-      }
+-}
+ static int jbt6k74_set_mode(struct lcd_device *ld, struct fb_videomode *m)
+ {
+@@ -649,7 +632,7 @@ static int jbt6k74_set_mode(struct lcd_device *ld, struct fb_videomode *m)
+               ret = jbt6k74_set_resolution(jbt, JBT_RESOLUTION_VGA);
+       } else {
+               dev_err(&jbt->spi->dev, "Unknown resolution.\n");
+-              jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_SLEEP);
++              jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF);
+       }
+       return ret;
+@@ -671,11 +654,11 @@ static int jbt6k74_set_power(struct lcd_device *ld, int power)
+               break;
+       case FB_BLANK_NORMAL:
+               dev_dbg(&jbt->spi->dev, "blank\n");
+-              ret = schedule_delayed_work(&jbt->blank_work, HZ);
++              ret = jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF);
+               break;
+       case FB_BLANK_POWERDOWN:
+               dev_dbg(&jbt->spi->dev, "powerdown\n");
+-              ret = schedule_delayed_work(&jbt->blank_work, HZ);
++              ret = jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF);
+               break;
+       default:
+               break;
+@@ -691,10 +674,10 @@ static int jbt6k74_get_power(struct lcd_device *ld)
+       switch (jbt->power_mode) {
+       case JBT_POWER_MODE_NORMAL:
+               return FB_BLANK_UNBLANK;
+-      case JBT_POWER_MODE_SLEEP:
++      case JBT_POWER_MODE_OFF:
+               return FB_BLANK_NORMAL;
+       default:
+-              return JBT_POWER_MODE_DEEP_STANDBY;
++              return JBT_POWER_MODE_OFF;
+       }
+ }
+@@ -728,6 +711,8 @@ static int __devinit jbt_probe(struct spi_device *spi)
+       if (!jbt)
+               return -ENOMEM;
++      jbt_global = jbt;
++
+       jbt->spi = spi;
+       jbt->lcd_dev = lcd_device_register("jbt6k74-lcd", &spi->dev, jbt,
+@@ -738,11 +723,9 @@ static int __devinit jbt_probe(struct spi_device *spi)
+               goto err_free_drvdata;
+       }
+-      INIT_DELAYED_WORK(&jbt->blank_work, jbt_blank_worker);
+-
+       jbt->resolution = JBT_RESOLUTION_VGA;
+-      jbt->power_mode = JBT_POWER_MODE_DEEP_STANDBY;
+       jbt->next_sleep = jiffies + msecs_to_jiffies(120);
++      jbt->power_mode = JBT_POWER_MODE_OFF;
+       mutex_init(&jbt->lock);
+       dev_set_drvdata(&spi->dev, jbt);
+@@ -831,6 +814,24 @@ static int __devexit jbt_remove(struct spi_device *spi)
+       return 0;
+ }
++/* Begin horrible layering violations in the interest of making stuff work */
++
++int jbt6k74_finish_resolutionchange(enum jbt_resolution new_resolution)
++{
++      if ( !jbt_global ) return 0;
++      return jbt6k74_set_resolution(jbt_global, new_resolution);
++}
++EXPORT_SYMBOL_GPL(jbt6k74_finish_resolutionchange);
++
++void jbt6k74_setpower(enum jbt_power_mode new_power)
++{
++      if ( !jbt_global ) return;
++      jbt6k74_enter_power_mode(jbt_global, new_power);
++}
++EXPORT_SYMBOL_GPL(jbt6k74_setpower);
++
++/* End horrible layering violations */
++
+ #ifdef CONFIG_PM
+ static int jbt_suspend(struct spi_device *spi, pm_message_t state)
+ {
+@@ -838,8 +839,7 @@ static int jbt_suspend(struct spi_device *spi, pm_message_t state)
+       jbt->suspend_mode = jbt->power_mode;
+-      jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_DEEP_STANDBY);
+-      jbt->suspended = 1;
++      jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF);
+       dev_info(&spi->dev, "suspended\n");
+diff --git a/include/linux/jbt6k74.h b/include/linux/jbt6k74.h
+index 75488c4..2010bdc 100644
+--- a/include/linux/jbt6k74.h
++++ b/include/linux/jbt6k74.h
+@@ -3,6 +3,22 @@
+ #include <linux/spi/spi.h>
++enum jbt_resolution {
++      JBT_RESOLUTION_VGA,
++      JBT_RESOLUTION_QVGA,
++};
++
++enum jbt_power_mode {
++      JBT_POWER_MODE_OFF,
++      JBT_POWER_MODE_STANDBY,
++      JBT_POWER_MODE_NORMAL,
++};
++
++extern void jbt6k74_setpower(enum jbt_power_mode new_power);
++extern int jbt6k74_prepare_resolutionchange(enum jbt_resolution new_resolution);
++extern int jbt6k74_finish_resolutionchange(enum jbt_resolution new_resolution);
++
++
+ /*
+  *  struct jbt6k74_platform_data - Platform data for jbt6k74 driver
+  *  @probe_completed: Callback to be called when the driver has been
+@@ -13,7 +29,6 @@
+  */
+ struct jbt6k74_platform_data {
+       void (*probe_completed)(struct device *dev);
+-      void (*enable_pixel_clock)(struct device *dev, int enable);
+       int gpio_reset;
+ };
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0004-save_regs.patch.patch b/recipes/linux/linux-openmoko-2.6.34/0004-save_regs.patch.patch
new file mode 100644 (file)
index 0000000..185d9a1
--- /dev/null
@@ -0,0 +1,140 @@
+From b166d6d4fb0f5ed66b785e0c662db5fbd2f86e8c Mon Sep 17 00:00:00 2001
+From: Radek Polak <psonek2@seznam.cz>
+Date: Fri, 9 Apr 2010 09:22:23 +0200
+Subject: [PATCH 4/4] save_regs.patch
+
+With this patch wifi can survive suspend.
+---
+ drivers/mmc/core/core.c   |    3 +-
+ drivers/mmc/host/s3cmci.c |   46 +++++++++++++++++++++++++++++++++++++++++++-
+ drivers/mmc/host/s3cmci.h |    8 +++++++
+ include/linux/mmc/core.h  |    2 +
+ 4 files changed, 56 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
+index 3168ebd..8504458 100644
+--- a/drivers/mmc/core/core.c
++++ b/drivers/mmc/core/core.c
+@@ -75,10 +75,11 @@ static int mmc_schedule_delayed_work(struct delayed_work *work,
+ /*
+  * Internal function. Flush all scheduled work from the MMC work queue.
+  */
+-static void mmc_flush_scheduled_work(void)
++void mmc_flush_scheduled_work(void)
+ {
+       flush_workqueue(workqueue);
+ }
++EXPORT_SYMBOL_GPL(mmc_flush_scheduled_work);
+ /**
+  *    mmc_request_done - finish processing an MMC request
+diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
+index 2fdf768..5f3effc 100644
+--- a/drivers/mmc/host/s3cmci.c
++++ b/drivers/mmc/host/s3cmci.c
+@@ -1878,19 +1878,61 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
+ #ifdef CONFIG_PM
++static int save_regs(struct mmc_host *mmc)
++{
++      struct s3cmci_host *host = mmc_priv(mmc);
++      unsigned long flags;
++      unsigned from;
++      u32 *to = host->saved;
++
++      mmc_flush_scheduled_work();
++
++      local_irq_save(flags);
++      for (from = S3C2410_SDICON; from != S3C2410_SDIIMSK+4; from += 4)
++              if (from != host->sdidata)
++                      *to++ = readl(host->base + from);
++      BUG_ON(to-host->saved != ARRAY_SIZE(host->saved));
++      local_irq_restore(flags);
++
++      return 0;
++}
++
++static int restore_regs(struct mmc_host *mmc)
++{
++      struct s3cmci_host *host = mmc_priv(mmc);
++      unsigned long flags;
++      unsigned to;
++      u32 *from = host->saved;
++
++      /*
++       * Before we begin with the necromancy, make sure we don't
++       * inadvertently start something we'll regret microseconds later.
++       */
++      from[S3C2410_SDICMDCON - S3C2410_SDICON] = 0;
++
++      local_irq_save(flags);
++      for (to = S3C2410_SDICON; to != S3C2410_SDIIMSK+4; to += 4)
++              if (to != host->sdidata)
++                      writel(*from++, host->base + to);
++      BUG_ON(from-host->saved != ARRAY_SIZE(host->saved));
++      local_irq_restore(flags);
++
++      return 0;
++}
++
+ static int s3cmci_suspend(struct device *dev)
+ {
+       struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
+       struct pm_message event = { PM_EVENT_SUSPEND };
+-      return mmc_suspend_host(mmc, event);
++      return save_regs(mmc);
+ }
+ static int s3cmci_resume(struct device *dev)
+ {
+       struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
+-      return mmc_resume_host(mmc);
++      return restore_regs(mmc);
+ }
+ static const struct dev_pm_ops s3cmci_pm = {
+diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
+index c76b53d..551e715 100644
+--- a/drivers/mmc/host/s3cmci.h
++++ b/drivers/mmc/host/s3cmci.h
+@@ -8,6 +8,8 @@
+  * published by the Free Software Foundation.
+  */
++#include <mach/regs-sdi.h>
++
+ enum s3cmci_waitfor {
+       COMPLETION_NONE,
+       COMPLETION_FINALIZE,
+@@ -27,6 +29,12 @@ struct s3cmci_host {
+       int                     irq;
+       int                     irq_cd;
+       int                     dma;
++       /*
++        * Here's where we save the registers during suspend. Note that we skip
++        * SDIDATA, which is at different positions on 2410 and 2440, so
++        * there's no "+1" in the array size.
++        */
++      u32                     saved[(S3C2410_SDIIMSK-S3C2410_SDICON)/4];
+       unsigned long           clk_rate;
+       unsigned long           clk_div;
+diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
+index e4898e9..b49d674 100644
+--- a/include/linux/mmc/core.h
++++ b/include/linux/mmc/core.h
+@@ -129,6 +129,8 @@ struct mmc_request {
+ struct mmc_host;
+ struct mmc_card;
++extern void mmc_flush_scheduled_work(void);
++
+ extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
+ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
+ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0005-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch b/recipes/linux/linux-openmoko-2.6.34/0005-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch
new file mode 100644 (file)
index 0000000..cf73609
--- /dev/null
@@ -0,0 +1,38 @@
+From 9cd5001fc2f4c817d5614b1ee5445d69af05d483 Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Thu, 4 Feb 2010 00:07:25 +0100
+Subject: [PATCH 05/13] Fix crash when reading Glamo registers via sysfs
+
+glamo-core didn't ioremap() some areas, so don't try to read them.
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/mfd/glamo/glamo-core.c |    8 ++++----
+ 1 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c
+index f894c91..a54106b 100644
+--- a/drivers/mfd/glamo/glamo-core.c
++++ b/drivers/mfd/glamo/glamo-core.c
+@@ -91,14 +91,14 @@ static const struct reg_range reg_range[] = {
+ /*    { 0x0500, 0x300,        "ISP",          0 }, */
+ /*    { 0x0800, 0x400,        "JPEG",         0 }, */
+ /*    { 0x0c00, 0xcc,         "MPEG",         0 }, */
+-      { 0x1100, 0xb2,         "LCD 1",        1 },
+-      { 0x1200, 0x64,         "LCD 2",        1 },
+-      { 0x1400, 0x42,         "MMC",          1 },
++      { 0x1100, 0xb2,         "LCD 1",        0 },
++      { 0x1200, 0x64,         "LCD 2",        0 },
++      { 0x1400, 0x42,         "MMC",          0 },
+ /*    { 0x1500, 0x080,        "MPU 0",        0 },
+       { 0x1580, 0x080,        "MPU 1",        0 },
+       { 0x1600, 0x080,        "Cmd Queue",    0 },
+       { 0x1680, 0x080,        "RISC CPU",     0 },*/
+-      { 0x1700, 0x400,        "2D Unit",      1 },
++      { 0x1700, 0x400,        "2D Unit",      0 },
+ /*    { 0x1b00, 0x900,        "3D Unit",      0 }, */
+ };
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0006-Fix-dynamic-command-queue-allocation.patch b/recipes/linux/linux-openmoko-2.6.34/0006-Fix-dynamic-command-queue-allocation.patch
new file mode 100644 (file)
index 0000000..ea57072
--- /dev/null
@@ -0,0 +1,131 @@
+From 752dfe99a5e2b361f740f2f986de93f5682b1a8d Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Thu, 6 May 2010 05:40:15 -0700
+Subject: [PATCH 06/13] Fix dynamic command queue allocation
+
+(Not tested...)
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/mfd/glamo/glamo-cmdq.c    |   10 +++++++---
+ drivers/mfd/glamo/glamo-cmdq.h    |    2 +-
+ drivers/mfd/glamo/glamo-drm-drv.c |   17 +++++++----------
+ 3 files changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/mfd/glamo/glamo-cmdq.c b/drivers/mfd/glamo/glamo-cmdq.c
+index 442963c..28c5268 100644
+--- a/drivers/mfd/glamo/glamo-cmdq.c
++++ b/drivers/mfd/glamo/glamo-cmdq.c
+@@ -57,6 +57,7 @@
+ #include "glamo-core.h"
+ #include "glamo-drm-private.h"
+ #include "glamo-regs.h"
++#include "glamo-buffer.h"
+ #define GLAMO_CMDQ_SIZE (128 * 1024)    /* 128k ring buffer */
+@@ -495,10 +496,11 @@ int glamo_cmdq_setup(struct glamodrm_handle *gdrm)
+ }
+-int glamo_cmdq_init(struct glamodrm_handle *gdrm)
++int glamo_cmdq_init(struct drm_device *dev)
+ {
+       struct drm_gem_object *obj;
+       struct drm_glamo_gem_object *gobj;
++      struct glamodrm_handle *gdrm = dev->dev_private;
+       int ret = 0;
+       obj = glamo_gem_object_alloc(dev, GLAMO_CMDQ_SIZE, 4);
+@@ -507,9 +509,10 @@ int glamo_cmdq_init(struct glamodrm_handle *gdrm)
+               ret = -ENOMEM;
+               goto out;
+       }
+-      gobj = fbo->driver_private;
++      gobj = obj->driver_private;
+       gdrm->cmdq_offs = GLAMO_OFFSET_FB + gobj->block->start;
+-      gdrm->cmdq_base = ioremap(gdrm->vram->start + offs, GLAMO_CMDQ_SIZE);
++      gdrm->cmdq_base = ioremap(gdrm->vram->start + gdrm->cmdq_offs,
++                                GLAMO_CMDQ_SIZE);
+       /* Set up registers */
+       glamo_cmdq_setup(gdrm);
+@@ -521,6 +524,7 @@ out:
+ int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm)
+ {
++      iounmap(gdrm->cmdq_base);
+       return 0;
+ }
+diff --git a/drivers/mfd/glamo/glamo-cmdq.h b/drivers/mfd/glamo/glamo-cmdq.h
+index 510d195..6d7f184 100644
+--- a/drivers/mfd/glamo/glamo-cmdq.h
++++ b/drivers/mfd/glamo/glamo-cmdq.h
+@@ -38,7 +38,7 @@ extern int glamo_ioctl_cmdburst(struct drm_device *dev, void *data,
+ extern void glamo_cmdq_blank(struct glamodrm_handle *gdrm,
+                              struct drm_gem_object *obj);
+-extern int glamo_cmdq_init(struct glamodrm_handle *gdrm);
++extern int glamo_cmdq_init(struct drm_device *de);
+ extern int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm);
+ extern void glamo_cmdq_suspend(struct glamodrm_handle *gdrm);
+ extern void glamo_cmdq_resume(struct glamodrm_handle *gdrm);
+diff --git a/drivers/mfd/glamo/glamo-drm-drv.c b/drivers/mfd/glamo/glamo-drm-drv.c
+index 774eaff..ee648c1 100644
+--- a/drivers/mfd/glamo/glamo-drm-drv.c
++++ b/drivers/mfd/glamo/glamo-drm-drv.c
+@@ -128,7 +128,7 @@ static int glamodrm_load(struct drm_device *dev, unsigned long flags)
+       gdrm = dev->dev_private;
+       glamo_buffer_init(gdrm);
+-      glamo_cmdq_init(gdrm);
++      glamo_cmdq_init(dev);
+       glamo_fence_init(gdrm);
+       glamo_display_init(dev);
+@@ -237,14 +237,14 @@ static int glamodrm_probe(struct platform_device *pdev)
+       if ( !gdrm->vram ) {
+               dev_err(&pdev->dev, "Unable to find VRAM.\n");
+               rc = -ENOENT;
+-              goto out_unmap_cmdq;
++              goto out_unmap_regs;
+       }
+       gdrm->vram = request_mem_region(gdrm->vram->start,
+                                       resource_size(gdrm->vram), pdev->name);
+       if ( !gdrm->vram ) {
+               dev_err(&pdev->dev, "failed to request VRAM region\n");
+               rc = -ENOENT;
+-              goto out_unmap_cmdq;
++              goto out_unmap_regs;
+       }
+       /* Find the LCD controller */
+@@ -316,10 +316,6 @@ out_release_lcd:
+                          resource_size(gdrm->lcd_regs));
+ out_release_vram:
+       release_mem_region(gdrm->vram->start, resource_size(gdrm->vram));
+-out_unmap_cmdq:
+-      iounmap(gdrm->cmdq_base);
+-out_release_cmdq:
+-      release_mem_region(gdrm->cmdq->start, resource_size(gdrm->cmdq));
+ out_unmap_regs:
+       iounmap(gdrm->reg_base);
+ out_release_regs:
+@@ -349,9 +345,10 @@ static int glamodrm_remove(struct platform_device *pdev)
+       /* Release VRAM */
+       release_mem_region(gdrm->vram->start, resource_size(gdrm->vram));
+-      /* Release command queue */
+-      iounmap(gdrm->cmdq_base);
+-      release_mem_region(gdrm->cmdq->start, resource_size(gdrm->cmdq));
++      /* Release LCD registers */
++      iounmap(gdrm->lcd_base);
++      release_mem_region(gdrm->lcd_regs->start,
++                         resource_size(gdrm->lcd_regs));
+       /* Release 2D engine  */
+       iounmap(gdrm->twod_base);
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0007-Debug-statements-for-testing.patch b/recipes/linux/linux-openmoko-2.6.34/0007-Debug-statements-for-testing.patch
new file mode 100644 (file)
index 0000000..1321d29
--- /dev/null
@@ -0,0 +1,78 @@
+From 6d0e40938fa71d6a3a1f3c9892251053bda3be6d Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Thu, 6 May 2010 05:40:33 -0700
+Subject: [PATCH 07/13] Debug statements for testing
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/mfd/glamo/glamo-display.c |    7 +++++++
+ drivers/video/backlight/jbt6k74.c |    4 +++-
+ 2 files changed, 10 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c
+index 75ad734..57292ff 100644
+--- a/drivers/mfd/glamo/glamo-display.c
++++ b/drivers/mfd/glamo/glamo-display.c
+@@ -257,6 +257,8 @@ static int glamo_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+       u32 addr;
+       u16 addr_low, addr_high;
++      printk(KERN_CRIT "Setting base!\n");
++
+       if (!crtc->fb) {
+               DRM_DEBUG("No FB bound\n");
+               return -EINVAL;
+@@ -311,6 +313,8 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc,
+               msleep(500);
+       }
++      printk(KERN_CRIT "Setting mode!\n");
++
+       /* Rotate? */
+       if ( (mode->hdisplay == 640) && (mode->vdisplay == 480) ) {
+               rot = GLAMO_LCD_ROT_MODE_90;
+@@ -935,15 +939,18 @@ void glamo_lcd_power(struct glamodrm_handle *gdrm, int mode)
+       struct glamo_crtc *gcrtc = to_glamo_crtc(crtc);
+       if ( mode ) {
++              printk(KERN_CRIT "Power on sequence\n");
+               glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_LCD);
+               gcrtc->pixel_clock_on = 1;
+               jbt6k74_setpower(JBT_POWER_MODE_NORMAL);
+               if ( gcrtc->current_mode_set ) {
++                      printk(KERN_CRIT "Setting previous mode\n");
+                       glamo_crtc_mode_set(crtc, &gcrtc->current_mode,
+                                           &gcrtc->current_mode, 0, 0,
+                                           gcrtc->current_fb);
+               }
+       } else {
++              printk(KERN_CRIT "Power off sequence\n");
+               jbt6k74_setpower(JBT_POWER_MODE_OFF);
+               glamo_engine_suspend(gdrm->glamo_core, GLAMO_ENGINE_LCD);
+               gcrtc->pixel_clock_on = 0;
+diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c
+index 91651e7..56a3e3a 100644
+--- a/drivers/video/backlight/jbt6k74.c
++++ b/drivers/video/backlight/jbt6k74.c
+@@ -389,7 +389,7 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt,
+ {
+       int ret = -EINVAL;
+-      dev_dbg(&jbt->spi->dev, "entering (old_state=%s, new_state=%s)\n",
++      printk(KERN_CRIT "entering (old_state=%s, new_state=%s)\n",
+                       jbt_power_mode_names[jbt->power_mode],
+                       jbt_power_mode_names[new_mode]);
+@@ -825,7 +825,9 @@ EXPORT_SYMBOL_GPL(jbt6k74_finish_resolutionchange);
+ void jbt6k74_setpower(enum jbt_power_mode new_power)
+ {
++      printk(KERN_CRIT "About to set power..\n");
+       if ( !jbt_global ) return;
++      printk(KERN_CRIT "Setting JBT power.. %i\n", new_power);
+       jbt6k74_enter_power_mode(jbt_global, new_power);
+ }
+ EXPORT_SYMBOL_GPL(jbt6k74_setpower);
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0008-Fix-claim-of-2D-register-resource.patch b/recipes/linux/linux-openmoko-2.6.34/0008-Fix-claim-of-2D-register-resource.patch
new file mode 100644 (file)
index 0000000..613f3a1
--- /dev/null
@@ -0,0 +1,27 @@
+From 92118a72fdf779a20693cb7d01d1a12fc8e12ace Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Thu, 6 May 2010 08:37:04 -0700
+Subject: [PATCH 08/13] Fix claim of 2D register resource
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/mfd/glamo/glamo-drm-drv.c |    3 ++-
+ 1 files changed, 2 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/mfd/glamo/glamo-drm-drv.c b/drivers/mfd/glamo/glamo-drm-drv.c
+index ee648c1..4f982ad 100644
+--- a/drivers/mfd/glamo/glamo-drm-drv.c
++++ b/drivers/mfd/glamo/glamo-drm-drv.c
+@@ -272,7 +272,8 @@ static int glamodrm_probe(struct platform_device *pdev)
+       }
+       /* Find the 2D engine */
+-      gdrm->twod_regs = platform_get_resource(pdev, IORESOURCE_MEM, 4);
++      gdrm->twod_regs = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++                                                     "glamo-2d-regs");
+       if ( !gdrm->twod_regs ) {
+               dev_err(&pdev->dev, "Unable to find 2D registers.\n");
+               rc = -ENOENT;
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/0009-Use-unlocked_ioctl-rather-than-ioctl.patch b/recipes/linux/linux-openmoko-2.6.34/0009-Use-unlocked_ioctl-rather-than-ioctl.patch
new file mode 100644 (file)
index 0000000..5937cb9
--- /dev/null
@@ -0,0 +1,28 @@
+From ea9d004f7b0d5c93610a2abdcbdf54af2a871c4b Mon Sep 17 00:00:00 2001
+From: Thomas White <taw@bitwiz.org.uk>
+Date: Mon, 24 May 2010 21:49:14 +0200
+Subject: [PATCH 09/13] Use unlocked_ioctl rather than ioctl
+
+The arguments changed between 2.6.32 and 2.6.34.
+
+Signed-off-by: Thomas White <taw@bitwiz.org.uk>
+---
+ drivers/mfd/glamo/glamo-drm-drv.c |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/mfd/glamo/glamo-drm-drv.c b/drivers/mfd/glamo/glamo-drm-drv.c
+index 4f982ad..75b5233 100644
+--- a/drivers/mfd/glamo/glamo-drm-drv.c
++++ b/drivers/mfd/glamo/glamo-drm-drv.c
+@@ -179,7 +179,7 @@ static struct drm_driver glamodrm_drm_driver = {
+               .owner = THIS_MODULE,
+               .open = drm_open,
+               .release = drm_release,
+-              .ioctl = drm_ioctl,
++              .unlocked_ioctl = drm_ioctl,
+               .mmap = drm_gem_mmap,
+               .poll = drm_poll,
+               .fasync = drm_fasync,
+-- 
+1.7.1
+
diff --git a/recipes/linux/linux-openmoko-2.6.34/gta02-defconfig-update-for-2.6.34.patch b/recipes/linux/linux-openmoko-2.6.34/gta02-defconfig-update-for-2.6.34.patch
deleted file mode 100644 (file)
index bc6257a..0000000
+++ /dev/null
@@ -1,880 +0,0 @@
-From cabf6246eec8f2d546b8d0ec2335bb1b9c83b84f Mon Sep 17 00:00:00 2001
-From: Martin Jansa <Martin.Jansa@gmail.com>
-Date: Tue, 18 May 2010 08:33:59 +0200
-Subject: [PATCH] gta02-defconfig update for 2.6.34
-
----
- arch/arm/configs/gta02_defconfig |  248 +++++++++++++++++++++++++-------------
- 1 files changed, 163 insertions(+), 85 deletions(-)
-
-diff --git a/arch/arm/configs/gta02_defconfig b/arch/arm/configs/gta02_defconfig
-index 73d26ba..f8142fd 100644
---- a/arch/arm/configs/gta02_defconfig
-+++ b/arch/arm/configs/gta02_defconfig
-@@ -1,12 +1,13 @@
- #
- # Automatically generated make config: don't edit
--# Linux kernel version: 2.6.32
--# Wed Apr  7 23:27:29 2010
-+# Linux kernel version: 2.6.34
-+# Tue May 18 09:20:24 2010
- #
- CONFIG_ARM=y
- CONFIG_HAVE_PWM=y
- CONFIG_SYS_SUPPORTS_APM_EMULATION=y
- CONFIG_GENERIC_GPIO=y
-+CONFIG_HAVE_PROC_CPU=y
- CONFIG_NO_IOPORT=y
- CONFIG_GENERIC_HARDIRQS=y
- CONFIG_STACKTRACE_SUPPORT=y
-@@ -19,6 +20,7 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y
- CONFIG_ARCH_HAS_CPUFREQ=y
- CONFIG_GENERIC_HWEIGHT=y
- CONFIG_GENERIC_CALIBRATE_DELAY=y
-+CONFIG_NEED_DMA_MAP_STATE=y
- CONFIG_FIQ=y
- CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
- CONFIG_VECTORS_BASE=0xffff0000
-@@ -31,8 +33,14 @@ CONFIG_CONSTRUCTORS=y
- CONFIG_EXPERIMENTAL=y
- CONFIG_BROKEN_ON_SMP=y
- CONFIG_INIT_ENV_ARG_LIMIT=32
--CONFIG_LOCALVERSION="v20"
-+CONFIG_LOCALVERSION=""
- # CONFIG_LOCALVERSION_AUTO is not set
-+CONFIG_HAVE_KERNEL_GZIP=y
-+CONFIG_HAVE_KERNEL_LZO=y
-+CONFIG_KERNEL_GZIP=y
-+# CONFIG_KERNEL_BZIP2 is not set
-+# CONFIG_KERNEL_LZMA is not set
-+# CONFIG_KERNEL_LZO is not set
- CONFIG_SWAP=y
- CONFIG_SYSVIPC=y
- CONFIG_SYSVIPC_SYSCTL=y
-@@ -46,13 +54,13 @@ CONFIG_SYSVIPC_SYSCTL=y
- #
- CONFIG_TREE_RCU=y
- # CONFIG_TREE_PREEMPT_RCU is not set
-+# CONFIG_TINY_RCU is not set
- # CONFIG_RCU_TRACE is not set
- CONFIG_RCU_FANOUT=32
- # CONFIG_RCU_FANOUT_EXACT is not set
- # CONFIG_TREE_RCU_TRACE is not set
- # CONFIG_IKCONFIG is not set
- CONFIG_LOG_BUF_SHIFT=18
--# CONFIG_GROUP_SCHED is not set
- # CONFIG_CGROUPS is not set
- # CONFIG_SYSFS_DEPRECATED_V2 is not set
- # CONFIG_RELAY is not set
-@@ -67,6 +75,7 @@ CONFIG_INITRAMFS_SOURCE=""
- CONFIG_RD_GZIP=y
- CONFIG_RD_BZIP2=y
- CONFIG_RD_LZMA=y
-+CONFIG_RD_LZO=y
- # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
- CONFIG_SYSCTL=y
- CONFIG_ANON_INODES=y
-@@ -87,10 +96,14 @@ CONFIG_TIMERFD=y
- CONFIG_EVENTFD=y
- CONFIG_SHMEM=y
- CONFIG_AIO=y
-+CONFIG_HAVE_PERF_EVENTS=y
-+CONFIG_PERF_USE_VMALLOC=y
- #
- # Kernel Performance Events And Counters
- #
-+# CONFIG_PERF_EVENTS is not set
-+# CONFIG_PERF_COUNTERS is not set
- CONFIG_VM_EVENT_COUNTERS=y
- CONFIG_COMPAT_BRK=y
- CONFIG_SLAB=y
-@@ -119,21 +132,48 @@ CONFIG_MODULE_FORCE_UNLOAD=y
- # CONFIG_MODULE_SRCVERSION_ALL is not set
- CONFIG_BLOCK=y
- # CONFIG_LBDAF is not set
--# CONFIG_BLK_DEV_BSG is not set
-+CONFIG_BLK_DEV_BSG=y
- # CONFIG_BLK_DEV_INTEGRITY is not set
- #
- # IO Schedulers
- #
- CONFIG_IOSCHED_NOOP=y
--CONFIG_IOSCHED_AS=m
- CONFIG_IOSCHED_DEADLINE=y
- CONFIG_IOSCHED_CFQ=m
--# CONFIG_DEFAULT_AS is not set
- CONFIG_DEFAULT_DEADLINE=y
- # CONFIG_DEFAULT_CFQ is not set
- # CONFIG_DEFAULT_NOOP is not set
- CONFIG_DEFAULT_IOSCHED="deadline"
-+# CONFIG_INLINE_SPIN_TRYLOCK is not set
-+# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
-+# CONFIG_INLINE_SPIN_LOCK is not set
-+# CONFIG_INLINE_SPIN_LOCK_BH is not set
-+# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
-+# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
-+CONFIG_INLINE_SPIN_UNLOCK=y
-+# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
-+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
-+# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
-+# CONFIG_INLINE_READ_TRYLOCK is not set
-+# CONFIG_INLINE_READ_LOCK is not set
-+# CONFIG_INLINE_READ_LOCK_BH is not set
-+# CONFIG_INLINE_READ_LOCK_IRQ is not set
-+# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
-+CONFIG_INLINE_READ_UNLOCK=y
-+# CONFIG_INLINE_READ_UNLOCK_BH is not set
-+CONFIG_INLINE_READ_UNLOCK_IRQ=y
-+# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
-+# CONFIG_INLINE_WRITE_TRYLOCK is not set
-+# CONFIG_INLINE_WRITE_LOCK is not set
-+# CONFIG_INLINE_WRITE_LOCK_BH is not set
-+# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
-+# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
-+CONFIG_INLINE_WRITE_UNLOCK=y
-+# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
-+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
-+# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
-+# CONFIG_MUTEX_SPIN_ON_OWNER is not set
- CONFIG_FREEZER=y
- #
-@@ -145,6 +185,7 @@ CONFIG_MMU=y
- # CONFIG_ARCH_REALVIEW is not set
- # CONFIG_ARCH_VERSATILE is not set
- # CONFIG_ARCH_AT91 is not set
-+# CONFIG_ARCH_BCMRING is not set
- # CONFIG_ARCH_CLPS711X is not set
- # CONFIG_ARCH_GEMINI is not set
- # CONFIG_ARCH_EBSA110 is not set
-@@ -154,7 +195,6 @@ CONFIG_MMU=y
- # CONFIG_ARCH_STMP3XXX is not set
- # CONFIG_ARCH_NETX is not set
- # CONFIG_ARCH_H720X is not set
--# CONFIG_ARCH_NOMADIK is not set
- # CONFIG_ARCH_IOP13XX is not set
- # CONFIG_ARCH_IOP32X is not set
- # CONFIG_ARCH_IOP33X is not set
-@@ -162,6 +202,7 @@ CONFIG_MMU=y
- # CONFIG_ARCH_IXP2000 is not set
- # CONFIG_ARCH_IXP4XX is not set
- # CONFIG_ARCH_L7200 is not set
-+# CONFIG_ARCH_DOVE is not set
- # CONFIG_ARCH_KIRKWOOD is not set
- # CONFIG_ARCH_LOKI is not set
- # CONFIG_ARCH_MV78XX0 is not set
-@@ -170,32 +211,27 @@ CONFIG_MMU=y
- # CONFIG_ARCH_KS8695 is not set
- # CONFIG_ARCH_NS9XXX is not set
- # CONFIG_ARCH_W90X900 is not set
-+# CONFIG_ARCH_NUC93X is not set
- # CONFIG_ARCH_PNX4008 is not set
- # CONFIG_ARCH_PXA is not set
- # CONFIG_ARCH_MSM is not set
-+# CONFIG_ARCH_SHMOBILE is not set
- # CONFIG_ARCH_RPC is not set
- # CONFIG_ARCH_SA1100 is not set
- CONFIG_ARCH_S3C2410=y
- # CONFIG_ARCH_S3C64XX is not set
-+# CONFIG_ARCH_S5P6440 is not set
-+# CONFIG_ARCH_S5P6442 is not set
- # CONFIG_ARCH_S5PC1XX is not set
-+# CONFIG_ARCH_S5PV210 is not set
- # CONFIG_ARCH_SHARK is not set
- # CONFIG_ARCH_LH7A40X is not set
- # CONFIG_ARCH_U300 is not set
-+# CONFIG_ARCH_U8500 is not set
-+# CONFIG_ARCH_NOMADIK is not set
- # CONFIG_ARCH_DAVINCI is not set
- # CONFIG_ARCH_OMAP is not set
--# CONFIG_ARCH_BCMRING is not set
--CONFIG_PLAT_S3C24XX=y
--CONFIG_S3C2410_CLOCK=y
--CONFIG_CPU_S3C244X=y
--CONFIG_S3C24XX_PWM=y
--CONFIG_S3C24XX_GPIO_EXTRA=64
--CONFIG_S3C24XX_GPIO_EXTRA64=y
--CONFIG_S3C2410_DMA=y
--# CONFIG_S3C2410_DMA_DEBUG is not set
--CONFIG_S3C24XX_ADC=y
--CONFIG_PLAT_S3C=y
--CONFIG_CPU_LLSERIAL_S3C2440_ONLY=y
--CONFIG_CPU_LLSERIAL_S3C2440=y
-+CONFIG_PLAT_SAMSUNG=y
- #
- # Boot options
-@@ -203,17 +239,27 @@ CONFIG_CPU_LLSERIAL_S3C2440=y
- # CONFIG_S3C_BOOT_WATCHDOG is not set
- # CONFIG_S3C_BOOT_ERROR_RESET is not set
- CONFIG_S3C_BOOT_UART_FORCE_FIFO=y
--
--#
--# Power management
--#
--# CONFIG_S3C2410_PM_DEBUG is not set
--# CONFIG_S3C2410_PM_CHECK is not set
- CONFIG_S3C_LOWLEVEL_UART_PORT=2
-+CONFIG_SAMSUNG_GPIO_EXTRA=0
- CONFIG_S3C_GPIO_SPACE=0
--CONFIG_S3C_DMA=y
-+CONFIG_S3C_ADC=y
- CONFIG_S3C_DEV_USB_HOST=y
- CONFIG_S3C_DEV_NAND=y
-+CONFIG_S3C_DMA=y
-+
-+#
-+# Power management
-+#
-+# CONFIG_SAMSUNG_PM_DEBUG is not set
-+# CONFIG_SAMSUNG_PM_CHECK is not set
-+CONFIG_PLAT_S3C24XX=y
-+CONFIG_CPU_LLSERIAL_S3C2440_ONLY=y
-+CONFIG_CPU_LLSERIAL_S3C2440=y
-+CONFIG_S3C2410_CLOCK=y
-+CONFIG_S3C24XX_PWM=y
-+CONFIG_S3C24XX_GPIO_EXTRA=0
-+CONFIG_S3C2410_DMA=y
-+# CONFIG_S3C2410_DMA_DEBUG is not set
- #
- # S3C2400 Machines
-@@ -242,23 +288,22 @@ CONFIG_S3C2410_GPIO=y
- # CONFIG_MACH_SMDK2413 is not set
- # CONFIG_MACH_SMDK2412 is not set
- # CONFIG_MACH_VSTMS is not set
-+CONFIG_CPU_S3C2442=y
-+CONFIG_CPU_S3C244X=y
- #
--# S3C2440 Machines
-+# S3C2440 and S3C2442 Machines
- #
- # CONFIG_MACH_ANUBIS is not set
-+CONFIG_MACH_NEO1973_GTA02=y
- # CONFIG_MACH_OSIRIS is not set
- # CONFIG_MACH_RX3715 is not set
- # CONFIG_ARCH_S3C2440 is not set
- # CONFIG_MACH_NEXCODER_2440 is not set
-+# CONFIG_SMDK2440_CPU2440 is not set
-+# CONFIG_SMDK2440_CPU2442 is not set
- # CONFIG_MACH_AT2440EVB is not set
- # CONFIG_MACH_MINI2440 is not set
--CONFIG_CPU_S3C2442=y
--
--#
--# S3C2442 Machines
--#
--CONFIG_MACH_NEO1973_GTA02=y
- #
- # S3C2443 Machines
-@@ -268,7 +313,6 @@ CONFIG_MACH_NEO1973_GTA02=y
- #
- # Processor Type
- #
--CONFIG_CPU_32=y
- CONFIG_CPU_ARM920T=y
- CONFIG_CPU_32v4T=y
- CONFIG_CPU_ABRT_EV4T=y
-@@ -319,12 +363,10 @@ CONFIG_FLATMEM_MANUAL=y
- CONFIG_FLATMEM=y
- CONFIG_FLAT_NODE_MEM_MAP=y
- CONFIG_PAGEFLAGS_EXTENDED=y
--CONFIG_SPLIT_PTLOCK_CPUS=4096
-+CONFIG_SPLIT_PTLOCK_CPUS=999999
- # CONFIG_PHYS_ADDR_T_64BIT is not set
- CONFIG_ZONE_DMA_FLAG=0
- CONFIG_VIRT_TO_BUS=y
--CONFIG_HAVE_MLOCK=y
--CONFIG_HAVE_MLOCKED_PAGE_BIT=y
- # CONFIG_KSM is not set
- CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
- CONFIG_ALIGNMENT_TRAP=y
-@@ -335,7 +377,7 @@ CONFIG_ALIGNMENT_TRAP=y
- #
- CONFIG_ZBOOT_ROM_TEXT=0x0
- CONFIG_ZBOOT_ROM_BSS=0x0
--CONFIG_CMDLINE="unused -- bootloader passes ATAG list"
-+CONFIG_CMDLINE="unused -- bootloader passes ATAG list quiet "
- # CONFIG_XIP_KERNEL is not set
- # CONFIG_KEXEC is not set
-@@ -373,6 +415,7 @@ CONFIG_SUSPEND=y
- CONFIG_SUSPEND_FREEZER=y
- # CONFIG_APM_EMULATION is not set
- # CONFIG_PM_RUNTIME is not set
-+CONFIG_PM_OPS=y
- CONFIG_ARCH_SUSPEND_POSSIBLE=y
- CONFIG_NET=y
-@@ -380,7 +423,6 @@ CONFIG_NET=y
- # Networking options
- #
- CONFIG_PACKET=y
--CONFIG_PACKET_MMAP=y
- CONFIG_UNIX=y
- CONFIG_XFRM=y
- CONFIG_XFRM_USER=m
-@@ -458,6 +500,7 @@ CONFIG_INET6_XFRM_MODE_TUNNEL=m
- CONFIG_INET6_XFRM_MODE_BEET=m
- # CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
- CONFIG_IPV6_SIT=m
-+# CONFIG_IPV6_SIT_6RD is not set
- CONFIG_IPV6_NDISC_NODETYPE=y
- CONFIG_IPV6_TUNNEL=m
- # CONFIG_IPV6_MULTIPLE_TABLES is not set
-@@ -496,6 +539,7 @@ CONFIG_NF_CT_NETLINK=m
- CONFIG_NETFILTER_XTABLES=m
- CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
- CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
-+# CONFIG_NETFILTER_XT_TARGET_CT is not set
- CONFIG_NETFILTER_XT_TARGET_DSCP=m
- CONFIG_NETFILTER_XT_TARGET_HL=m
- # CONFIG_NETFILTER_XT_TARGET_LED is not set
-@@ -631,6 +675,7 @@ CONFIG_BRIDGE_EBT_ULOG=m
- # CONFIG_ATM is not set
- CONFIG_STP=y
- CONFIG_BRIDGE=y
-+CONFIG_BRIDGE_IGMP_SNOOPING=y
- # CONFIG_NET_DSA is not set
- # CONFIG_VLAN_8021Q is not set
- # CONFIG_DECNET is not set
-@@ -712,13 +757,15 @@ CONFIG_BT_HCIBTUSB=y
- # CONFIG_BT_HCIBFUSB is not set
- # CONFIG_BT_HCIVHCI is not set
- # CONFIG_BT_MRVL is not set
-+# CONFIG_BT_ATH3K is not set
- # CONFIG_AF_RXRPC is not set
- CONFIG_FIB_RULES=y
- CONFIG_WIRELESS=y
--# CONFIG_CFG80211 is not set
--CONFIG_CFG80211_DEFAULT_PS_VALUE=0
--CONFIG_WIRELESS_OLD_REGULATORY=y
- CONFIG_WIRELESS_EXT=y
-+CONFIG_WEXT_CORE=y
-+CONFIG_WEXT_PROC=y
-+CONFIG_WEXT_PRIV=y
-+# CONFIG_CFG80211 is not set
- CONFIG_WIRELESS_EXT_SYSFS=y
- # CONFIG_LIB80211 is not set
-@@ -736,7 +783,7 @@ CONFIG_WIRELESS_EXT_SYSFS=y
- #
- # Generic Driver Options
- #
--CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-+CONFIG_UEVENT_HELPER_PATH=""
- CONFIG_DEVTMPFS=y
- CONFIG_DEVTMPFS_MOUNT=y
- CONFIG_STANDALONE=y
-@@ -850,6 +897,7 @@ CONFIG_BLK_DEV=y
- # CONFIG_BLK_DEV_COW_COMMON is not set
- CONFIG_BLK_DEV_LOOP=m
- # CONFIG_BLK_DEV_CRYPTOLOOP is not set
-+# CONFIG_BLK_DEV_DRBD is not set
- # CONFIG_BLK_DEV_NBD is not set
- CONFIG_BLK_DEV_UB=m
- CONFIG_BLK_DEV_RAM=y
-@@ -866,6 +914,7 @@ CONFIG_HAVE_IDE=y
- #
- # SCSI device support
- #
-+CONFIG_SCSI_MOD=m
- # CONFIG_RAID_ATTRS is not set
- CONFIG_SCSI=m
- CONFIG_SCSI_DMA=y
-@@ -895,6 +944,7 @@ CONFIG_SCSI_WAIT_SCAN=m
- # CONFIG_SCSI_SPI_ATTRS is not set
- # CONFIG_SCSI_FC_ATTRS is not set
- # CONFIG_SCSI_ISCSI_ATTRS is not set
-+# CONFIG_SCSI_SAS_ATTRS is not set
- # CONFIG_SCSI_SAS_LIBSAS is not set
- # CONFIG_SCSI_SRP_ATTRS is not set
- CONFIG_SCSI_LOWLEVEL=y
-@@ -918,8 +968,8 @@ CONFIG_MII=m
- # CONFIG_NETDEV_1000 is not set
- # CONFIG_NETDEV_10000 is not set
- CONFIG_WLAN=y
--# CONFIG_WLAN_PRE80211 is not set
--# CONFIG_WLAN_80211 is not set
-+# CONFIG_USB_ZD1201 is not set
-+# CONFIG_HOSTAP is not set
- #
- # Enable WiMAX (Networking options) to see the WiMAX drivers
-@@ -933,6 +983,7 @@ CONFIG_USB_KAWETH=m
- CONFIG_USB_PEGASUS=m
- CONFIG_USB_RTL8150=m
- # CONFIG_USB_USBNET is not set
-+# CONFIG_USB_IPHETH is not set
- # CONFIG_WAN is not set
- CONFIG_PPP=m
- CONFIG_PPP_MULTILINK=y
-@@ -958,6 +1009,7 @@ CONFIG_SLHC=m
- CONFIG_INPUT=y
- # CONFIG_INPUT_FF_MEMLESS is not set
- # CONFIG_INPUT_POLLDEV is not set
-+# CONFIG_INPUT_SPARSEKMAP is not set
- #
- # Userland interfaces
-@@ -991,20 +1043,15 @@ CONFIG_KEYBOARD_STOWAWAY=m
- # CONFIG_INPUT_JOYSTICK is not set
- # CONFIG_INPUT_TABLET is not set
- CONFIG_INPUT_TOUCHSCREEN=y
--CONFIG_TOUCHSCREEN_FILTER=y
--CONFIG_TOUCHSCREEN_FILTER_GROUP=y
--CONFIG_TOUCHSCREEN_FILTER_MEDIAN=y
--CONFIG_TOUCHSCREEN_FILTER_MEAN=y
--CONFIG_TOUCHSCREEN_FILTER_LINEAR=y
--CONFIG_TOUCHSCREEN_S3C2410=y
--# CONFIG_TOUCHSCREEN_S3C2410_DEBUG is not set
- # CONFIG_TOUCHSCREEN_ADS7846 is not set
- # CONFIG_TOUCHSCREEN_AD7877 is not set
- # CONFIG_TOUCHSCREEN_AD7879_I2C is not set
- # CONFIG_TOUCHSCREEN_AD7879_SPI is not set
- # CONFIG_TOUCHSCREEN_AD7879 is not set
-+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
- # CONFIG_TOUCHSCREEN_EETI is not set
- # CONFIG_TOUCHSCREEN_FUJITSU is not set
-+CONFIG_TOUCHSCREEN_S3C2410=y
- # CONFIG_TOUCHSCREEN_GUNZE is not set
- # CONFIG_TOUCHSCREEN_ELO is not set
- # CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
-@@ -1029,7 +1076,6 @@ CONFIG_INPUT_MISC=y
- CONFIG_INPUT_UINPUT=m
- CONFIG_INPUT_PCF50633_PMU=y
- # CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
--CONFIG_INPUT_LIS302DL=y
- #
- # Hardware I/O ports
-@@ -1037,6 +1083,7 @@ CONFIG_INPUT_LIS302DL=y
- CONFIG_SERIO=y
- # CONFIG_SERIO_SERPORT is not set
- # CONFIG_SERIO_RAW is not set
-+# CONFIG_SERIO_ALTERA_PS2 is not set
- # CONFIG_GAMEPORT is not set
- #
-@@ -1065,6 +1112,7 @@ CONFIG_SERIAL_S3C2440=y
- # CONFIG_SERIAL_MAX3100 is not set
- CONFIG_SERIAL_CORE=y
- CONFIG_SERIAL_CORE_CONSOLE=y
-+# CONFIG_SERIAL_TIMBERDALE is not set
- CONFIG_UNIX98_PTYS=y
- # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
- # CONFIG_LEGACY_PTYS is not set
-@@ -1078,6 +1126,7 @@ CONFIG_I2C_BOARDINFO=y
- # CONFIG_I2C_COMPAT is not set
- CONFIG_I2C_CHARDEV=y
- # CONFIG_I2C_HELPER_AUTO is not set
-+# CONFIG_I2C_SMBUS is not set
- #
- # I2C Algorithms
-@@ -1098,6 +1147,7 @@ CONFIG_I2C_CHARDEV=y
- # CONFIG_I2C_OCORES is not set
- CONFIG_I2C_S3C2410=y
- # CONFIG_I2C_SIMTEC is not set
-+# CONFIG_I2C_XILINX is not set
- #
- # External I2C/SMBus adapter drivers
-@@ -1111,16 +1161,9 @@ CONFIG_I2C_S3C2410=y
- #
- # CONFIG_I2C_PCA_PLATFORM is not set
- # CONFIG_I2C_STUB is not set
--
--#
--# Miscellaneous I2C Chip support
--#
--# CONFIG_DS1682 is not set
--# CONFIG_SENSORS_TSL2550 is not set
- # CONFIG_I2C_DEBUG_CORE is not set
- # CONFIG_I2C_DEBUG_ALGO is not set
- # CONFIG_I2C_DEBUG_BUS is not set
--# CONFIG_I2C_DEBUG_CHIP is not set
- CONFIG_SPI=y
- CONFIG_SPI_MASTER=y
-@@ -1130,7 +1173,10 @@ CONFIG_SPI_MASTER=y
- CONFIG_SPI_BITBANG=y
- CONFIG_SPI_GPIO=y
- CONFIG_SPI_S3C24XX=y
-+# CONFIG_SPI_S3C24XX_FIQ is not set
- CONFIG_SPI_S3C24XX_GPIO=y
-+# CONFIG_SPI_XILINX is not set
-+# CONFIG_SPI_DESIGNWARE is not set
- #
- # SPI Protocol Masters
-@@ -1149,13 +1195,16 @@ CONFIG_GPIO_SYSFS=y
- #
- # Memory mapped GPIO expanders:
- #
-+# CONFIG_GPIO_IT8761E is not set
- #
- # I2C GPIO expanders:
- #
-+# CONFIG_GPIO_MAX7300 is not set
- # CONFIG_GPIO_MAX732X is not set
- # CONFIG_GPIO_PCA953X is not set
- # CONFIG_GPIO_PCF857X is not set
-+# CONFIG_GPIO_ADP5588 is not set
- CONFIG_GPIO_PCF50633=y
- #
-@@ -1194,6 +1243,7 @@ CONFIG_WATCHDOG=y
- #
- # CONFIG_SOFT_WATCHDOG is not set
- CONFIG_S3C2410_WATCHDOG=y
-+# CONFIG_MAX63XX_WATCHDOG is not set
- #
- # USB-based Watchdog Cards
-@@ -1210,10 +1260,12 @@ CONFIG_SSB_POSSIBLE=y
- # Multifunction device drivers
- #
- CONFIG_MFD_CORE=y
-+# CONFIG_MFD_88PM860X is not set
- # CONFIG_MFD_SM501 is not set
- # CONFIG_MFD_ASIC3 is not set
- # CONFIG_HTC_EGPIO is not set
- # CONFIG_HTC_PASIC3 is not set
-+# CONFIG_HTC_I2CPLD is not set
- # CONFIG_TPS65010 is not set
- # CONFIG_TWL4030_CORE is not set
- # CONFIG_MFD_TMIO is not set
-@@ -1221,25 +1273,32 @@ CONFIG_MFD_CORE=y
- # CONFIG_MFD_TC6387XB is not set
- # CONFIG_MFD_TC6393XB is not set
- # CONFIG_PMIC_DA903X is not set
-+# CONFIG_PMIC_ADP5520 is not set
-+# CONFIG_MFD_MAX8925 is not set
- # CONFIG_MFD_WM8400 is not set
- # CONFIG_MFD_WM831X is not set
- # CONFIG_MFD_WM8350_I2C is not set
-+# CONFIG_MFD_WM8994 is not set
- CONFIG_MFD_PCF50633=y
- # CONFIG_MFD_MC13783 is not set
- CONFIG_PCF50633_ADC=y
- # CONFIG_AB3100_CORE is not set
- # CONFIG_EZX_PCAP is not set
-+# CONFIG_AB4500_CORE is not set
- CONFIG_MFD_GLAMO=y
- CONFIG_MFD_GLAMO_FB=y
- CONFIG_MFD_GLAMO_GPIO=y
- CONFIG_MFD_GLAMO_MCI=y
- CONFIG_REGULATOR=y
- # CONFIG_REGULATOR_DEBUG is not set
-+# CONFIG_REGULATOR_DUMMY is not set
- CONFIG_REGULATOR_FIXED_VOLTAGE=y
- # CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
- CONFIG_REGULATOR_USERSPACE_CONSUMER=y
- # CONFIG_REGULATOR_BQ24022 is not set
- # CONFIG_REGULATOR_MAX1586 is not set
-+# CONFIG_REGULATOR_MAX8649 is not set
-+# CONFIG_REGULATOR_MAX8660 is not set
- CONFIG_REGULATOR_PCF50633=y
- # CONFIG_REGULATOR_LP3971 is not set
- # CONFIG_REGULATOR_TPS65023 is not set
-@@ -1283,6 +1342,7 @@ CONFIG_FB_CFB_IMAGEBLIT=y
- # CONFIG_FB_BROADSHEET is not set
- CONFIG_BACKLIGHT_LCD_SUPPORT=y
- CONFIG_LCD_CLASS_DEVICE=y
-+# CONFIG_LCD_L4F00242T03 is not set
- # CONFIG_LCD_LMS283GF05 is not set
- # CONFIG_LCD_LTV350QV is not set
- # CONFIG_LCD_ILI9320 is not set
-@@ -1319,10 +1379,7 @@ CONFIG_FONT_6x11=y
- # CONFIG_FONT_SUN8x16 is not set
- # CONFIG_FONT_SUN12x22 is not set
- # CONFIG_FONT_10x18 is not set
--CONFIG_LOGO=y
--# CONFIG_LOGO_LINUX_MONO is not set
--# CONFIG_LOGO_LINUX_VGA16 is not set
--# CONFIG_LOGO_LINUX_CLUT224 is not set
-+# CONFIG_LOGO is not set
- CONFIG_SOUND=y
- CONFIG_SOUND_OSS_CORE=y
- CONFIG_SOUND_OSS_CORE_PRECLAIM=y
-@@ -1362,6 +1419,7 @@ CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753=y
- CONFIG_SND_SOC_I2C_AND_SPI=y
- # CONFIG_SND_SOC_ALL_CODECS is not set
- CONFIG_SND_SOC_WM8753=y
-+CONFIG_SND_SOC_LM4857=y
- # CONFIG_SOUND_PRIME is not set
- CONFIG_HID_SUPPORT=y
- CONFIG_HID=y
-@@ -1377,6 +1435,7 @@ CONFIG_USB_HID=y
- #
- # Special HID drivers
- #
-+# CONFIG_HID_3M_PCT is not set
- CONFIG_HID_A4TECH=y
- CONFIG_HID_APPLE=y
- CONFIG_HID_BELKIN=y
-@@ -1393,14 +1452,20 @@ CONFIG_HID_KENSINGTON=y
- CONFIG_HID_LOGITECH=y
- # CONFIG_LOGITECH_FF is not set
- # CONFIG_LOGIRUMBLEPAD2_FF is not set
-+# CONFIG_LOGIG940_FF is not set
-+# CONFIG_HID_MAGICMOUSE is not set
- CONFIG_HID_MICROSOFT=y
-+# CONFIG_HID_MOSART is not set
- CONFIG_HID_MONTEREY=y
- CONFIG_HID_NTRIG=y
-+CONFIG_HID_ORTEK=y
- CONFIG_HID_PANTHERLORD=y
- # CONFIG_PANTHERLORD_FF is not set
- CONFIG_HID_PETALYNX=y
-+# CONFIG_HID_QUANTA is not set
- CONFIG_HID_SAMSUNG=y
- CONFIG_HID_SONY=y
-+# CONFIG_HID_STANTUM is not set
- CONFIG_HID_SUNPLUS=y
- CONFIG_HID_GREENASIA=y
- # CONFIG_GREENASIA_FF is not set
-@@ -1426,8 +1491,6 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
- # CONFIG_USB_DEVICEFS is not set
- # CONFIG_USB_DEVICE_CLASS is not set
- # CONFIG_USB_DYNAMIC_MINORS is not set
--CONFIG_USB_SUSPEND=y
--# CONFIG_USB_OTG is not set
- # CONFIG_USB_MON is not set
- # CONFIG_USB_WUSB is not set
- # CONFIG_USB_WUSB_CBAF is not set
-@@ -1522,6 +1585,7 @@ CONFIG_USB_SERIAL_MOS7840=m
- CONFIG_USB_SERIAL_NAVMAN=m
- CONFIG_USB_SERIAL_PL2303=m
- # CONFIG_USB_SERIAL_OTI6858 is not set
-+# CONFIG_USB_SERIAL_QCAUX is not set
- # CONFIG_USB_SERIAL_QUALCOMM is not set
- # CONFIG_USB_SERIAL_SPCP8X5 is not set
- CONFIG_USB_SERIAL_HP4X=m
-@@ -1536,6 +1600,7 @@ CONFIG_USB_SERIAL_XIRCOM=m
- CONFIG_USB_SERIAL_OPTION=m
- CONFIG_USB_SERIAL_OMNINET=m
- # CONFIG_USB_SERIAL_OPTICON is not set
-+# CONFIG_USB_SERIAL_VIVOPAY_SERIAL is not set
- # CONFIG_USB_SERIAL_DEBUG is not set
- #
-@@ -1548,7 +1613,6 @@ CONFIG_USB_SERIAL_OMNINET=m
- # CONFIG_USB_RIO500 is not set
- # CONFIG_USB_LEGOTOWER is not set
- # CONFIG_USB_LCD is not set
--CONFIG_USB_BERRY_CHARGE=m
- # CONFIG_USB_LED is not set
- # CONFIG_USB_CYPRESS_CY7C63 is not set
- # CONFIG_USB_CYTHERM is not set
-@@ -1560,7 +1624,6 @@ CONFIG_USB_TRANCEVIBRATOR=m
- CONFIG_USB_IOWARRIOR=m
- # CONFIG_USB_TEST is not set
- # CONFIG_USB_ISIGHTFW is not set
--# CONFIG_USB_VST is not set
- CONFIG_USB_GADGET=y
- # CONFIG_USB_GADGET_DEBUG_FILES is not set
- CONFIG_USB_GADGET_VBUS_DRAW=500
-@@ -1589,20 +1652,24 @@ CONFIG_USB_S3C2410=y
- # CONFIG_USB_GADGET_DUALSPEED is not set
- # CONFIG_USB_ZERO is not set
- # CONFIG_USB_AUDIO is not set
--CONFIG_USB_ETH=y
-+CONFIG_USB_ETH=m
- CONFIG_USB_ETH_RNDIS=y
- # CONFIG_USB_ETH_EEM is not set
- # CONFIG_USB_GADGETFS is not set
- # CONFIG_USB_FILE_STORAGE is not set
-+# CONFIG_USB_MASS_STORAGE is not set
- # CONFIG_USB_G_SERIAL is not set
- # CONFIG_USB_MIDI_GADGET is not set
- # CONFIG_USB_G_PRINTER is not set
- # CONFIG_USB_CDC_COMPOSITE is not set
-+# CONFIG_USB_G_NOKIA is not set
-+# CONFIG_USB_G_MULTI is not set
- #
- # OTG and related infrastructure
- #
- # CONFIG_USB_GPIO_VBUS is not set
-+# CONFIG_USB_ULPI is not set
- # CONFIG_NOP_USB_XCEIV is not set
- CONFIG_MMC=y
- # CONFIG_MMC_DEBUG is not set
-@@ -1620,8 +1687,6 @@ CONFIG_MMC_BLOCK_BOUNCE=y
- # MMC/SD/SDIO Host Controller Drivers
- #
- # CONFIG_MMC_SDHCI is not set
--# CONFIG_MMC_AT91 is not set
--# CONFIG_MMC_ATMELMCI is not set
- # CONFIG_MMC_SPI is not set
- CONFIG_MMC_S3C=y
- # CONFIG_MMC_S3C_HW_SDIO_IRQ is not set
-@@ -1635,7 +1700,7 @@ CONFIG_LEDS_CLASS=y
- #
- # LED drivers
- #
--# CONFIG_LEDS_S3C24XX is not set
-+CONFIG_LEDS_S3C24XX=y
- # CONFIG_LEDS_PCA9532 is not set
- CONFIG_LEDS_GPIO=y
- CONFIG_LEDS_GPIO_PLATFORM=y
-@@ -1643,12 +1708,14 @@ CONFIG_LEDS_GPIO_PLATFORM=y
- # CONFIG_LEDS_PCA955X is not set
- # CONFIG_LEDS_DAC124S085 is not set
- # CONFIG_LEDS_PWM is not set
-+# CONFIG_LEDS_REGULATOR is not set
- # CONFIG_LEDS_BD2802 is not set
-+# CONFIG_LEDS_LT3593 is not set
-+CONFIG_LEDS_TRIGGERS=y
- #
- # LED Triggers
- #
--CONFIG_LEDS_TRIGGERS=y
- CONFIG_LEDS_TRIGGER_TIMER=y
- CONFIG_LEDS_TRIGGER_HEARTBEAT=y
- # CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
-@@ -1687,6 +1754,7 @@ CONFIG_RTC_INTF_DEV=y
- # CONFIG_RTC_DRV_PCF8563 is not set
- # CONFIG_RTC_DRV_PCF8583 is not set
- # CONFIG_RTC_DRV_M41T80 is not set
-+# CONFIG_RTC_DRV_BQ32K is not set
- # CONFIG_RTC_DRV_S35390A is not set
- # CONFIG_RTC_DRV_FM3130 is not set
- # CONFIG_RTC_DRV_RX8581 is not set
-@@ -1716,7 +1784,9 @@ CONFIG_RTC_INTF_DEV=y
- # CONFIG_RTC_DRV_M48T86 is not set
- # CONFIG_RTC_DRV_M48T35 is not set
- # CONFIG_RTC_DRV_M48T59 is not set
-+# CONFIG_RTC_DRV_MSM6242 is not set
- # CONFIG_RTC_DRV_BQ4802 is not set
-+# CONFIG_RTC_DRV_RP5C01 is not set
- # CONFIG_RTC_DRV_V3020 is not set
- CONFIG_RTC_DRV_PCF50633=y
-@@ -1738,14 +1808,9 @@ CONFIG_STAGING=y
- # CONFIG_ECHO is not set
- # CONFIG_COMEDI is not set
- # CONFIG_ASUS_OLED is not set
--# CONFIG_INPUT_MIMIO is not set
- # CONFIG_TRANZPORT is not set
- #
--# Android
--#
--
--#
- # Qualcomm MSM Camera And Video
- #
-@@ -1753,9 +1818,7 @@ CONFIG_STAGING=y
- # Camera Sensor Selection
- #
- # CONFIG_INPUT_GPIO is not set
--# CONFIG_DST is not set
- # CONFIG_POHMELFS is not set
--# CONFIG_PLAN9AUTH is not set
- # CONFIG_LINE6_USB is not set
- # CONFIG_USB_SERIAL_QUATECH2 is not set
- # CONFIG_USB_SERIAL_QUATECH_USB2 is not set
-@@ -1767,6 +1830,10 @@ CONFIG_STAGING=y
- #
- # CONFIG_RAR_REGISTER is not set
- # CONFIG_IIO is not set
-+# CONFIG_RAMZSWAP is not set
-+# CONFIG_BATMAN_ADV is not set
-+# CONFIG_STRIP is not set
-+# CONFIG_FB_SM7XX is not set
- CONFIG_AR6000_WLAN=y
- # CONFIG_AR6000_WLAN_DEBUG is not set
- # CONFIG_AR6000_WLAN_RESET is not set
-@@ -1787,7 +1854,7 @@ CONFIG_REISERFS_FS=m
- # CONFIG_REISERFS_PROC_INFO is not set
- # CONFIG_REISERFS_FS_XATTR is not set
- # CONFIG_JFS_FS is not set
--# CONFIG_FS_POSIX_ACL is not set
-+CONFIG_FS_POSIX_ACL=y
- # CONFIG_XFS_FS is not set
- # CONFIG_OCFS2_FS is not set
- # CONFIG_BTRFS_FS is not set
-@@ -1802,6 +1869,7 @@ CONFIG_INOTIFY_USER=y
- CONFIG_AUTOFS4_FS=m
- CONFIG_FUSE_FS=m
- CONFIG_CUSE=m
-+CONFIG_GENERIC_ACL=y
- #
- # Caches
-@@ -1835,7 +1903,7 @@ CONFIG_PROC_SYSCTL=y
- CONFIG_PROC_PAGE_MONITOR=y
- CONFIG_SYSFS=y
- CONFIG_TMPFS=y
--# CONFIG_TMPFS_POSIX_ACL is not set
-+CONFIG_TMPFS_POSIX_ACL=y
- # CONFIG_HUGETLB_PAGE is not set
- CONFIG_CONFIGFS_FS=m
- CONFIG_MISC_FILESYSTEMS=y
-@@ -1857,6 +1925,7 @@ CONFIG_JFFS2_ZLIB=y
- # CONFIG_JFFS2_LZO is not set
- CONFIG_JFFS2_RTIME=y
- # CONFIG_JFFS2_RUBIN is not set
-+# CONFIG_LOGFS is not set
- # CONFIG_CRAMFS is not set
- CONFIG_SQUASHFS=m
- # CONFIG_SQUASHFS_EMBEDDED is not set
-@@ -1886,6 +1955,7 @@ CONFIG_SUNRPC=m
- # CONFIG_RPCSEC_GSS_KRB5 is not set
- # CONFIG_RPCSEC_GSS_SPKM3 is not set
- # CONFIG_SMB_FS is not set
-+# CONFIG_CEPH_FS is not set
- CONFIG_CIFS=m
- # CONFIG_CIFS_STATS is not set
- # CONFIG_CIFS_WEAK_PW_HASH is not set
-@@ -1985,6 +2055,7 @@ CONFIG_TRACING_SUPPORT=y
- CONFIG_HAVE_ARCH_KGDB=y
- # CONFIG_ARM_UNWIND is not set
- # CONFIG_DEBUG_USER is not set
-+# CONFIG_OC_ETM is not set
- CONFIG_DEBUG_S3C_UART=2
- #
-@@ -1993,7 +2064,11 @@ CONFIG_DEBUG_S3C_UART=2
- # CONFIG_KEYS is not set
- # CONFIG_SECURITY is not set
- # CONFIG_SECURITYFS is not set
--# CONFIG_SECURITY_FILE_CAPABILITIES is not set
-+# CONFIG_DEFAULT_SECURITY_SELINUX is not set
-+# CONFIG_DEFAULT_SECURITY_SMACK is not set
-+# CONFIG_DEFAULT_SECURITY_TOMOYO is not set
-+CONFIG_DEFAULT_SECURITY_DAC=y
-+CONFIG_DEFAULT_SECURITY=""
- CONFIG_CRYPTO=y
- #
-@@ -2109,9 +2184,11 @@ CONFIG_CRC7=y
- CONFIG_LIBCRC32C=m
- CONFIG_ZLIB_INFLATE=y
- CONFIG_ZLIB_DEFLATE=y
-+CONFIG_LZO_DECOMPRESS=y
- CONFIG_DECOMPRESS_GZIP=y
- CONFIG_DECOMPRESS_BZIP2=y
- CONFIG_DECOMPRESS_LZMA=y
-+CONFIG_DECOMPRESS_LZO=y
- CONFIG_TEXTSEARCH=y
- CONFIG_TEXTSEARCH_KMP=m
- CONFIG_TEXTSEARCH_BM=m
-@@ -2119,3 +2196,4 @@ CONFIG_TEXTSEARCH_FSM=m
- CONFIG_HAS_IOMEM=y
- CONFIG_HAS_DMA=y
- CONFIG_NLATTR=y
-+CONFIG_GENERIC_ATOMIC64=y
--- 
-1.7.1
-
index f600a02..6d3b52b 100644 (file)
@@ -5,21 +5,34 @@ DESCRIPTION_${PN} = "Linux ${KERNEL_RELEASE} kernel for the Openmoko Neo GSM Sma
 
 KERNEL_RELEASE="2.6.34"
 
-SRCREV = "e23313fc76e2724fe56354526275458a0bdc10c3"
-OEV = "oe1"
+SRCREV = "dd1225cc08c3375bf80289ac1965c724881b149a"
+OEV = "oe2"
 PV = "${KERNEL_RELEASE}-${OEV}+gitr${SRCPV}"
 
 SRC_URI = "\
   git://git.openmoko.org/git/kernel.git;protocol=git;branch=om-gta02-2.6.34 \
 # build fix
   file://wm8753-fix-build-with-gcc-4.4.2-which-works-ok-with-.patch;patch=1 \
-# defconfig updates
-  file://gta02-defconfig-update-for-2.6.34.patch;patch=1 \
+# patches from Thomas White's gdrm-for-merging branch
+  file://0001-DRM-for-platform-devices.patch;patch=1 \
+  file://0002-Glamo-DRM-and-KMS-driver.patch;patch=1 \
+  file://0003-Work-on-Glamo-core-for-DRM.patch;patch=1 \
+  file://0004-JBT6k74-work-for-KMS.patch;patch=1 \
+  file://0005-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch;patch=1 \
+  file://0006-Fix-dynamic-command-queue-allocation.patch;patch=1 \
+  file://0007-Debug-statements-for-testing.patch;patch=1 \
+  file://0008-Fix-claim-of-2D-register-resource.patch;patch=1 \
+  file://0009-Use-unlocked_ioctl-rather-than-ioctl.patch;patch=1 \
+# patches from Radek Polak used in qtmoko
+  file://0001-accels.patch.patch;patch=1 \
+  file://0002-usbhost.patch.patch;patch=1 \
+  file://0003-ar6000_delay.patch.patch;patch=1 \
+  file://0004-save_regs.patch.patch;patch=1 \
 "
 
 S = "${WORKDIR}/git"
 
-CONFIG_NAME_om-gta02 = "gta02_defconfig"
+CONFIG_NAME_om-gta02 = "gta02_drm_defconfig"
 
 do_configure_prepend() {
         install -m 644 ./arch/arm/configs/${CONFIG_NAME} ${WORKDIR}/defconfig-oe