aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Jansa <Martin.Jansa@gmail.com>2010-05-19 18:53:47 +0200
committerMartin Jansa <Martin.Jansa@gmail.com>2010-05-25 09:09:24 +0200
commitedc90bdfa987ddc140c72891de4b41ae0bb2ef04 (patch)
treec0c44d7e3c7fb947a61b6e54eb8c0bab185be964
parenta9102cac0e6f56766e7317d9ea7af357a1593af7 (diff)
downloadopenembedded-edc90bdfa987ddc140c72891de4b41ae0bb2ef04.tar.gz
openembedded-edc90bdfa987ddc140c72891de4b41ae0bb2ef04.tar.bz2
openembedded-edc90bdfa987ddc140c72891de4b41ae0bb2ef04.zip
linux-openmoko-2.6.34: add rebased gdrm and qtmoko patches, use gta02_drm_defconfig again
Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch458
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch1759
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0002-Glamo-DRM-and-KMS-driver.patch3869
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0002-usbhost.patch.patch414
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0003-Work-on-Glamo-core-for-DRM.patch162
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0003-ar6000_delay.patch.patch29
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0004-JBT6k74-work-for-KMS.patch540
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0004-save_regs.patch.patch140
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0005-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch38
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0006-Fix-dynamic-command-queue-allocation.patch131
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0007-Debug-statements-for-testing.patch78
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0008-Fix-claim-of-2D-register-resource.patch27
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/0009-Use-unlocked_ioctl-rather-than-ioctl.patch28
-rw-r--r--recipes/linux/linux-openmoko-2.6.34/gta02-defconfig-update-for-2.6.34.patch880
-rw-r--r--recipes/linux/linux-openmoko-2.6.34_git.bb23
15 files changed, 7691 insertions, 885 deletions
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
index 0000000000..56197a263f
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0001-DRM-for-platform-devices.patch
@@ -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
index 0000000000..1b6964aee9
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch
@@ -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
index 0000000000..14dca73ab2
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0002-Glamo-DRM-and-KMS-driver.patch
@@ -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
index 0000000000..ace16502c8
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0002-usbhost.patch.patch
@@ -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
index 0000000000..c9afc89c96
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0003-Work-on-Glamo-core-for-DRM.patch
@@ -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
index 0000000000..8241a1f7fa
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0003-ar6000_delay.patch.patch
@@ -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
index 0000000000..692896f957
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0004-JBT6k74-work-for-KMS.patch
@@ -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
index 0000000000..185d9a1b99
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0004-save_regs.patch.patch
@@ -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
index 0000000000..cf73609478
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0005-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch
@@ -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
index 0000000000..ea570723d4
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0006-Fix-dynamic-command-queue-allocation.patch
@@ -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
index 0000000000..1321d29491
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0007-Debug-statements-for-testing.patch
@@ -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
index 0000000000..613f3a1d9a
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0008-Fix-claim-of-2D-register-resource.patch
@@ -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
index 0000000000..5937cb9820
--- /dev/null
+++ b/recipes/linux/linux-openmoko-2.6.34/0009-Use-unlocked_ioctl-rather-than-ioctl.patch
@@ -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
index bc6257aa46..0000000000
--- a/recipes/linux/linux-openmoko-2.6.34/gta02-defconfig-update-for-2.6.34.patch
+++ /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
-
diff --git a/recipes/linux/linux-openmoko-2.6.34_git.bb b/recipes/linux/linux-openmoko-2.6.34_git.bb
index f600a02036..6d3b52b101 100644
--- a/recipes/linux/linux-openmoko-2.6.34_git.bb
+++ b/recipes/linux/linux-openmoko-2.6.34_git.bb
@@ -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