From c3bfaaa2b9b5e6b9595758a4d2ed05940bf20722 Mon Sep 17 00:00:00 2001 From: Martin Jansa Date: Tue, 22 Dec 2009 15:24:20 +0100 Subject: linux-openmoko-2.6.32: enable glamo DRM Signed-off-by: Martin Jansa --- .../0001-DRM-for-platform-devices.patch | 458 +++ ...se-dev_set_drvdata-instead-of-setting-dri.patch | 29 + .../0002-Glamo-DRM-and-KMS-driver.patch | 3818 ++++++++++++++++++++ ...-select-DRM_KMS_HELPER-for-crtc-functions.patch | 24 + .../0003-Work-on-Glamo-core-for-DRM.patch | 252 ++ .../0004-Add-JBT6k74-hook-for-use-by-KMS.patch | 49 + .../linux/linux-openmoko-2.6.32/om-gta02/defconfig | 163 +- recipes/linux/linux-openmoko-2.6.32_git.bb | 10 +- 8 files changed, 4734 insertions(+), 69 deletions(-) create mode 100644 recipes/linux/linux-openmoko-2.6.32/0001-DRM-for-platform-devices.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0001-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0002-Glamo-DRM-and-KMS-driver.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0002-glamo-drm-select-DRM_KMS_HELPER-for-crtc-functions.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0003-Work-on-Glamo-core-for-DRM.patch create mode 100644 recipes/linux/linux-openmoko-2.6.32/0004-Add-JBT6k74-hook-for-use-by-KMS.patch (limited to 'recipes/linux') diff --git a/recipes/linux/linux-openmoko-2.6.32/0001-DRM-for-platform-devices.patch b/recipes/linux/linux-openmoko-2.6.32/0001-DRM-for-platform-devices.patch new file mode 100644 index 0000000000..2c9b611165 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0001-DRM-for-platform-devices.patch @@ -0,0 +1,458 @@ +From da270cf61e67d912b38e314719511efc4c2ea085 Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Tue, 20 Oct 2009 15:52:30 +0200 +Subject: [PATCH 1/4] 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 39b393d..cef3d2c 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 6246e3f..b9f15bf 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 b39d7bf..a7861e8 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..a7069ad 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 = kmalloc(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 f7a615b..fea2b71 100644 +--- a/drivers/gpu/drm/drm_sysfs.c ++++ b/drivers/gpu/drm/drm_sysfs.c +@@ -482,7 +482,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 45b67d9..66253f9 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 +@@ -113,6 +114,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... */ +@@ -981,6 +983,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 */ +@@ -1091,12 +1094,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); +@@ -1314,6 +1325,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.5.3 + diff --git a/recipes/linux/linux-openmoko-2.6.32/0001-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch b/recipes/linux/linux-openmoko-2.6.32/0001-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch new file mode 100644 index 0000000000..d9afec8830 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0001-glamo-drm-use-dev_set_drvdata-instead-of-setting-dri.patch @@ -0,0 +1,29 @@ +From eadea0d7bda93fec0e2c7a3b675fc6ab21cdfe61 Mon Sep 17 00:00:00 2001 +From: Martin Jansa +Date: Tue, 22 Dec 2009 16:11:27 +0100 +Subject: [PATCH 1/2] 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 81215f4..4352091 100644 +--- a/drivers/mfd/glamo/glamo-drm-drv.c ++++ b/drivers/mfd/glamo/glamo-drm-drv.c +@@ -355,7 +355,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.rc4 + diff --git a/recipes/linux/linux-openmoko-2.6.32/0002-Glamo-DRM-and-KMS-driver.patch b/recipes/linux/linux-openmoko-2.6.32/0002-Glamo-DRM-and-KMS-driver.patch new file mode 100644 index 0000000000..4a837e9b08 --- /dev/null +++ b/recipes/linux/linux-openmoko-2.6.32/0002-Glamo-DRM-and-KMS-driver.patch @@ -0,0 +1,3818 @@ +From 01435b6f8fba2031e6941756a6a4e42be553f4a0 Mon Sep 17 00:00:00 2001 +From: Thomas White +Date: Tue, 20 Oct 2009 16:14:55 +0200 +Subject: [PATCH 2/4] 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/gpu/drm/drm_stub.c | 2 +- + 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 | 875 +++++++++++++++++++++++++++++++++ + drivers/mfd/glamo/glamo-display.h | 39 ++ + drivers/mfd/glamo/glamo-drm-drv.c | 453 +++++++++++++++++ + drivers/mfd/glamo/glamo-drm-private.h | 156 ++++++ + drivers/mfd/glamo/glamo-fence.c | 329 +++++++++++++ + drivers/mfd/glamo/glamo-fence.h | 36 ++ + 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 ++++++ + 17 files changed, 3652 insertions(+), 2 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/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c +index a7069ad..5e3d65a 100644 +--- a/drivers/gpu/drm/drm_stub.c ++++ b/drivers/gpu/drm/drm_stub.c +@@ -471,7 +471,7 @@ int drm_get_platform_dev(struct platform_device *pdev, + int ret; + DRM_DEBUG("\n"); + +- dev = kmalloc(sizeof(*dev), GFP_KERNEL); ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev_private = priv; +diff --git a/drivers/mfd/glamo/Kconfig b/drivers/mfd/glamo/Kconfig +index 8c93bcb..375e5db 100644 +--- a/drivers/mfd/glamo/Kconfig ++++ b/drivers/mfd/glamo/Kconfig +@@ -39,3 +39,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..93aa917 +--- /dev/null ++++ b/drivers/mfd/glamo/glamo-display.c +@@ -0,0 +1,875 @@ ++/* ++ * 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) ++{ ++} ++ ++ ++stati