From 14e1ed70361f7f5a5e54d93a606a7c8d53460b95 Mon Sep 17 00:00:00 2001 From: Martin Jansa Date: Mon, 17 May 2010 13:31:01 +0200 Subject: linux-openmoko-2.6.32: add patches from gdrm-for-merging instead of gdrm-2.6.32 branch and rebase Signed-off-by: Martin Jansa --- ...build-with-gcc-4.4.2-which-works-ok-with-.patch | 6 +- .../0002-DRM-for-platform-devices.patch | 458 --- .../linux-openmoko-2.6.32/0002-accels.patch.patch | 1759 +++++++++ .../0003-Glamo-DRM-and-KMS-driver.patch | 3788 ------------------- .../linux-openmoko-2.6.32/0003-usbhost.patch.patch | 233 ++ .../0004-Work-on-Glamo-core-for-DRM.patch | 171 - .../0004-ar6000_delay.patch.patch | 211 ++ .../0005-Add-JBT6k74-hook-for-use-by-KMS.patch | 49 - .../0005-save_regs.patch.patch | 140 + ...se-dev_set_drvdata-instead-of-setting-dri.patch | 29 - ...nfigs-enable-LEDS_S3C24XX-and-dummy-batte.patch | 66 + .../0007-DRM-for-platform-devices.patch | 458 +++ ...-select-DRM_KMS_HELPER-for-crtc-functions.patch | 24 - ...sh-when-reading-Glamo-registers-via-sysfs.patch | 38 - .../0008-Glamo-DRM-and-KMS-driver.patch | 3869 ++++++++++++++++++++ .../0009-A-couple-of-GEM-refcounting-fixes.patch | 42 - .../0009-Work-on-Glamo-core-for-DRM.patch | 162 + .../0010-JBT6k74-work-for-KMS.patch | 540 +++ .../0010-Simplify-the-JBT6k74-driver.patch | 667 ---- ...-if-userspace-provides-a-pixel-clock-valu.patch | 79 - ...sh-when-reading-Glamo-registers-via-sysfs.patch | 38 + ...0012-Fix-dynamic-command-queue-allocation.patch | 131 + ...rt-all-FB-modes-given-by-the-lower-levels.patch | 102 - .../0013-Change-connector-type-to-LVDS.patch | 29 - .../0013-Debug-statements-for-testing.patch | 78 + ...-JBT-hooks-and-allow-resolution-switching.patch | 223 -- .../0014-Fix-claim-of-2D-register-resource.patch | 27 + ...display-before-trying-to-set-mode-or-base.patch | 103 - .../linux-openmoko-2.6.32/0016-accels.patch.patch | 1759 --------- .../linux-openmoko-2.6.32/0017-usbhost.patch.patch | 233 -- .../0018-ar6000_delay.patch.patch | 211 -- .../0019-save_regs.patch.patch | 140 - ...0020-Fix-KMS-framebuffer-physical-address.patch | 38 - .../0021-Reject-modes-with-clock-0.patch | 25 - ...BT6k74-tweaks-Make-resolution-switch-work.patch | 316 -- ...023-Remove-a-couple-of-debugging-messages.patch | 35 - ...le-dummy-frames-when-switching-resolution.patch | 26 - .../0025-Almost-make-rotation-work.patch | 212 -- ...0026-gta02-defconfigs-enable-LEDS_S3C24XX.patch | 48 - recipes/linux/linux-openmoko-2.6.32_git.bb | 44 +- 40 files changed, 7732 insertions(+), 8875 deletions(-) delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0002-DRM-for-platform-devices.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0002-accels.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0003-Glamo-DRM-and-KMS-driver.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0003-usbhost.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0004-Work-on-Glamo-core-for-DRM.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0004-ar6000_delay.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0005-Add-JBT6k74-hook-for-use-by-KMS.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0005-save_regs.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0006-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0006-gta02-defconfigs-enable-LEDS_S3C24XX-and-dummy-batte.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0007-DRM-for-platform-devices.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0007-glamo-drm-select-DRM_KMS_HELPER-for-crtc-functions.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0008-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0008-Glamo-DRM-and-KMS-driver.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0009-A-couple-of-GEM-refcounting-fixes.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0009-Work-on-Glamo-core-for-DRM.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0010-JBT6k74-work-for-KMS.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0010-Simplify-the-JBT6k74-driver.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0011-Don-t-choke-if-userspace-provides-a-pixel-clock-valu.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0011-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0012-Fix-dynamic-command-queue-allocation.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0012-Report-all-FB-modes-given-by-the-lower-levels.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0013-Change-connector-type-to-LVDS.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0013-Debug-statements-for-testing.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0014-Clean-up-JBT-hooks-and-allow-resolution-switching.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0014-Fix-claim-of-2D-register-resource.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0015-Enable-display-before-trying-to-set-mode-or-base.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0016-accels.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0017-usbhost.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0018-ar6000_delay.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0019-save_regs.patch.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0020-Fix-KMS-framebuffer-physical-address.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0021-Reject-modes-with-clock-0.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0022-JBT6k74-tweaks-Make-resolution-switch-work.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0023-Remove-a-couple-of-debugging-messages.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0024-Enable-dummy-frames-when-switching-resolution.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0025-Almost-make-rotation-work.patch delete mode 100644 recipes/linux/linux-openmoko-2.6.32/0026-gta02-defconfigs-enable-LEDS_S3C24XX.patch diff --git a/recipes/linux/linux-openmoko-2.6.32/0001-wm8753-fix-build-with-gcc-4.4.2-which-works-ok-with-.patch b/recipes/linux/linux-openmoko-2.6.32/0001-wm8753-fix-build-with-gcc-4.4.2-which-works-ok-with-.patch index c6a16ab4d5..52fe6262cc 100644 --- a/recipes/linux/linux-openmoko-2.6.32/0001-wm8753-fix-build-with-gcc-4.4.2-which-works-ok-with-.patch +++ b/recipes/linux/linux-openmoko-2.6.32/0001-wm8753-fix-build-with-gcc-4.4.2-which-works-ok-with-.patch @@ -1,7 +1,7 @@ -From b3b22a8f23e6b49d21bbfe70f278a0d79419e3df Mon Sep 17 00:00:00 2001 +From 16722526979959805c493deabaac8cf9ba2f8ef6 Mon Sep 17 00:00:00 2001 From: Martin Jansa Date: Sat, 7 Nov 2009 20:33:06 +0100 -Subject: [PATCH 1/9] wm8753: fix build with gcc-4.4.2, which works ok with 4.1.2 +Subject: [PATCH 01/14] wm8753: fix build with gcc-4.4.2, which works ok with 4.1.2 Signed-off-by: Martin Jansa --- @@ -24,5 +24,5 @@ index 5ad677c..e148406 100644 K = Kpart & 0xFFFFFFFF; -- -1.6.6.1 +1.7.1 diff --git a/recipes/linux/linux-openmoko-2.6.32/0002-DRM-for-platform-devices.patch b/recipes/linux/linux-openmoko-2.6.32/0002-DRM-for-platform-devices.patch deleted file mode 100644 index 4e47a088d0..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0002-DRM-for-platform-devices.patch +++ /dev/null @@ -1,458 +0,0 @@ -From 660570997a5c998aaecc94798b6ea8d60b0da053 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Tue, 20 Oct 2009 15:52:30 +0200 -Subject: [PATCH 2/9] 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 ---- - 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 96eddd1..8b050ad 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 8417cc4..5a3b203 100644 ---- a/drivers/gpu/drm/drm_bufs.c -+++ b/drivers/gpu/drm/drm_bufs.c -@@ -188,7 +188,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 a75ca63..26005d9 100644 ---- a/drivers/gpu/drm/drm_drv.c -+++ b/drivers/gpu/drm/drm_drv.c -@@ -247,6 +247,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; -@@ -280,11 +281,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 55bb8a8..5e3d65a 100644 ---- a/drivers/gpu/drm/drm_stub.c -+++ b/drivers/gpu/drm/drm_stub.c -@@ -230,8 +230,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; -@@ -449,6 +451,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 7e42b7e..c08e7b7 100644 ---- a/drivers/gpu/drm/drm_sysfs.c -+++ b/drivers/gpu/drm/drm_sysfs.c -@@ -486,7 +486,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 7ad3faa..1d9a229 100644 ---- a/include/drm/drmP.h -+++ b/include/drm/drmP.h -@@ -55,6 +55,7 @@ - #include - #include - #include -+#include - #if defined(__alpha__) || defined(__powerpc__) - #include /* For pte_wrprotect */ - #endif -@@ -143,6 +144,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... */ -@@ -1008,6 +1010,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 */ -@@ -1118,12 +1121,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 int drm_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); -@@ -1342,6 +1353,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.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0002-accels.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0002-accels.patch.patch new file mode 100644 index 0000000000..9644473eda --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0002-accels.patch.patch @@ -0,0 +1,1759 @@ +From b96dbc75c274014171a53da13686bd90d239c023 Mon Sep 17 00:00:00 2001 +From: Radek Polak +Date: Fri, 9 Apr 2010 09:15:40 +0200 +Subject: [PATCH 02/14] 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 +--- + arch/arm/mach-s3c2410/include/mach/spi-gpio.h | 3 +- + arch/arm/mach-s3c2442/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 980a099..0ff8949 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-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c +index 7965cad..0675181 100644 +--- a/arch/arm/mach-s3c2442/mach-gta02.c ++++ b/arch/arm/mach-s3c2442/mach-gta02.c +@@ -63,6 +63,7 @@ + + #include + #include ++#include + + #include + #include +@@ -120,6 +121,22 @@ + #include + #endif + ++#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 ++ + struct pcf50633 *gta02_pcf; + + /* +@@ -775,6 +792,60 @@ const 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", +@@ -785,6 +856,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 = >a02_spigpio_cfg, ++ }, + }; + + /* BQ27000 Battery */ +@@ -1135,6 +1281,7 @@ static struct platform_device *gta02_devices[] __initdata = { + static struct platform_device *gta02_devices_pmu_children[] = { + &s3c_device_ts, + >a02_glamo_dev, ++ >a02_spi_gpio_dev, + &s3c_device_timer[2], + >a02_hdq_device, + >a02_gps_userspace_consumer +@@ -1361,6 +1508,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; ++ } ++ + s3c_device_ts.name = "s3c2440-ts"; + + bus_register_notifier(&platform_bus_type, >a02_device_register_notifier); +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index a9bb254..2e9bc5d 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -317,4 +317,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 a8b8485..7e63293 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -30,4 +30,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 ++ * converted to private bitbang by: ++ * Andy Green ++ * ability to set acceleration threshold added by: ++ * Simon Kagstrom ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* 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 "); ++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 f1db395..5c50329 100644 +--- a/drivers/spi/spi_bitbang.c ++++ b/drivers/spi/spi_bitbang.c +@@ -253,134 +253,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; +@@ -455,6 +460,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 ++#include ++#include ++#include ++ ++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 97b60b3..9c5484b 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -291,6 +291,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); + }; +@@ -542,6 +549,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 eed4254..f92de01 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.32/0003-Glamo-DRM-and-KMS-driver.patch b/recipes/linux/linux-openmoko-2.6.32/0003-Glamo-DRM-and-KMS-driver.patch deleted file mode 100644 index 6c1cc415d9..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0003-Glamo-DRM-and-KMS-driver.patch +++ /dev/null @@ -1,3788 +0,0 @@ -From eff70b6d14cc372944bbeda0dd0afe8599327f96 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Tue, 20 Oct 2009 16:14:55 +0200 -Subject: [PATCH 3/9] Glamo DRM and KMS driver - -This adds the Glamo DRM and KMS driver, but not the modifications needed -elsewhere to support it. - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/Kconfig | 15 + - drivers/mfd/glamo/Makefile | 5 +- - drivers/mfd/glamo/glamo-buffer.c | 372 ++++++++++++++ - drivers/mfd/glamo/glamo-buffer.h | 60 +++ - drivers/mfd/glamo/glamo-cmdq.c | 528 ++++++++++++++++++++ - drivers/mfd/glamo/glamo-cmdq.h | 49 ++ - drivers/mfd/glamo/glamo-display.c | 869 +++++++++++++++++++++++++++++++++ - drivers/mfd/glamo/glamo-display.h | 39 ++ - drivers/mfd/glamo/glamo-drm-drv.c | 444 +++++++++++++++++ - drivers/mfd/glamo/glamo-drm-private.h | 156 ++++++ - drivers/mfd/glamo/glamo-fence.c | 330 +++++++++++++ - drivers/mfd/glamo/glamo-fence.h | 34 ++ - drivers/mfd/glamo/glamo-kms-fb.c | 540 ++++++++++++++++++++ - drivers/mfd/glamo/glamo-kms-fb.h | 41 ++ - include/drm/Kbuild | 1 + - include/drm/glamo_drm.h | 153 ++++++ - 16 files changed, 3635 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..214c70e 100644 ---- a/drivers/mfd/glamo/Kconfig -+++ b/drivers/mfd/glamo/Kconfig -@@ -40,3 +40,18 @@ 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 -+ 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..45500d3 ---- /dev/null -+++ b/drivers/mfd/glamo/glamo-buffer.c -@@ -0,0 +1,372 @@ -+/* -+ * SMedia Glamo 336x/337x memory management -+ * -+ * Copyright (c) 2009 Thomas White -+ * -+ * 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 . -+ * -+ * -+ * 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 -+ */ -+ -+ -+#include -+#include -+ -+#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) { -+ 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 -+ * -+ * 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 -+ -+#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..caedc27 ---- /dev/null -+++ b/drivers/mfd/glamo/glamo-cmdq.c -@@ -0,0 +1,528 @@ -+/* -+ * SMedia Glamo 336x/337x command queue handling -+ * -+ * Copyright (C) 2008-2009 Thomas White -+ * Copyright (C) 2009 Andreas Pokorny -+ * 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 -+ * -+ * 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 -+#include -+ -+#include "glamo-core.h" -+#include "glamo-drm-private.h" -+#include "glamo-regs.h" -+ -+ -+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 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 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_CRIT "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_init(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; icmdq_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, -+ GLAMO_OFFSET_CMDQ & 0xffff); -+ reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRH, -+ (GLAMO_OFFSET_CMDQ >> 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_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_init(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; isize; 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 -+ * Copyright (c) 2009 Andreas Pokorny -+ * Based on xf86-video-glamo -+ * Copyright 2007 OpenMoko, Inc. -+ * Copyright © 2009 Lars-Peter Clausen -+ * -+ * 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 -+ -+#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..34a665a ---- /dev/null -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -0,0 +1,869 @@ -+/* -+ * SMedia Glamo 336x/337x display -+ * -+ * Copyright (C) 2008-2009 Thomas White -+ * -+ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc. -+ * Author: Harald Welte -+ * 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 -+ * -+ */ -+ -+#define DEBUG 1 -+ -+#include -+#include -+#include -+#include -+ -+#include "glamo-core.h" -+#include "glamo-drm-private.h" -+#include "glamo-regs.h" -+#include "glamo-kms-fb.h" -+#include "glamo-display.h" -+#include -+ -+ -+#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; -+} -+ -+ -+extern void jbt6k74_action(int val); -+ -+/* Power on/off */ -+static void glamo_crtc_dpms(struct drm_crtc *crtc, int mode) -+{ -+} -+ -+ -+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! */ -+ -+ 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; -+ -+ /* Dig out our handle */ -+ gcrtc = to_glamo_crtc(crtc); -+ gdrm = gcrtc->gdrm; /* Here it is! */ -+ -+ glamo_lcd_cmd_mode(gdrm, 1); -+ -+ glamo_engine_reclock(gdrm->glamo_core, GLAMO_ENGINE_LCD, mode->clock); -+ gdrm->saved_clock = 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); -+ -+ /* 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); -+ -+ glamo_lcd_cmd_mode(gdrm, 0); -+ -+ glamo_crtc_mode_set_base(crtc, 0, 0, old_fb); -+ -+ return 0; -+} -+ -+ -+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 drm_display_mode *mode; -+ struct glamo_fb_platform_data *fb_info; -+ struct glamo_output *goutput = to_glamo_output(connector); -+ struct glamodrm_handle *gdrm = goutput->gdrm; -+ -+ /* Dig out the record which will tell us about the hardware */ -+ fb_info = gdrm->glamo_core->pdata->fb_data; -+ -+ mode = drm_mode_create(connector->dev); -+ if (!mode) -+ return 0; -+ /* Fill in 'mode' here */ -+ mode->type = DRM_MODE_TYPE_DEFAULT | DRM_MODE_TYPE_PREFERRED; -+ -+ /* Convert framebuffer timings into KMS timings */ -+ mode->clock = 1000000000UL / fb_info->modes[0].pixclock; /* ps -> kHz */ -+ mode->clock *= 1000; /* kHz -> Hz */ -+ mode->hdisplay = fb_info->modes[0].xres; -+ mode->hsync_start = fb_info->modes[0].right_margin + mode->hdisplay; -+ mode->hsync_end = mode->hsync_start + fb_info->modes[0].hsync_len; -+ mode->htotal = mode->hsync_end + fb_info->modes[0].left_margin; -+ mode->hskew = 0; -+ -+ mode->vdisplay = fb_info->modes[0].yres; -+ mode->vsync_start = fb_info->modes[0].lower_margin + mode->vdisplay; -+ mode->vsync_end = mode->vsync_start + fb_info->modes[0].vsync_len; -+ mode->vtotal = mode->vsync_end + fb_info->modes[0].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 1; /* one mode, for now */ -+} -+ -+ -+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) -+{ -+ 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 = 320; -+ dev->mode_config.max_width = 480; -+ 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; -+ 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_Unknown); -+ 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_display_suspend(struct glamodrm_handle *gdrm) -+{ -+ gdrm->saved_width = reg_read_lcd(gdrm, GLAMO_REG_LCD_WIDTH); -+ gdrm->saved_height = reg_read_lcd(gdrm, GLAMO_REG_LCD_HEIGHT); -+ gdrm->saved_pitch = reg_read_lcd(gdrm, GLAMO_REG_LCD_PITCH); -+ gdrm->saved_htotal = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_TOTAL); -+ gdrm->saved_hrtrst = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_START); -+ gdrm->saved_hrtren = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_END); -+ gdrm->saved_hdspst = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_START); -+ gdrm->saved_hdspen = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_END); -+ gdrm->saved_vtotal = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_TOTAL); -+ gdrm->saved_vrtrst = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_START); -+ gdrm->saved_vrtren = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_END); -+ gdrm->saved_vdspst = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_START); -+ gdrm->saved_vdspen = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_END); -+} -+ -+ -+void glamo_display_resume(struct glamodrm_handle *gdrm) -+{ -+ 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)); -+ -+ /* Restore timings */ -+ glamo_lcd_cmd_mode(gdrm, 1); -+ glamo_engine_reclock(gdrm->glamo_core, GLAMO_ENGINE_LCD, -+ gdrm->saved_clock); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_WIDTH, GLAMO_LCD_WIDTH_MASK, -+ gdrm->saved_width); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HEIGHT, GLAMO_LCD_HEIGHT_MASK, -+ gdrm->saved_height); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_PITCH, GLAMO_LCD_PITCH_MASK, -+ gdrm->saved_pitch); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_TOTAL, -+ GLAMO_LCD_HV_TOTAL_MASK, gdrm->saved_htotal); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_START, -+ GLAMO_LCD_HV_RETR_START_MASK, gdrm->saved_hrtrst); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_END, -+ GLAMO_LCD_HV_RETR_END_MASK, gdrm->saved_hrtren); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_START, -+ GLAMO_LCD_HV_RETR_DISP_START_MASK, -+ gdrm->saved_hdspst); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_END, -+ GLAMO_LCD_HV_RETR_DISP_END_MASK, -+ gdrm->saved_hdspen); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_TOTAL, -+ GLAMO_LCD_HV_TOTAL_MASK, gdrm->saved_vtotal); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_START, -+ GLAMO_LCD_HV_RETR_START_MASK, gdrm->saved_vrtrst); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_END, -+ GLAMO_LCD_HV_RETR_END_MASK, gdrm->saved_vrtren); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_START, -+ GLAMO_LCD_HV_RETR_DISP_START_MASK, -+ gdrm->saved_vdspst); -+ reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_END, -+ GLAMO_LCD_HV_RETR_DISP_END_MASK, -+ gdrm->saved_vdspen); -+ glamo_lcd_cmd_mode(gdrm, 0); -+} -diff --git a/drivers/mfd/glamo/glamo-display.h b/drivers/mfd/glamo/glamo-display.h -new file mode 100644 -index 0000000..d6f21bc ---- /dev/null -+++ b/drivers/mfd/glamo/glamo-display.h -@@ -0,0 +1,39 @@ -+/* Smedia Glamo 336x/337x Display -+ * -+ * Copyright (c) 2008-2009 Thomas White -+ * -+ * 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 -+#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); -+ -+#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..f30097e ---- /dev/null -+++ b/drivers/mfd/glamo/glamo-drm-drv.c -@@ -0,0 +1,444 @@ -+/* Smedia Glamo 336x/337x Graphics Driver -+ * -+ * Copyright (C) 2009 Openmoko, Inc. Jorge Luis Zapata -+ * Copyright (C) 2008-2009 Thomas White -+ * Copyright (C) 2009 Andreas Pokorny -+ * -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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, -+}; -+ -+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 command queue itself */ -+ gdrm->cmdq = platform_get_resource_byname(pdev, IORESOURCE_MEM, -+ "glamo-command-queue"); -+ if ( !gdrm->cmdq ) { -+ dev_err(&pdev->dev, "Unable to find command queue.\n"); -+ rc = -ENOENT; -+ goto out_unmap_regs; -+ } -+ gdrm->cmdq = request_mem_region(gdrm->cmdq->start, -+ resource_size(gdrm->cmdq), pdev->name); -+ if ( !gdrm->cmdq ) { -+ dev_err(&pdev->dev, "failed to request command queue region\n"); -+ rc = -ENOENT; -+ goto out_unmap_regs; -+ } -+ gdrm->cmdq_base = ioremap_nocache(gdrm->cmdq->start, -+ resource_size(gdrm->cmdq)); -+ if ( !gdrm->cmdq_base ) { -+ dev_err(&pdev->dev, "failed to ioremap() command queue\n"); -+ rc = -ENOENT; -+ goto out_release_cmdq; -+ } -+ -+ /* 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); -+ pdev->dev.driver_data = 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..7949a2e ---- /dev/null -+++ b/drivers/mfd/glamo/glamo-drm-private.h -@@ -0,0 +1,156 @@ -+/* Smedia Glamo 336x/337x DRM private bits -+ * -+ * Copyright (C) 2008-2009 Thomas White -+ * Copyright (C) 2009 Andreas Pokorny -+ * Based on xf86-video-glamo -+ * Copyright 2007 OpenMoko, Inc. -+ * Copyright © 2009 Lars-Peter Clausen -+ * -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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 */ -+ struct resource *cmdq; -+ char __iomem *cmdq_base; -+ -+ /* 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; -+}; -+ -+ -+/* 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; -+}; -+ -+ -+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 -+ * -+ * 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 . -+ * -+ * -+ * 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 -+ * Dave Airlie -+ */ -+ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+ * -+ * 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 . -+ * -+ */ -+ -+#ifndef __GLAMO_FENCE_H -+#define __GLAMO_FENCE_H -+ -+#include -+ -+#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..61cd605 ---- /dev/null -+++ b/drivers/mfd/glamo/glamo-kms-fb.c -@@ -0,0 +1,540 @@ -+/* -+ * SMedia Glamo 336x/337x KMS Framebuffer -+ * -+ * Copyright (C) 2009 Thomas White -+ * -+ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc. -+ * Author: Harald Welte -+ * 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 -+ * -+ */ -+ -+ -+#include -+#include -+#include -+#include -+ -+#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; -+ -+ DRM_DEBUG("%d %d\n", var->xres, var->pixclock); -+ -+ if (var->pixclock != -1) { -+ -+ DRM_ERROR("PIXEL CLOCK SET\n"); -+ return -EINVAL; -+ } else { -+ struct drm_crtc *crtc; -+ int ret; -+ -+ 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 drm_crtc *crtc; -+ struct drm_encoder *encoder; -+ int i; -+ -+ /* -+ * For each CRTC in this fb, find all associated encoders -+ * and turn them off, then turn off the CRTC. -+ */ -+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; -+ -+ for (i = 0; i < par->crtc_count; i++) -+ if (crtc->base.id == par->crtc_ids[i]) -+ break; -+ -+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); -+ -+ /* Found a CRTC on this fb, now find encoders */ -+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -+ if (encoder->crtc == crtc) { -+ struct drm_encoder_helper_funcs *encoder_funcs; -+ encoder_funcs = encoder->helper_private; -+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); -+ } -+ } -+ } -+} -+ -+static void glamofb_off(struct fb_info *info, int dpms_mode) -+{ -+ struct glamofb_par *par = info->par; -+ struct drm_device *dev = par->dev; -+ struct drm_crtc *crtc; -+ struct drm_encoder *encoder; -+ int i; -+ -+ /* -+ * For each CRTC in this fb, find all associated encoders -+ * and turn them off, then turn off the CRTC. -+ */ -+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; -+ -+ for (i = 0; i < par->crtc_count; i++) -+ if (crtc->base.id == par->crtc_ids[i]) -+ break; -+ -+ /* Found a CRTC on this fb, now find encoders */ -+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -+ if (encoder->crtc == crtc) { -+ struct drm_encoder_helper_funcs *encoder_funcs; -+ encoder_funcs = encoder->helper_private; -+ encoder_funcs->dpms(encoder, dpms_mode); -+ } -+ } -+ if (dpms_mode == DRM_MODE_DPMS_OFF) -+ crtc_funcs->dpms(crtc, dpms_mode); -+ } -+} -+ -+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->fix.smem_start = dev->mode_config.fb_base -+ + (unsigned long) gdrm->vram->start; -+ info->fix.smem_len = size; -+ -+ 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->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 -+ * -+ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc. -+ * Author: Harald Welte -+ * 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 -+#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 b940fdf..48b7b55 100644 ---- a/include/drm/Kbuild -+++ b/include/drm/Kbuild -@@ -8,3 +8,4 @@ unifdef-y += radeon_drm.h - unifdef-y += sis_drm.h - unifdef-y += savage_drm.h - unifdef-y += via_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 -+ * Kevin E. Martin -+ * Gareth Hughes -+ * Keith Whitwell -+ */ -+ -+#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.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0003-usbhost.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0003-usbhost.patch.patch new file mode 100644 index 0000000000..80b05eac18 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0003-usbhost.patch.patch @@ -0,0 +1,233 @@ +From 1000aee5e65dc824e02aafe2a70ad5b3f0d44d2d Mon Sep 17 00:00:00 2001 +From: Radek Polak +Date: Fri, 9 Apr 2010 09:17:28 +0200 +Subject: [PATCH 03/14] 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 +--- + arch/arm/mach-s3c2442/Makefile | 1 + + arch/arm/mach-s3c2442/mach-gta02.c | 50 ++++++++++++++++++++++++++++++++++- + drivers/usb/host/ohci-s3c2410.c | 48 ++++++++++++++++++++++++++++++++++ + 3 files changed, 97 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-s3c2442/Makefile b/arch/arm/mach-s3c2442/Makefile +index 6247ca0..3e7145c 100644 +--- a/arch/arm/mach-s3c2442/Makefile ++++ b/arch/arm/mach-s3c2442/Makefile +@@ -17,6 +17,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 + # Machine support + +diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c +index 0675181..779d6df 100644 +--- a/arch/arm/mach-s3c2442/mach-gta02.c ++++ b/arch/arm/mach-s3c2442/mach-gta02.c +@@ -322,6 +322,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", + }; +@@ -331,6 +335,11 @@ static struct regulator_consumer_supply gsm_supply_consumer = { + .supply = "GSM", + }; + ++static struct regulator_consumer_supply usbhost_supply_consumer = { ++ .dev = >a02_pm_usbhost_dev.dev, ++ .supply = "USBHOST", ++}; ++ + static struct regulator_init_data gsm_supply_init_data = { + .constraints = { + .min_uV = 3700000, +@@ -342,6 +351,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, +@@ -350,6 +370,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, +@@ -358,6 +386,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, ++ }, ++}; ++ + static struct regulator_bulk_data gta02_gps_consumer_supply = { + .supply = "GPS", + }; +@@ -1331,12 +1367,17 @@ static struct platform_device* gta02_glamo_gpio_children[] = { + + static struct platform_device* gta02_pcf50633_gpio_children[] = { + >a02_gsm_supply_device, ++ >a02_usbhost_supply_device, + }; + + static struct platform_device* gta02_gsm_supply_children[] = { + >a02_pm_gsm_dev, + }; + ++static struct platform_device* gta02_usbhost_supply_children[] = { ++ >a02_pm_usbhost_dev, ++}; ++ + static struct platform_device* gta02_hdq_children[] = { + &bq27000_battery_device, + }; +@@ -1363,14 +1404,19 @@ static struct gta02_device_children gta02_device_children[] = { + }, + { + .dev_name = "pcf50633-gpio", +- .num_children = 1, ++ .num_children = 2, + .children = gta02_pcf50633_gpio_children, + }, + { + .dev_name = "reg-fixed-voltage.1", + .num_children = 1, + .children = gta02_gsm_supply_children, +- } ++ }, ++ { ++ .dev_name = "reg-fixed-voltage.2", ++ .num_children = 1, ++ .children = gta02_usbhost_supply_children, ++ }, + }; + + static int gta02_add_child_devices(struct device *parent, +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 + #include + #include ++#include ++#include ++#include ++#include + + #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.32/0004-Work-on-Glamo-core-for-DRM.patch b/recipes/linux/linux-openmoko-2.6.32/0004-Work-on-Glamo-core-for-DRM.patch deleted file mode 100644 index 2bddd1758a..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0004-Work-on-Glamo-core-for-DRM.patch +++ /dev/null @@ -1,171 +0,0 @@ -From eb0614d6a8a879bd3ac69050b3f5b012ef562b9e Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Tue, 17 Nov 2009 23:45:29 +0100 -Subject: [PATCH 4/9] 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 ---- - drivers/mfd/glamo/glamo-core.c | 33 +++++++++++++++++++++++++++------ - drivers/mfd/glamo/glamo-core.h | 35 +++++++++++++++++++++++++---------- - drivers/mfd/glamo/glamo-regs.h | 24 ++++++++++++++++++++++++ - include/linux/mfd/glamo.h | 7 ++----- - 4 files changed, 78 insertions(+), 21 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c -index 82a906a..a614d44 100644 ---- a/drivers/mfd/glamo/glamo-core.c -+++ b/drivers/mfd/glamo/glamo-core.c -@@ -221,10 +221,31 @@ 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-command-queue", -+ .start = GLAMO_MEM_BASE + GLAMO_OFFSET_CMDQ, -+ .end = GLAMO_MEM_BASE + GLAMO_OFFSET_CMDQ + -+ GLAMO_CMDQ_SIZE - 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[] = { -@@ -235,9 +256,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..d21bdc4 100644 ---- a/drivers/mfd/glamo/glamo-core.h -+++ b/drivers/mfd/glamo/glamo-core.h -@@ -3,18 +3,33 @@ - - #include - -+/* 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 */ -+#define GLAMO_CMDQ_SIZE (128 * 1024) /* 128k ring 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 \ -+ - GLAMO_CMDQ_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) -+#define GLAMO_OFFSET_CMDQ (GLAMO_OFFSET_MMC + GLAMO_MMC_BUFFER_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.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0004-ar6000_delay.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0004-ar6000_delay.patch.patch new file mode 100644 index 0000000000..104a8c8951 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0004-ar6000_delay.patch.patch @@ -0,0 +1,211 @@ +From c9580261d79d8d1664fbd2da52dcd2148da9ef14 Mon Sep 17 00:00:00 2001 +From: Radek Polak +Date: Fri, 9 Apr 2010 09:18:02 +0200 +Subject: [PATCH 04/14] 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 +--- + arch/arm/mach-s3c2442/gta02-pm-usbhost.c | 174 ++++++++++++++++++++++++++++++ + drivers/ar6000/hif/hif2.c | 2 + + 2 files changed, 176 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/mach-s3c2442/gta02-pm-usbhost.c + +diff --git a/arch/arm/mach-s3c2442/gta02-pm-usbhost.c b/arch/arm/mach-s3c2442/gta02-pm-usbhost.c +new file mode 100644 +index 0000000..233340a +--- /dev/null ++++ b/arch/arm/mach-s3c2442/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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++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 (>a02_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, >a02_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, >a02_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(>a02_usbhost_driver); ++} ++module_init(gta02_usbhost_init); ++ ++static void gta02_usbhost_exit(void) ++{ ++ platform_driver_unregister(>a02_usbhost_driver); ++} ++module_exit(gta02_usbhost_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte "); ++MODULE_DESCRIPTION("Openmoko Freerunner USBHOST Power Management"); +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.32/0005-Add-JBT6k74-hook-for-use-by-KMS.patch b/recipes/linux/linux-openmoko-2.6.32/0005-Add-JBT6k74-hook-for-use-by-KMS.patch deleted file mode 100644 index 744004e7b1..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0005-Add-JBT6k74-hook-for-use-by-KMS.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 972c0a58e9e238c266adc77c88dcd8de6f123742 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sat, 21 Nov 2009 21:42:16 +0100 -Subject: [PATCH 5/9] Add JBT6k74 hook for use by KMS - -Signed-off-by: Thomas White ---- - drivers/video/backlight/jbt6k74.c | 18 ++++++++++++++++++ - 1 files changed, 18 insertions(+), 0 deletions(-) - -diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c -index 8450904..95ca4cf 100644 ---- a/drivers/video/backlight/jbt6k74.c -+++ b/drivers/video/backlight/jbt6k74.c -@@ -698,6 +698,22 @@ static int jbt6k74_get_power(struct lcd_device *ld) - } - } - -+/* This is utterly, totally horrible. I'm REALLY sorry... */ -+struct jbt_info *jbt_global; -+void jbt6k74_action(int val) -+{ -+ if ( !jbt_global ) { -+ printk(KERN_CRIT "JBT not initialised!!!\n"); -+ return; -+ } -+ if ( val == 0 ) { -+ jbt6k74_enter_power_mode(jbt_global, JBT_POWER_MODE_SLEEP); -+ } else { -+ jbt6k74_enter_power_mode(jbt_global, JBT_POWER_MODE_NORMAL); -+ } -+} -+EXPORT_SYMBOL_GPL(jbt6k74_action); -+ - struct lcd_ops jbt6k74_lcd_ops = { - .set_power = jbt6k74_set_power, - .get_power = jbt6k74_get_power, -@@ -728,6 +744,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, --- -1.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0005-save_regs.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0005-save_regs.patch.patch new file mode 100644 index 0000000000..7873f850e4 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0005-save_regs.patch.patch @@ -0,0 +1,140 @@ +From f143dfe95e9b48aebc8fad111086a25c915bf479 Mon Sep 17 00:00:00 2001 +From: Radek Polak +Date: Fri, 9 Apr 2010 09:22:23 +0200 +Subject: [PATCH 05/14] 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 7dab2e5..7df08dd 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -59,10 +59,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 fba147c..06423cb 100644 +--- a/drivers/mmc/host/s3cmci.c ++++ b/drivers/mmc/host/s3cmci.c +@@ -1879,19 +1879,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 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 ++ + 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.32/0006-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch b/recipes/linux/linux-openmoko-2.6.32/0006-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch deleted file mode 100644 index f685608253..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0006-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch +++ /dev/null @@ -1,29 +0,0 @@ -From be2d944c2b29b19b4cee7c64b00bd75cf7209724 Mon Sep 17 00:00:00 2001 -From: Martin Jansa -Date: Tue, 22 Dec 2009 16:11:27 +0100 -Subject: [PATCH 6/9] glamo-drm: use dev_set_drvdata instead of setting driver_data directly - -* driver_data is private since 2.6.32 - http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=b4028437876866aba4747a655ede00f892089e14 - -Signed-off-by: Martin Jansa ---- - 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 f30097e..1907a47 100644 ---- a/drivers/mfd/glamo/glamo-drm-drv.c -+++ b/drivers/mfd/glamo/glamo-drm-drv.c -@@ -347,7 +347,7 @@ out_release_regs: - release_mem_region(gdrm->reg->start, resource_size(gdrm->reg)); - out_free: - kfree(gdrm); -- pdev->dev.driver_data = NULL; -+ dev_set_drvdata(&pdev->dev, NULL); - return rc; - } - --- -1.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0006-gta02-defconfigs-enable-LEDS_S3C24XX-and-dummy-batte.patch b/recipes/linux/linux-openmoko-2.6.32/0006-gta02-defconfigs-enable-LEDS_S3C24XX-and-dummy-batte.patch new file mode 100644 index 0000000000..103c06c886 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0006-gta02-defconfigs-enable-LEDS_S3C24XX-and-dummy-batte.patch @@ -0,0 +1,66 @@ +From 7e4a1fee4f08ef7ccedd4706b4890afc71f37dc4 Mon Sep 17 00:00:00 2001 +From: Martin Jansa +Date: Mon, 17 May 2010 12:41:06 +0200 +Subject: [PATCH 06/14] gta02 defconfigs, enable LEDS_S3C24XX and dummy battery driver + +--- + arch/arm/configs/gta02_defconfig | 2 +- + arch/arm/configs/gta02_drm_defconfig | 10 +++++----- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/arch/arm/configs/gta02_defconfig b/arch/arm/configs/gta02_defconfig +index 73d26ba..55832ac 100644 +--- a/arch/arm/configs/gta02_defconfig ++++ b/arch/arm/configs/gta02_defconfig +@@ -1635,7 +1635,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 +diff --git a/arch/arm/configs/gta02_drm_defconfig b/arch/arm/configs/gta02_drm_defconfig +index 05aa130..62c1ee4 100644 +--- a/arch/arm/configs/gta02_drm_defconfig ++++ b/arch/arm/configs/gta02_drm_defconfig +@@ -1,7 +1,7 @@ + # + # Automatically generated make config: don't edit +-# Linux kernel version: 2.6.32.11 +-# Sun Apr 11 14:20:07 2010 ++# Linux kernel version: 2.6.32.13 ++# Mon May 17 14:13:35 2010 + # + CONFIG_ARM=y + CONFIG_HAVE_PWM=y +@@ -1233,7 +1233,7 @@ CONFIG_POWER_SUPPLY=y + CONFIG_CHARGER_PCF50633=y + CONFIG_BATTERY_BQ27000_HDQ=y + CONFIG_HDQ_GPIO_BITBANG=y +-# CONFIG_BATTERY_PLATFORM is not set ++CONFIG_BATTERY_PLATFORM=y + # CONFIG_HWMON is not set + # CONFIG_THERMAL is not set + CONFIG_WATCHDOG=y +@@ -1681,14 +1681,14 @@ 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 + # CONFIG_LEDS_LP3944 is not set + # CONFIG_LEDS_PCA955X is not set + # CONFIG_LEDS_DAC124S085 is not set +-# CONFIG_LEDS_PWM is not set ++CONFIG_LEDS_PWM=y + # CONFIG_LEDS_BD2802 is not set + + # +-- +1.7.1 + diff --git a/recipes/linux/linux-openmoko-2.6.32/0007-DRM-for-platform-devices.patch b/recipes/linux/linux-openmoko-2.6.32/0007-DRM-for-platform-devices.patch new file mode 100644 index 0000000000..e16379fecf --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0007-DRM-for-platform-devices.patch @@ -0,0 +1,458 @@ +From d05d2c82e2ec110720c5c714ad18d60ef676aa1d Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Tue, 20 Oct 2009 15:52:30 +0200 +Subject: [PATCH 07/14] 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 +--- + 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 96eddd1..8b050ad 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 8417cc4..5a3b203 100644 +--- a/drivers/gpu/drm/drm_bufs.c ++++ b/drivers/gpu/drm/drm_bufs.c +@@ -188,7 +188,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 a75ca63..26005d9 100644 +--- a/drivers/gpu/drm/drm_drv.c ++++ b/drivers/gpu/drm/drm_drv.c +@@ -247,6 +247,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; +@@ -280,11 +281,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 55bb8a8..5e3d65a 100644 +--- a/drivers/gpu/drm/drm_stub.c ++++ b/drivers/gpu/drm/drm_stub.c +@@ -230,8 +230,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; +@@ -449,6 +451,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 7e42b7e..c08e7b7 100644 +--- a/drivers/gpu/drm/drm_sysfs.c ++++ b/drivers/gpu/drm/drm_sysfs.c +@@ -486,7 +486,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 7ad3faa..1d9a229 100644 +--- a/include/drm/drmP.h ++++ b/include/drm/drmP.h +@@ -55,6 +55,7 @@ + #include + #include + #include ++#include + #if defined(__alpha__) || defined(__powerpc__) + #include /* For pte_wrprotect */ + #endif +@@ -143,6 +144,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... */ +@@ -1008,6 +1010,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 */ +@@ -1118,12 +1121,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 int drm_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +@@ -1342,6 +1353,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.32/0007-glamo-drm-select-DRM_KMS_HELPER-for-crtc-functions.patch b/recipes/linux/linux-openmoko-2.6.32/0007-glamo-drm-select-DRM_KMS_HELPER-for-crtc-functions.patch deleted file mode 100644 index 68751942a9..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0007-glamo-drm-select-DRM_KMS_HELPER-for-crtc-functions.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 8bb128ce2474ef10c71ea681fa01e574c1ec3114 Mon Sep 17 00:00:00 2001 -From: Martin Jansa -Date: Tue, 22 Dec 2009 16:32:57 +0100 -Subject: [PATCH 7/9] glamo-drm: select DRM_KMS_HELPER for crtc functions - ---- - drivers/mfd/glamo/Kconfig | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - -diff --git a/drivers/mfd/glamo/Kconfig b/drivers/mfd/glamo/Kconfig -index 214c70e..a12ebf6 100644 ---- a/drivers/mfd/glamo/Kconfig -+++ b/drivers/mfd/glamo/Kconfig -@@ -47,6 +47,7 @@ config 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). --- -1.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0008-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch b/recipes/linux/linux-openmoko-2.6.32/0008-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch deleted file mode 100644 index f5da51ed95..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0008-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch +++ /dev/null @@ -1,38 +0,0 @@ -From f6debba05c9f6d0337324dc0eaa8e892d2d3142b Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Thu, 4 Feb 2010 00:07:25 +0100 -Subject: [PATCH 8/9] 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 ---- - 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 a614d44..faaf269 100644 ---- a/drivers/mfd/glamo/glamo-core.c -+++ b/drivers/mfd/glamo/glamo-core.c -@@ -90,14 +90,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.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0008-Glamo-DRM-and-KMS-driver.patch b/recipes/linux/linux-openmoko-2.6.32/0008-Glamo-DRM-and-KMS-driver.patch new file mode 100644 index 0000000000..cb680f1ed5 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0008-Glamo-DRM-and-KMS-driver.patch @@ -0,0 +1,3869 @@ +From ee4092984dd817e3889a48c0b51c173e96c71913 Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Tue, 20 Oct 2009 16:14:55 +0200 +Subject: [PATCH 08/14] 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 +Signed-off-by: Martin Jansa +--- + 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 ++ * ++ * 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 . ++ * ++ * ++ * 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 ++ */ ++ ++ ++#include ++#include ++ ++#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 ++ * ++ * 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 ++ ++#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 ++ * Copyright (C) 2009 Andreas Pokorny ++ * 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 ++ * ++ * 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 ++#include ++ ++#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 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 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; icmdq_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; isize; 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 ++ * Copyright (c) 2009 Andreas Pokorny ++ * Based on xf86-video-glamo ++ * Copyright 2007 OpenMoko, Inc. ++ * Copyright © 2009 Lars-Peter Clausen ++ * ++ * 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 ++ ++#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 ++ * ++ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc. ++ * Author: Harald Welte ++ * 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 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; inum_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 ++ * ++ * 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 ++#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 ++ * Copyright (C) 2008-2009 Thomas White ++ * Copyright (C) 2009 Andreas Pokorny ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#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 ++ * Copyright (C) 2009 Andreas Pokorny ++ * Based on xf86-video-glamo ++ * Copyright 2007 OpenMoko, Inc. ++ * Copyright © 2009 Lars-Peter Clausen ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#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 ++ * ++ * 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 . ++ * ++ * ++ * 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 ++ * Dave Airlie ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ * ++ * 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 . ++ * ++ */ ++ ++#ifndef __GLAMO_FENCE_H ++#define __GLAMO_FENCE_H ++ ++#include ++ ++#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 ++ * ++ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc. ++ * Author: Harald Welte ++ * 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 ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#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 ++ * ++ * Based on glamo-fb.c (C) 2007-2008 by Openmoko, Inc. ++ * Author: Harald Welte ++ * 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 ++#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 b940fdf..48b7b55 100644 +--- a/include/drm/Kbuild ++++ b/include/drm/Kbuild +@@ -8,3 +8,4 @@ unifdef-y += radeon_drm.h + unifdef-y += sis_drm.h + unifdef-y += savage_drm.h + unifdef-y += via_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 ++ * Kevin E. Martin ++ * Gareth Hughes ++ * Keith Whitwell ++ */ ++ ++#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.32/0009-A-couple-of-GEM-refcounting-fixes.patch b/recipes/linux/linux-openmoko-2.6.32/0009-A-couple-of-GEM-refcounting-fixes.patch deleted file mode 100644 index 7fb2fa8762..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0009-A-couple-of-GEM-refcounting-fixes.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 75e660e406cd4941f253ddbdeaa45f59d107a014 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sun, 7 Feb 2010 13:27:20 +0100 -Subject: [PATCH 9/9] A couple of GEM refcounting fixes - -This allows GEM objects to be freed properly, which wasn't working before. - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-buffer.c | 2 ++ - drivers/mfd/glamo/glamo-drm-drv.c | 2 ++ - 2 files changed, 4 insertions(+), 0 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-buffer.c b/drivers/mfd/glamo/glamo-buffer.c -index 45500d3..82ea0c0 100644 ---- a/drivers/mfd/glamo/glamo-buffer.c -+++ b/drivers/mfd/glamo/glamo-buffer.c -@@ -245,6 +245,8 @@ int glamo_ioctl_gem_mmap(struct drm_device *dev, void *data, - 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; - } -diff --git a/drivers/mfd/glamo/glamo-drm-drv.c b/drivers/mfd/glamo/glamo-drm-drv.c -index 1907a47..e7220d5 100644 ---- a/drivers/mfd/glamo/glamo-drm-drv.c -+++ b/drivers/mfd/glamo/glamo-drm-drv.c -@@ -153,6 +153,8 @@ static int glamodrm_unload(struct drm_device *dev) - - 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 = { --- -1.6.6.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0009-Work-on-Glamo-core-for-DRM.patch b/recipes/linux/linux-openmoko-2.6.32/0009-Work-on-Glamo-core-for-DRM.patch new file mode 100644 index 0000000000..79b0ea4e0f --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0009-Work-on-Glamo-core-for-DRM.patch @@ -0,0 +1,162 @@ +From 8cdc10f17ecd42c9f71f7f4a657d979ab20f9e26 Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Tue, 17 Nov 2009 23:45:29 +0100 +Subject: [PATCH 09/14] 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 +--- + 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 82a906a..74da668 100644 +--- a/drivers/mfd/glamo/glamo-core.c ++++ b/drivers/mfd/glamo/glamo-core.c +@@ -221,10 +221,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[] = { +@@ -235,9 +250,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 + ++/* 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.32/0010-JBT6k74-work-for-KMS.patch b/recipes/linux/linux-openmoko-2.6.32/0010-JBT6k74-work-for-KMS.patch new file mode 100644 index 0000000000..2105ab60aa --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0010-JBT6k74-work-for-KMS.patch @@ -0,0 +1,540 @@ +From 5788c7777a6fa63dc56093277ae14c4dac4ee6cb Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Sat, 21 Nov 2009 21:42:16 +0100 +Subject: [PATCH 10/14] 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 +--- + 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 + ++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.32/0010-Simplify-the-JBT6k74-driver.patch b/recipes/linux/linux-openmoko-2.6.32/0010-Simplify-the-JBT6k74-driver.patch deleted file mode 100644 index ca0c2af15b..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0010-Simplify-the-JBT6k74-driver.patch +++ /dev/null @@ -1,667 +0,0 @@ -From cb84d543b9465a8ea48b504b1dfb745c6682a50e Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sun, 28 Feb 2010 15:20:57 +0100 -Subject: [PATCH 1/2] Simplify the JBT6k74 driver - -Simplify the JBT6k74 driver - -We don't need all the native sleep states - they just complicate things. Instead, just -use the LDO "power switch" to send it firmly to sleep and wake it up in a virgin state -each time. - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-display.c | 116 ++++++++++++------------ - drivers/mfd/glamo/glamo-display.h | 2 + - drivers/mfd/glamo/glamo-drm-private.h | 10 ++ - drivers/mfd/glamo/glamo-kms-fb.c | 54 ++---------- - drivers/video/backlight/jbt6k74.c | 160 ++++++++------------------------- - include/linux/jbt6k74.h | 1 - - 6 files changed, 115 insertions(+), 228 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index 34a665a..a384c8b 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -239,14 +239,6 @@ static int glamo_run_lcd_script(struct glamodrm_handle *gdrm, - } - - --extern void jbt6k74_action(int val); -- --/* Power on/off */ --static void glamo_crtc_dpms(struct drm_crtc *crtc, int mode) --{ --} -- -- - static bool glamo_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -@@ -275,6 +267,12 @@ static int glamo_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - gcrtc = to_glamo_crtc(crtc); - gdrm = gcrtc->gdrm; /* Here it is! */ - -+ if ( !gcrtc->pixel_clock_on ) { -+ printk(KERN_WARNING "[glamo-drm] Refusing to set base while " -+ "pixel clock is off.\n"); -+ return -EBUSY; -+ } -+ - gfb = to_glamo_framebuffer(crtc->fb); - obj = gfb->obj; - gobj = obj->driver_private; -@@ -306,6 +304,12 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - gcrtc = to_glamo_crtc(crtc); - gdrm = gcrtc->gdrm; /* Here it is! */ - -+ if ( !gcrtc->pixel_clock_on ) { -+ printk(KERN_WARNING "[glamo-drm] Refusing to set mode while " -+ "pixel clock is off.\n"); -+ return -EBUSY; -+ } -+ - glamo_lcd_cmd_mode(gdrm, 1); - - glamo_engine_reclock(gdrm->glamo_core, GLAMO_ENGINE_LCD, mode->clock); -@@ -354,10 +358,47 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - - glamo_crtc_mode_set_base(crtc, 0, 0, old_fb); - -+ gcrtc->current_mode = *mode; -+ gcrtc->current_mode_set = 1; -+ gcrtc->current_fb = old_fb; -+ - return 0; - } - - -+extern void jbt6k74_action(int val); -+ -+/* 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 */ -+} -+ -+ -+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_action(1); -+ if ( gcrtc->current_mode_set ) { -+ glamo_crtc_mode_set(crtc, &gcrtc->current_mode, -+ &gcrtc->current_mode, 0, 0, -+ gcrtc->current_fb); -+ } -+ } else { -+ jbt6k74_action(0); -+ glamo_engine_suspend(gdrm->glamo_core, GLAMO_ENGINE_LCD); -+ gcrtc->pixel_clock_on = 0; -+ } -+} -+ -+ - static void glamo_crtc_prepare(struct drm_crtc *crtc) - { - } -@@ -725,6 +766,8 @@ int glamo_display_init(struct drm_device *dev) - + 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); -@@ -808,62 +851,23 @@ int glamo_display_init(struct drm_device *dev) - - void glamo_display_suspend(struct glamodrm_handle *gdrm) - { -- gdrm->saved_width = reg_read_lcd(gdrm, GLAMO_REG_LCD_WIDTH); -- gdrm->saved_height = reg_read_lcd(gdrm, GLAMO_REG_LCD_HEIGHT); -- gdrm->saved_pitch = reg_read_lcd(gdrm, GLAMO_REG_LCD_PITCH); -- gdrm->saved_htotal = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_TOTAL); -- gdrm->saved_hrtrst = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_START); -- gdrm->saved_hrtren = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_END); -- gdrm->saved_hdspst = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_START); -- gdrm->saved_hdspen = reg_read_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_END); -- gdrm->saved_vtotal = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_TOTAL); -- gdrm->saved_vrtrst = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_START); -- gdrm->saved_vrtren = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_END); -- gdrm->saved_vdspst = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_START); -- gdrm->saved_vdspen = reg_read_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_END); -+ /* 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)); - -- /* Restore timings */ -- glamo_lcd_cmd_mode(gdrm, 1); -- glamo_engine_reclock(gdrm->glamo_core, GLAMO_ENGINE_LCD, -- gdrm->saved_clock); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_WIDTH, GLAMO_LCD_WIDTH_MASK, -- gdrm->saved_width); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HEIGHT, GLAMO_LCD_HEIGHT_MASK, -- gdrm->saved_height); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_PITCH, GLAMO_LCD_PITCH_MASK, -- gdrm->saved_pitch); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_TOTAL, -- GLAMO_LCD_HV_TOTAL_MASK, gdrm->saved_htotal); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_START, -- GLAMO_LCD_HV_RETR_START_MASK, gdrm->saved_hrtrst); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_RETR_END, -- GLAMO_LCD_HV_RETR_END_MASK, gdrm->saved_hrtren); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_START, -- GLAMO_LCD_HV_RETR_DISP_START_MASK, -- gdrm->saved_hdspst); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_HORIZ_DISP_END, -- GLAMO_LCD_HV_RETR_DISP_END_MASK, -- gdrm->saved_hdspen); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_TOTAL, -- GLAMO_LCD_HV_TOTAL_MASK, gdrm->saved_vtotal); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_START, -- GLAMO_LCD_HV_RETR_START_MASK, gdrm->saved_vrtrst); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_RETR_END, -- GLAMO_LCD_HV_RETR_END_MASK, gdrm->saved_vrtren); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_START, -- GLAMO_LCD_HV_RETR_DISP_START_MASK, -- gdrm->saved_vdspst); -- reg_set_bit_mask_lcd(gdrm, GLAMO_REG_LCD_VERT_DISP_END, -- GLAMO_LCD_HV_RETR_DISP_END_MASK, -- gdrm->saved_vdspen); -- glamo_lcd_cmd_mode(gdrm, 0); -+ 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 -index d6f21bc..728bba5 100644 ---- a/drivers/mfd/glamo/glamo-display.h -+++ b/drivers/mfd/glamo/glamo-display.h -@@ -36,4 +36,6 @@ extern int glamo_framebuffer_create(struct drm_device *dev, - 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-private.h b/drivers/mfd/glamo/glamo-drm-private.h -index 7949a2e..4e24019 100644 ---- a/drivers/mfd/glamo/glamo-drm-private.h -+++ b/drivers/mfd/glamo/glamo-drm-private.h -@@ -106,6 +106,9 @@ struct glamodrm_handle { - - /* A scratch block */ - struct drm_mm_node *scratch; -+ -+ /* We only have one */ -+ struct drm_crtc *crtc; - }; - - -@@ -118,11 +121,18 @@ struct drm_glamo_gem_object { - - - 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; - }; - - -diff --git a/drivers/mfd/glamo/glamo-kms-fb.c b/drivers/mfd/glamo/glamo-kms-fb.c -index 61cd605..57680ed 100644 ---- a/drivers/mfd/glamo/glamo-kms-fb.c -+++ b/drivers/mfd/glamo/glamo-kms-fb.c -@@ -253,64 +253,24 @@ static void glamofb_on(struct fb_info *info) - { - struct glamofb_par *par = info->par; - struct drm_device *dev = par->dev; -- struct drm_crtc *crtc; -- struct drm_encoder *encoder; -- int i; -+ struct glamodrm_handle *gdrm = dev->dev_private; - -- /* -- * For each CRTC in this fb, find all associated encoders -- * and turn them off, then turn off the CRTC. -- */ -- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -- struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; -+ printk(KERN_CRIT "Turning on display...\n"); - -- for (i = 0; i < par->crtc_count; i++) -- if (crtc->base.id == par->crtc_ids[i]) -- break; -- -- crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); -+ gdrm = dev->dev_private; - -- /* Found a CRTC on this fb, now find encoders */ -- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -- if (encoder->crtc == crtc) { -- struct drm_encoder_helper_funcs *encoder_funcs; -- encoder_funcs = encoder->helper_private; -- encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); -- } -- } -- } -+ 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 drm_crtc *crtc; -- struct drm_encoder *encoder; -- int i; -+ struct glamodrm_handle *gdrm = dev->dev_private; - -- /* -- * For each CRTC in this fb, find all associated encoders -- * and turn them off, then turn off the CRTC. -- */ -- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -- struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; -- -- for (i = 0; i < par->crtc_count; i++) -- if (crtc->base.id == par->crtc_ids[i]) -- break; -+ printk(KERN_CRIT "Turning off display...\n"); - -- /* Found a CRTC on this fb, now find encoders */ -- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { -- if (encoder->crtc == crtc) { -- struct drm_encoder_helper_funcs *encoder_funcs; -- encoder_funcs = encoder->helper_private; -- encoder_funcs->dpms(encoder, dpms_mode); -- } -- } -- if (dpms_mode == DRM_MODE_DPMS_OFF) -- crtc_funcs->dpms(crtc, dpms_mode); -- } -+ glamo_lcd_power(gdrm, 0); - } - - static int glamofb_blank(int blank, struct fb_info *info) -diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c -index 95ca4cf..9554cb3 100644 ---- a/drivers/video/backlight/jbt6k74.c -+++ b/drivers/video/backlight/jbt6k74.c -@@ -107,14 +107,12 @@ enum jbt_resolution { - }; - - enum jbt_power_mode { -- JBT_POWER_MODE_DEEP_STANDBY, -- JBT_POWER_MODE_SLEEP, -+ JBT_POWER_MODE_OFF, - 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_NORMAL] = "normal", - }; - -@@ -260,14 +258,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(30); -+ - /* three times command zero */ - ret |= jbt_reg_write_nodata(jbt, 0x00); - mdelay(1); -@@ -279,18 +281,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,53 +315,34 @@ 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 */ -- mdelay(5); -- -- return ret ? -EIO : 0; --} -- --static int jbt_normal_to_sleep(struct jbt_info *jbt) --{ -- int ret; -- -- /* Make sure we are 120 ms after SLEEP_OUT */ -- while (time_before(jiffies, jbt->next_sleep)) -- cpu_relax(); -- -- 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); - -- /* Allow the internal circuits to stop automatically */ -+ /* Allow the booster and display controller to restart stably */ - mdelay(5); - - return ret ? -EIO : 0; - } - --static int jbt_sleep_to_standby(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; - -- ret = jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x00); -- -- if (!ret) -- ret = regulator_bulk_disable(ARRAY_SIZE(jbt->supplies), jbt->supplies); -+ /* 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; -+ 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 +351,14 @@ 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: -- switch (new_mode) { -- case JBT_POWER_MODE_DEEP_STANDBY: -- 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); -- break; -- } -- break; -- case JBT_POWER_MODE_SLEEP: -+ case JBT_POWER_MODE_OFF: - switch (new_mode) { -- case JBT_POWER_MODE_SLEEP: -+ case JBT_POWER_MODE_OFF: - 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); -+ ret = jbt_off_to_normal(jbt); - break; - } - break; -@@ -426,25 +367,16 @@ 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); -- break; -- case JBT_POWER_MODE_SLEEP: -- ret = jbt_normal_to_sleep(jbt); -+ case JBT_POWER_MODE_OFF: -+ ret = jbt_normal_to_off(jbt); - 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); - } - -@@ -470,8 +402,8 @@ static int jbt6k74_set_resolution(struct jbt_info *jbt, - if (jbt->power_mode == JBT_POWER_MODE_NORMAL) { - - /* first transition into sleep */ -- ret = jbt_normal_to_sleep(jbt); -- ret |= jbt_sleep_to_normal(jbt); -+ ret = jbt_normal_to_off(jbt); -+ ret |= jbt_off_to_normal(jbt); - - if (ret) { - jbt->resolution = old_resolution; -@@ -589,7 +521,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 +548,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 +560,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 +582,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 +602,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; - } - } - -@@ -707,7 +618,7 @@ void jbt6k74_action(int val) - return; - } - if ( val == 0 ) { -- jbt6k74_enter_power_mode(jbt_global, JBT_POWER_MODE_SLEEP); -+ jbt6k74_enter_power_mode(jbt_global, JBT_POWER_MODE_OFF); - } else { - jbt6k74_enter_power_mode(jbt_global, JBT_POWER_MODE_NORMAL); - } -@@ -756,11 +667,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); -@@ -856,9 +765,10 @@ 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; -+ printk(KERN_CRIT "[jbt] powering off for suspend\n"); -+ jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF); - -+ printk(KERN_CRIT "[jbt] done\n"); - dev_info(&spi->dev, "suspended\n"); - - return 0; -@@ -869,11 +779,13 @@ int jbt6k74_resume(struct spi_device *spi) - struct jbt_info *jbt = dev_get_drvdata(&spi->dev); - dev_info(&spi->dev, "starting resume: %d\n", jbt->suspend_mode); - -+ printk(KERN_CRIT "[jbt] powering on for resume\n"); - mdelay(20); - - jbt->suspended = 0; - jbt6k74_enter_power_mode(jbt, jbt->suspend_mode); - -+ printk(KERN_CRIT "[jbt] done\n"); - dev_info(&spi->dev, "resumed: %d\n", jbt->suspend_mode); - - return 0; -diff --git a/include/linux/jbt6k74.h b/include/linux/jbt6k74.h -index 75488c4..4cf760c 100644 ---- a/include/linux/jbt6k74.h -+++ b/include/linux/jbt6k74.h -@@ -13,7 +13,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.0 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0011-Don-t-choke-if-userspace-provides-a-pixel-clock-valu.patch b/recipes/linux/linux-openmoko-2.6.32/0011-Don-t-choke-if-userspace-provides-a-pixel-clock-valu.patch deleted file mode 100644 index 0d47c1263f..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0011-Don-t-choke-if-userspace-provides-a-pixel-clock-valu.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 73abadc5bbbc94f4ca67effc5f5c3174c2a3ac48 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sun, 28 Feb 2010 15:40:13 +0100 -Subject: [PATCH 2/2] Don't choke if userspace provides a pixel clock value - -Don't choke if userspace provides a pixel clock value - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-kms-fb.c | 46 ++++++++++++++++++------------------- - 1 files changed, 22 insertions(+), 24 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-kms-fb.c b/drivers/mfd/glamo/glamo-kms-fb.c -index 57680ed..6d4b9ea 100644 ---- a/drivers/mfd/glamo/glamo-kms-fb.c -+++ b/drivers/mfd/glamo/glamo-kms-fb.c -@@ -177,37 +177,35 @@ static int glamofb_set_par(struct fb_info *info) - 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); -+ } - -- DRM_ERROR("PIXEL CLOCK SET\n"); -- return -EINVAL; -- } else { -- struct drm_crtc *crtc; -- int ret; -- -- 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; -- } -+ 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; - } -+ return 0; - } - - static int glamofb_pan_display(struct fb_var_screeninfo *var, --- -1.7.0 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0011-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch b/recipes/linux/linux-openmoko-2.6.32/0011-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch new file mode 100644 index 0000000000..0f5af99025 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0011-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch @@ -0,0 +1,38 @@ +From d5f07ab010afe531312874872904fe1a0348ee2b Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Thu, 4 Feb 2010 00:07:25 +0100 +Subject: [PATCH 11/14] 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 +--- + 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 74da668..33a7a59 100644 +--- a/drivers/mfd/glamo/glamo-core.c ++++ b/drivers/mfd/glamo/glamo-core.c +@@ -90,14 +90,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.32/0012-Fix-dynamic-command-queue-allocation.patch b/recipes/linux/linux-openmoko-2.6.32/0012-Fix-dynamic-command-queue-allocation.patch new file mode 100644 index 0000000000..68e170a588 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0012-Fix-dynamic-command-queue-allocation.patch @@ -0,0 +1,131 @@ +From 3b328bfd4aef3ffc9b0192ca512fbc899148e8f5 Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Thu, 6 May 2010 05:40:15 -0700 +Subject: [PATCH 12/14] Fix dynamic command queue allocation + +(Not tested...) + +Signed-off-by: Thomas White +--- + 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.32/0012-Report-all-FB-modes-given-by-the-lower-levels.patch b/recipes/linux/linux-openmoko-2.6.32/0012-Report-all-FB-modes-given-by-the-lower-levels.patch deleted file mode 100644 index ac19dbd5d2..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0012-Report-all-FB-modes-given-by-the-lower-levels.patch +++ /dev/null @@ -1,102 +0,0 @@ -From f8448d2b08c1e2cfd0faba0e3f3c4c58e4ecaa9f Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Mon, 1 Mar 2010 22:02:26 +0100 -Subject: [PATCH 12/13] Report all FB modes given by the lower levels - -Signed-off-by: Thomas White -Signed-off-by: Martin Jansa ---- - drivers/mfd/glamo/glamo-display.c | 70 +++++++++++++++++++++---------------- - 1 files changed, 40 insertions(+), 30 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index a384c8b..bc3a2ea 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -456,43 +456,53 @@ static void glamo_connector_destroy(struct drm_connector *connector) - - static int glamo_connector_get_modes(struct drm_connector *connector) - { -- struct drm_display_mode *mode; - 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; - -- mode = drm_mode_create(connector->dev); -- if (!mode) -- return 0; -- /* Fill in 'mode' here */ -- mode->type = DRM_MODE_TYPE_DEFAULT | DRM_MODE_TYPE_PREFERRED; -- -- /* Convert framebuffer timings into KMS timings */ -- mode->clock = 1000000000UL / fb_info->modes[0].pixclock; /* ps -> kHz */ -- mode->clock *= 1000; /* kHz -> Hz */ -- mode->hdisplay = fb_info->modes[0].xres; -- mode->hsync_start = fb_info->modes[0].right_margin + mode->hdisplay; -- mode->hsync_end = mode->hsync_start + fb_info->modes[0].hsync_len; -- mode->htotal = mode->hsync_end + fb_info->modes[0].left_margin; -- mode->hskew = 0; -- -- mode->vdisplay = fb_info->modes[0].yres; -- mode->vsync_start = fb_info->modes[0].lower_margin + mode->vdisplay; -- mode->vsync_end = mode->vsync_start + fb_info->modes[0].vsync_len; -- mode->vtotal = mode->vsync_end + fb_info->modes[0].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 1; /* one mode, for now */ -+ for ( i=0; inum_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; - } - - --- -1.7.0 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0013-Change-connector-type-to-LVDS.patch b/recipes/linux/linux-openmoko-2.6.32/0013-Change-connector-type-to-LVDS.patch deleted file mode 100644 index 1b8c653607..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0013-Change-connector-type-to-LVDS.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 3f1154d1b763f981791ad54987e61d31bde43ec7 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Mon, 1 Mar 2010 22:07:57 +0100 -Subject: [PATCH 13/13] Change connector type to LVDS - -Not certain that this exactly describes what it is, and too lazy to check, but for sure -it's a better description than "Unknown". - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-display.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index bc3a2ea..5f04697 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -795,7 +795,7 @@ int glamo_display_init(struct drm_device *dev) - - /* Initialise the connector */ - drm_connector_init(dev, connector, &glamo_connector_funcs, -- DRM_MODE_CONNECTOR_Unknown); -+ DRM_MODE_CONNECTOR_LVDS); - drm_sysfs_connector_add(connector); - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; --- -1.7.0 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0013-Debug-statements-for-testing.patch b/recipes/linux/linux-openmoko-2.6.32/0013-Debug-statements-for-testing.patch new file mode 100644 index 0000000000..cc58d1629e --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0013-Debug-statements-for-testing.patch @@ -0,0 +1,78 @@ +From fa66cc905714c94bca6580c9adf7b57fa3b1da02 Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Thu, 6 May 2010 05:40:33 -0700 +Subject: [PATCH 13/14] Debug statements for testing + +Signed-off-by: Thomas White +--- + 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.32/0014-Clean-up-JBT-hooks-and-allow-resolution-switching.patch b/recipes/linux/linux-openmoko-2.6.32/0014-Clean-up-JBT-hooks-and-allow-resolution-switching.patch deleted file mode 100644 index 287214b677..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0014-Clean-up-JBT-hooks-and-allow-resolution-switching.patch +++ /dev/null @@ -1,223 +0,0 @@ -From 644aab63501ebff3e819fd25d23c63da6b6e1cf5 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Mon, 8 Mar 2010 23:51:09 +0100 -Subject: [PATCH 14/15] Clean up JBT hooks, and allow resolution switching - -Signed-off-by: Thomas White ---- - arch/arm/mach-s3c2442/mach-gta02.c | 2 +- - drivers/mfd/glamo/glamo-display.c | 17 ++++++---- - drivers/video/backlight/jbt6k74.c | 57 +++++++++++++++++++---------------- - include/linux/jbt6k74.h | 14 +++++++++ - 4 files changed, 56 insertions(+), 34 deletions(-) - -diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c -index a969037..b580b1b 100644 ---- a/arch/arm/mach-s3c2442/mach-gta02.c -+++ b/arch/arm/mach-s3c2442/mach-gta02.c -@@ -229,7 +229,7 @@ static struct fb_videomode gta02_glamo_modes[] = { - .name = "240x320", - .xres = 240, - .yres = 320, -- .pixclock = 40816, -+ .pixclock = 100000, - .left_margin = 8, - .right_margin = 16, - .upper_margin = 2, -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index 5f04697..e56cdb4 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -52,19 +52,18 @@ - * - */ - --#define DEBUG 1 -- - #include - #include - #include - #include -+#include -+#include - - #include "glamo-core.h" - #include "glamo-drm-private.h" - #include "glamo-regs.h" - #include "glamo-kms-fb.h" - #include "glamo-display.h" --#include - - - #define GLAMO_LCD_WIDTH_MASK 0x03FF -@@ -356,6 +355,12 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - - glamo_lcd_cmd_mode(gdrm, 0); - -+ if ( mode->hdisplay == 240 ) { -+ jbt6k74_setresolution(JBT_RESOLUTION_QVGA); -+ } else { -+ jbt6k74_setresolution(JBT_RESOLUTION_VGA); -+ } -+ - glamo_crtc_mode_set_base(crtc, 0, 0, old_fb); - - gcrtc->current_mode = *mode; -@@ -366,8 +371,6 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - } - - --extern void jbt6k74_action(int val); -- - /* 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) */ -@@ -385,14 +388,14 @@ void glamo_lcd_power(struct glamodrm_handle *gdrm, int mode) - if ( mode ) { - glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_LCD); - gcrtc->pixel_clock_on = 1; -- jbt6k74_action(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_action(0); -+ 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 9554cb3..40c31f6 100644 ---- a/drivers/video/backlight/jbt6k74.c -+++ b/drivers/video/backlight/jbt6k74.c -@@ -101,15 +101,6 @@ enum jbt_register { - JBT_REG_HCLOCK_QVGA = 0xed, - }; - --enum jbt_resolution { -- JBT_RESOLUTION_VGA, -- JBT_RESOLUTION_QVGA, --}; -- --enum jbt_power_mode { -- JBT_POWER_MODE_OFF, -- JBT_POWER_MODE_NORMAL, --}; - - static const char *jbt_power_mode_names[] = { - [JBT_POWER_MODE_OFF] = "off", -@@ -121,6 +112,7 @@ static const char *jbt_resolution_names[] = { - [JBT_RESOLUTION_QVGA] = "qvga", - }; - -+ - struct jbt_info { - struct mutex lock; /* protects this structure */ - enum jbt_resolution resolution; -@@ -139,6 +131,8 @@ struct jbt_info { - uint16_t reg_cache[0xEE]; - }; - -+struct jbt_info *jbt_global; -+ - #define JBT_COMMAND 0x000 - #define JBT_DATA 0x100 - -@@ -401,8 +395,9 @@ static int jbt6k74_set_resolution(struct jbt_info *jbt, - - if (jbt->power_mode == JBT_POWER_MODE_NORMAL) { - -- /* first transition into sleep */ -+ /* "Reboot" the LCM */ - ret = jbt_normal_to_off(jbt); -+ mdelay(1000); - ret |= jbt_off_to_normal(jbt); - - if (ret) { -@@ -609,22 +604,6 @@ static int jbt6k74_get_power(struct lcd_device *ld) - } - } - --/* This is utterly, totally horrible. I'm REALLY sorry... */ --struct jbt_info *jbt_global; --void jbt6k74_action(int val) --{ -- if ( !jbt_global ) { -- printk(KERN_CRIT "JBT not initialised!!!\n"); -- return; -- } -- if ( val == 0 ) { -- jbt6k74_enter_power_mode(jbt_global, JBT_POWER_MODE_OFF); -- } else { -- jbt6k74_enter_power_mode(jbt_global, JBT_POWER_MODE_NORMAL); -- } --} --EXPORT_SYMBOL_GPL(jbt6k74_action); -- - struct lcd_ops jbt6k74_lcd_ops = { - .set_power = jbt6k74_set_power, - .get_power = jbt6k74_get_power, -@@ -758,6 +737,32 @@ static int __devexit jbt_remove(struct spi_device *spi) - return 0; - } - -+/* Begin horrible layering violations (in the interest of making stuff work) */ -+ -+int jbt6k74_setresolution(enum jbt_resolution new_resolution) -+{ -+ if ( !jbt_global ) { -+ printk(KERN_CRIT "JBT not initialised!!!\n"); -+ return -1; -+ } -+ jbt6k74_set_resolution(jbt_global, new_resolution); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(jbt6k74_setresolution); -+ -+/* This is utterly, totally horrible. I'm REALLY sorry... */ -+void jbt6k74_setpower(enum jbt_power_mode new_power) -+{ -+ if ( !jbt_global ) { -+ printk(KERN_CRIT "JBT not initialised!!!\n"); -+ 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) - { -diff --git a/include/linux/jbt6k74.h b/include/linux/jbt6k74.h -index 4cf760c..f430e5a 100644 ---- a/include/linux/jbt6k74.h -+++ b/include/linux/jbt6k74.h -@@ -3,6 +3,20 @@ - - #include - -+enum jbt_resolution { -+ JBT_RESOLUTION_VGA, -+ JBT_RESOLUTION_QVGA, -+}; -+ -+enum jbt_power_mode { -+ JBT_POWER_MODE_OFF, -+ JBT_POWER_MODE_NORMAL, -+}; -+ -+extern void jbt6k74_setpower(enum jbt_power_mode new_power); -+extern int jbt6k74_setresolution(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 --- -1.7.0.2 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0014-Fix-claim-of-2D-register-resource.patch b/recipes/linux/linux-openmoko-2.6.32/0014-Fix-claim-of-2D-register-resource.patch new file mode 100644 index 0000000000..8dbdb70ce2 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0014-Fix-claim-of-2D-register-resource.patch @@ -0,0 +1,27 @@ +From 8c753fc51c4954b88bacba68ee0591fb673de8fc Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Thu, 6 May 2010 08:37:04 -0700 +Subject: [PATCH 14/14] Fix claim of 2D register resource + +Signed-off-by: Thomas White +--- + 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.32/0015-Enable-display-before-trying-to-set-mode-or-base.patch b/recipes/linux/linux-openmoko-2.6.32/0015-Enable-display-before-trying-to-set-mode-or-base.patch deleted file mode 100644 index 4e7a8abe10..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0015-Enable-display-before-trying-to-set-mode-or-base.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 986df52e3e02a1cdbf43eafccd483f0f86c696bd Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sun, 21 Mar 2010 20:55:18 +0100 -Subject: [PATCH 16/16] Enable display before trying to set mode or base - -This makes it possible to change mode when the screen is switched off. - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-display.c | 56 ++++++++++++++++++------------------ - 1 files changed, 28 insertions(+), 28 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index e56cdb4..24a1a31 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -267,9 +267,9 @@ static int glamo_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - gdrm = gcrtc->gdrm; /* Here it is! */ - - if ( !gcrtc->pixel_clock_on ) { -- printk(KERN_WARNING "[glamo-drm] Refusing to set base while " -- "pixel clock is off.\n"); -- return -EBUSY; -+ 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); -@@ -304,9 +304,9 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - gdrm = gcrtc->gdrm; /* Here it is! */ - - if ( !gcrtc->pixel_clock_on ) { -- printk(KERN_WARNING "[glamo-drm] Refusing to set mode while " -- "pixel clock is off.\n"); -- return -EBUSY; -+ printk(KERN_WARNING "[glamo-drm] Display is off - " -+ "enabling it before setting mode.\n"); -+ glamo_lcd_power(gdrm, 1); - } - - glamo_lcd_cmd_mode(gdrm, 1); -@@ -380,28 +380,6 @@ static void glamo_crtc_dpms(struct drm_crtc *crtc, int mode) - } - - --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; -- } --} -- -- - static void glamo_crtc_prepare(struct drm_crtc *crtc) - { - } -@@ -862,6 +840,28 @@ int glamo_display_init(struct drm_device *dev) - } - - -+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 */ --- -1.7.0.2 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0016-accels.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0016-accels.patch.patch deleted file mode 100644 index 16f379d055..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0016-accels.patch.patch +++ /dev/null @@ -1,1759 +0,0 @@ -From a08be3ba79daf13baca6de98249c8784ede31297 Mon Sep 17 00:00:00 2001 -From: Radek Polak -Date: Fri, 9 Apr 2010 09:15:40 +0200 -Subject: [PATCH 17/22] 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 ---- - arch/arm/mach-s3c2410/include/mach/spi-gpio.h | 3 +- - arch/arm/mach-s3c2442/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 980a099..0ff8949 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-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c -index b580b1b..8e3cc91 100644 ---- a/arch/arm/mach-s3c2442/mach-gta02.c -+++ b/arch/arm/mach-s3c2442/mach-gta02.c -@@ -63,6 +63,7 @@ - - #include - #include -+#include - - #include - #include -@@ -120,6 +121,22 @@ - #include - #endif - -+#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 -+ - struct pcf50633 *gta02_pcf; - - /* -@@ -776,6 +793,60 @@ const 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", -@@ -786,6 +857,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 = >a02_spigpio_cfg, -+ }, - }; - - /* BQ27000 Battery */ -@@ -1136,6 +1282,7 @@ static struct platform_device *gta02_devices[] __initdata = { - static struct platform_device *gta02_devices_pmu_children[] = { - &s3c_device_ts, - >a02_glamo_dev, -+ >a02_spi_gpio_dev, - &s3c_device_timer[2], - >a02_hdq_device, - >a02_gps_userspace_consumer -@@ -1362,6 +1509,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; -+ } -+ - s3c_device_ts.name = "s3c2440-ts"; - - bus_register_notifier(&platform_bus_type, >a02_device_register_notifier); -diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig -index a9bb254..2e9bc5d 100644 ---- a/drivers/input/misc/Kconfig -+++ b/drivers/input/misc/Kconfig -@@ -317,4 +317,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 a8b8485..7e63293 100644 ---- a/drivers/input/misc/Makefile -+++ b/drivers/input/misc/Makefile -@@ -30,4 +30,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 -+ * converted to private bitbang by: -+ * Andy Green -+ * ability to set acceleration threshold added by: -+ * Simon Kagstrom -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+/* 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 "); -+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 f1db395..5c50329 100644 ---- a/drivers/spi/spi_bitbang.c -+++ b/drivers/spi/spi_bitbang.c -@@ -253,134 +253,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; -@@ -455,6 +460,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 -+#include -+#include -+#include -+ -+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 97b60b3..9c5484b 100644 ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -291,6 +291,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); - }; -@@ -542,6 +549,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 eed4254..f92de01 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.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0017-usbhost.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0017-usbhost.patch.patch deleted file mode 100644 index 16ed3746fd..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0017-usbhost.patch.patch +++ /dev/null @@ -1,233 +0,0 @@ -From d71534de6ab5e132d0798df30408042c229cbb62 Mon Sep 17 00:00:00 2001 -From: Radek Polak -Date: Fri, 9 Apr 2010 09:17:28 +0200 -Subject: [PATCH 18/22] 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 ---- - arch/arm/mach-s3c2442/Makefile | 1 + - arch/arm/mach-s3c2442/mach-gta02.c | 50 ++++++++++++++++++++++++++++++++++- - drivers/usb/host/ohci-s3c2410.c | 48 ++++++++++++++++++++++++++++++++++ - 3 files changed, 97 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/mach-s3c2442/Makefile b/arch/arm/mach-s3c2442/Makefile -index 6247ca0..3e7145c 100644 ---- a/arch/arm/mach-s3c2442/Makefile -+++ b/arch/arm/mach-s3c2442/Makefile -@@ -17,6 +17,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 - # Machine support - -diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c -index 8e3cc91..f1d4d51 100644 ---- a/arch/arm/mach-s3c2442/mach-gta02.c -+++ b/arch/arm/mach-s3c2442/mach-gta02.c -@@ -322,6 +322,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", - }; -@@ -331,6 +335,11 @@ static struct regulator_consumer_supply gsm_supply_consumer = { - .supply = "GSM", - }; - -+static struct regulator_consumer_supply usbhost_supply_consumer = { -+ .dev = >a02_pm_usbhost_dev.dev, -+ .supply = "USBHOST", -+}; -+ - static struct regulator_init_data gsm_supply_init_data = { - .constraints = { - .min_uV = 3700000, -@@ -342,6 +351,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, -@@ -350,6 +370,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, -@@ -358,6 +386,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, -+ }, -+}; -+ - static struct regulator_bulk_data gta02_gps_consumer_supply = { - .supply = "GPS", - }; -@@ -1332,12 +1368,17 @@ static struct platform_device* gta02_glamo_gpio_children[] = { - - static struct platform_device* gta02_pcf50633_gpio_children[] = { - >a02_gsm_supply_device, -+ >a02_usbhost_supply_device, - }; - - static struct platform_device* gta02_gsm_supply_children[] = { - >a02_pm_gsm_dev, - }; - -+static struct platform_device* gta02_usbhost_supply_children[] = { -+ >a02_pm_usbhost_dev, -+}; -+ - static struct platform_device* gta02_hdq_children[] = { - &bq27000_battery_device, - }; -@@ -1364,14 +1405,19 @@ static struct gta02_device_children gta02_device_children[] = { - }, - { - .dev_name = "pcf50633-gpio", -- .num_children = 1, -+ .num_children = 2, - .children = gta02_pcf50633_gpio_children, - }, - { - .dev_name = "reg-fixed-voltage.1", - .num_children = 1, - .children = gta02_gsm_supply_children, -- } -+ }, -+ { -+ .dev_name = "reg-fixed-voltage.2", -+ .num_children = 1, -+ .children = gta02_usbhost_supply_children, -+ }, - }; - - static int gta02_add_child_devices(struct device *parent, -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 - #include - #include -+#include -+#include -+#include -+#include - - #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.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0018-ar6000_delay.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0018-ar6000_delay.patch.patch deleted file mode 100644 index 54200d558f..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0018-ar6000_delay.patch.patch +++ /dev/null @@ -1,211 +0,0 @@ -From cf93a0f8d2d61a2e13590c261dee4c4d74412db7 Mon Sep 17 00:00:00 2001 -From: Radek Polak -Date: Fri, 9 Apr 2010 09:18:02 +0200 -Subject: [PATCH 19/22] 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 ---- - arch/arm/mach-s3c2442/gta02-pm-usbhost.c | 174 ++++++++++++++++++++++++++++++ - drivers/ar6000/hif/hif2.c | 2 + - 2 files changed, 176 insertions(+), 0 deletions(-) - create mode 100644 arch/arm/mach-s3c2442/gta02-pm-usbhost.c - -diff --git a/arch/arm/mach-s3c2442/gta02-pm-usbhost.c b/arch/arm/mach-s3c2442/gta02-pm-usbhost.c -new file mode 100644 -index 0000000..233340a ---- /dev/null -+++ b/arch/arm/mach-s3c2442/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 -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+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 (>a02_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, >a02_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, >a02_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(>a02_usbhost_driver); -+} -+module_init(gta02_usbhost_init); -+ -+static void gta02_usbhost_exit(void) -+{ -+ platform_driver_unregister(>a02_usbhost_driver); -+} -+module_exit(gta02_usbhost_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Harald Welte "); -+MODULE_DESCRIPTION("Openmoko Freerunner USBHOST Power Management"); -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.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0019-save_regs.patch.patch b/recipes/linux/linux-openmoko-2.6.32/0019-save_regs.patch.patch deleted file mode 100644 index 8fc2cea326..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0019-save_regs.patch.patch +++ /dev/null @@ -1,140 +0,0 @@ -From a9d6c115089a0a34fbca89c539de50148a2cf34e Mon Sep 17 00:00:00 2001 -From: Radek Polak -Date: Fri, 9 Apr 2010 09:22:23 +0200 -Subject: [PATCH 20/22] 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 7dab2e5..7df08dd 100644 ---- a/drivers/mmc/core/core.c -+++ b/drivers/mmc/core/core.c -@@ -59,10 +59,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 fba147c..06423cb 100644 ---- a/drivers/mmc/host/s3cmci.c -+++ b/drivers/mmc/host/s3cmci.c -@@ -1879,19 +1879,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 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 -+ - 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.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0020-Fix-KMS-framebuffer-physical-address.patch b/recipes/linux/linux-openmoko-2.6.32/0020-Fix-KMS-framebuffer-physical-address.patch deleted file mode 100644 index 3ce4d59042..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0020-Fix-KMS-framebuffer-physical-address.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 1b83b6d7f2e1bf5473cd17a742737a1cfe450509 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Fri, 20 Nov 2009 22:06:30 +0100 -Subject: [PATCH 24/24] Fix KMS framebuffer physical address - -This allows "/dev/fb0" to work correctly with mmap(). - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-kms-fb.c | 5 ++--- - 1 files changed, 2 insertions(+), 3 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-kms-fb.c b/drivers/mfd/glamo/glamo-kms-fb.c -index 6d4b9ea..04de83b 100644 ---- a/drivers/mfd/glamo/glamo-kms-fb.c -+++ b/drivers/mfd/glamo/glamo-kms-fb.c -@@ -387,9 +387,6 @@ int glamofb_create(struct drm_device *dev, uint32_t fb_width, - info->fbops = &glamofb_ops; - - info->fix.line_length = fb->pitch; -- info->fix.smem_start = dev->mode_config.fb_base -- + (unsigned long) gdrm->vram->start; -- info->fix.smem_len = size; - - info->flags = FBINFO_DEFAULT; - -@@ -401,6 +398,8 @@ int glamofb_create(struct drm_device *dev, uint32_t fb_width, - 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; --- -1.7.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0021-Reject-modes-with-clock-0.patch b/recipes/linux/linux-openmoko-2.6.32/0021-Reject-modes-with-clock-0.patch deleted file mode 100644 index 7201602551..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0021-Reject-modes-with-clock-0.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 6a6957d634daa88f381e44353c98f27e4c39300a Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sun, 18 Apr 2010 14:25:14 +0200 -Subject: [PATCH 27/31] Reject modes with clock=0 - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-display.c | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index 24a1a31..f57d273 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -522,6 +522,7 @@ 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; - } - --- -1.7.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0022-JBT6k74-tweaks-Make-resolution-switch-work.patch b/recipes/linux/linux-openmoko-2.6.32/0022-JBT6k74-tweaks-Make-resolution-switch-work.patch deleted file mode 100644 index 6acefa3382..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0022-JBT6k74-tweaks-Make-resolution-switch-work.patch +++ /dev/null @@ -1,316 +0,0 @@ -From 6296b2623da88372bc6762160ba2a990cc99902c Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sat, 24 Apr 2010 21:04:46 +0200 -Subject: [PATCH 28/31] JBT6k74 tweaks: Make resolution switch work - -Signed-off-by: Thomas White ---- - arch/arm/mach-s3c2442/mach-gta02.c | 2 +- - drivers/mfd/glamo/glamo-display.c | 9 ++- - drivers/video/backlight/jbt6k74.c | 121 +++++++++++++++++++++++++++-------- - include/linux/jbt6k74.h | 4 +- - 4 files changed, 102 insertions(+), 34 deletions(-) - -diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c -index 8b59fa7..9a30687 100644 ---- a/arch/arm/mach-s3c2442/mach-gta02.c -+++ b/arch/arm/mach-s3c2442/mach-gta02.c -@@ -246,7 +246,7 @@ static struct fb_videomode gta02_glamo_modes[] = { - .name = "240x320", - .xres = 240, - .yres = 320, -- .pixclock = 100000, -+ .pixclock = 40816, - .left_margin = 8, - .right_margin = 16, - .upper_margin = 2, -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index f57d273..a551968 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -307,6 +307,7 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - printk(KERN_WARNING "[glamo-drm] Display is off - " - "enabling it before setting mode.\n"); - glamo_lcd_power(gdrm, 1); -+ msleep(500); - } - - glamo_lcd_cmd_mode(gdrm, 1); -@@ -355,14 +356,14 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - - glamo_lcd_cmd_mode(gdrm, 0); - -+ glamo_crtc_mode_set_base(crtc, 0, 0, old_fb); -+ - if ( mode->hdisplay == 240 ) { -- jbt6k74_setresolution(JBT_RESOLUTION_QVGA); -+ jbt6k74_finish_resolutionchange(JBT_RESOLUTION_QVGA); - } else { -- jbt6k74_setresolution(JBT_RESOLUTION_VGA); -+ jbt6k74_finish_resolutionchange(JBT_RESOLUTION_VGA); - } - -- glamo_crtc_mode_set_base(crtc, 0, 0, old_fb); -- - gcrtc->current_mode = *mode; - gcrtc->current_mode_set = 1; - gcrtc->current_fb = old_fb; -diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c -index 40c31f6..43249ad 100644 ---- a/drivers/video/backlight/jbt6k74.c -+++ b/drivers/video/backlight/jbt6k74.c -@@ -104,6 +104,7 @@ enum jbt_register { - - static const char *jbt_power_mode_names[] = { - [JBT_POWER_MODE_OFF] = "off", -+ [JBT_POWER_MODE_STANDBY] = "standby", - [JBT_POWER_MODE_NORMAL] = "normal", - }; - -@@ -148,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; - } - -@@ -165,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; - } - -@@ -183,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; - } - -@@ -262,7 +269,7 @@ static int jbt_off_to_normal(struct jbt_info *jbt) - gpio_set_value_cansleep(pdata->gpio_reset, 1); - ret = regulator_bulk_enable(ARRAY_SIZE(jbt->supplies), jbt->supplies); - -- mdelay(30); -+ mdelay(120); - - /* three times command zero */ - ret |= jbt_reg_write_nodata(jbt, 0x00); -@@ -334,6 +341,49 @@ static int jbt_normal_to_off(struct jbt_info *jbt) - } - - -+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(150); -+ -+ jbt->power_mode = JBT_POWER_MODE_STANDBY; -+ -+ return ret ? -EIO : 0; -+} -+ -+ -+static int jbt_standby_to_normal(struct jbt_info *jbt) -+{ -+ int ret; -+ -+ if ( jbt->power_mode != JBT_POWER_MODE_STANDBY ) return 0; -+ -+ /* Make sure we are 120 ms after SLEEP_{IN,OUT} */ -+ while (time_before(jiffies, jbt->next_sleep)) cpu_relax(); -+ -+ ret = jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT); -+ jbt->next_sleep = jiffies + msecs_to_jiffies(150); -+ -+ 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) - { -@@ -354,6 +404,9 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt, - case JBT_POWER_MODE_NORMAL: - ret = jbt_off_to_normal(jbt); - break; -+ case JBT_POWER_MODE_STANDBY: -+ ret = -EINVAL; -+ break; - } - break; - case JBT_POWER_MODE_NORMAL: -@@ -364,7 +417,14 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt, - case JBT_POWER_MODE_OFF: - ret = jbt_normal_to_off(jbt); - break; -+ case JBT_POWER_MODE_STANDBY: -+ ret = -EINVAL; -+ break; - } -+ break; -+ case JBT_POWER_MODE_STANDBY: -+ ret = -EINVAL; -+ break; - } - - if (ret == 0) { -@@ -382,29 +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); -+ -+ 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); -+ } - -- /* "Reboot" the LCM */ -- ret = jbt_normal_to_off(jbt); -- mdelay(1000); -- ret |= jbt_off_to_normal(jbt); -+ 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: -@@ -737,26 +814,18 @@ static int __devexit jbt_remove(struct spi_device *spi) - return 0; - } - --/* Begin horrible layering violations (in the interest of making stuff work) */ -+/* Begin horrible layering violations in the interest of making stuff work */ - --int jbt6k74_setresolution(enum jbt_resolution new_resolution) -+int jbt6k74_finish_resolutionchange(enum jbt_resolution new_resolution) - { -- if ( !jbt_global ) { -- printk(KERN_CRIT "JBT not initialised!!!\n"); -- return -1; -- } -- jbt6k74_set_resolution(jbt_global, new_resolution); -- return 0; -+ if ( !jbt_global ) return 0; -+ return jbt6k74_set_resolution(jbt_global, new_resolution); - } --EXPORT_SYMBOL_GPL(jbt6k74_setresolution); -+EXPORT_SYMBOL_GPL(jbt6k74_finish_resolutionchange); - --/* This is utterly, totally horrible. I'm REALLY sorry... */ - void jbt6k74_setpower(enum jbt_power_mode new_power) - { -- if ( !jbt_global ) { -- printk(KERN_CRIT "JBT not initialised!!!\n"); -- return; -- } -+ if ( !jbt_global ) return; - jbt6k74_enter_power_mode(jbt_global, new_power); - } - EXPORT_SYMBOL_GPL(jbt6k74_setpower); -@@ -770,10 +839,8 @@ static int jbt_suspend(struct spi_device *spi, pm_message_t state) - - jbt->suspend_mode = jbt->power_mode; - -- printk(KERN_CRIT "[jbt] powering off for suspend\n"); - jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF); - -- printk(KERN_CRIT "[jbt] done\n"); - dev_info(&spi->dev, "suspended\n"); - - return 0; -@@ -784,13 +851,11 @@ int jbt6k74_resume(struct spi_device *spi) - struct jbt_info *jbt = dev_get_drvdata(&spi->dev); - dev_info(&spi->dev, "starting resume: %d\n", jbt->suspend_mode); - -- printk(KERN_CRIT "[jbt] powering on for resume\n"); - mdelay(20); - - jbt->suspended = 0; - jbt6k74_enter_power_mode(jbt, jbt->suspend_mode); - -- printk(KERN_CRIT "[jbt] done\n"); - dev_info(&spi->dev, "resumed: %d\n", jbt->suspend_mode); - - return 0; -diff --git a/include/linux/jbt6k74.h b/include/linux/jbt6k74.h -index f430e5a..2010bdc 100644 ---- a/include/linux/jbt6k74.h -+++ b/include/linux/jbt6k74.h -@@ -10,11 +10,13 @@ enum jbt_resolution { - - 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_setresolution(enum jbt_resolution new_resolution); -+extern int jbt6k74_prepare_resolutionchange(enum jbt_resolution new_resolution); -+extern int jbt6k74_finish_resolutionchange(enum jbt_resolution new_resolution); - - - /* --- -1.7.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0023-Remove-a-couple-of-debugging-messages.patch b/recipes/linux/linux-openmoko-2.6.32/0023-Remove-a-couple-of-debugging-messages.patch deleted file mode 100644 index 7ec2a9582e..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0023-Remove-a-couple-of-debugging-messages.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 171e3f48e87b7ce366116b4dc5af5b1286c19554 Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sat, 24 Apr 2010 21:10:09 +0200 -Subject: [PATCH 29/31] Remove a couple of debugging messages - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-kms-fb.c | 4 ---- - 1 files changed, 0 insertions(+), 4 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-kms-fb.c b/drivers/mfd/glamo/glamo-kms-fb.c -index 04de83b..d76dd24 100644 ---- a/drivers/mfd/glamo/glamo-kms-fb.c -+++ b/drivers/mfd/glamo/glamo-kms-fb.c -@@ -253,8 +253,6 @@ static void glamofb_on(struct fb_info *info) - struct drm_device *dev = par->dev; - struct glamodrm_handle *gdrm = dev->dev_private; - -- printk(KERN_CRIT "Turning on display...\n"); -- - gdrm = dev->dev_private; - - glamo_lcd_power(gdrm, 1); -@@ -266,8 +264,6 @@ static void glamofb_off(struct fb_info *info, int dpms_mode) - struct drm_device *dev = par->dev; - struct glamodrm_handle *gdrm = dev->dev_private; - -- printk(KERN_CRIT "Turning off display...\n"); -- - glamo_lcd_power(gdrm, 0); - } - --- -1.7.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0024-Enable-dummy-frames-when-switching-resolution.patch b/recipes/linux/linux-openmoko-2.6.32/0024-Enable-dummy-frames-when-switching-resolution.patch deleted file mode 100644 index 1e97ace698..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0024-Enable-dummy-frames-when-switching-resolution.patch +++ /dev/null @@ -1,26 +0,0 @@ -From ed45a9c582b2626a7f44e9fe64ae313d9dcbfbfb Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Sat, 24 Apr 2010 21:45:38 +0200 -Subject: [PATCH 30/31] Enable dummy frames when switching resolution - -Signed-off-by: Thomas White ---- - drivers/video/backlight/jbt6k74.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c -index 43249ad..91651e7 100644 ---- a/drivers/video/backlight/jbt6k74.c -+++ b/drivers/video/backlight/jbt6k74.c -@@ -217,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); --- -1.7.0.4 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0025-Almost-make-rotation-work.patch b/recipes/linux/linux-openmoko-2.6.32/0025-Almost-make-rotation-work.patch deleted file mode 100644 index 84132c7649..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0025-Almost-make-rotation-work.patch +++ /dev/null @@ -1,212 +0,0 @@ -From 2f37934f81b3f46ad5c3dac6484a2c3c255066fc Mon Sep 17 00:00:00 2001 -From: Thomas White -Date: Wed, 28 Apr 2010 00:09:00 +0200 -Subject: [PATCH 27/27] Almost make rotation work - -Signed-off-by: Thomas White ---- - drivers/mfd/glamo/glamo-display.c | 171 ++++++++++++++++++++++++++++--------- - 1 files changed, 129 insertions(+), 42 deletions(-) - -diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c -index a551968..75ad734 100644 ---- a/drivers/mfd/glamo/glamo-display.c -+++ b/drivers/mfd/glamo/glamo-display.c -@@ -298,6 +298,7 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - 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); -@@ -310,49 +311,135 @@ static int glamo_crtc_mode_set(struct drm_crtc *crtc, - 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); - -- glamo_engine_reclock(gdrm->glamo_core, GLAMO_ENGINE_LCD, mode->clock); -- gdrm->saved_clock = mode->clock; -+ /* 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); - -- 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); -- -- /* 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); -+ } -+ -+ gdrm->saved_clock = mode->clock; - - glamo_lcd_cmd_mode(gdrm, 0); - -@@ -747,8 +834,8 @@ int glamo_display_init(struct drm_device *dev) - drm_mode_config_init(dev); - - dev->mode_config.min_width = 240; -- dev->mode_config.min_height = 320; -- dev->mode_config.max_width = 480; -+ 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; --- -1.7.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32/0026-gta02-defconfigs-enable-LEDS_S3C24XX.patch b/recipes/linux/linux-openmoko-2.6.32/0026-gta02-defconfigs-enable-LEDS_S3C24XX.patch deleted file mode 100644 index 7a40345689..0000000000 --- a/recipes/linux/linux-openmoko-2.6.32/0026-gta02-defconfigs-enable-LEDS_S3C24XX.patch +++ /dev/null @@ -1,48 +0,0 @@ -From f80beb203e503696cd2e74e194176b8d6f49b27b Mon Sep 17 00:00:00 2001 -From: Martin Jansa -Date: Mon, 17 May 2010 12:41:06 +0200 -Subject: [PATCH] gta02 defconfigs, enable LEDS_S3C24XX and dummy battery driver - ---- - arch/arm/configs/gta02_defconfig | 2 +- - arch/arm/configs/gta02_drm_defconfig | 4 ++-- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/arm/configs/gta02_defconfig b/arch/arm/configs/gta02_defconfig -index 73d26ba..55832ac 100644 ---- a/arch/arm/configs/gta02_defconfig -+++ b/arch/arm/configs/gta02_defconfig -@@ -1635,7 +1635,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 -diff --git a/arch/arm/configs/gta02_drm_defconfig b/arch/arm/configs/gta02_drm_defconfig -index 05aa130..4d85e9a 100644 ---- a/arch/arm/configs/gta02_drm_defconfig -+++ b/arch/arm/configs/gta02_drm_defconfig -@@ -1233,7 +1233,7 @@ CONFIG_POWER_SUPPLY=y - CONFIG_CHARGER_PCF50633=y - CONFIG_BATTERY_BQ27000_HDQ=y - CONFIG_HDQ_GPIO_BITBANG=y --# CONFIG_BATTERY_PLATFORM is not set -+CONFIG_BATTERY_PLATFORM=y - # CONFIG_HWMON is not set - # CONFIG_THERMAL is not set - CONFIG_WATCHDOG=y -@@ -1681,7 +1681,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 --- -1.7.1 - diff --git a/recipes/linux/linux-openmoko-2.6.32_git.bb b/recipes/linux/linux-openmoko-2.6.32_git.bb index fb842419dd..133e5afe2d 100644 --- a/recipes/linux/linux-openmoko-2.6.32_git.bb +++ b/recipes/linux/linux-openmoko-2.6.32_git.bb @@ -6,7 +6,7 @@ DESCRIPTION_${PN} = "Linux ${KERNEL_RELEASE} kernel for the Openmoko Neo GSM Sma KERNEL_RELEASE="2.6.32.13" SRCREV = "a9254be10ac2294ea20165a87c09ea6afcf66d94" -OEV = "oe1" +OEV = "oe2" PV = "${KERNEL_RELEASE}-${OEV}+gitr${SRCPV}" SRC_URI = "\ @@ -16,32 +16,22 @@ SRC_URI = "\ ${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/patch-${KERNEL_RELEASE}.bz2;patch=1;name=stablepatch \ # build fix file://0001-wm8753-fix-build-with-gcc-4.4.2-which-works-ok-with-.patch;patch=1 \ -# patches from Weiss's gdrm-2.6.32 branch - file://0002-DRM-for-platform-devices.patch;patch=1 \ - file://0003-Glamo-DRM-and-KMS-driver.patch;patch=1 \ - file://0004-Work-on-Glamo-core-for-DRM.patch;patch=1 \ - file://0005-Add-JBT6k74-hook-for-use-by-KMS.patch;patch=1 \ - file://0006-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch;patch=1 \ - file://0007-glamo-drm-select-DRM_KMS_HELPER-for-crtc-functions.patch;patch=1 \ - file://0008-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch;patch=1 \ - file://0009-A-couple-of-GEM-refcounting-fixes.patch;patch=1 \ - file://0010-Simplify-the-JBT6k74-driver.patch;patch=1 \ - file://0011-Don-t-choke-if-userspace-provides-a-pixel-clock-valu.patch;patch=1 \ - file://0012-Report-all-FB-modes-given-by-the-lower-levels.patch;patch=1 \ - file://0013-Change-connector-type-to-LVDS.patch;patch=1 \ - file://0014-Clean-up-JBT-hooks-and-allow-resolution-switching.patch;patch=1 \ - file://0015-Enable-display-before-trying-to-set-mode-or-base.patch;patch=1 \ - file://0016-accels.patch.patch;patch=1 \ - file://0017-usbhost.patch.patch;patch=1 \ - file://0018-ar6000_delay.patch.patch;patch=1 \ - file://0019-save_regs.patch.patch;patch=1 \ - file://0020-Fix-KMS-framebuffer-physical-address.patch;patch=1 \ - file://0021-Reject-modes-with-clock-0.patch;patch=1 \ - file://0022-JBT6k74-tweaks-Make-resolution-switch-work.patch;patch=1 \ - file://0023-Remove-a-couple-of-debugging-messages.patch;patch=1 \ - file://0024-Enable-dummy-frames-when-switching-resolution.patch;patch=1 \ - file://0025-Almost-make-rotation-work.patch;patch=1 \ - file://0026-gta02-defconfigs-enable-LEDS_S3C24XX.patch;patch=1 \ +# patches from Radek Polak used in qtmoko + file://0002-accels.patch.patch;patch=1 \ + file://0003-usbhost.patch.patch;patch=1 \ + file://0004-ar6000_delay.patch.patch;patch=1 \ + file://0005-save_regs.patch.patch;patch=1 \ +# defconfig updates + file://0006-gta02-defconfigs-enable-LEDS_S3C24XX-and-dummy-batte.patch;patch=1 \ +# patches from Weiss's gdrm-for-merging branch + file://0007-DRM-for-platform-devices.patch;patch=1 \ + file://0008-Glamo-DRM-and-KMS-driver.patch;patch=1 \ + file://0009-Work-on-Glamo-core-for-DRM.patch;patch=1 \ + file://0010-JBT6k74-work-for-KMS.patch;patch=1 \ + file://0011-Fix-crash-when-reading-Glamo-registers-via-sysfs.patch;patch=1 \ + file://0012-Fix-dynamic-command-queue-allocation.patch;patch=1 \ + file://0013-Debug-statements-for-testing.patch;patch=1 \ + file://0014-Fix-claim-of-2D-register-resource.patch;patch=1 \ " SRC_URI[stablepatch.md5sum] = "ba6abb1ffee513a1d4f831599ddae490" -- cgit 1.2.3-korg