diff mbox

RFC: omapdrm DRM/KMS driver for TI OMAP platforms

Message ID 1314994047-4101-1-git-send-email-rob.clark@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Rob Clark Sept. 2, 2011, 8:07 p.m. UTC
From: Rob Clark <rob@ti.com>

A DRM display driver for TI OMAP platform.  Similar to omapfb (fbdev)
and omap_vout (v4l2 display) drivers in the past, this driver uses the
DSS2 driver to access the display hardware, including support for
HDMI, DVI, and various types of LCD panels.  And it implements GEM
support for buffer allocation (for KMS as well as offscreen buffers
used by the xf86-video-omap userspace xorg driver).

The driver maps CRTCs to overlays, encoders to overlay-managers, and
connectors to dssdev's.  Note that this arrangement might change slightly
when support for drm_plane overlays is added.

For GEM support, non-scanout buffers are using the shmem backed pages
provided by GEM core (In drm_gem_object_init()).  In the case of scanout
buffers, which need to be physically contiguous, those are allocated from
omap_vram and use drm_gem_private_object_init() (Thanks to "gem: RFC: add
support for private objects" patch from Alan Cox.)  Also used is the
"drm/gem: add functions for mmap offset creation" patch to avoid
duplicating some functions for mmap offset management in the omapdrm
driver.

Note that the usage of omap_vram carveout will be replaced by CMA in
the future.

A simple "plugin" mechanism is provided to allow integration with
external kernel modules (*cough* PVR), but also to keep the code more
modular as support is added for various other accelerator blocks that
exist in various different OMAP variants.  (2D accel, video encode/
decode, etc.)  However this driver and userspace xorg driver are
themselves fully functional without any closed-source components. It
doesn't (and won't) provide any command submission to IP blocks with
closed-source kernel or userspace components.

See userspace xorg driver: git://github.com/robclark/xf86-video-omap.git
---
Note that this patch depends on a few other patches which have been
submitted to respective mailing lists (linux-omap for DSS2 and
dri-devel for DRM), but not yet merged.  But this is only a RFC, and
I thought it was about time to put on the asbestos pants and let the
good folks of dri-devel get their flame on.

Special thanks to Tomi Valkeinen (DSS2 author) for patches to add the
needed APIs for hotplug detection and EDID to DSS2 and various other
help with the DRM driver.

 drivers/staging/Kconfig                  |    2 +
 drivers/staging/Makefile                 |    1 +
 drivers/staging/omapdrm/Kconfig          |   25 +
 drivers/staging/omapdrm/Makefile         |    8 +
 drivers/staging/omapdrm/TODO.txt         |   15 +
 drivers/staging/omapdrm/omap_connector.c |  375 ++++++++++++
 drivers/staging/omapdrm/omap_crtc.c      |  348 ++++++++++++
 drivers/staging/omapdrm/omap_drv.c       |  910 ++++++++++++++++++++++++++++++
 drivers/staging/omapdrm/omap_drv.h       |  117 ++++
 drivers/staging/omapdrm/omap_encoder.c   |  198 +++++++
 drivers/staging/omapdrm/omap_fb.c        |  265 +++++++++
 drivers/staging/omapdrm/omap_fbdev.c     |  303 ++++++++++
 drivers/staging/omapdrm/omap_gem.c       |  851 ++++++++++++++++++++++++++++
 drivers/video/omap2/omapfb/Kconfig       |    4 +-
 include/linux/Kbuild                     |    1 +
 include/linux/omap_drm.h                 |  191 +++++++
 16 files changed, 3612 insertions(+), 2 deletions(-)
 create mode 100644 drivers/staging/omapdrm/Kconfig
 create mode 100644 drivers/staging/omapdrm/Makefile
 create mode 100644 drivers/staging/omapdrm/TODO.txt
 create mode 100644 drivers/staging/omapdrm/omap_connector.c
 create mode 100644 drivers/staging/omapdrm/omap_crtc.c
 create mode 100644 drivers/staging/omapdrm/omap_drv.c
 create mode 100644 drivers/staging/omapdrm/omap_drv.h
 create mode 100644 drivers/staging/omapdrm/omap_encoder.c
 create mode 100644 drivers/staging/omapdrm/omap_fb.c
 create mode 100644 drivers/staging/omapdrm/omap_fbdev.c
 create mode 100644 drivers/staging/omapdrm/omap_gem.c
 create mode 100644 include/linux/omap_drm.h

Comments

Dave Airlie Sept. 3, 2011, 6:57 a.m. UTC | #1
>
> A simple "plugin" mechanism is provided to allow integration with
> external kernel modules (*cough* PVR), but also to keep the code more
> modular as support is added for various other accelerator blocks that
> exist in various different OMAP variants.  (2D accel, video encode/
> decode, etc.)  However this driver and userspace xorg driver are
> themselves fully functional without any closed-source components. It
> doesn't (and won't) provide any command submission to IP blocks with
> closed-source kernel or userspace components.

At the moment I'd rather not merge any sort of plugin layer, until we
had some users for it that were part of an open driver stack.

I'll try and review the rest next week.

Dave.
Rob Clark Sept. 3, 2011, 1:24 p.m. UTC | #2
On Sat, Sep 3, 2011 at 1:57 AM, Dave Airlie <airlied@gmail.com> wrote:
>>
>> A simple "plugin" mechanism is provided to allow integration with
>> external kernel modules (*cough* PVR), but also to keep the code more
>> modular as support is added for various other accelerator blocks that
>> exist in various different OMAP variants.  (2D accel, video encode/
>> decode, etc.)  However this driver and userspace xorg driver are
>> themselves fully functional without any closed-source components. It
>> doesn't (and won't) provide any command submission to IP blocks with
>> closed-source kernel or userspace components.
>
> At the moment I'd rather not merge any sort of plugin layer, until we
> had some users for it that were part of an open driver stack.

Ok, that is fair.  It should be not too hard to keep this as a patch
on top for now.  I'm hoping to have the first open user of the API in
a month or so.

> I'll try and review the rest next week.

Thanks

BR,
-R

> Dave.
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Daniel Vetter Sept. 3, 2011, 6:53 p.m. UTC | #3
Hi Rob,

I've taken a look at your driver, some comments inline. Can't really
comment much on the kms side because of lack of expertise in that area.

I think most can be trivially addressed by dropping the plugin layer ;-)

Cheers, Daniel

On Fri, Sep 02, 2011 at 03:07:27PM -0500, Rob Clark wrote:

> [snip]

> +int omap_connector_sync(struct drm_connector *connector)
> +{
> +	struct omap_connector *omap_connector = to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +	struct omap_dss_driver *dssdrv = dssdev->driver;
> +
> +	DBG("%s", omap_connector->dssdev->name);
> +
> +	if (dssdrv->sync) {
> +		return dssdrv->sync(dssdev);
> +	}
> +
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL(omap_connector_sync);

What is this for? There seem to be a few other EXPORT_SYMBOLS (beside
those to register plugins) that look a bit strange - especially since
there already is the dss layer to arbitrage between all the drivers (drm,
v4l, fbdev). Or is that all stuff needed by your plugins? If so, better
drop them until the first user shows up (and even then it's likely easer
if the glue code comes as a separate patch).

> [snip]

> +/** ensure backing pages are allocated */
> +/* NOTE: a bit similar to psb_gtt_attach_pages().. perhaps could be common?
> + * and maybe a bit similar to i915_gem_object_get_pages_gtt()
> + */

Actually see also i915_gem_object_get_pages_gtt for yet another copy of
this. This is a bit a mess and I'm not sure what to do, safe to slowly
evolve gem into something resembling ttm ...

> +static int omap_gem_attach_pages(struct drm_gem_object *obj)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	struct inode *inode;
> +	struct address_space *mapping;
> +	struct page *p;
> +	int i, npages;
> +	gfp_t gfpmask;
> +
> +	WARN_ON(omap_obj->pages);
> +
> +	/* This is the shared memory object that backs the GEM resource */
> +	inode = obj->filp->f_path.dentry->d_inode;
> +	mapping = inode->i_mapping;
> +
> +	npages = obj->size >> PAGE_SHIFT;
> +
> +	omap_obj->pages = kmalloc(npages * sizeof(struct page *), GFP_KERNEL);
> +	if (omap_obj->pages == NULL)
> +		return -ENOMEM;
> +
> +	/* FIXME: review flags later */
> +	gfpmask = __GFP_DMA32 | mapping_gfp_mask(mapping);
> +
> +	for (i = 0; i < npages; i++) {
> +		p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
> +		if (IS_ERR(p))
> +			goto fail;
> +		omap_obj->pages[i] = p;
> +	}
> +
> +	return 0;
> +
> +fail:
> +	while (i--) {
> +		page_cache_release(omap_obj->pages[i]);
> +	}
> +	kfree(omap_obj->pages);
> +	omap_obj->pages = NULL;
> +	return PTR_ERR(p);
> +
> +
> +}
> +
> +/** release backing pages */
> +/* NOTE: a bit similar to psb_gtt_detatch_pages().. perhaps could be common? */
> +static void omap_gem_detach_pages(struct drm_gem_object *obj)

See i915_gem_object_put_pages_gtt (minus the madv logic for purgeable objects).

> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	int i, npages;
> +
> +	npages = obj->size >> PAGE_SHIFT;
> +
> +	for (i = 0; i < npages; i++) {
> +		/* FIXME: do we need to force dirty */
> +		set_page_dirty(omap_obj->pages[i]);
> +		/* Undo the reference we took when populating the table */
> +		page_cache_release(omap_obj->pages[i]);
> +	}
> +
> +	kfree(omap_obj->pages);
> +	omap_obj->pages = NULL;
> +}
> +
> +/* Get physical address for DMA.. if 'remap' is true, and the buffer is not

> [snip]

> + * already contiguous, remap it to pin in physically contiguous memory.. (ie.
> + * map in TILER)
> + */
> +int omap_gem_get_paddr(struct drm_gem_object *obj,
> +		unsigned long *paddr, bool remap)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	if (!(omap_obj->flags & OMAP_BO_DMA)) {
> +		/* TODO: remap to TILER */
> +		return -ENOMEM;
> +	}
> +
> +	*paddr = omap_obj->paddr;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_get_paddr);
> +
> +/* Release physical address, when DMA is no longer being performed.. this
> + * could potentially unpin and unmap buffers from TILER
> + */
> +int omap_gem_put_paddr(struct drm_gem_object *obj)
> +{
> +	/* do something here when remap to TILER is used.. */
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_put_paddr);
> +
> +/* acquire pages when needed (for example, for DMA where physically
> + * contiguous buffer is not required
> + */
> +int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	/* TODO: we could attach/detach pages on demand */
> +	int ret;  // XXX below is common in _fault()..
> +	if (obj->filp && !omap_obj->pages) {
> +		ret = omap_gem_attach_pages(obj);
> +		if (ret) {
> +			dev_err(obj->dev->dev, "could not attach pages\n");
> +			return ret;
> +		}
> +	}
> +	/* TODO: even phys-contig.. we should have a list of pages */
> +	*pages = omap_obj->pages;
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_get_pages);
> +
> +/* release pages when DMA no longer being performed */
> +int omap_gem_put_pages(struct drm_gem_object *obj)
> +{
> +	/* do something here if we dynamically attach/detach pages.. at
> +	 * least they would no longer need to be pinned if everyone has
> +	 * released the pages..
> +	 */
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_put_pages);

Above set of get/put functions seem to do very little. Drop them for the
first round?

> [snip]

> +/* Buffer Synchronization:
> + */
> +
> +struct omap_gem_sync_waiter {
> +	struct list_head list;
> +	struct omap_gem_object *omap_obj;
> +	enum omap_gem_op op;
> +	uint32_t read_target, write_target;
> +	/* notify called w/ sync_lock held */
> +	void (*notify)(void *arg);
> +	void *arg;
> +};
> +
> +/* list of omap_gem_sync_waiter.. the notify fxn gets called back when
> + * the read and/or write target count is achieved which can call a user
> + * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for
> + * cpu access), etc.
> + */
> +static LIST_HEAD(waiters);
> +
> +static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
> +{
> +	struct omap_gem_object *omap_obj = waiter->omap_obj;
> +	if ((waiter->op & OMAP_GEM_READ) &&
> +			(omap_obj->sync->read_complete < waiter->read_target))
> +		return true;
> +	if ((waiter->op & OMAP_GEM_WRITE) &&
> +			(omap_obj->sync->write_complete < waiter->write_target))
> +		return true;
> +	return false;
> +}

On a quick read this looks awfully like handrolled gpu sync objects. For
which we already have a fully-featured implementation in ttm. And
and something handrolled in i915 (request tracking). Can we do better?

[ Looks like it's part of the plugin layer, so problem postponed. Puhh ]

> [snip]

> +void omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	spin_lock(&sync_lock);
> +
> +	if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) {
> +		/* clearing a previously set syncobj */
> +		syncobj = kzalloc(sizeof(*omap_obj->sync), GFP_KERNEL|GFP_ATOMIC);
> +		memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
> +		omap_obj->flags &= ~OMAP_BO_EXT_SYNC;
> +		omap_obj->sync = syncobj;
> +	} else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
> +		/* replacing an existing syncobj */
> +		if (omap_obj->sync) {
> +			memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
> +			kfree(omap_obj->sync);
> +		}
> +		omap_obj->flags |= OMAP_BO_EXT_SYNC;
> +		omap_obj->sync = syncobj;
> +	}
> +	spin_unlock(&sync_lock);
> +}
> +EXPORT_SYMBOL(omap_gem_set_sync_object);

Abstraction layers to adapt some blobs interface (be it the Windows kernel
driver interface or a blob driver core) is a bit frowned upon (actually I
think some people would nuke them first, ask questions later ;-). Maybe
hide that in your own vendor patch?

> +int omap_gem_init_object(struct drm_gem_object *obj)
> +{
> +	return -EINVAL;          /* unused */
> +}
> +
> +/* don't call directly.. called from GEM core when it is time to actually
> + * free the object..
> + */
> +void omap_gem_free_object(struct drm_gem_object *obj)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	if (obj->map_list.map) {
> +		drm_gem_free_mmap_offset(obj);
> +	}
> +
> +	/* don't free externally allocated backing memory */
> +	if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {


Nobody sets OMAP_BO_EXT_MEM flag. What's this for? Better implement this
when it actually gets used ...

> +		if (!obj->filp) {
> +			/* TODO: is this the best way to check? */
> +			omap_vram_free(omap_obj->paddr, obj->size);
> +		}
> +
> +		if (omap_obj->pages) {
> +			omap_gem_detach_pages(obj);
> +		}
> +	}
> +
> +	/* don't free externally allocated syncobj */
> +	if (!(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
> +		kfree(omap_obj->sync);
> +	}
> +
> +	drm_gem_object_release(obj);
> +
> +	kfree(obj);
> +}
> +
> +/* convenience method to construct a GEM buffer object, and userspace handle */

Inline with its only user above and scrap the forward decl?

> +int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
> +		size_t size, uint32_t flags, uint32_t *handle, uint64_t *offset)
> +{
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	obj = omap_gem_new(dev, size, flags);
> +	if (!obj)
> +		return -ENOMEM;
> +
> +	ret = drm_gem_handle_create(file, obj, handle);
> +	if (ret) {
> +		drm_gem_object_release(obj);
> +		kfree(obj); /* TODO isn't there a dtor to call? just copying i915 */
> +		return ret;
> +	}
> +
> +	if (offset) {
> +		*offset = omap_gem_mmap_offset(obj);
> +	}
> +
> +	/* drop reference from allocate - handle holds it now */
> +	drm_gem_object_unreference_unlocked(obj);
> +
> +	return 0;
> +}
> +/* common constructor body */
> +static struct drm_gem_object * omap_gem_new_impl(struct drm_device *dev,
> +		size_t size, uint32_t flags, unsigned long paddr, struct page **pages,
> +		struct omap_gem_vm_ops *ops)
> +{
> +	struct omap_gem_object *omap_obj;
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	size = PAGE_ALIGN(size);
> +
> +	omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL);
> +	if (!omap_obj) {
> +		dev_err(dev->dev, "could not allocate GEM object\n");
> +		goto fail;
> +	}
> +
> +	obj = &omap_obj->base;
> +
> +	if (paddr) {
> +		flags |= OMAP_BO_DMA;
> +	}
> +
> +	omap_obj->paddr = paddr;
> +	omap_obj->pages = pages;
> +	omap_obj->flags = flags;
> +	omap_obj->ops   = ops;
> +
> +	if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
> +		ret = drm_gem_private_object_init(dev, obj, size);
> +	} else {
> +		ret = drm_gem_object_init(dev, obj, size);
> +	}
> +
> +	if (ret) {
> +		/* Yikes!  need to clean up! XXX */
> +		goto fail;
> +	}
> +
> +	/* Make it mmapable */
> +	ret = drm_gem_create_mmap_offset(obj);
> +	if (ret) {
> +		dev_err(dev->dev, "could not allocate mmap offset");

The drm mmap offset manager isn't the fatest beast in the world. Maybe
create mmappings on demand only when userspace actually needs them? I.e.
in the ioctl_gem_info function.

> +		goto fail;
> +	}
> +
> +	return obj;
> +
> +fail:
> +	if (omap_obj) {
> +		kfree(omap_obj);
> +	}
> +	return NULL;
> +}
> +

> [snip]
Rob Clark Sept. 4, 2011, 4:29 p.m. UTC | #4
On Sat, Sep 3, 2011 at 1:53 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> Hi Rob,
>
> I've taken a look at your driver, some comments inline. Can't really
> comment much on the kms side because of lack of expertise in that area.
>
> I think most can be trivially addressed by dropping the plugin layer ;-)
>
> Cheers, Daniel
>
> On Fri, Sep 02, 2011 at 03:07:27PM -0500, Rob Clark wrote:
>
>> [snip]
>
>> +int omap_connector_sync(struct drm_connector *connector)
>> +{
>> +     struct omap_connector *omap_connector = to_omap_connector(connector);
>> +     struct omap_dss_device *dssdev = omap_connector->dssdev;
>> +     struct omap_dss_driver *dssdrv = dssdev->driver;
>> +
>> +     DBG("%s", omap_connector->dssdev->name);
>> +
>> +     if (dssdrv->sync) {
>> +             return dssdrv->sync(dssdev);
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +EXPORT_SYMBOL(omap_connector_sync);
>
> What is this for? There seem to be a few other EXPORT_SYMBOLS (beside
> those to register plugins) that look a bit strange - especially since
> there already is the dss layer to arbitrage between all the drivers (drm,
> v4l, fbdev). Or is that all stuff needed by your plugins? If so, better
> drop them until the first user shows up (and even then it's likely easer
> if the glue code comes as a separate patch).

this particular one is a remnant from an earlier version, where page
flipping and vsync sync'ing was handled in the PVR code.. once I
started adding sync-obj I've moved this into omap_crtc using the
normal page-flip ioctl.. it looks like I missed a bit of cleanup of
some of these fxns which are no longer needed since I moved the page
flipping into the KMS code.

At this point, I think it is just the exported symbols in the GEM bits
that are needed by the PVR plugin (and at least some would be needed
by other upcoming plugins for video encode/decode and then 2d).

>> [snip]
>
>> +/** ensure backing pages are allocated */
>> +/* NOTE: a bit similar to psb_gtt_attach_pages().. perhaps could be common?
>> + * and maybe a bit similar to i915_gem_object_get_pages_gtt()
>> + */
>
> Actually see also i915_gem_object_get_pages_gtt for yet another copy of
> this. This is a bit a mess and I'm not sure what to do, safe to slowly
> evolve gem into something resembling ttm ...

Yeah, I'd like to refactor out the attach/detach_pages stuff.. it is
on my todo list (I guess I should at least mention that in the TODO
file..)

>> [snip]
>
>> + * already contiguous, remap it to pin in physically contiguous memory.. (ie.
>> + * map in TILER)
>> + */
>> +int omap_gem_get_paddr(struct drm_gem_object *obj,
>> +             unsigned long *paddr, bool remap)
>> +{
>> +     struct omap_gem_object *omap_obj = to_omap_bo(obj);
>> +
>> +     if (!(omap_obj->flags & OMAP_BO_DMA)) {
>> +             /* TODO: remap to TILER */
>> +             return -ENOMEM;
>> +     }
>> +
>> +     *paddr = omap_obj->paddr;
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(omap_gem_get_paddr);
>> +
>> +/* Release physical address, when DMA is no longer being performed.. this
>> + * could potentially unpin and unmap buffers from TILER
>> + */
>> +int omap_gem_put_paddr(struct drm_gem_object *obj)
>> +{
>> +     /* do something here when remap to TILER is used.. */
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(omap_gem_put_paddr);
>> +
>> +/* acquire pages when needed (for example, for DMA where physically
>> + * contiguous buffer is not required
>> + */
>> +int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages)
>> +{
>> +     struct omap_gem_object *omap_obj = to_omap_bo(obj);
>> +     /* TODO: we could attach/detach pages on demand */
>> +     int ret;  // XXX below is common in _fault()..
>> +     if (obj->filp && !omap_obj->pages) {
>> +             ret = omap_gem_attach_pages(obj);
>> +             if (ret) {
>> +                     dev_err(obj->dev->dev, "could not attach pages\n");
>> +                     return ret;
>> +             }
>> +     }
>> +     /* TODO: even phys-contig.. we should have a list of pages */
>> +     *pages = omap_obj->pages;
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(omap_gem_get_pages);
>> +
>> +/* release pages when DMA no longer being performed */
>> +int omap_gem_put_pages(struct drm_gem_object *obj)
>> +{
>> +     /* do something here if we dynamically attach/detach pages.. at
>> +      * least they would no longer need to be pinned if everyone has
>> +      * released the pages..
>> +      */
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(omap_gem_put_pages);
>
> Above set of get/put functions seem to do very little. Drop them for the
> first round?

The intention is to do attach/detach_pages here.. and in case of
get/put_paddr do remapping into TILER if the buffer isn't physically
contiguous.  (Although in the TILER case, we are seeing how/if we can
fit this into the IOMMU framework.. so API's here are still in flux.
Non-tiled buffers are a natural fit for IOMMU, I think... but tiled
buffers, perhaps not.)

I wanted to at least get the right API's in place here, even though
the implementation is still being sorted out.

>> [snip]
>
>> +/* Buffer Synchronization:
>> + */
>> +
>> +struct omap_gem_sync_waiter {
>> +     struct list_head list;
>> +     struct omap_gem_object *omap_obj;
>> +     enum omap_gem_op op;
>> +     uint32_t read_target, write_target;
>> +     /* notify called w/ sync_lock held */
>> +     void (*notify)(void *arg);
>> +     void *arg;
>> +};
>> +
>> +/* list of omap_gem_sync_waiter.. the notify fxn gets called back when
>> + * the read and/or write target count is achieved which can call a user
>> + * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for
>> + * cpu access), etc.
>> + */
>> +static LIST_HEAD(waiters);
>> +
>> +static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
>> +{
>> +     struct omap_gem_object *omap_obj = waiter->omap_obj;
>> +     if ((waiter->op & OMAP_GEM_READ) &&
>> +                     (omap_obj->sync->read_complete < waiter->read_target))
>> +             return true;
>> +     if ((waiter->op & OMAP_GEM_WRITE) &&
>> +                     (omap_obj->sync->write_complete < waiter->write_target))
>> +             return true;
>> +     return false;
>> +}
>
> On a quick read this looks awfully like handrolled gpu sync objects. For
> which we already have a fully-featured implementation in ttm. And
> and something handrolled in i915 (request tracking). Can we do better?
>
> [ Looks like it's part of the plugin layer, so problem postponed. Puhh ]

yeah, it is a bit handrolled sync-objects.  I've looked a bit
(although maybe not enough) at the TTM code, although not immediately
sure how to do better.  For better or for worse, some of the
implementation (like the in-memory layout) is dictated by SGX.  It's
an area that I'm still working on and trying to figure out how to
improve, but somehow has to coexist w/ SGX otherwise the page-flipping
in the KMS part won't work.

>> [snip]
>
>> +void omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj)
>> +{
>> +     struct omap_gem_object *omap_obj = to_omap_bo(obj);
>> +
>> +     spin_lock(&sync_lock);
>> +
>> +     if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) {
>> +             /* clearing a previously set syncobj */
>> +             syncobj = kzalloc(sizeof(*omap_obj->sync), GFP_KERNEL|GFP_ATOMIC);
>> +             memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
>> +             omap_obj->flags &= ~OMAP_BO_EXT_SYNC;
>> +             omap_obj->sync = syncobj;
>> +     } else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
>> +             /* replacing an existing syncobj */
>> +             if (omap_obj->sync) {
>> +                     memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
>> +                     kfree(omap_obj->sync);
>> +             }
>> +             omap_obj->flags |= OMAP_BO_EXT_SYNC;
>> +             omap_obj->sync = syncobj;
>> +     }
>> +     spin_unlock(&sync_lock);
>> +}
>> +EXPORT_SYMBOL(omap_gem_set_sync_object);
>
> Abstraction layers to adapt some blobs interface (be it the Windows kernel
> driver interface or a blob driver core) is a bit frowned upon (actually I
> think some people would nuke them first, ask questions later ;-). Maybe
> hide that in your own vendor patch?

just fwiw, this is a bit of a symptom of how sync-objects get mapped
to SGX's MMU.. we can't really afford to burn one page per
sync-object, so when GEM objects get mapped to the GPU it might be
required to relocate the sync-object.  It is more a result of how SGX
firmware works.  (At least nothing to do w/ drivers from other
operating systems.)  Of course, life would be much nicer for me if the
rest of the IMG userspace stuff was open, but I'm just trying to do
the best with what I've got.  But even if it was open, I think it
would still have to work the same way.  (The kernel part of pvr is
GPL, fwiw.. not that it helps much here.)

>> +int omap_gem_init_object(struct drm_gem_object *obj)
>> +{
>> +     return -EINVAL;          /* unused */
>> +}
>> +
>> +/* don't call directly.. called from GEM core when it is time to actually
>> + * free the object..
>> + */
>> +void omap_gem_free_object(struct drm_gem_object *obj)
>> +{
>> +     struct omap_gem_object *omap_obj = to_omap_bo(obj);
>> +
>> +     if (obj->map_list.map) {
>> +             drm_gem_free_mmap_offset(obj);
>> +     }
>> +
>> +     /* don't free externally allocated backing memory */
>> +     if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {
>
>
> Nobody sets OMAP_BO_EXT_MEM flag. What's this for? Better implement this
> when it actually gets used ...
>

This is used by "the plugin".. which needs a way to share the same
mmap(), so other things that get mapped to userspace end up getting
wrapped as a GEM object..


>> +             if (!obj->filp) {
>> +                     /* TODO: is this the best way to check? */
>> +                     omap_vram_free(omap_obj->paddr, obj->size);
>> +             }
>> +
>> +             if (omap_obj->pages) {
>> +                     omap_gem_detach_pages(obj);
>> +             }
>> +     }
>> +
>> +     /* don't free externally allocated syncobj */
>> +     if (!(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
>> +             kfree(omap_obj->sync);
>> +     }
>> +
>> +     drm_gem_object_release(obj);
>> +
>> +     kfree(obj);
>> +}
>> +
>> +/* convenience method to construct a GEM buffer object, and userspace handle */
>
> Inline with its only user above and scrap the forward decl?

omap_gem_new_handle?  It is used both in omap_gem_dumb_create() and
also ioctl_gem_new() for userspace creation of GEM buffers..  or where
you talking about something else?

>> +int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
>> +             size_t size, uint32_t flags, uint32_t *handle, uint64_t *offset)
>> +{
>> +     struct drm_gem_object *obj;
>> +     int ret;
>> +
>> +     obj = omap_gem_new(dev, size, flags);
>> +     if (!obj)
>> +             return -ENOMEM;
>> +
>> +     ret = drm_gem_handle_create(file, obj, handle);
>> +     if (ret) {
>> +             drm_gem_object_release(obj);
>> +             kfree(obj); /* TODO isn't there a dtor to call? just copying i915 */
>> +             return ret;
>> +     }
>> +
>> +     if (offset) {
>> +             *offset = omap_gem_mmap_offset(obj);
>> +     }
>> +
>> +     /* drop reference from allocate - handle holds it now */
>> +     drm_gem_object_unreference_unlocked(obj);
>> +
>> +     return 0;
>> +}
>> +/* common constructor body */
>> +static struct drm_gem_object * omap_gem_new_impl(struct drm_device *dev,
>> +             size_t size, uint32_t flags, unsigned long paddr, struct page **pages,
>> +             struct omap_gem_vm_ops *ops)
>> +{
>> +     struct omap_gem_object *omap_obj;
>> +     struct drm_gem_object *obj;
>> +     int ret;
>> +
>> +     size = PAGE_ALIGN(size);
>> +
>> +     omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL);
>> +     if (!omap_obj) {
>> +             dev_err(dev->dev, "could not allocate GEM object\n");
>> +             goto fail;
>> +     }
>> +
>> +     obj = &omap_obj->base;
>> +
>> +     if (paddr) {
>> +             flags |= OMAP_BO_DMA;
>> +     }
>> +
>> +     omap_obj->paddr = paddr;
>> +     omap_obj->pages = pages;
>> +     omap_obj->flags = flags;
>> +     omap_obj->ops   = ops;
>> +
>> +     if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
>> +             ret = drm_gem_private_object_init(dev, obj, size);
>> +     } else {
>> +             ret = drm_gem_object_init(dev, obj, size);
>> +     }
>> +
>> +     if (ret) {
>> +             /* Yikes!  need to clean up! XXX */
>> +             goto fail;
>> +     }
>> +
>> +     /* Make it mmapable */
>> +     ret = drm_gem_create_mmap_offset(obj);
>> +     if (ret) {
>> +             dev_err(dev->dev, "could not allocate mmap offset");
>
> The drm mmap offset manager isn't the fatest beast in the world. Maybe
> create mmappings on demand only when userspace actually needs them? I.e.
> in the ioctl_gem_info function.

ok.. initially I didn't have DRM_IOCTL_OMAP_GEM_INFO so I was
returning the offset in DRM_IOCTL_OMAP_GEM_NEW, but that is a good
point that this is no longer required.  I'll remove it from
DRM_IOCTL_OMAP_GEM_NEW and the constructor.


BR,
-R

>> +             goto fail;
>> +     }
>> +
>> +     return obj;
>> +
>> +fail:
>> +     if (omap_obj) {
>> +             kfree(omap_obj);
>> +     }
>> +     return NULL;
>> +}
>> +
>
>> [snip]
> --
> Daniel Vetter
> Mail: daniel@ffwll.ch
> Mobile: +41 (0)79 365 57 48
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Daniel Vetter Sept. 4, 2011, 7:49 p.m. UTC | #5
On Sun, Sep 04, 2011 at 11:29:43AM -0500, Rob Clark wrote:
> > Above set of get/put functions seem to do very little. Drop them for the
> > first round?
> 
> The intention is to do attach/detach_pages here.. and in case of
> get/put_paddr do remapping into TILER if the buffer isn't physically
> contiguous.  (Although in the TILER case, we are seeing how/if we can
> fit this into the IOMMU framework.. so API's here are still in flux.
> Non-tiled buffers are a natural fit for IOMMU, I think... but tiled
> buffers, perhaps not.)
> 
> I wanted to at least get the right API's in place here, even though
> the implementation is still being sorted out.

If I've grepped that one correctly, there not (yet) used, so just confuse
when reviewing. They're also easier to understand with actual users ;-)

I think that's even true (perhaps even more so) for userspace stuff -
there's an enormous body of precedence for adding feature flags in drm
land for such stuff.

> >> [snip]
> >
> >> +/* Buffer Synchronization:
> >> + */
> >> +
> >> +struct omap_gem_sync_waiter {
> >> +     struct list_head list;
> >> +     struct omap_gem_object *omap_obj;
> >> +     enum omap_gem_op op;
> >> +     uint32_t read_target, write_target;
> >> +     /* notify called w/ sync_lock held */
> >> +     void (*notify)(void *arg);
> >> +     void *arg;
> >> +};
> >> +
> >> +/* list of omap_gem_sync_waiter.. the notify fxn gets called back when
> >> + * the read and/or write target count is achieved which can call a user
> >> + * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for
> >> + * cpu access), etc.
> >> + */
> >> +static LIST_HEAD(waiters);
> >> +
> >> +static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
> >> +{
> >> +     struct omap_gem_object *omap_obj = waiter->omap_obj;
> >> +     if ((waiter->op & OMAP_GEM_READ) &&
> >> +                     (omap_obj->sync->read_complete < waiter->read_target))
> >> +             return true;
> >> +     if ((waiter->op & OMAP_GEM_WRITE) &&
> >> +                     (omap_obj->sync->write_complete < waiter->write_target))
> >> +             return true;
> >> +     return false;
> >> +}
> >
> > On a quick read this looks awfully like handrolled gpu sync objects. For
> > which we already have a fully-featured implementation in ttm. And
> > and something handrolled in i915 (request tracking). Can we do better?
> >
> > [ Looks like it's part of the plugin layer, so problem postponed. Puhh ]
> 
> yeah, it is a bit handrolled sync-objects.  I've looked a bit
> (although maybe not enough) at the TTM code, although not immediately
> sure how to do better.  For better or for worse, some of the
> implementation (like the in-memory layout) is dictated by SGX.  It's
> an area that I'm still working on and trying to figure out how to
> improve, but somehow has to coexist w/ SGX otherwise the page-flipping
> in the KMS part won't work.

My gripes aren't with the hw interfacing side but more with the
wheel-reinventing for the signalling and boilerblate accounting code.

Which ttm has a complete framework for.

Up to now only i915 has been the odd man out, adding more is imo Not So
Good (tm). Unfortunately there's no easy way out: Unifying ttm and i915
style gem is a hellalotta work, and growing gem into something like ttm is
pretty pointless (which code like this will eventually lead to).

> > Inline with its only user above and scrap the forward decl?
> 
> omap_gem_new_handle?  It is used both in omap_gem_dumb_create() and
> also ioctl_gem_new() for userspace creation of GEM buffers..  or where
> you talking about something else?

Oops, grepping gone wrong. Sorry for the noise.

Generally I think the harder issues are all with the plugin layer. And
maybe I'll get my act toghether and clean the i915-gem vs ttm mess a bit
up until you've got the first plugin merge-ready ;-)

Cheers, Daniel
Rob Clark Sept. 4, 2011, 8:59 p.m. UTC | #6
On Sun, Sep 4, 2011 at 2:49 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sun, Sep 04, 2011 at 11:29:43AM -0500, Rob Clark wrote:
>> > Above set of get/put functions seem to do very little. Drop them for the
>> > first round?
>>
>> The intention is to do attach/detach_pages here.. and in case of
>> get/put_paddr do remapping into TILER if the buffer isn't physically
>> contiguous.  (Although in the TILER case, we are seeing how/if we can
>> fit this into the IOMMU framework.. so API's here are still in flux.
>> Non-tiled buffers are a natural fit for IOMMU, I think... but tiled
>> buffers, perhaps not.)
>>
>> I wanted to at least get the right API's in place here, even though
>> the implementation is still being sorted out.
>
> If I've grepped that one correctly, there not (yet) used, so just confuse
> when reviewing. They're also easier to understand with actual users ;-)

The omap_fb.c stuff does a put_paddr() when it is done using a buffer
for scanout, fwiw.  But the put_paddr() stuff will make more sense
when I add support to remap discontiguous buffers for scanout.

> I think that's even true (perhaps even more so) for userspace stuff -
> there's an enormous body of precedence for adding feature flags in drm
> land for such stuff.
>
>> >> [snip]
>> >
>> >> +/* Buffer Synchronization:
>> >> + */
>> >> +
>> >> +struct omap_gem_sync_waiter {
>> >> +     struct list_head list;
>> >> +     struct omap_gem_object *omap_obj;
>> >> +     enum omap_gem_op op;
>> >> +     uint32_t read_target, write_target;
>> >> +     /* notify called w/ sync_lock held */
>> >> +     void (*notify)(void *arg);
>> >> +     void *arg;
>> >> +};
>> >> +
>> >> +/* list of omap_gem_sync_waiter.. the notify fxn gets called back when
>> >> + * the read and/or write target count is achieved which can call a user
>> >> + * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for
>> >> + * cpu access), etc.
>> >> + */
>> >> +static LIST_HEAD(waiters);
>> >> +
>> >> +static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
>> >> +{
>> >> +     struct omap_gem_object *omap_obj = waiter->omap_obj;
>> >> +     if ((waiter->op & OMAP_GEM_READ) &&
>> >> +                     (omap_obj->sync->read_complete < waiter->read_target))
>> >> +             return true;
>> >> +     if ((waiter->op & OMAP_GEM_WRITE) &&
>> >> +                     (omap_obj->sync->write_complete < waiter->write_target))
>> >> +             return true;
>> >> +     return false;
>> >> +}
>> >
>> > On a quick read this looks awfully like handrolled gpu sync objects. For
>> > which we already have a fully-featured implementation in ttm. And
>> > and something handrolled in i915 (request tracking). Can we do better?
>> >
>> > [ Looks like it's part of the plugin layer, so problem postponed. Puhh ]
>>
>> yeah, it is a bit handrolled sync-objects.  I've looked a bit
>> (although maybe not enough) at the TTM code, although not immediately
>> sure how to do better.  For better or for worse, some of the
>> implementation (like the in-memory layout) is dictated by SGX.  It's
>> an area that I'm still working on and trying to figure out how to
>> improve, but somehow has to coexist w/ SGX otherwise the page-flipping
>> in the KMS part won't work.
>
> My gripes aren't with the hw interfacing side but more with the
> wheel-reinventing for the signalling and boilerblate accounting code.

ahh, ok, I get your point.  When I started, I thought full blown TTM
would be overkill for a UMA setup.. but maybe I should revisit that.
Or if nothing else, see how to somehow refactor some of that out.. I'm
not really sure what the best thing to do would be at this point.
Although I suppose the gma500 driver would be in the same boat..

> Which ttm has a complete framework for.
>
> Up to now only i915 has been the odd man out, adding more is imo Not So
> Good (tm). Unfortunately there's no easy way out: Unifying ttm and i915
> style gem is a hellalotta work, and growing gem into something like ttm is
> pretty pointless (which code like this will eventually lead to).

yeah, that makes sense.. well, I'm open to suggestions here :-)

BR,
-R
대인기/Tizen Platform Lab(SR)/삼성전자 Sept. 5, 2011, 9:58 a.m. UTC | #7
Hello, Rob.
I didn't look into any comments from other develops yet , so my comments
could be duplicated with other ones. below is my opinions and I commented
your codes also.

We can discuss crtc, encoder and connector. in x86 world, my understanding,
each one means the following.
Crtc :
- independent of hardware device, this means a crtc object has no any hw
specific callbacks.
- having information common to all HWs such as overlay, framebuffer, mode
data and offset to any region of the framebuffer to be displayed on output
device.

Encoder:
- dependent of  a hardware device, this means a encoder object has hw
specific callbacks.
- taking the digital bitstream from the crtc and converting it to
appropriate analog levels for transmission across connector to the monitor.
And for this, containing some callbacks which are pointing to
device-specific functions to control these HWs.

Connector:
- dependent of a hardware device, this means a connector object has hw
specific callbacks.
- providing appropriate physical plug (HDMI, DVI-D, VGA, S-VIDEO and so on)
for the monitor to connect to.

And maybe now drm framework have been considered for only x86 system. :(

x86 system and embedded SoCs would be different each other. so I presume the
difference between x86 and embedded system are as the following.
- for mode setting, output devices such as lcd panel, analog tv and digital
tv, have no any registers. These registers belong to display controller and
hdmi controller. in case of graphic cares for x86 system, containing all
controllers, not so. please could anyone give me the explanation about x86
system?

With the things I mentioned above, we need to think of which object, crtc,
encoder or encoder, should contain hw specific callbacks, and common data
structures.

In my opinion, it's as the following:
Crtc:
- having a overlay structure common to all HWs. this overlay information
would be set to real hw driver through encoder/connector object. the reason,
crtc should be independent object, is that for clone, crtc object should be
only one object. you can refer to drm_target_cloned function of
drm_fb_helper.c. in this function, it is just returned if
fb_helper->crtc_count is more than 1.
- created as independent object and any crtc object could be connected to
any encoder/connector.

Encoder:
- having hw specific callbacks for overlay setting, clock on/off and vblank
on/off.

Connector:
- having hw specific callbacks for display timing setting and display
devices, such as digital tv, analog tv or lcd panel on/off.

Call path for accessing to any hw is as the following:
1. setcrtc is called with fb id, crtc id, connector id, and mode data, 
2. the crtc corresponding to crtc id is connected to the encoder/connector
corresponding to each id.
3. mode and overlay information are updated by crtc.
4. it gets the encoder/connector connected to this crtc and then access to
any hw through this encoder/connector with hw specific callbacks.

Above is just my opinions. if you have other any ideas, I'd be pleased you
to give me your opinions.

Best Regards,
Inki Dae.

> -----Original Message-----
> From: Rob Clark [mailto:robdclark@gmail.com] On Behalf Of Rob Clark
> Sent: Saturday, September 03, 2011 5:07 AM
> To: dri-devel@lists.freedesktop.org
> Cc: linaro-dev@lists.linaro.org; inki.dae@samsung.com; Rob Clark
> Subject: [PATCH] RFC: omapdrm DRM/KMS driver for TI OMAP platforms
> 
> From: Rob Clark <rob@ti.com>
> 
> A DRM display driver for TI OMAP platform.  Similar to omapfb (fbdev)
> and omap_vout (v4l2 display) drivers in the past, this driver uses the
> DSS2 driver to access the display hardware, including support for
> HDMI, DVI, and various types of LCD panels.  And it implements GEM
> support for buffer allocation (for KMS as well as offscreen buffers
> used by the xf86-video-omap userspace xorg driver).
> 
> The driver maps CRTCs to overlays, encoders to overlay-managers, and
> connectors to dssdev's.  Note that this arrangement might change slightly
> when support for drm_plane overlays is added.
> 
> For GEM support, non-scanout buffers are using the shmem backed pages
> provided by GEM core (In drm_gem_object_init()).  In the case of scanout
> buffers, which need to be physically contiguous, those are allocated from
> omap_vram and use drm_gem_private_object_init() (Thanks to "gem: RFC: add
> support for private objects" patch from Alan Cox.)  Also used is the
> "drm/gem: add functions for mmap offset creation" patch to avoid
> duplicating some functions for mmap offset management in the omapdrm
> driver.
> 
> Note that the usage of omap_vram carveout will be replaced by CMA in
> the future.
> 
> A simple "plugin" mechanism is provided to allow integration with
> external kernel modules (*cough* PVR), but also to keep the code more
> modular as support is added for various other accelerator blocks that
> exist in various different OMAP variants.  (2D accel, video encode/
> decode, etc.)  However this driver and userspace xorg driver are
> themselves fully functional without any closed-source components. It
> doesn't (and won't) provide any command submission to IP blocks with
> closed-source kernel or userspace components.
> 
> See userspace xorg driver: git://github.com/robclark/xf86-video-omap.git
> ---
> Note that this patch depends on a few other patches which have been
> submitted to respective mailing lists (linux-omap for DSS2 and
> dri-devel for DRM), but not yet merged.  But this is only a RFC, and
> I thought it was about time to put on the asbestos pants and let the
> good folks of dri-devel get their flame on.
> 
> Special thanks to Tomi Valkeinen (DSS2 author) for patches to add the
> needed APIs for hotplug detection and EDID to DSS2 and various other
> help with the DRM driver.
> 
>  drivers/staging/Kconfig                  |    2 +
>  drivers/staging/Makefile                 |    1 +
>  drivers/staging/omapdrm/Kconfig          |   25 +
>  drivers/staging/omapdrm/Makefile         |    8 +
>  drivers/staging/omapdrm/TODO.txt         |   15 +
>  drivers/staging/omapdrm/omap_connector.c |  375 ++++++++++++
>  drivers/staging/omapdrm/omap_crtc.c      |  348 ++++++++++++
>  drivers/staging/omapdrm/omap_drv.c       |  910
> ++++++++++++++++++++++++++++++
>  drivers/staging/omapdrm/omap_drv.h       |  117 ++++
>  drivers/staging/omapdrm/omap_encoder.c   |  198 +++++++
>  drivers/staging/omapdrm/omap_fb.c        |  265 +++++++++
>  drivers/staging/omapdrm/omap_fbdev.c     |  303 ++++++++++
>  drivers/staging/omapdrm/omap_gem.c       |  851
> ++++++++++++++++++++++++++++
>  drivers/video/omap2/omapfb/Kconfig       |    4 +-
>  include/linux/Kbuild                     |    1 +
>  include/linux/omap_drm.h                 |  191 +++++++
>  16 files changed, 3612 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/staging/omapdrm/Kconfig
>  create mode 100644 drivers/staging/omapdrm/Makefile
>  create mode 100644 drivers/staging/omapdrm/TODO.txt
>  create mode 100644 drivers/staging/omapdrm/omap_connector.c
>  create mode 100644 drivers/staging/omapdrm/omap_crtc.c
>  create mode 100644 drivers/staging/omapdrm/omap_drv.c
>  create mode 100644 drivers/staging/omapdrm/omap_drv.h
>  create mode 100644 drivers/staging/omapdrm/omap_encoder.c
>  create mode 100644 drivers/staging/omapdrm/omap_fb.c
>  create mode 100644 drivers/staging/omapdrm/omap_fbdev.c
>  create mode 100644 drivers/staging/omapdrm/omap_gem.c
>  create mode 100644 include/linux/omap_drm.h
> 
> diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
> index 196284d..99b7547 100644
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -160,4 +160,6 @@ source "drivers/staging/mei/Kconfig"
> 
>  source "drivers/staging/nvec/Kconfig"
> 
> +source "drivers/staging/omapdrm/Kconfig"
> +
>  endif # STAGING
> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
> index fa41b9c..91ec21f 100644
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -71,3 +71,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4)	+=
> ste_rmi4/
>  obj-$(CONFIG_DRM_PSB)		+= gma500/
>  obj-$(CONFIG_INTEL_MEI)		+= mei/
>  obj-$(CONFIG_MFD_NVEC)		+= nvec/
> +obj-$(CONFIG_DRM_OMAP)		+= omapdrm/
> diff --git a/drivers/staging/omapdrm/Kconfig
> b/drivers/staging/omapdrm/Kconfig
> new file mode 100644
> index 0000000..bddf52b
> --- /dev/null
> +++ b/drivers/staging/omapdrm/Kconfig
> @@ -0,0 +1,25 @@
> +
> +config DRM_OMAP
> +	tristate "OMAP DRM (EXPERIMENTAL)"
> +	depends on DRM && !CONFIG_FB_OMAP2
> +	select DRM_KMS_HELPER
> +	select OMAP2_VRAM
> +	select OMAP2_DSS
> +	select FB_SYS_FILLRECT
> +	select FB_SYS_COPYAREA
> +	select FB_SYS_IMAGEBLIT
> +	select FB_SYS_FOPS
> +	default n
> +	help
> +	  DRM display driver for OMAP2/3/4 based boards.
> +
> +config DRM_OMAP_NUM_CRTCS
> +	int "Number of CRTCs"
> +	range 1 10
> +	default 1  if ARCH_OMAP2 || ARCH_OMAP3
> +	default 2  if ARCH_OMAP4
> +	depends on DRM_OMAP
> +	help
> +	  Select the number of video overlays which can be used as
> framebuffers.
> +	  The remaining overlays are reserved for video.
> +
> diff --git a/drivers/staging/omapdrm/Makefile
> b/drivers/staging/omapdrm/Makefile
> new file mode 100644
> index 0000000..da74337
> --- /dev/null
> +++ b/drivers/staging/omapdrm/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Makefile for the drm device driver.  This driver provides support for
> the
> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> +
> +ccflags-y := -Iinclude/drm -Werror
> +omapdrm-y := omap_drv.o omap_crtc.o omap_encoder.o omap_connector.o
> omap_fb.o omap_fbdev.o omap_gem.o
> +
> +obj-$(CONFIG_DRM_OMAP)	+= omapdrm.o
> diff --git a/drivers/staging/omapdrm/TODO.txt
> b/drivers/staging/omapdrm/TODO.txt
> new file mode 100644
> index 0000000..b64f067
> --- /dev/null
> +++ b/drivers/staging/omapdrm/TODO.txt
> @@ -0,0 +1,15 @@
> +TODO
> +. move to CMA for scanout buffer allocation
> +. check error handling/cleanup paths
> +. add drm_plane / overlay support
> +. add video decode/encode support (via syslink3 + codec-engine)
> +. still some rough edges with flipping.. event back to userspace should
> +  really come after VSYNC interrupt
> +. support for tiled buffers
> +
> +Userspace:
> +. git://github.com/robclark/xf86-video-omap.git
> +
> +Currently tested on
> +. OMAP3530 beagleboard
> +. OMAP4430 pandaboard
> +. OMAP4460 pandaboard
> diff --git a/drivers/staging/omapdrm/omap_connector.c
> b/drivers/staging/omapdrm/omap_connector.c
> new file mode 100644
> index 0000000..7f7403b
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_connector.c
> @@ -0,0 +1,375 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_connector.c
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/omap_drm.h>
> +#include "omap_drv.h"
> +
> +#include "drm_crtc.h"
> +#include "drm_crtc_helper.h"
> +
> +/*
> + * connector funcs
> + */
> +
> +#define to_omap_connector(x) container_of(x, struct omap_connector, base)
> +
> +struct omap_connector {
> +	struct drm_connector base;
> +	struct omap_dss_device *dssdev;
> +};
> +
> +static inline void copy_timings_omap_to_drm(struct drm_display_mode
*mode,
> +		struct omap_video_timings *timings)
> +{
> +	mode->clock = timings->pixel_clock;
> +
> +	mode->hdisplay = timings->x_res;
> +	mode->hsync_start = mode->hdisplay + timings->hfp;
> +	mode->hsync_end = mode->hsync_start + timings->hsw;
> +	mode->htotal = mode->hsync_end + timings->hbp;
> +
> +	mode->vdisplay = timings->y_res;
> +	mode->vsync_start = mode->vdisplay + timings->vfp;
> +	mode->vsync_end = mode->vsync_start + timings->vsw;
> +	mode->vtotal = mode->vsync_end + timings->vbp;
> +
> +	/* note: whether or not it is interlaced, +/- h/vsync, etc,
> +	 * which should be set in the mode flags, is not exposed in
> +	 * the omap_video_timings struct.. but hdmi driver tracks
> +	 * those separately so all we have to have to set the mode
> +	 * is the way to recover these timings values, and the
> +	 * omap_dss_driver would do the rest.
> +	 */
> +}
> +
> +static inline void copy_timings_drm_to_omap(struct omap_video_timings
> *timings,
> +		struct drm_display_mode *mode)
> +{
> +	timings->pixel_clock = mode->clock;
> +
> +	timings->x_res = mode->hdisplay;
> +	timings->hfp = mode->hsync_start - mode->hdisplay;
> +	timings->hsw = mode->hsync_end - mode->hsync_start;
> +	timings->hbp = mode->htotal - mode->hsync_end;
> +
> +	timings->y_res = mode->vdisplay;
> +	timings->vfp = mode->vsync_start - mode->vdisplay;
> +	timings->vsw = mode->vsync_end - mode->vsync_start;
> +	timings->vbp = mode->vtotal - mode->vsync_end;
> +}
> +
> +void omap_connector_dpms(struct drm_connector *connector, int mode)
> +{
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +	int ret;
> +
> +	DBG("%s: %d", dssdev->name, mode);
> +
> +	if (mode == DRM_MODE_DPMS_ON) {
> +		/* store resume info for suspended displays */
> +		switch (dssdev->state) {
> +		case OMAP_DSS_DISPLAY_SUSPENDED:
> +			dssdev->activate_after_resume = true;
> +			break;
> +		case OMAP_DSS_DISPLAY_DISABLED:
> +			ret = dssdev->driver->enable(dssdev);
> +			if (ret) {
> +				DBG("%s: failed to enable: %d",
dssdev->name,
> ret);
> +				dssdev->driver->disable(dssdev);
> +			}
> +			break;
> +		default:
> +			break;
> +		}
> +	} else {
> +		/* TODO */
> +	}
> +}
> +
> +enum drm_connector_status omap_connector_detect(
> +		struct drm_connector *connector, bool force)
> +{
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +	struct omap_dss_driver *dssdrv = dssdev->driver;
> +	enum drm_connector_status ret;
> +
> +	/* hmm.. ideally dssdrv would take care of this internally if (and
> +	 * only if) power-on was required to detect connection..
> +	 */
> +	omap_connector_dpms(connector, DRM_MODE_DPMS_ON);
> +
> +	if (dssdrv->is_detected(dssdev)) {
> +		ret = connector_status_connected;
> +	} else {
> +		ret = connector_status_disconnected;
> +	}
> +
> +	VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
> +
> +	return ret;
> +}
> +
> +static void omap_connector_destroy(struct drm_connector *connector)
> +{
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +
> +	dssdev->driver->disable(dssdev);
> +
> +	DBG("%s", omap_connector->dssdev->name);
> +	drm_sysfs_connector_remove(connector);
> +	drm_connector_cleanup(connector);
> +	kfree(omap_connector);
> +
> +	omap_dss_put_device(dssdev);
> +}
> +
> +#define MAX_EDID  512
> +
> +static int omap_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +	struct omap_dss_driver *dssdrv = dssdev->driver;
> +	struct drm_device *dev = connector->dev;
> +	int n = 0;
> +
> +	DBG("%s", omap_connector->dssdev->name);
> +
> +	/* if display exposes EDID, then we parse that in the normal way to
> +	 * build table of supported modes.. otherwise (ie. fixed resolution
> +	 * LCD panels) we just return a single mode corresponding to the
> +	 * currently configured timings:
> +	 */
> +	if (dssdrv->read_edid) {
> +		void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
> +
> +		if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) == 0) &&
> +				drm_edid_is_valid(edid)) {
> +			drm_mode_connector_update_edid_property(connector,
> edid);
> +			n = drm_add_edid_modes(connector, edid);
> +			kfree(connector->display_info.raw_edid);
> +			connector->display_info.raw_edid = edid;
> +		} else {
> +			drm_mode_connector_update_edid_property(connector,
> NULL);
> +			connector->display_info.raw_edid = NULL;
> +			kfree(edid);
> +		}
> +	} else {
> +		struct drm_display_mode *mode = drm_mode_create(dev);
> +		struct omap_video_timings timings;
> +
> +		dssdrv->get_timings(dssdev, &timings);
> +
> +		copy_timings_omap_to_drm(mode, &timings);
> +
> +		mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
> +		drm_mode_set_name(mode);
> +		drm_mode_probed_add(connector, mode);
> +
> +		n = 1;
> +	}
> +
> +	return n;
> +}
> +
> +static int omap_connector_mode_valid(struct drm_connector *connector,
> +				 struct drm_display_mode *mode)
> +{
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +	struct omap_dss_driver *dssdrv = dssdev->driver;
> +	struct omap_video_timings timings = {0};
> +	struct drm_device *dev = connector->dev;
> +	struct drm_display_mode *new_mode;
> +	int ret = MODE_BAD;
> +
> +	copy_timings_drm_to_omap(&timings, mode);
> +	mode->vrefresh = drm_mode_vrefresh(mode);
> +
> +	if (!dssdrv->check_timings(dssdev, &timings)) {
> +		/* check if vrefresh is still valid */
> +		new_mode = drm_mode_duplicate(dev, mode);
> +		new_mode->clock = timings.pixel_clock;
> +		new_mode->vrefresh = 0;
> +		if (mode->vrefresh == drm_mode_vrefresh(new_mode))
> +			ret = MODE_OK;
> +		drm_mode_destroy(dev, new_mode);
> +	}
> +

is there any reason that you call drm_mode_duplicate() to get a clone of a
mode? I just wonder it.

> +	DBG("connector: mode %s: "
> +			"%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
> +			(ret == MODE_OK) ? "valid" : "invalid",
> +			mode->base.id, mode->name, mode->vrefresh,
mode->clock,
> +			mode->hdisplay, mode->hsync_start,
> +			mode->hsync_end, mode->htotal,
> +			mode->vdisplay, mode->vsync_start,
> +			mode->vsync_end, mode->vtotal, mode->type, mode-
> >flags);
> +
> +	return ret;
> +}
> +
> +struct drm_encoder * omap_connector_attached_encoder(
> +		struct drm_connector *connector)
> +{
> +	int i;
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +
> +	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
> +		struct drm_mode_object *obj;
> +
> +		if (connector->encoder_ids[i] == 0)
> +			break;
> +
> +		obj = drm_mode_object_find(connector->dev,
> +				connector->encoder_ids[i],
> +				DRM_MODE_OBJECT_ENCODER);
> +
> +		if (obj) {
> +			struct drm_encoder *encoder = obj_to_encoder(obj);
> +			struct omap_overlay_manager *mgr =
> +					omap_encoder_get_manager(encoder);
> +			DBG("%s: found %s", omap_connector->dssdev->name,
> +					mgr->name);
> +			return encoder;
> +		}
> +	}
> +
> +	DBG("%s: no encoder", omap_connector->dssdev->name);
> +
> +	return NULL;
> +}
> +
> +static const struct drm_connector_funcs omap_connector_funcs = {
> +	.dpms = drm_helper_connector_dpms,
> +	.detect = omap_connector_detect,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = omap_connector_destroy,
> +};
> +
> +static const struct drm_connector_helper_funcs
> omap_connector_helper_funcs = {
> +	.get_modes = omap_connector_get_modes,
> +	.mode_valid = omap_connector_mode_valid,
> +	.best_encoder = omap_connector_attached_encoder,
> +};
> +
> +/* called from encoder when mode is set, to propagate settings to the
> dssdev */
> +void omap_connector_mode_set(struct drm_connector *connector,
> +		struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +	struct omap_dss_driver *dssdrv = dssdev->driver;
> +	struct omap_video_timings timings;
> +
> +	copy_timings_drm_to_omap(&timings, mode);
> +
> +	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x
> 0x%x",
> +			omap_connector->dssdev->name,
> +			mode->base.id, mode->name, mode->vrefresh,
mode->clock,
> +			mode->hdisplay, mode->hsync_start,
> +			mode->hsync_end, mode->htotal,
> +			mode->vdisplay, mode->vsync_start,
> +			mode->vsync_end, mode->vtotal, mode->type, mode-
> >flags);
> +
> +	if (dssdrv->check_timings(dssdev, &timings)) {
> +		dev_err(dev->dev, "could not set timings\n");
> +		return;
> +	}
> +
> +	dssdrv->set_timings(dssdev, &timings);
> +}
> +
> +int omap_connector_sync(struct drm_connector *connector)
> +{
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +	struct omap_dss_device *dssdev = omap_connector->dssdev;
> +	struct omap_dss_driver *dssdrv = dssdev->driver;
> +
> +	DBG("%s", omap_connector->dssdev->name);
> +
> +	if (dssdrv->sync) {
> +		return dssdrv->sync(dssdev);
> +	}
> +
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL(omap_connector_sync);
> +
> +/* flush an area of the framebuffer (in case of manual update display
> that
> + * is not automatically flushed)
> + */
> +void omap_connector_flush(struct drm_connector *connector,
> +		int x, int y, int w, int h)
> +{
> +	struct omap_connector *omap_connector =
> to_omap_connector(connector);
> +
> +	/* TODO: enable when supported in dss */
> +	VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
> +}
> +
> +/* initialize connector */
> +struct drm_connector * omap_connector_init(struct drm_device *dev,
> +		int connector_type, struct omap_dss_device *dssdev)
> +{
> +	struct drm_connector *connector = NULL;

It appears that this connector is initialized with NULL. This is just minor
issue. :)

> +	struct omap_connector *omap_connector;
> +
> +	DBG("%s", dssdev->name);
> +
> +	omap_dss_get_device(dssdev);
> +
> +	omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);
> +	if (!omap_connector) {
> +		dev_err(dev->dev, "could not allocate connector\n");
> +		goto fail;
> +	}
> +
> +	omap_connector->dssdev = dssdev;
> +	connector = &omap_connector->base;
> +
> +	drm_connector_init(dev, connector, &omap_connector_funcs,
> +				connector_type);
> +	drm_connector_helper_add(connector, &omap_connector_helper_funcs);
> +
> +#if 0 /* enable when dss2 supports hotplug */
> +	if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD)
> +		connector->polled = 0;
> +	else
> +#endif
> +		connector->polled = DRM_CONNECTOR_POLL_CONNECT |
> +				DRM_CONNECTOR_POLL_DISCONNECT;
> +
> +	connector->interlace_allowed = 1;
> +	connector->doublescan_allowed = 0;
> +
> +	drm_sysfs_connector_add(connector);
> +
> +	return connector;
> +
> +fail:
> +	if (connector) {
> +		omap_connector_destroy(connector);
> +	}

it doesn't need the code below because if "omap_connector =
kzalloc(sizeof(struct omap_connector), GFP_KERNEL);" is fail then
omap_connector object has no any instance, please so remove it. just minor
issue.

if (connector) {
	omap_connector_destroy(connector);
}

> +

> +	return NULL;
> +}
> diff --git a/drivers/staging/omapdrm/omap_crtc.c
> b/drivers/staging/omapdrm/omap_crtc.c
> new file mode 100644
> index 0000000..3714caa
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_crtc.c
> @@ -0,0 +1,348 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_crtc.c
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/omap_drm.h>
> +#include "omap_drv.h"
> +
> +#include "drm_mode.h"
> +#include "drm_crtc.h"
> +#include "drm_crtc_helper.h"
> +
> +#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
> +
> +struct omap_crtc {
> +	struct drm_crtc base;
> +	struct omap_overlay *ovl;
> +	struct omap_overlay_info info;
> +	int id;
> +
> +	/* if there is a pending flip, this will be non-null: */
> +	struct drm_pending_vblank_event *event;
> +};
> +
> +/* push changes down to dss2 */
> +static int commit(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	struct omap_overlay *ovl = omap_crtc->ovl;
> +	struct omap_overlay_info *info = &omap_crtc->info;
> +	int ret;
> +
> +	DBG("%s", omap_crtc->ovl->name);
> +	DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info-
> >out_width,
> +			info->out_height, info->screen_width);
> +	DBG("%d,%d %p %08x", info->pos_x, info->pos_y, info->vaddr,
> +			info->paddr);
> +
> +	/* NOTE: do we want to do this at all here, or just wait
> +	 * for dpms(ON) since other CRTC's may not have their mode
> +	 * set yet, so fb dimensions may still change..
> +	 */
> +	ret = ovl->set_overlay_info(ovl, info);
> +	if (ret) {
> +		dev_err(dev->dev, "could not set overlay info\n");
> +		return ret;
> +	}
> +
> +	/* our encoder doesn't necessarily get a commit() after this, in
> +	 * particular in the dpms() and mode_set_base() cases, so force the
> +	 * manager to update:
> +	 *
> +	 * could this be in the encoder somehow?
> +	 */
> +	if (ovl->manager) {
> +		ret = ovl->manager->apply(ovl->manager);
> +		if (ret) {
> +			dev_err(dev->dev, "could not apply\n");
> +			return ret;
> +		}
> +	}
> +
> +	if (info->enabled) {
> +		omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y,
> +				crtc->fb->width, crtc->fb->height);
> +	}
> +
> +	return 0;
> +}
> +
> +/* update parameters that are dependent on the framebuffer dimensions and
> + * position within the fb that this crtc scans out from. This is called
> + * when framebuffer dimensions or x,y base may have changed, either due
> + * to our mode, or a change in another crtc that is scanning out of the
> + * same fb.
> + */
> +static void update_scanout(struct drm_crtc *crtc)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	unsigned long paddr;
> +	void __iomem *vaddr;
> +	int screen_width;
> +
> +	omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y,
> +			&vaddr, &paddr, &screen_width);
> +
> +	DBG("%s: %d,%d: %p %08lx (%d)", omap_crtc->ovl->name,
> +			crtc->x, crtc->y, vaddr, paddr, screen_width);
> +
> +	omap_crtc->info.paddr = paddr;
> +	omap_crtc->info.vaddr = vaddr;
> +	omap_crtc->info.screen_width = screen_width;
> +}
> +
> +static void omap_crtc_gamma_set(struct drm_crtc *crtc,
> +		u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t
> size)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	DBG("%s", omap_crtc->ovl->name);
> +}
> +
> +static void omap_crtc_destroy(struct drm_crtc *crtc)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	DBG("%s", omap_crtc->ovl->name);
> +	drm_crtc_cleanup(crtc);
> +	kfree(omap_crtc);
> +}
> +
> +static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +
> +	DBG("%s: %d", omap_crtc->ovl->name, mode);
> +
> +	if (mode == DRM_MODE_DPMS_ON) {
> +		update_scanout(crtc);
> +		omap_crtc->info.enabled = true;
> +	} else {
> +		omap_crtc->info.enabled = false;
> +	}
> +
> +	commit(crtc);
> +}
> +
> +static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
> +				  struct drm_display_mode *mode,
> +				  struct drm_display_mode *adjusted_mode)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	DBG("%s", omap_crtc->ovl->name);
> +	return true;
> +}
> +
> +static int omap_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 omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +
> +	DBG("%s: %d,%d: %dx%d",omap_crtc->ovl->name, x, y,
> +			mode->hdisplay, mode->vdisplay);
> +
> +	/* just use adjusted mode */
> +	mode = adjusted_mode;
> +
> +	omap_crtc->info.width = mode->hdisplay;
> +	omap_crtc->info.height = mode->vdisplay;
> +	omap_crtc->info.out_width = mode->hdisplay;
> +	omap_crtc->info.out_height = mode->vdisplay;
> +	omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U;
> +	omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA;
> +	omap_crtc->info.rotation = OMAP_DSS_ROT_0;
> +	omap_crtc->info.global_alpha = 0xff;
> +	omap_crtc->info.mirror = 0;
> +	omap_crtc->info.mirror = 0;
> +	omap_crtc->info.pos_x = 0;
> +	omap_crtc->info.pos_y = 0;
> +#if 0 /* re-enable when these are available in DSS2 driver */
> +	omap_crtc->info.zorder = 3;        /* GUI in the front, video behind
> */
> +	omap_crtc->info.min_x_decim = 1;
> +	omap_crtc->info.max_x_decim = 1;
> +	omap_crtc->info.min_y_decim = 1;
> +	omap_crtc->info.max_y_decim = 1;
> +#endif
> +
> +	update_scanout(crtc);
> +
> +	return 0;
> +}
> +
> +static void omap_crtc_prepare(struct drm_crtc *crtc)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	struct omap_overlay *ovl = omap_crtc->ovl;
> +
> +	DBG("%s", omap_crtc->ovl->name);
> +
> +	ovl->get_overlay_info(ovl, &omap_crtc->info);
> +
> +	omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
> +}
> +
> +static void omap_crtc_commit(struct drm_crtc *crtc)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	DBG("%s", omap_crtc->ovl->name);
> +	omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
> +}
> +
> +static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> +		    struct drm_framebuffer *old_fb)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +
> +	DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb);
> +
> +	update_scanout(crtc);
> +
> +	return commit(crtc);
> +}
> +
> +static void omap_crtc_load_lut(struct drm_crtc *crtc)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	DBG("%s", omap_crtc->ovl->name);
> +}
> +
> +static void page_flip_cb(void *arg)
> +{
> +	struct drm_crtc *crtc = arg;
> +	struct drm_device *dev = crtc->dev;
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	struct drm_pending_vblank_event *event = omap_crtc->event;
> +	struct timeval now;
> +	unsigned long flags;
> +
> +	WARN_ON(!event);
> +
> +	omap_crtc->event = NULL;
> +
> +	update_scanout(crtc);
> +	commit(crtc);
> +
> +	/* wakeup userspace */
> +	// TODO: this should happen *after* flip.. somehow..
> +	if (event) {
> +		spin_lock_irqsave(&dev->event_lock, flags);
> +		event->event.sequence =
> +				drm_vblank_count_and_time(dev,
omap_crtc->id,
> &now);
> +		event->event.tv_sec = now.tv_sec;
> +		event->event.tv_usec = now.tv_usec;
> +		list_add_tail(&event->base.link,
> +				&event->base.file_priv->event_list);
> +		wake_up_interruptible(&event->base.file_priv->event_wait);
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
> +	}

How about moving codes above into interrupt handler for vblank? maybe there
would be some reason that spin_lock_* are used here.

> +}
> +
> +static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
> +		 struct drm_framebuffer *fb,
> +		 struct drm_pending_vblank_event *event)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +
> +	DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
> +
> +	if (omap_crtc->event) {
> +		dev_err(dev->dev, "already a pending flip\n");
> +		return -EINVAL;
> +	}
> +
> +	crtc->fb = fb;
> +	omap_crtc->event = event;
> +
> +	omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ,
> +			page_flip_cb, crtc);
> +
> +	return 0;
> +}
> +

Where are drm_vblank_get() and drm_vblank_put() called to enable and disable
vsync interrupt? and does the this page flip need user-requested drm_control
ioctl to install/uninstall a handler?. I think the page_flip feature should
work it fine itself.

like this:
at omap_crtc_page_flip_locked()
drm_vblank_get();   <- enable vsync interrupt.
update famebuffer. <- update overlay and set it to hw.

at interrupt handler
handle page flip event.
wake_up_interruptible() <- notify  user of page flip event.
drm_vblank_put();  <- disable vsync interrupt.

I think that page flip callback would be called after vsync interrupt
occurred. for this, you can refer to set_mode function of libdrm library. a
event has user's apage flip handler and when user received any event from
kernel side, this handler would be called to update framebuffer repeatedly.


> +int omap_crtc_page_flip(struct drm_crtc *crtc,
> +		 struct drm_framebuffer *fb,
> +		 struct drm_pending_vblank_event *event)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	int ret;
> +
> +	mutex_lock(&dev->mode_config.mutex);
> +	ret = omap_crtc_page_flip_locked(crtc, fb, event);
> +	mutex_unlock(&dev->mode_config.mutex);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(omap_crtc_page_flip);
> +
> +static const struct drm_crtc_funcs omap_crtc_funcs = {
> +	.gamma_set = omap_crtc_gamma_set,
> +	.set_config = drm_crtc_helper_set_config,
> +	.destroy = omap_crtc_destroy,
> +	.page_flip = omap_crtc_page_flip_locked,
> +};
> +
> +static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
> +	.dpms = omap_crtc_dpms,
> +	.mode_fixup = omap_crtc_mode_fixup,
> +	.mode_set = omap_crtc_mode_set,
> +	.prepare = omap_crtc_prepare,
> +	.commit = omap_crtc_commit,
> +	.mode_set_base = omap_crtc_mode_set_base,
> +	.load_lut = omap_crtc_load_lut,
> +};
> +
> +struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc)
> +{
> +	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> +	return omap_crtc->ovl;
> +}
> +
> +/* initialize crtc */
> +struct drm_crtc * omap_crtc_init(struct drm_device *dev,
> +		struct omap_overlay *ovl, int id)
> +{
> +	struct drm_crtc *crtc = NULL;
> +	struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc),
> GFP_KERNEL);
> +
> +	DBG("%s", ovl->name);
> +
> +	if (!omap_crtc) {
> +		dev_err(dev->dev, "could not allocate CRTC\n");
> +		goto fail;
> +	}
> +
> +	omap_crtc->ovl = ovl;
> +	omap_crtc->id = id;
> +	crtc = &omap_crtc->base;
> +	drm_crtc_init(dev, crtc, &omap_crtc_funcs);
> +	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
> +
> +	return crtc;
> +
> +fail:
> +	if (crtc) {
> +		drm_crtc_cleanup(crtc);
> +		kfree(omap_crtc);
> +	}
> +	return NULL;
> +}
> diff --git a/drivers/staging/omapdrm/omap_drv.c
> b/drivers/staging/omapdrm/omap_drv.c
> new file mode 100644
> index 0000000..720f89e
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_drv.c
> @@ -0,0 +1,910 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_drv.c
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/omap_drm.h>
> +#include "omap_drv.h"
> +
> +#include "drm_crtc_helper.h"
> +#include "drm_fb_helper.h"
> +
> +#define DRIVER_NAME		MODULE_NAME
> +#define DRIVER_DESC		"OMAP DRM"
> +#define DRIVER_DATE		"20110717"
> +#define DRIVER_MAJOR		1
> +#define DRIVER_MINOR		0
> +#define DRIVER_PATCHLEVEL	0
> +
> +struct drm_device *drm_device;
> +
> +LIST_HEAD(plugin_list);
> +
> +/* keep track of whether we are already loaded.. we may need to call
> + * plugin's load() if they register after we are already loaded
> + */
> +static bool loaded = false;
> +
> +/*
> + * mode config funcs
> + */
> +
> +/* Notes about mapping DSS and DRM entities:
> + *    CRTC:        overlay
> + *    encoder:     manager.. with some extension to allow one primary
CRTC
> + *                 and zero or more video CRTC's to be mapped to one
encoder?
> + *    connector:   dssdev.. manager can be attached/detached from
> different
> + *                 devices
> + */
> +
> +static void omap_fb_output_poll_changed(struct drm_device *dev)
> +{
> +	struct omap_drm_private *priv = dev->dev_private;
> +	DBG("dev=%p", dev);
> +	if (priv->fbdev) {
> +		drm_fb_helper_hotplug_event(priv->fbdev);
> +	}
> +}
> +
> +static struct drm_mode_config_funcs omap_mode_config_funcs = {
> +	.fb_create = omap_framebuffer_create,
> +	.output_poll_changed = omap_fb_output_poll_changed,
> +};
> +
> +static int get_connector_type(struct omap_dss_device *dssdev)
> +{
> +	switch (dssdev->type) {
> +	case OMAP_DISPLAY_TYPE_HDMI:
> +		return DRM_MODE_CONNECTOR_HDMIA;
> +	case OMAP_DISPLAY_TYPE_DPI:
> +		if (!strcmp(dssdev->name, "dvi"))
> +			return DRM_MODE_CONNECTOR_DVID;
> +	default:
> +		return DRM_MODE_CONNECTOR_Unknown;
> +	}
> +}
> +
> +#if 0 /* enable when dss2 supports hotplug */
> +static int omap_drm_notifier(struct notifier_block *nb,
> +		unsigned long evt, void *arg)
> +{
> +	switch (evt) {
> +	case OMAP_DSS_SIZE_CHANGE:
> +	case OMAP_DSS_HOTPLUG_CONNECT:
> +	case OMAP_DSS_HOTPLUG_DISCONNECT: {
> +		struct drm_device *dev = drm_device;
> +		DBG("hotplug event: evt=%d, dev=%p", evt, dev);
> +		if (dev) {
> +			drm_sysfs_hotplug_event(dev);
> +		}
> +		return NOTIFY_OK;
> +	}
> +	default:  /* don't care about other events for now */
> +		return NOTIFY_DONE;
> +	}
> +}
> +#endif
> +
> +static void dump_video_chains(void)
> +{
> +	int i;
> +
> +	DBG("dumping video chains: ");
> +	for (i = 0; i < omap_dss_get_num_overlays(); i++) {
> +		struct omap_overlay *ovl = omap_dss_get_overlay(i);
> +		struct omap_overlay_manager *mgr = ovl->manager;
> +		struct omap_dss_device *dssdev = mgr ? mgr->device : NULL;
> +		if (dssdev) {
> +			DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name,
> +						dssdev->name);
> +		} else if (mgr) {
> +			DBG("%d: %s -> %s", i, ovl->name, mgr->name);
> +		} else {
> +			DBG("%d: %s", i, ovl->name);
> +		}
> +	}
> +}
> +
> +static int omap_modeset_init(struct drm_device *dev)
> +{
> +	const struct omap_drm_platform_data *pdata = dev->dev-
> >platform_data;
> +	struct omap_drm_private *priv = dev->dev_private;
> +	struct omap_dss_device *dssdev = NULL;
> +	int i, j;
> +	unsigned int connected_connectors = 0;
> +
> +	/* create encoders for each manager */
> +	int create_encoder(int i) {
> +		struct omap_overlay_manager *mgr =
> +				omap_dss_get_overlay_manager(i);
> +		struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
> +
> +		if (!encoder) {
> +			dev_err(dev->dev, "could not create encoder\n");
> +			return -ENOMEM;
> +		}
> +
> +		priv->encoders[priv->num_encoders++] = encoder;
> +
> +		return 0;
> +	}
> +
> +	/* create connectors for each display device */
> +	int create_connector(struct omap_dss_device *dssdev) {
> +		static struct notifier_block *notifier;
> +		struct drm_connector *connector;
> +
> +		if (!dssdev->driver) {
> +			dev_warn(dev->dev, "%s has no driver.. skipping
it\n",
> +					dssdev->name);
> +			return 0;
> +		}
> +
> +		if (!(dssdev->driver->get_timings ||
> +					dssdev->driver->read_edid)) {
> +			dev_warn(dev->dev, "%s driver does not support "
> +				"get_timings or read_edid.. skipping it!\n",
> +				dssdev->name);
> +			return 0;
> +		}
> +
> +		connector = omap_connector_init(dev,
> +				get_connector_type(dssdev), dssdev);
> +
> +		if (!connector) {
> +			dev_err(dev->dev, "could not create connector\n");
> +			return -ENOMEM;
> +		}
> +
> +		/* track what is already connected.. rather than looping
> thru
> +		 * all connectors twice later, first for connected then for
> +		 * remainder (which could be a race condition if connected
> +		 * status changes)
> +		 */
> +		if (omap_connector_detect(connector, true) ==
> +				connector_status_connected) {
> +			connected_connectors |= (1 << priv->num_connectors);
> +		}
> +
> +		priv->connectors[priv->num_connectors++] = connector;
> +
> +#if 0 /* enable when dss2 supports hotplug */
> +		notifier = kzalloc(sizeof(struct notifier_block),
> GFP_KERNEL);
> +		notifier->notifier_call = omap_drm_notifier;
> +		omap_dss_add_notify(dssdev, notifier);
> +#else
> +		notifier = NULL;
> +#endif
> +
> +		for (j = 0; j < priv->num_encoders; j++) {
> +			struct omap_overlay_manager *mgr =
> +				omap_encoder_get_manager(priv->encoders[j]);
> +			if (mgr->device == dssdev) {
> +				drm_mode_connector_attach_encoder(connector,
> +						priv->encoders[j]);
> +			}
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* create up to max_overlays CRTCs mapping to overlays.. by default,
> +	 * connect the overlays to different managers/encoders, giving
> priority
> +	 * to encoders connected to connectors with a detected connection
> +	 */
> +	int create_crtc(int i) {
> +		struct omap_overlay *ovl = omap_dss_get_overlay(i);
> +		struct omap_overlay_manager *mgr = NULL;
> +		struct drm_crtc *crtc;
> +
> +		if (ovl->manager) {
> +			DBG("disconnecting %s from %s", ovl->name,
> +						ovl->manager->name);
> +			ovl->unset_manager(ovl);
> +		}
> +
> +		/* find next best connector, ones with detected connection
> first
> +		 */
> +		while (j < priv->num_connectors && !mgr) {
> +			if (connected_connectors & (1 << j)) {
> +				struct drm_encoder * encoder =
> +					omap_connector_attached_encoder(
> +
priv->connectors[j]);
> +				if (encoder) {
> +					mgr =
omap_encoder_get_manager(encoder);
> +				}
> +			}
> +			j++;
> +		}
> +
> +		/* if we couldn't find another connected connector, lets
> start
> +		 * looking at the unconnected connectors:
> +		 */
> +		while (j < 2 * priv->num_connectors && !mgr) {
> +			int idx = j - priv->num_connectors;
> +			if (!(connected_connectors & (1 << idx))) {
> +				struct drm_encoder * encoder =
> +					omap_connector_attached_encoder(
> +
priv->connectors[idx]);
> +				if (encoder) {
> +					mgr =
omap_encoder_get_manager(encoder);
> +				}
> +			}
> +			j++;
> +		}
> +
> +		if (mgr) {
> +			DBG("connecting %s to %s", ovl->name, mgr->name);
> +			ovl->set_manager(ovl, mgr);
> +		}
> +
> +		crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
> +
> +		if (!crtc) {
> +			dev_err(dev->dev, "could not create CRTC\n");
> +			return -ENOMEM;
> +		}
> +
> +		priv->crtcs[priv->num_crtcs++] = crtc;
> +
> +		return 0;
> +	}
> +
> +	drm_mode_config_init(dev);
> +
> +	if (pdata) {
> +		/* if platform data is provided by the board file, use it to
> +		 * control which overlays, managers, and devices we own.
> +		 */
> +		for (i = 0; i < pdata->mgr_cnt; i++) {
> +			create_encoder(pdata->mgr_ids[i]);
> +		}
> +
> +		for (i = 0; i < pdata->dev_cnt; i++) {
> +			int m(struct omap_dss_device *dssdev, void *data) {
> +				return ! strcmp(dssdev->name, data);
> +			}
> +			struct omap_dss_device *dssdev =
> +				omap_dss_find_device(
> +					(void *)pdata->dev_names[i], m);
> +			if (!dssdev) {
> +				dev_warn(dev->dev, "no such dssdev: %s\n",
> +						pdata->dev_names[i]);
> +				continue;
> +			}
> +			create_connector(dssdev);
> +		}
> +
> +		j = 0;
> +		for (i = 0; i < pdata->ovl_cnt; i++) {
> +			create_crtc(pdata->ovl_ids[i]);
> +		}
> +	} else {
> +		/* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and
> try
> +		 * to make educated guesses about everything else
> +		 */
> +		int max_overlays = min(omap_dss_get_num_overlays(),
> +					CONFIG_DRM_OMAP_NUM_CRTCS);
> +
> +		for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
> +			create_encoder(i);
> +		}
> +
> +		for_each_dss_dev(dssdev) {
> +			create_connector(dssdev);
> +		}
> +
> +		j = 0;
> +		for (i = 0; i < max_overlays; i++) {
> +			create_crtc(i);
> +		}
> +	}
> +
> +	/* for now keep the mapping of CRTCs and encoders static.. */
> +	for (i = 0; i < priv->num_encoders; i++) {
> +		struct drm_encoder *encoder = priv->encoders[i];
> +		struct omap_overlay_manager *mgr =
> +				omap_encoder_get_manager(encoder);
> +
> +		encoder->possible_crtcs = 0;
> +
> +		for (j = 0; j < priv->num_crtcs; j++) {
> +			struct omap_overlay *ovl =
> +
omap_crtc_get_overlay(priv->crtcs[j]);
> +			if (ovl->manager == mgr) {
> +				encoder->possible_crtcs |= (1 << j);
> +			}
> +		}
> +
> +		DBG("%s: possible_crtcs=%08x", mgr->name,
> +					encoder->possible_crtcs);
> +	}
> +
> +	dump_video_chains();
> +
> +	dev->mode_config.min_width = 640;
> +	dev->mode_config.min_height = 480;
> +
> +	/* note: pvr can't currently handle dst surfaces larger than 2k by
> 2k */
> +	dev->mode_config.max_width = 2048;
> +	dev->mode_config.max_height = 2048;
> +
> +	dev->mode_config.funcs = &omap_mode_config_funcs;
> +
> +	return 0;
> +}

For code clean, how about moving create_encoder(), create_connector() and
create_crtc() into omap_encoder.c, omap_connector.c and omap_crtc.c file?.
omap_modeset_init function contains so many codes. it looks like not good.

> +
> +static void omap_modeset_free(struct drm_device *dev)
> +{
> +	drm_mode_config_cleanup(dev);
> +}
> +
> +/*
> + * drm ioctl funcs
> + */
> +
> +
> +static int ioctl_get_param(struct drm_device *dev, void *data,
> +		struct drm_file *file_priv)
> +{
> +	struct drm_omap_param *args = data;
> +
> +	DBG("%p: param=%llu", dev, args->param);
> +
> +	switch (args->param) {
> +	case OMAP_PARAM_CHIPSET_ID:
> +		args->value = GET_OMAP_TYPE;
> +		break;
> +	default:
> +		DBG("unknown parameter %lld", args->param);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ioctl_set_param(struct drm_device *dev, void *data,
> +		struct drm_file *file_priv)
> +{
> +	struct drm_omap_param *args = data;
> +
> +	switch (args->param) {
> +	default:
> +		DBG("unknown parameter %lld", args->param);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ioctl_get_base(struct drm_device *dev, void *data,
> +		struct drm_file *file_priv)
> +{
> +	struct drm_omap_get_base *args = data;
> +	struct omap_drm_plugin *plugin;
> +
> +	/* be safe: */
> +	args->plugin_name[ARRAY_SIZE(args->plugin_name) - 1] = '\0';
> +
> +	DBG("%p: plugin_name=%s", dev, args->plugin_name);
> +
> +	list_for_each_entry(plugin, &plugin_list, list) {
> +		if (!strcmp(args->plugin_name, plugin->name)) {
> +			args->ioctl_base = plugin->ioctl_base;
> +			return 0;
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int ioctl_gem_new(struct drm_device *dev, void *data,
> +		struct drm_file *file_priv)
> +{
> +	struct drm_omap_gem_new *args = data;
> +	DBG("%p: size=%d, flags=%08x", dev, args->size.bytes, args->flags);
> +	return omap_gem_new_handle(dev, file_priv, args->size.bytes,
> +			args->flags, &args->handle, &args->offset);
> +}
> +
> +static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
> +		struct drm_file *file_priv)
> +{
> +	struct drm_omap_gem_cpu_prep *args = data;
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	DBG("%p: handle=%d, op=%x", dev, args->handle, args->op);
> +
> +	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
> +	if (!obj) {
> +		return -ENOENT;
> +	}
> +
> +	ret = omap_gem_op_sync(obj, args->op);
> +
> +	if (!ret) {
> +		omap_gem_op_start(obj, args->op);
> +	}
> +
> +	drm_gem_object_unreference_unlocked(obj);
> +
> +	return ret;
> +}
> +
> +static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
> +		struct drm_file *file_priv)
> +{
> +	struct drm_omap_gem_cpu_fini *args = data;
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	DBG("%p: handle=%d", dev, args->handle);
> +
> +	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
> +	if (!obj) {
> +		return -ENOENT;
> +	}
> +
> +	/* XXX flushy, flushy */
> +	ret = 0;
> +
> +	if (!ret) {
> +		omap_gem_op_finish(obj, args->op);
> +	}
> +
> +	drm_gem_object_unreference_unlocked(obj);
> +
> +	return ret;
> +}
> +
> +static int ioctl_gem_info(struct drm_device *dev, void *data,
> +		struct drm_file *file_priv)
> +{
> +	struct drm_omap_gem_info *args = data;
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	DBG("%p: handle=%d", dev, args->handle);
> +
> +	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
> +	if (!obj) {
> +		return -ENOENT;
> +	}
> +
> +	args->offset = omap_gem_mmap_offset(obj);
> +
> +	drm_gem_object_unreference_unlocked(obj);
> +
> +	return ret;
> +}
> +
> +struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
> +	DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param,
> DRM_UNLOCKED|DRM_AUTH),
> +	DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param,
> DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
> +	DRM_IOCTL_DEF_DRV(OMAP_GET_BASE, ioctl_get_base,
> DRM_UNLOCKED|DRM_AUTH),
> +	DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new,
> DRM_UNLOCKED|DRM_AUTH),
> +	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep,
> DRM_UNLOCKED|DRM_AUTH),
> +	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini,
> DRM_UNLOCKED|DRM_AUTH),
> +	DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info,
> DRM_UNLOCKED|DRM_AUTH),
> +};

You also should consider security issue. :)

> +
> +/*
> + * drm driver funcs
> + */
> +
> +/**
> + * load - setup chip and create an initial config
> + * @dev: DRM device
> + * @flags: startup flags
> + *
> + * The driver load routine has to do several things:
> + *   - initialize the memory manager
> + *   - allocate initial config memory
> + *   - setup the DRM framebuffer with the allocated memory
> + */
> +static int dev_load(struct drm_device *dev, unsigned long flags)
> +{
> +	struct omap_drm_private *priv;
> +	struct omap_drm_plugin *plugin;
> +	int ret;
> +
> +	DBG("load: dev=%p", dev);
> +
> +	drm_device = dev;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv) {
> +		dev_err(dev->dev, "could not allocate priv\n");
> +		return -1;
> +	}
> +
> +	dev->dev_private = priv;
> +
> +	ret = omap_modeset_init(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n",
ret);
> +	}
> +
> +	priv->fbdev = omap_fbdev_init(dev);
> +	if (!priv->fbdev) {
> +		dev_err(dev->dev, "omap_fbdev_init failed\n");
> +	}
> +
> +	drm_kms_helper_poll_init(dev);
> +
> +	ret = drm_vblank_init(dev, priv->num_crtcs);
> +	if (ret) {
> +		dev_err(dev->dev, "could not init vblank\n");
> +	}
> +
> +	loaded = true;
> +
> +	list_for_each_entry(plugin, &plugin_list, list) {
> +		ret = plugin->load(dev, flags);
> +	}
> +
> +	return 0;
> +}
> +
> +static int dev_unload(struct drm_device *dev)
> +{
> +	struct omap_drm_plugin *plugin;
> +	int ret;
> +
> +	DBG("unload: dev=%p", dev);
> +
> +	list_for_each_entry(plugin, &plugin_list, list) {
> +		ret = plugin->unload(dev);
> +	}
> +
> +	drm_vblank_cleanup(dev);
> +	drm_kms_helper_poll_fini(dev);
> +
> +	omap_fbdev_free(dev);
> +
> +	omap_modeset_free(dev);
> +
> +	kfree(dev->dev_private);
> +	dev->dev_private = NULL;
> +
> +	loaded = false;
> +
> +	return 0;
> +}
> +
> +static int dev_open(struct drm_device *dev, struct drm_file *file)
> +{
> +	struct omap_drm_plugin *plugin;
> +	bool found_pvr = false;
> +	int ret;
> +
> +	file->driver_priv = NULL;
> +
> +	DBG("open: dev=%p, file=%p", dev, file);
> +
> +	list_for_each_entry(plugin, &plugin_list, list) {
> +		if (!strcmp(DRIVER_NAME "_pvr", plugin->name)) {
> +			found_pvr = true;
> +			break;
> +		}
> +	}
> +
> +	if (!found_pvr) {
> +		DBG("open: PVR submodule not loaded.. let's try now");
> +		request_module(DRIVER_NAME "_pvr");
> +	}
> +
> +	list_for_each_entry(plugin, &plugin_list, list) {
> +		ret = plugin->open(dev, file);
> +	}
> +
> +	return 0;
> +}
> +
> +static int dev_firstopen(struct drm_device *dev)
> +{
> +	DBG("firstopen: dev=%p", dev);
> +	return 0;
> +}
> +
> +/**
> + * lastclose - clean up after all DRM clients have exited
> + * @dev: DRM device
> + *
> + * Take care of cleaning up after all DRM clients have exited.  In the
> + * mode setting case, we want to restore the kernel's initial mode (just
> + * in case the last client left us in a bad state).
> + *
> + * Additionally, in the non-mode setting case, we'll tear down the AGP
> + * and DMA structures, since the kernel won't be using them, and clean
> + * up any GEM state.
> + */
> +static void dev_lastclose(struct drm_device * dev)
> +{
> +	DBG("lastclose: dev=%p", dev);
> +}
> +
> +static void dev_preclose(struct drm_device * dev, struct drm_file *file)
> +{
> +	DBG("preclose: dev=%p", dev);
> +}
> +
> +static void dev_postclose(struct drm_device *dev, struct drm_file *file)
> +{
> +	struct omap_drm_plugin *plugin;
> +	int ret;
> +
> +	DBG("postclose: dev=%p, file=%p", dev, file);
> +
> +	list_for_each_entry(plugin, &plugin_list, list) {
> +		ret = plugin->release(dev, file);
> +	}
> +
> +	return;
> +}
> +
> +/**
> + * enable_vblank - enable vblank interrupt events
> + * @dev: DRM device
> + * @crtc: which irq to enable
> + *
> + * Enable vblank interrupts for @crtc.  If the device doesn't have
> + * a hardware vblank counter, this routine should be a no-op, since
> + * interrupts will have to stay on to keep the count accurate.
> + *
> + * RETURNS
> + * Zero on success, appropriate errno if the given @crtc's vblank
> + * interrupt cannot be enabled.
> + */
> +static int dev_enable_vblank(struct drm_device *dev, int crtc)
> +{
> +	DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc);
> +	return 0;
> +}
> +
> +/**
> + * disable_vblank - disable vblank interrupt events
> + * @dev: DRM device
> + * @crtc: which irq to enable
> + *
> + * Disable vblank interrupts for @crtc.  If the device doesn't have
> + * a hardware vblank counter, this routine should be a no-op, since
> + * interrupts will have to stay on to keep the count accurate.
> + */
> +static void dev_disable_vblank(struct drm_device *dev, int crtc)
> +{
> +	DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc);
> +}
> +
> +static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS)
> +{
> +	return IRQ_HANDLED;
> +}
> +
> +static void dev_irq_preinstall(struct drm_device *dev)
> +{
> +	DBG("irq_preinstall: dev=%p", dev);
> +}
> +
> +static int dev_irq_postinstall(struct drm_device *dev)
> +{
> +	DBG("irq_postinstall: dev=%p", dev);
> +	return 0;
> +}
> +
> +static void dev_irq_uninstall(struct drm_device *dev)
> +{
> +	DBG("irq_uninstall: dev=%p", dev);
> +}
> +
> +static struct vm_operations_struct omap_gem_vm_ops = {
> +	.fault = omap_gem_fault,
> +	.open = omap_gem_vm_open,
> +	.close = omap_gem_vm_close,
> +};
> +
> +static struct drm_driver omap_drm_driver = {
> +		.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
> DRIVER_GEM,
> +		.load = dev_load,
> +		.unload = dev_unload,
> +		.open = dev_open,
> +		.firstopen = dev_firstopen,
> +		.lastclose = dev_lastclose,
> +		.preclose = dev_preclose,
> +		.postclose = dev_postclose,
> +		.get_vblank_counter = drm_vblank_count,
> +		.enable_vblank = dev_enable_vblank,
> +		.disable_vblank = dev_disable_vblank,
> +		.irq_preinstall = dev_irq_preinstall,
> +		.irq_postinstall = dev_irq_postinstall,
> +		.irq_uninstall = dev_irq_uninstall,
> +		.irq_handler = dev_irq_handler,
> +		.reclaim_buffers = drm_core_reclaim_buffers,
> +		.gem_init_object = omap_gem_init_object,
> +		.gem_free_object = omap_gem_free_object,
> +		.gem_vm_ops = &omap_gem_vm_ops,
> +		.dumb_create = omap_gem_dumb_create,
> +		.dumb_map_offset = omap_gem_dumb_map_offset,
> +		.dumb_destroy = omap_gem_dumb_destroy,
> +		.ioctls = ioctls,
> +		.num_ioctls = DRM_OMAP_NUM_IOCTLS,
> +		.fops = {
> +				.owner = THIS_MODULE,
> +				.open = drm_open,
> +				.unlocked_ioctl = drm_ioctl,
> +				.release = drm_release,
> +				.mmap = omap_gem_mmap,
> +				.poll = drm_poll,
> +				.fasync = drm_fasync,
> +				.read = drm_read,
> +				.llseek = noop_llseek,
> +		},
> +		.name = DRIVER_NAME,
> +		.desc = DRIVER_DESC,
> +		.date = DRIVER_DATE,
> +		.major = DRIVER_MAJOR,
> +		.minor = DRIVER_MINOR,
> +		.patchlevel = DRIVER_PATCHLEVEL,
> +};
> +
> +int omap_drm_register_plugin(struct omap_drm_plugin *plugin)
> +{
> +	struct drm_device *dev = drm_device;
> +	static int ioctl_base = DRM_OMAP_NUM_IOCTLS;
> +	int i;
> +
> +	DBG("register plugin: %p (%s)", plugin, plugin->name);
> +
> +	plugin->ioctl_base = ioctl_base;
> +
> +	list_add_tail(&plugin->list, &plugin_list);
> +
> +	/* register the plugin's ioctl's */
> +	for (i = 0; i < plugin->num_ioctls; i++) {
> +		int nr = i + ioctl_base;
> +
> +		/* check for out of bounds ioctl nr or already registered
> ioctl */
> +		if (nr > ARRAY_SIZE(ioctls) || ioctls[nr].func) {
> +			dev_err(dev->dev, "invalid ioctl: %d (nr=%d)\n", i,
> nr);
> +			return -EINVAL;
> +		}
> +
> +		DBG("register ioctl: %d %08x", nr, plugin->ioctls[i].cmd);
> +
> +		ioctls[nr] = plugin->ioctls[i];
> +
> +		if (nr >= omap_drm_driver.num_ioctls) {
> +			omap_drm_driver.num_ioctls = nr + 1;
> +		}
> +	}
> +
> +	ioctl_base += plugin->num_ioctls;
> +
> +	if (loaded) {
> +		plugin->load(dev, 0);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_drm_register_plugin);
> +
> +int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin)
> +{
> +	list_del(&plugin->list);
> +	/* TODO remove ioctl fxns */
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_drm_unregister_plugin);
> +
> +static int nmappers = 0;
> +
> +/* create buffer mapper id, to access per-mapper private data.  See
> + * omap_gem_{get,set}_priv().
> + */
> +int omap_drm_register_mapper(void)
> +{
> +	if (nmappers >= MAX_MAPPERS) {
> +		return -ENOMEM;
> +	}
> +	return nmappers++;
> +}
> +EXPORT_SYMBOL(omap_drm_register_mapper);
> +
> +/* retire a mapper id, previously acquired from
omap_drm_register_mapper()
> + */
> +void omap_drm_unregister_mapper(int mapper_id)
> +{
> +	/* currently no-op.. */
> +}
> +EXPORT_SYMBOL(omap_drm_unregister_mapper);
> +
> +static int pdev_suspend(struct platform_device *pDevice, pm_message_t
> state)
> +{
> +	DBG("");
> +	return 0;
> +}
> +
> +static int pdev_resume(struct platform_device *device)
> +{
> +	DBG("");
> +	return 0;
> +}
> +
> +static void pdev_shutdown(struct platform_device *device)
> +{
> +	DBG("");
> +}
> +
> +static int pdev_probe(struct platform_device *device)
> +{
> +	DBG("%s", device->name);
> +	return drm_platform_init(&omap_drm_driver, device);
> +}
> +
> +static int pdev_remove(struct platform_device *device)
> +{
> +	DBG("");
> +	drm_platform_exit(&omap_drm_driver, device);
> +	return 0;
> +}
> +
> +struct platform_driver pdev = {
> +		.driver = {
> +			.name = DRIVER_NAME,
> +			.owner = THIS_MODULE,
> +		},
> +		.probe = pdev_probe,
> +		.remove = pdev_remove,
> +		.suspend = pdev_suspend,
> +		.resume = pdev_resume,
> +		.shutdown = pdev_shutdown,
> +};
> +
> +static int __init omap_drm_init(void)
> +{
> +	DBG("init");
> +	return platform_driver_register(&pdev);
> +}
> +
> +static void __exit omap_drm_fini(void)
> +{
> +	DBG("fini");
> +	platform_driver_unregister(&pdev);
> +}
> +
> +/* need late_initcall() so we load after dss_driver's are loaded */
> +late_initcall(omap_drm_init);
> +module_exit(omap_drm_fini);
> +
> +MODULE_AUTHOR("Rob Clark <rob@ti.com>");
> +MODULE_DESCRIPTION("OMAP DRM Display Driver");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/omapdrm/omap_drv.h
> b/drivers/staging/omapdrm/omap_drv.h
> new file mode 100644
> index 0000000..d17e3a8
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_drv.h
> @@ -0,0 +1,117 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_drv.h
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __OMAP_DRV_H__
> +#define __OMAP_DRV_H__
> +
> +#include <video/omapdss.h>
> +#include <linux/module.h>
> +
> +#define DBG(fmt,...)  DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
> +#define VERB(fmt,...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose
> debug */
> +
> +#define MODULE_NAME     "omapdrm"
> +
> +/* max # of mapper-id's that can be assigned.. todo, come up with a
> better
> + * (but still inexpensive) way to store/access per-buffer mapper private
> + * data..
> + */
> +#define MAX_MAPPERS 2
> +
> +struct omap_drm_private {
> +	int num_crtcs;
> +	struct drm_crtc *crtcs[8];
> +	int num_encoders;
> +	struct drm_encoder *encoders[8];
> +	int num_connectors;
> +	struct drm_connector *connectors[8];
> +
> +	struct drm_fb_helper *fbdev;
> +};
> +
> +struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev);
> +void omap_fbdev_free(struct drm_device *dev);
> +
> +struct drm_crtc * omap_crtc_init(struct drm_device *dev,
> +		struct omap_overlay *ovl, int id);
> +struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc);
> +int omap_crtc_page_flip(struct drm_crtc *crtc,
> +		 struct drm_framebuffer *fb,
> +		 struct drm_pending_vblank_event *event);
> +
> +struct drm_encoder * omap_encoder_init(struct drm_device *dev,
> +		struct omap_overlay_manager *mgr);
> +struct omap_overlay_manager * omap_encoder_get_manager(
> +		struct drm_encoder *encoder);
> +struct drm_encoder * omap_connector_attached_encoder (
> +		struct drm_connector *connector);
> +enum drm_connector_status omap_connector_detect(
> +		struct drm_connector *connector, bool force);
> +int omap_encoder_wait_for_vsync(struct drm_encoder *encoder);
> +
> +struct drm_connector * omap_connector_init(struct drm_device *dev,
> +		int connector_type, struct omap_dss_device *dssdev);
> +void omap_connector_mode_set(struct drm_connector *connector,
> +		struct drm_display_mode *mode);
> +void omap_connector_flush(struct drm_connector *connector,
> +		int x, int y, int w, int h);
> +void omap_connector_dpms(struct drm_connector *connector, int mode);
> +int omap_connector_sync(struct drm_connector *connector);
> +
> +struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
> +		struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd);
> +struct drm_gem_object * omap_framebuffer_bo(struct drm_framebuffer *fb);
> +struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev,
> +		struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object
*bo);
> +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
> +		void **vaddr, unsigned long *paddr, int *screen_width);
> +struct drm_connector * omap_framebuffer_get_next_connector(
> +		struct drm_framebuffer *fb, struct drm_connector *from);
> +void omap_framebuffer_flush(struct drm_framebuffer *fb,
> +		int x, int y, int w, int h);
> +
> +struct drm_gem_object *omap_gem_new(struct drm_device *dev,
> +						  size_t size, uint32_t
flags);
> +int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
> +		size_t size, uint32_t flags, uint32_t *handle, uint64_t
> *offset);
> +void omap_gem_free_object(struct drm_gem_object *obj);
> +int omap_gem_init_object(struct drm_gem_object *obj);
> +int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device
> *dev,
> +			 uint32_t handle, uint64_t *offset);
> +int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
> +			uint32_t handle);
> +int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			struct drm_mode_create_dumb *args);
> +int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma);
> +int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
> +void omap_gem_vm_open(struct vm_area_struct *vma);
> +void omap_gem_vm_close(struct vm_area_struct *vma);
> +
> +static inline int align_pitch(int pitch, int width, int bpp)
> +{
> +	int bytespp = (bpp + 7) / 8;
> +	/* in case someone tries to feed us a completely bogus stride: */
> +	pitch = max(pitch, width * bytespp);
> +	/* PVR needs alignment to 8 pixels.. right now that is the most
> +	 * restrictive stride requirement..
> +	 */
> +	return ALIGN(pitch, 8 * bytespp);
> +}
> +
> +#endif /* __OMAP_DRV_H__ */
> diff --git a/drivers/staging/omapdrm/omap_encoder.c
> b/drivers/staging/omapdrm/omap_encoder.c
> new file mode 100644
> index 0000000..810c7e2
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_encoder.c
> @@ -0,0 +1,198 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_encoder.c
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/omap_drm.h>
> +#include "omap_drv.h"
> +
> +#include "drm_crtc.h"
> +#include "drm_crtc_helper.h"
> +
> +/*
> + * encoder funcs
> + */
> +
> +#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
> +
> +struct omap_encoder {
> +	struct drm_encoder base;
> +	struct omap_overlay_manager *mgr;
> +};
> +
> +static void omap_encoder_destroy(struct drm_encoder *encoder)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	DBG("%s", omap_encoder->mgr->name);
> +	drm_encoder_cleanup(encoder);
> +	kfree(omap_encoder);
> +}
> +
> +static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	struct drm_device *dev = encoder->dev;
> +	struct omap_drm_private *priv = dev->dev_private;
> +	int i;
> +
> +	DBG("%s: %d", omap_encoder->mgr->name, mode);
> +
> +	/* managers don't need to do anything for DPMS.. but we do
> +	 * need to propagate to the connector, who is actually going
> +	 * to enable/disable as needed:
> +	 */
> +	for (i = 0; i < priv->num_connectors; i++) {
> +		struct drm_connector *connector = priv->connectors[i];
> +		if (connector->encoder == encoder) {
> +			omap_connector_dpms(connector, mode);
> +		}
> +	}
> +}
> +
> +static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
> +				  struct drm_display_mode *mode,
> +				  struct drm_display_mode *adjusted_mode)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	DBG("%s", omap_encoder->mgr->name);
> +	return true;
> +}
> +
> +static void omap_encoder_mode_set(struct drm_encoder *encoder,
> +				struct drm_display_mode *mode,
> +				struct drm_display_mode *adjusted_mode)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	struct drm_device *dev = encoder->dev;
> +	struct omap_drm_private *priv = dev->dev_private;
> +	int i;
> +
> +	mode = adjusted_mode;
> +
> +	DBG("%s: set mode: %dx%d", omap_encoder->mgr->name,
> +			mode->hdisplay, mode->vdisplay);
> +
> +	for (i = 0; i < priv->num_connectors; i++) {
> +		struct drm_connector *connector = priv->connectors[i];
> +		if (connector->encoder == encoder) {
> +			omap_connector_mode_set(connector, mode);
> +		}
> +	}
> +}
> +
> +static void omap_encoder_prepare(struct drm_encoder *encoder)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	struct drm_encoder_helper_funcs *encoder_funcs =
> +				encoder->helper_private;
> +	DBG("%s", omap_encoder->mgr->name);
> +	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
> +}
> +
> +static void omap_encoder_commit(struct drm_encoder *encoder)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	struct drm_encoder_helper_funcs *encoder_funcs =
> +				encoder->helper_private;
> +	DBG("%s", omap_encoder->mgr->name);
> +	omap_encoder->mgr->apply(omap_encoder->mgr);
> +	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
> +}
> +
> +static const struct drm_encoder_funcs omap_encoder_funcs = {
> +	.destroy = omap_encoder_destroy,
> +};
> +
> +static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs =
> {
> +	.dpms = omap_encoder_dpms,
> +	.mode_fixup = omap_encoder_mode_fixup,
> +	.mode_set = omap_encoder_mode_set,
> +	.prepare = omap_encoder_prepare,
> +	.commit = omap_encoder_commit,
> +};
> +
> +struct omap_overlay_manager * omap_encoder_get_manager(
> +		struct drm_encoder *encoder)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	return omap_encoder->mgr;
> +}
> +
> +/* maybe this could go away and we just use drm_vblank_wait()? */
> +int omap_encoder_wait_for_vsync(struct drm_encoder *encoder)
> +{
> +	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
> +	DBG("%s", omap_encoder->mgr->name);
> +	return omap_encoder->mgr->wait_for_vsync(omap_encoder->mgr);
> +}
> +EXPORT_SYMBOL(omap_encoder_wait_for_vsync);
> +
> +/* initialize encoder */
> +struct drm_encoder * omap_encoder_init(struct drm_device *dev,
> +		struct omap_overlay_manager *mgr)
> +{
> +	struct drm_encoder *encoder = NULL;
> +	struct omap_encoder *omap_encoder;
> +	struct omap_overlay_manager_info info;
> +	int ret;
> +
> +	DBG("%s", mgr->name);
> +
> +	omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
> +	if (!omap_encoder) {
> +		dev_err(dev->dev, "could not allocate encoder\n");
> +		goto fail;
> +	}
> +
> +	omap_encoder->mgr = mgr;
> +	encoder = &omap_encoder->base;
> +
> +	drm_encoder_init(dev, encoder, &omap_encoder_funcs,
> +			 DRM_MODE_ENCODER_TMDS);
> +	drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
> +
> +	mgr->get_manager_info(mgr, &info);
> +
> +	/* TODO: fix hard-coded setup.. */
> +	info.default_color = 0x00000000;
> +	info.trans_key = 0x00000000;
> +	info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
> +	info.trans_enabled = false;
> +	info.alpha_enabled = true;
> +
> +	ret = mgr->set_manager_info(mgr, &info);
> +	if (ret) {
> +		dev_err(dev->dev, "could not set manager info\n");
> +		goto fail;
> +	}
> +
> +	ret = mgr->apply(mgr);
> +	if (ret) {
> +		dev_err(dev->dev, "could not apply\n");
> +		goto fail;
> +	}
> +
> +	return encoder;
> +
> +fail:
> +	if (encoder) {
> +		drm_encoder_cleanup(encoder);
> +		kfree(omap_encoder);
> +	}
> +
> +	return NULL;
> +}
> diff --git a/drivers/staging/omapdrm/omap_fb.c
> b/drivers/staging/omapdrm/omap_fb.c
> new file mode 100644
> index 0000000..42bc146
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_fb.c
> @@ -0,0 +1,265 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_fb.c
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/omap_drm.h>
> +#include "omap_drv.h"
> +
> +#include "drm_crtc.h"
> +#include "drm_crtc_helper.h"
> +
> +
> +/*
> + * framebuffer funcs
> + */
> +
> +#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer,
> base)
> +
> +struct omap_framebuffer {
> +	struct drm_framebuffer base;
> +	struct drm_gem_object *bo;
> +	int size;
> +	void __iomem *vaddr;
> +	unsigned long paddr;
> +};
> +
> +static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
> +		struct drm_file *file_priv,
> +		unsigned int *handle)
> +{
> +	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
> +    return drm_gem_handle_create(file_priv, omap_fb->bo, handle);
> +}
> +
> +static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
> +{
> +	struct drm_device *dev = fb->dev;
> +	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
> +
> +	DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
> +
> +	drm_framebuffer_cleanup(fb);
> +
> +	if (omap_fb->vaddr) {
> +		iounmap(omap_fb->vaddr);
> +	}

You always use reserved memory for drm framebuffer.? If not so, for
instance, any physical memory was allocated by dma api such as dma_alloc_*
then you should call dma_free_*.

> +
> +	if (omap_gem_put_paddr(omap_fb->bo)) {
> +		dev_err(dev->dev, "could not unmap!\n");
> +	}
> +
> +	if (omap_fb->bo) {
> +		drm_gem_object_unreference_unlocked(omap_fb->bo);
> +	}
> +
> +	kfree(omap_fb);
> +}
> +
> +static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
> +		struct drm_file *file_priv, unsigned flags, unsigned color,
> +		struct drm_clip_rect *clips, unsigned num_clips)
> +{
> +	int i;
> +
> +	for (i = 0; i < num_clips; i++) {
> +		omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
> +					clips[i].x2 - clips[i].x1,
> +					clips[i].y2 - clips[i].y1);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
> +	.create_handle = omap_framebuffer_create_handle,
> +	.destroy = omap_framebuffer_destroy,
> +	.dirty = omap_framebuffer_dirty,
> +};
> +
> +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
> +		void **vaddr, unsigned long *paddr, int *screen_width)
> +{
> +	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
> +	int bpp = fb->depth / 8;
> +	unsigned long offset;
> +
> +	offset = (x * bpp) + (y * fb->pitch);
> +
> +	if (vaddr) {
> +		if (!omap_fb->vaddr) {
> +			omap_fb->vaddr = ioremap_wc(omap_fb->paddr, omap_fb-
> >size);
> +		}
> +		*vaddr = omap_fb->vaddr + offset;
> +	}

Did you use ioremap_wc() to map physical memory(reserved memory) that kernel
doesn't aware of to kernel space.? if not so, the memory region mapped to
kernel space as 1:1,  this way would be faced with duplicated cache
attribute issue mentioned by Russel King before. 1:1 mapping region is
mapped to kernel space with cachable attribute.

> +
> +	*paddr = omap_fb->paddr + offset;
> +	*screen_width = fb->pitch / bpp;
> +
> +	return omap_fb->size - offset;
> +}
> +EXPORT_SYMBOL(omap_framebuffer_get_buffer);
> +
> +struct drm_gem_object * omap_framebuffer_bo(struct drm_framebuffer *fb)
> +{
> +	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
> +	return omap_fb->bo;
> +}
> +
> +/* iterate thru all the connectors, returning ones that are attached
> + * to the same fb..
> + */
> +struct drm_connector * omap_framebuffer_get_next_connector(
> +		struct drm_framebuffer *fb, struct drm_connector *from)
> +{
> +	struct drm_device *dev = fb->dev;
> +	struct list_head *connector_list = &dev->mode_config.connector_list;
> +	struct drm_connector *connector = from;
> +
> +	if (!from) {
> +		return list_first_entry(connector_list, typeof(*from),
head);
> +	}
> +
> +	list_for_each_entry_from(connector, connector_list, head) {
> +		if (connector != from) {
> +			struct drm_encoder *encoder = connector->encoder;
> +			struct drm_crtc *crtc = encoder ? encoder->crtc :
NULL;
> +			if (crtc && crtc->fb == fb) {
> +				return connector;
> +			}
> +		}
> +	}
> +
> +	return NULL;
> +}
> +EXPORT_SYMBOL(omap_framebuffer_get_next_connector);
> +
> +/* flush an area of the framebuffer (in case of manual update display
> that
> + * is not automatically flushed)
> + */
> +void omap_framebuffer_flush(struct drm_framebuffer *fb,
> +		int x, int y, int w, int h)
> +{
> +	struct drm_connector *connector = NULL;
> +
> +	VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
> +
> +	while ((connector = omap_framebuffer_get_next_connector(fb,
> connector))) {
> +		/* only consider connectors that are part of a chain */
> +		if (connector->encoder && connector->encoder->crtc) {
> +			/* TODO: maybe this should propagate thru the crtc
who
> +			 * could do the coordinate translation..
> +			 */
> +			struct drm_crtc *crtc = connector->encoder->crtc;
> +			int cx = max(0, x - crtc->x);
> +			int cy = max(0, y - crtc->y);
> +			int cw = w + (x - crtc->x) - cx;
> +			int ch = h + (y - crtc->y) - cy;
> +
> +			omap_connector_flush(connector, cx, cy, cw, ch);
> +		}
> +	}
> +}
> +EXPORT_SYMBOL(omap_framebuffer_flush);
> +
> +struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
> +        struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd)
> +{
> +	struct drm_gem_object *bo;
> +	struct drm_framebuffer *fb;
> +	bo = drm_gem_object_lookup(dev, file, mode_cmd->handle);
> +	if (!bo) {
> +		return ERR_PTR(-ENOENT);
> +	}
> +	fb = omap_framebuffer_init(dev, mode_cmd, bo);
> +	if (!fb) {
> +		return ERR_PTR(-ENOMEM);
> +	}
> +	return fb;
> +}
> +
> +struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev,
> +		struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo)
> +{
> +	struct omap_framebuffer *omap_fb;
> +	struct drm_framebuffer *fb = NULL;
> +	int size, ret;
> +
> +	DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%d)", dev,
> +			mode_cmd, mode_cmd->width, mode_cmd->height,
mode_cmd-
> >bpp);
> +
> +	/* in case someone tries to feed us a completely bogus stride: */
> +	mode_cmd->pitch = align_pitch(mode_cmd->pitch,
> +			mode_cmd->width, mode_cmd->bpp);
> +
> +	omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
> +	if (!omap_fb) {
> +		dev_err(dev->dev, "could not allocate fb\n");
> +		goto fail;
> +	}
> +
> +	fb = &omap_fb->base;
> +	ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
> +	if (ret) {
> +		dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
> +		goto fail;
> +	}
> +
> +	DBG("create: FB ID: %d (%p)", fb->base.id, fb);
> +
> +	size = PAGE_ALIGN(mode_cmd->pitch * mode_cmd->height);
> +
> +	if (bo) {
> +		DBG("using existing %d byte buffer (needed %d)", bo->size,
> size);
> +		if (size > bo->size) {
> +			dev_err(dev->dev, "provided buffer object is too
> small!\n");
> +			goto fail;
> +		}
> +	} else {
> +		/* for convenience of all the various callers who don't want
> +		 * to be bothered to allocate their own buffer..
> +		 */
> +		DBG("allocating %d bytes for fb %d", size, dev->primary-
> >index);
> +		bo = omap_gem_new(dev, size, OMAP_BO_SCANOUT | OMAP_BO_WC);
> +		if (!bo) {
> +			dev_err(dev->dev, "failed to allocate buffer
> object\n");
> +			goto fail;
> +		}
> +	}
> +
> +	omap_fb->bo = bo;
> +	omap_fb->size = size;
> +
> +	/* TODO get paddr when buffer is being scanned out, put paddr when
> it
> +	 * is no longer scanned out..
> +	 */
> +	if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) {
> +		dev_err(dev->dev, "could not map!\n");
> +		goto fail;
> +	}
> +
> +	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
> +
> +	return fb;
> +
> +fail:
> +	if (fb) {
> +		omap_framebuffer_destroy(fb);
> +	}
> +	return NULL;
> +}
> +EXPORT_SYMBOL(omap_framebuffer_init);
> diff --git a/drivers/staging/omapdrm/omap_fbdev.c
> b/drivers/staging/omapdrm/omap_fbdev.c
> new file mode 100644
> index 0000000..ca6c28c
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_fbdev.c
> @@ -0,0 +1,303 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_fbdev.c
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/omap_drm.h>
> +#include "omap_drv.h"
> +
> +#include "drm_crtc.h"
> +#include "drm_fb_helper.h"
> +
> +/*
> + * fbdev funcs, to implement legacy fbdev interface on top of drm driver
> + */
> +
> +#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
> +
> +struct omap_fbdev {
> +	struct drm_fb_helper base;
> +	struct drm_framebuffer *fb;
> +};
> +
> +static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w,
> int h);
> +
> +static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user
> *buf,
> +		size_t count, loff_t *ppos)
> +{
> +	ssize_t res;
> +
> +	res = fb_sys_write(fbi, buf, count, ppos);
> +	omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
> +
> +	return res;
> +}
> +
> +static void omap_fbdev_fillrect(struct fb_info *fbi,
> +		const struct fb_fillrect *rect)
> +{
> +	sys_fillrect(fbi, rect);
> +	omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect-
> >height);
> +}
> +
> +static void omap_fbdev_copyarea(struct fb_info *fbi,
> +		const struct fb_copyarea *area)
> +{
> +	sys_copyarea(fbi, area);
> +	omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area-
> >height);
> +}
> +
> +static void omap_fbdev_imageblit(struct fb_info *fbi,
> +		const struct fb_image *image)
> +{
> +	sys_imageblit(fbi, image);
> +	omap_fbdev_flush(fbi, image->dx, image->dy,
> +				image->width, image->height);
> +}
> +
> +static struct fb_ops omap_fb_ops = {
> +	.owner = THIS_MODULE,
> +
> +	/* Note: to properly handle manual update displays, we wrap the
> +	 * basic fbdev ops which write to the framebuffer
> +	 */
> +	.fb_read = fb_sys_read,
> +	.fb_write = omap_fbdev_write,
> +	.fb_fillrect = omap_fbdev_fillrect,
> +	.fb_copyarea = omap_fbdev_copyarea,
> +	.fb_imageblit = omap_fbdev_imageblit,
> +
> +	.fb_check_var = drm_fb_helper_check_var,
> +	.fb_set_par = drm_fb_helper_set_par,
> +	.fb_pan_display = drm_fb_helper_pan_display,
> +	.fb_blank = drm_fb_helper_blank,
> +	.fb_setcmap = drm_fb_helper_setcmap,
> +
> +	.fb_debug_enter = drm_fb_helper_debug_enter,
> +	.fb_debug_leave = drm_fb_helper_debug_leave,
> +};
> +
> +static int omap_fbdev_create(struct drm_fb_helper *helper,
> +		struct drm_fb_helper_surface_size *sizes)
> +{
> +	struct omap_fbdev *fbdev = to_omap_fbdev(helper);
> +	struct drm_device *dev = helper->dev;
> +	struct drm_framebuffer *fb;
> +	struct fb_info *fbi;
> +	struct drm_mode_fb_cmd mode_cmd = {0};
> +	unsigned long paddr;
> +	void __iomem *vaddr;
> +	int size, screen_width;
> +	int ret;
> +
> +	/* only doing ARGB32 since this is what is needed to alpha-blend
> +	 * with video overlays:
> +	 */
> +	sizes->surface_bpp = 32;
> +	sizes->surface_depth = 32;
> +
> +	DBG("create fbdev: %dx%d@%d", sizes->surface_width,
> +			sizes->surface_height, sizes->surface_bpp);
> +
> +	mode_cmd.width = sizes->surface_width;
> +	mode_cmd.height = sizes->surface_height;
> +
> +	mode_cmd.bpp = sizes->surface_bpp;
> +	mode_cmd.depth = sizes->surface_depth;
> +
> +	mutex_lock(&dev->struct_mutex);
> +
> +	fbi = framebuffer_alloc(0, dev->dev);
> +	if (!fbi) {
> +		dev_err(dev->dev, "failed to allocate fb info\n");
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	DBG("fbi=%p, dev=%p", fbi, dev);
> +
> +	fbdev->fb = omap_framebuffer_init(dev, &mode_cmd, NULL);
> +	if (!fbdev->fb) {
> +		dev_err(dev->dev, "failed to allocate fb\n");
> +		ret = -ENOMEM;
> +		goto fail;

In case of fail, framebuffer_release should be called.

> +	}
> +
> +	fb = fbdev->fb;
> +	helper->fb = fb;
> +	helper->fbdev = fbi;
> +
> +	fbi->par = helper;
> +	fbi->flags = FBINFO_DEFAULT;
> +	fbi->fbops = &omap_fb_ops;
> +
> +	strcpy(fbi->fix.id, MODULE_NAME);
> +
> +	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
> +	if (ret) {
> +		ret = -ENOMEM;
> +		goto fail;

Also, omap_framebuffer_destroy() should be called to release resource.

> +	}
> +
> +	drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
> +	drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
> +
> +	size = omap_framebuffer_get_buffer(fb, 0, 0,
> +			&vaddr, &paddr, &screen_width);
> +
> +	dev->mode_config.fb_base = paddr;
> +
> +	fbi->screen_base = vaddr;
> +	fbi->screen_size = size;
> +	fbi->fix.smem_start = paddr;
> +	fbi->fix.smem_len = size;
> +
> +	DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
> +	DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
> +
> +	mutex_unlock(&dev->struct_mutex);
> +
> +	return 0;
> +
> +fail:
> +	mutex_unlock(&dev->struct_mutex);
> +	// TODO cleanup?
> +	return ret;
> +}
> +
> +static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
> +		u16 red, u16 green, u16 blue, int regno)
> +{
> +	DBG("fbdev: set gamma");
> +}
> +
> +static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
> +		u16 *red, u16 *green, u16 *blue, int regno)
> +{
> +	DBG("fbdev: get gamma");
> +}
> +
> +static int omap_fbdev_probe(struct drm_fb_helper *helper,
> +		struct drm_fb_helper_surface_size *sizes)
> +{
> +	int new_fb = 0;
> +	int ret;
> +
> +	if (!helper->fb) {
> +		ret = omap_fbdev_create(helper, sizes);
> +		if (ret)
> +			return ret;
> +		new_fb = 1;
> +	}
> +	return new_fb;
> +}
> +

This strange code must be because of mainline code below.
fb_probe callback of drm_fb_helper_single_fb_probe function needs return
values of three types. :(  I think this mainline code should be modified
properly.


> +static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
> +	.gamma_set = omap_crtc_fb_gamma_set,
> +	.gamma_get = omap_crtc_fb_gamma_get,
> +	.fb_probe = omap_fbdev_probe,
> +};
> +
> +static struct drm_fb_helper * get_fb(struct fb_info *fbi)
> +{
> +	if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
> +		/* these are not the fb's you're looking for */
> +		return NULL;
> +	}
> +	return fbi->par;
> +}
> +
> +/* flush an area of the framebuffer (in case of manual update display
> that
> + * is not automatically flushed)
> + */
> +static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w,
> int h)
> +{
> +	struct drm_fb_helper *helper = get_fb(fbi);
> +
> +	if (!helper)
> +		return;
> +
> +	VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
> +
> +	omap_framebuffer_flush(helper->fb, x, y, w, h);
> +}
> +EXPORT_SYMBOL(omap_fbdev_flush);
> +
> +/* initialize fbdev helper */
> +struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev)
> +{
> +	struct omap_drm_private *priv = dev->dev_private;
> +	struct omap_fbdev *fbdev = NULL;
> +	struct drm_fb_helper *helper;
> +	int ret = 0;
> +
> +	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
> +	if (!fbdev) {
> +		dev_err(dev->dev, "could not allocate fbdev\n");
> +		goto fail;
> +	}
> +
> +	helper = &fbdev->base;
> +
> +	helper->funcs = &omap_fb_helper_funcs;
> +
> +	ret = drm_fb_helper_init(dev, helper,
> +			priv->num_crtcs, priv->num_connectors);
> +	if (ret) {
> +		dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
> +		goto fail;
> +	}
> +
> +	drm_fb_helper_single_add_all_connectors(helper);
> +	drm_fb_helper_initial_config(helper, 32);
> +
> +	priv->fbdev = helper;
> +
> +	return helper;
> +
> +fail:
> +	if (fbdev) {
> +		kfree(fbdev);
> +	}
> +	return NULL;
> +}
> +
> +void omap_fbdev_free(struct drm_device *dev)
> +{
> +	struct omap_drm_private *priv = dev->dev_private;
> +	struct drm_fb_helper *helper = priv->fbdev;
> +	struct omap_fbdev *fbdev;
> +	struct fb_info *fbi;
> +
> +	DBG();
> +
> +	fbi = helper->fbdev;
> +
> +	unregister_framebuffer(fbi);
> +	framebuffer_release(fbi);
> +
> +	drm_fb_helper_fini(helper);
> +
> +	fbdev = to_omap_fbdev(priv->fbdev);
> +
> +	kfree(fbdev);
> +
> +	priv->fbdev = NULL;
> +}
> +
> diff --git a/drivers/staging/omapdrm/omap_gem.c
> b/drivers/staging/omapdrm/omap_gem.c
> new file mode 100644
> index 0000000..b172c49
> --- /dev/null
> +++ b/drivers/staging/omapdrm/omap_gem.c
> @@ -0,0 +1,851 @@
> +/*
> + * linux/drivers/staging/omapdrm/omap_gem.c
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob.clark@linaro.org>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +
> +#include <plat/vram.h>
> +#include <linux/spinlock.h>
> +#include <linux/shmem_fs.h>
> +
> +#include <linux/omap_drm.h>
> +#include "omap_drv.h"
> +
> +/*
> + * GEM buffer object implementation.
> + */
> +
> +#define to_omap_bo(x) container_of(x, struct omap_gem_object, base)
> +
> +/* note: we use upper 8 bits of flags for driver-internal flags: */
> +#define OMAP_BO_DMA		0x01000000	/* actually is physically
> contiguous */
> +#define OMAP_BO_EXT_SYNC	0x02000000	/* externally allocated sync
> object */
> +#define OMAP_BO_EXT_MEM	0x04000000	/* externally allocated
memory */
> +
> +
> +struct omap_gem_object {
> +	struct drm_gem_object base;
> +
> +	uint32_t flags;
> +
> +	/**
> +	 * if buffer is physically contiguous or remapped in TILER, the
> +	 * OMAP_BO_DMA flag is set and the paddr is valid.
> +	 *
> +	 * Note that OMAP_BO_SCANOUT is a hint from userspace that DMA
> capable
> +	 * buffer is requested, but doesn't mean that it is.  Use the
> +	 * OMAP_BO_DMA flag to determine if the buffer has a DMA capable
> +	 * physical address.
> +	 */
> +	unsigned long paddr;
> +
> +	/**
> +	 * Array of backing pages, if allocated.  Note that pages are never
> +	 * allocated for buffers originally allocated from contiguous
> memory
> +	 * (which will have obj->filp==NULL)
> +	 */
> +	struct page **pages;

Is this array of backing pages used for physically non-continuous memory?.
it appears that sgx core with iommu is considered.

> +
> +	/* note: intentionally nothing in here about vaddr right now..
> +	 * currently everything that might need a virtual address has a
> +	 * drm_framebuffer wrapping the GEM bo, so virtual mapping is
> +	 * handled there..
> +	 *
> +	 * But if there are other cases where kernel virtual mapping is
> +	 * needed, then I may revisit this..
> +	 */
> +
> +	/**
> +	 * sync-object allocated on demand (if needed)
> +	 */
> +	struct {
> +		uint32_t write_pending;
> +		uint32_t write_complete;
> +		uint32_t read_pending;
> +		uint32_t read_complete;
> +	} *sync;
> +
> +	struct omap_gem_vm_ops *ops;
> +
> +	/**
> +	 * per-mapper private data..
> +	 *
> +	 * TODO maybe there can be a more flexible way to store per-mapper
> data..
> +	 * for now I just keep it simple, and since this is only accessible
> +	 * externally via omap_gem_priv()/omap_get_set_priv()
> +	 */
> +	void *priv[MAX_MAPPERS];
> +};
> +
> +DEFINE_SPINLOCK(sync_lock);
> +
> +/** ensure backing pages are allocated */
> +/* NOTE: a bit similar to psb_gtt_attach_pages().. perhaps could be
> common?
> + * and maybe a bit similar to i915_gem_object_get_pages_gtt()
> + */
> +static int omap_gem_attach_pages(struct drm_gem_object *obj)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	struct inode *inode;
> +	struct address_space *mapping;
> +	struct page *p;
> +	int i, npages;
> +	gfp_t gfpmask;
> +
> +	WARN_ON(omap_obj->pages);
> +
> +	/* This is the shared memory object that backs the GEM resource */
> +	inode = obj->filp->f_path.dentry->d_inode;
> +	mapping = inode->i_mapping;
> +
> +	npages = obj->size >> PAGE_SHIFT;
> +
> +	omap_obj->pages = kmalloc(npages * sizeof(struct page *),
> GFP_KERNEL);
> +	if (omap_obj->pages == NULL)
> +		return -ENOMEM;
> +
> +	/* FIXME: review flags later */
> +	gfpmask = __GFP_DMA32 | mapping_gfp_mask(mapping);
> +
> +	for (i = 0; i < npages; i++) {
> +		p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
> +		if (IS_ERR(p))
> +			goto fail;
> +		omap_obj->pages[i] = p;
> +	}
> +
> +	return 0;
> +
> +fail:
> +	while (i--) {
> +		page_cache_release(omap_obj->pages[i]);
> +	}
> +	kfree(omap_obj->pages);
> +	omap_obj->pages = NULL;
> +	return PTR_ERR(p);
> +
> +
> +}
> +
> +/** release backing pages */
> +/* NOTE: a bit similar to psb_gtt_detatch_pages().. perhaps could be
> common? */
> +static void omap_gem_detach_pages(struct drm_gem_object *obj)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	int i, npages;
> +
> +	npages = obj->size >> PAGE_SHIFT;
> +
> +	for (i = 0; i < npages; i++) {
> +		/* FIXME: do we need to force dirty */
> +		set_page_dirty(omap_obj->pages[i]);
> +		/* Undo the reference we took when populating the table */
> +		page_cache_release(omap_obj->pages[i]);
> +	}
> +
> +	kfree(omap_obj->pages);
> +	omap_obj->pages = NULL;
> +}
> +
> +/** get mmap offset */
> +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
> +{
> +	return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
> +}
> +EXPORT_SYMBOL(omap_gem_mmap_offset);
> +
> +/**
> + * omap_gem_fault		-	pagefault handler for GEM objects
> + * @vma: the VMA of the GEM object
> + * @vmf: fault detail
> + *
> + * Invoked when a fault occurs on an mmap of a GEM managed area. GEM
> + * does most of the work for us including the actual map/unmap calls
> + * but we need to do the actual page work.
> + *
> + * The VMA was set up by GEM. In doing so it also ensured that the
> + * vma->vm_private_data points to the GEM object that is backing this
> + * mapping.
> + */
> +int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> +{
> +	struct drm_gem_object *obj = vma->vm_private_data;
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	struct drm_device *dev = obj->dev;
> +	unsigned long pfn;
> +	pgoff_t page_offset;
> +	int ret;
> +
> +	/* We don't use vmf->pgoff since that has the fake offset */
> +	page_offset = ((unsigned long)vmf->virtual_address -
> +			vma->vm_start) >> PAGE_SHIFT;
> +
> +	/* Make sure we don't parallel update on a fault, nor move or
> remove
> +	 * something from beneath our feet
> +	 */
> +	mutex_lock(&dev->struct_mutex);
> +
> +	/* if a shmem backed object, make sure we have pages attached now
> */
> +	if (obj->filp && !omap_obj->pages) {
> +		ret = omap_gem_attach_pages(obj);
> +		if (ret) {
> +			dev_err(dev->dev, "could not attach pages\n");
> +			goto fail;
> +		}
> +	}
> +
> +	if (omap_obj->pages) {
> +		pfn = page_to_pfn(omap_obj->pages[page_offset]);
> +	} else {
> +		BUG_ON(!(omap_obj->flags & OMAP_BO_DMA));
> +		pfn = (omap_obj->paddr >> PAGE_SHIFT) + page_offset;
> +	}
> +
> +	VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
> +			pfn, pfn << PAGE_SHIFT);
> +
> +	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address,
> pfn);
> +
> +fail:
> +	mutex_unlock(&dev->struct_mutex);
> +	switch (ret) {
> +	case 0:
> +	case -ERESTARTSYS:
> +	case -EINTR:
> +		return VM_FAULT_NOPAGE;
> +	case -ENOMEM:
> +		return VM_FAULT_OOM;
> +	default:
> +		return VM_FAULT_SIGBUS;
> +	}
> +}
> +
> +void omap_gem_vm_open(struct vm_area_struct *vma)
> +{
> +	struct drm_gem_object *obj = vma->vm_private_data;
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	if (omap_obj->ops && omap_obj->ops->open) {
> +		omap_obj->ops->open(vma);
> +	} else {
> +		drm_gem_vm_open(vma);
> +	}
> +}
> +
> +void omap_gem_vm_close(struct vm_area_struct *vma)
> +{
> +	struct drm_gem_object *obj = vma->vm_private_data;
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	if (omap_obj->ops && omap_obj->ops->close) {
> +		omap_obj->ops->close(vma);
> +	} else {
> +		drm_gem_vm_close(vma);
> +	}
> +}
> +
> +/** We override mainly to fix up some of the vm mapping flags.. */
> +int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	struct omap_gem_object *omap_obj;
> +	int ret;
> +
> +	ret = drm_gem_mmap(filp, vma);
> +	if (ret) {
> +		DBG("mmap failed: %d", ret);
> +		return ret;
> +	}
> +
> +	/* after drm_gem_mmap(), it is safe to access the obj */
> +	omap_obj = to_omap_bo(vma->vm_private_data);
> +
> +	vma->vm_flags &= ~VM_PFNMAP;
> +	vma->vm_flags |= VM_MIXEDMAP;
> +
> +	if (omap_obj->flags & OMAP_BO_WC) {
> +		vma->vm_page_prot =
> pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
> +	} else if (omap_obj->flags & OMAP_BO_UNCACHED) {
> +		vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma-
> >vm_flags));
> +	} else {
> +		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
> +	}
> +
> +	if (omap_obj->ops && omap_obj->ops->mmap) {
> +		omap_obj->ops->mmap(filp, vma);
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * omap_gem_dumb_create	-	create a dumb buffer
> + * @drm_file: our client file
> + * @dev: our device
> + * @args: the requested arguments copied from userspace
> + *
> + * Allocate a buffer suitable for use for a frame buffer of the
> + * form described by user space. Give userspace a handle by which
> + * to reference it.
> + */
> +int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
> +		struct drm_mode_create_dumb *args)
> +{
> +	/* in case someone tries to feed us a completely bogus stride: */
> +	args->pitch = align_pitch(args->pitch, args->width, args->bpp);
> +	args->size = PAGE_ALIGN(args->pitch * args->height);
> +
> +	return omap_gem_new_handle(dev, file, args->size,
> +			OMAP_BO_SCANOUT | OMAP_BO_WC, &args->handle, NULL);
> +}
> +
> +/**
> + * omap_gem_dumb_destroy	-	destroy a dumb buffer
> + * @file: client file
> + * @dev: our DRM device
> + * @handle: the object handle
> + *
> + * Destroy a handle that was created via omap_gem_dumb_create.
> + */
> +int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
> +		uint32_t handle)
> +{
> +	/* No special work needed, drop the reference and see what falls
> out */
> +	return drm_gem_handle_delete(file, handle);
> +}
> +
> +/**
> + * omap_gem_dumb_map	-	buffer mapping for dumb interface
> + * @file: our drm client file
> + * @dev: drm device
> + * @handle: GEM handle to the object (from dumb_create)
> + *
> + * Do the necessary setup to allow the mapping of the frame buffer
> + * into user memory. We don't have to do much here at the moment.
> + */
> +int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device
> *dev,
> +		uint32_t handle, uint64_t *offset)
> +{
> +	int ret = 0;
> +	struct drm_gem_object *obj;
> +
> +	mutex_lock(&dev->struct_mutex);
> +
> +	/* GEM does all our handle to object mapping */
> +	obj = drm_gem_object_lookup(dev, file, handle);
> +	if (obj == NULL) {
> +		ret = -ENOENT;
> +		goto fail;
> +	}
> +
> +	/* GEM should really work out the hash offsets for us */
> +	*offset = omap_gem_mmap_offset(obj);
> +
> +	drm_gem_object_unreference_unlocked(obj);
> +
> +fail:
> +	mutex_unlock(&dev->struct_mutex);
> +	return ret;
> +}
> +
> +/* Get physical address for DMA.. if 'remap' is true, and the buffer is
> not
> + * already contiguous, remap it to pin in physically contiguous memory..
> (ie.
> + * map in TILER)
> + */
> +int omap_gem_get_paddr(struct drm_gem_object *obj,
> +		unsigned long *paddr, bool remap)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	if (!(omap_obj->flags & OMAP_BO_DMA)) {
> +		/* TODO: remap to TILER */
> +		return -ENOMEM;
> +	}
> +
> +	*paddr = omap_obj->paddr;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_get_paddr);
> +
> +/* Release physical address, when DMA is no longer being performed.. this
> + * could potentially unpin and unmap buffers from TILER
> + */
> +int omap_gem_put_paddr(struct drm_gem_object *obj)
> +{
> +	/* do something here when remap to TILER is used.. */
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_put_paddr);
> +
> +/* acquire pages when needed (for example, for DMA where physically
> + * contiguous buffer is not required
> + */
> +int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	/* TODO: we could attach/detach pages on demand */
> +	int ret;  // XXX below is common in _fault()..
> +	if (obj->filp && !omap_obj->pages) {
> +		ret = omap_gem_attach_pages(obj);
> +		if (ret) {
> +			dev_err(obj->dev->dev, "could not attach pages\n");
> +			return ret;
> +		}
> +	}
> +	/* TODO: even phys-contig.. we should have a list of pages */
> +	*pages = omap_obj->pages;
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_get_pages);
> +
> +/* release pages when DMA no longer being performed */
> +int omap_gem_put_pages(struct drm_gem_object *obj)
> +{
> +	/* do something here if we dynamically attach/detach pages.. at
> +	 * least they would no longer need to be pinned if everyone has
> +	 * released the pages..
> +	 */
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_put_pages);
> +
> +/* get buffer flags */
> +uint32_t omap_gem_flags(struct drm_gem_object *obj)
> +{
> +	return to_omap_bo(obj)->flags;
> +}
> +EXPORT_SYMBOL(omap_gem_flags);
> +
> +void * omap_gem_priv(struct drm_gem_object *obj, int mapper_id)
> +{
> +	BUG_ON((mapper_id >= MAX_MAPPERS) || (mapper_id < 0));
> +	return to_omap_bo(obj)->priv[mapper_id];
> +}
> +EXPORT_SYMBOL(omap_gem_priv);
> +
> +void omap_gem_set_priv(struct drm_gem_object *obj, int mapper_id, void
> *priv)
> +{
> +	BUG_ON((mapper_id >= MAX_MAPPERS) || (mapper_id < 0));
> +	to_omap_bo(obj)->priv[mapper_id] = priv;
> +}
> +EXPORT_SYMBOL(omap_gem_set_priv);
> +
> +/* Buffer Synchronization:
> + */
> +
> +struct omap_gem_sync_waiter {
> +	struct list_head list;
> +	struct omap_gem_object *omap_obj;
> +	enum omap_gem_op op;
> +	uint32_t read_target, write_target;
> +	/* notify called w/ sync_lock held */
> +	void (*notify)(void *arg);
> +	void *arg;
> +};
> +
> +/* list of omap_gem_sync_waiter.. the notify fxn gets called back when
> + * the read and/or write target count is achieved which can call a user
> + * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for
> + * cpu access), etc.
> + */
> +static LIST_HEAD(waiters);
> +
> +static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
> +{
> +	struct omap_gem_object *omap_obj = waiter->omap_obj;
> +	if ((waiter->op & OMAP_GEM_READ) &&
> +			(omap_obj->sync->read_complete <
waiter->read_target))
> +		return true;
> +	if ((waiter->op & OMAP_GEM_WRITE) &&
> +			(omap_obj->sync->write_complete < waiter-
> >write_target))
> +		return true;
> +	return false;
> +}
> +
> +/* macro for sync debug.. */
> +#define SYNCDBG 0
> +#define SYNC(fmt,...) do { if(SYNCDBG) \
> +		printk(KERN_ERR "%s:%d: "fmt"\n", __func__, __LINE__,
> ##__VA_ARGS__); \
> +	} while (0)
> +
> +
> +static void sync_op_update(void)
> +{
> +	struct omap_gem_sync_waiter *waiter, *n;
> +	list_for_each_entry_safe(waiter, n, &waiters, list) {
> +		if (!is_waiting(waiter)) {
> +			list_del(&waiter->list);
> +			SYNC("notify: %p", waiter);
> +			waiter->notify(waiter->arg);
> +			kfree(waiter);
> +		}
> +	}
> +}
> +
> +static inline void sync_op(struct drm_gem_object *obj,
> +		enum omap_gem_op op, bool start)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	spin_lock(&sync_lock);
> +
> +	if (!omap_obj->sync) {
> +		omap_obj->sync = kzalloc(sizeof(*omap_obj->sync),
> GFP_KERNEL);
> +	}

If sync_op function is called at interrupt context, use GFP_ATOMIC instead
of GFP_KERNEL. from what you use spin_lock/unlock here, it appears that this
function would be called at interrupt context.

> +
> +	if (start) {
> +		if (op & OMAP_GEM_READ)  omap_obj->sync->read_pending++;
> +		if (op & OMAP_GEM_WRITE) omap_obj->sync->write_pending++;
> +	} else {
> +		if (op & OMAP_GEM_READ)  omap_obj->sync->read_complete++;
> +		if (op & OMAP_GEM_WRITE) omap_obj->sync->write_complete++;
> +		sync_op_update();
> +	}
> +
> +	spin_unlock(&sync_lock);
> +}
> +
> +/* it is a bit lame to handle updates in this sort of polling way, but
> + * in case of PVR, the GPU can directly update read/write complete
> + * values, and not really tell us which ones it updated.. this also
> + * means that sync_lock is not quite sufficient.  So we'll need to
> + * do something a bit better when it comes time to add support for
> + * separate 2d hw..
> + */
> +void omap_gem_op_update(void)
> +{
> +	spin_lock(&sync_lock);
> +	sync_op_update();
> +	spin_unlock(&sync_lock);
> +}
> +EXPORT_SYMBOL(omap_gem_op_update);
> +
> +/* mark the start of read and/or write operation */
> +void omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op)
> +{
> +	sync_op(obj, op, true);
> +}
> +EXPORT_SYMBOL(omap_gem_op_start);
> +
> +void omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op)
> +{
> +	sync_op(obj, op, false);
> +}
> +EXPORT_SYMBOL(omap_gem_op_finish);
> +
> +static DECLARE_WAIT_QUEUE_HEAD(sync_event);
> +
> +static void sync_notify(void *arg)
> +{
> +	struct task_struct **waiter_task = arg;
> +	*waiter_task = NULL;
> +	wake_up_all(&sync_event);
> +}
> +
> +int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	int ret = 0;
> +	if (omap_obj->sync)
> +	{
> +		volatile struct task_struct *waiter_task = current;
> +		struct omap_gem_sync_waiter *waiter =
> +				kzalloc(sizeof(*waiter), GFP_KERNEL);
> +
> +		if (!waiter) {
> +			return -ENOMEM;
> +		}
> +
> +		waiter->omap_obj = omap_obj;
> +		waiter->op = op;
> +		waiter->read_target = omap_obj->sync->read_pending;
> +		waiter->write_target = omap_obj->sync->write_pending;
> +		waiter->notify = sync_notify;
> +		waiter->arg = &waiter_task;
> +
> +		spin_lock(&sync_lock);
> +		if (is_waiting(waiter)) {
> +			SYNC("waited: %p", waiter);
> +			list_add_tail(&waiter->list, &waiters);
> +			spin_unlock(&sync_lock);
> +			ret = wait_event_interruptible(sync_event,
> +					(waiter_task == NULL));
> +			spin_lock(&sync_lock);
> +			if (waiter_task) {
> +				SYNC("interrupted: %p", waiter);
> +				/* we were interrupted */
> +				list_del(&waiter->list);
> +				waiter_task = NULL;
> +			} else {
> +				/* freed in sync_op_update() */
> +				waiter = NULL;
> +			}
> +		}
> +		spin_unlock(&sync_lock);
> +
> +		if (waiter) {
> +			kfree(waiter);
> +		}
> +	}
> +	return ret;
> +}
> +EXPORT_SYMBOL(omap_gem_op_sync);
> +
> +/* call fxn(arg), either synchronously or asynchronously if the op
> + * is currently blocked..  fxn() can be called from any context
> + *
> + * (TODO for now fxn is called back from whichever context calls
> + * omap_gem_op_update().. but this could be better defined later
> + * if needed)
> + *
> + * TODO more code in common w/ _sync()..
> + */
> +int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
> +		void (*fxn)(void *arg), void *arg)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +	if (omap_obj->sync) {
> +		struct omap_gem_sync_waiter *waiter =
> +				kzalloc(sizeof(*waiter),
GFP_KERNEL|GFP_ATOMIC);
> +
> +		if (!waiter) {
> +			return -ENOMEM;
> +		}
> +
> +		waiter->omap_obj = omap_obj;
> +		waiter->op = op;
> +		waiter->read_target = omap_obj->sync->read_pending;
> +		waiter->write_target = omap_obj->sync->write_pending;
> +		waiter->notify = fxn;
> +		waiter->arg = arg;
> +
> +		spin_lock(&sync_lock);
> +		if (is_waiting(waiter)) {
> +			SYNC("waited: %p", waiter);
> +			list_add_tail(&waiter->list, &waiters);
> +			spin_unlock(&sync_lock);
> +			return 0;
> +		}
> +
> +		spin_unlock(&sync_lock);
> +	}
> +
> +	/* no waiting.. */
> +	fxn(arg);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_gem_op_async);
> +
> +/* special API so PVR can update the buffer to use a sync-object
> allocated
> + * from it's sync-obj heap.  Only used for a newly allocated (from PVR's
> + * perspective) sync-object, so we overwrite the new syncobj w/ values
> + * from the already allocated syncobj (if there is one)
> + */
> +void omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	spin_lock(&sync_lock);
> +
> +	if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) {
> +		/* clearing a previously set syncobj */
> +		syncobj = kzalloc(sizeof(*omap_obj->sync),
> GFP_KERNEL|GFP_ATOMIC);
> +		memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
> +		omap_obj->flags &= ~OMAP_BO_EXT_SYNC;
> +		omap_obj->sync = syncobj;
> +	} else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
> +		/* replacing an existing syncobj */
> +		if (omap_obj->sync) {
> +			memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj-
> >sync));
> +			kfree(omap_obj->sync);
> +		}
> +		omap_obj->flags |= OMAP_BO_EXT_SYNC;
> +		omap_obj->sync = syncobj;
> +	}
> +	spin_unlock(&sync_lock);
> +}
> +EXPORT_SYMBOL(omap_gem_set_sync_object);
> +
> +
> +int omap_gem_init_object(struct drm_gem_object *obj)
> +{
> +	return -EINVAL;          /* unused */
> +}
> +
> +/* don't call directly.. called from GEM core when it is time to actually
> + * free the object..
> + */
> +void omap_gem_free_object(struct drm_gem_object *obj)
> +{
> +	struct omap_gem_object *omap_obj = to_omap_bo(obj);
> +
> +	if (obj->map_list.map) {
> +		drm_gem_free_mmap_offset(obj);
> +	}
> +
> +	/* don't free externally allocated backing memory */
> +	if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {
> +		if (!obj->filp) {
> +			/* TODO: is this the best way to check? */
> +			omap_vram_free(omap_obj->paddr, obj->size);
> +		}
> +
> +		if (omap_obj->pages) {
> +			omap_gem_detach_pages(obj);
> +		}
> +	}
> +
> +	/* don't free externally allocated syncobj */
> +	if (!(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
> +		kfree(omap_obj->sync);
> +	}
> +
> +	drm_gem_object_release(obj);
> +
> +	kfree(obj);
> +}
> +
> +/* convenience method to construct a GEM buffer object, and userspace
> handle */
> +int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
> +		size_t size, uint32_t flags, uint32_t *handle, uint64_t
> *offset)
> +{
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	obj = omap_gem_new(dev, size, flags);
> +	if (!obj)
> +		return -ENOMEM;
> +
> +	ret = drm_gem_handle_create(file, obj, handle);
> +	if (ret) {
> +		drm_gem_object_release(obj);
> +		kfree(obj); /* TODO isn't there a dtor to call? just copying
> i915 */
> +		return ret;
> +	}
> +
> +	if (offset) {
> +		*offset = omap_gem_mmap_offset(obj);
> +	}
> +
> +	/* drop reference from allocate - handle holds it now */
> +	drm_gem_object_unreference_unlocked(obj);
> +
> +	return 0;
> +}
> +
> +/* common constructor body */
> +static struct drm_gem_object * omap_gem_new_impl(struct drm_device *dev,
> +		size_t size, uint32_t flags, unsigned long paddr, struct
> page **pages,
> +		struct omap_gem_vm_ops *ops)
> +{

so many arguments are used at this function. I encourage you to use maximum
four arguments for ARM system.


> +	struct omap_gem_object *omap_obj;
> +	struct drm_gem_object *obj;
> +	int ret;
> +
> +	size = PAGE_ALIGN(size);
> +
> +	omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL);
> +	if (!omap_obj) {
> +		dev_err(dev->dev, "could not allocate GEM object\n");
> +		goto fail;
> +	}
> +
> +	obj = &omap_obj->base;
> +
> +	if (paddr) {
> +		flags |= OMAP_BO_DMA;
> +	}
> +
> +	omap_obj->paddr = paddr;
> +	omap_obj->pages = pages;
> +	omap_obj->flags = flags;
> +	omap_obj->ops   = ops;
> +
> +	if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
> +		ret = drm_gem_private_object_init(dev, obj, size);
> +	} else {
> +		ret = drm_gem_object_init(dev, obj, size);
> +	}
> +
> +	if (ret) {
> +		/* Yikes!  need to clean up! XXX */
> +		goto fail;
> +	}
> +
> +	/* Make it mmapable */
> +	ret = drm_gem_create_mmap_offset(obj);
> +	if (ret) {
> +		dev_err(dev->dev, "could not allocate mmap offset");
> +		goto fail;
> +	}
> +
> +	return obj;
> +
> +fail:
> +	if (omap_obj) {
> +		kfree(omap_obj);
> +	}
> +	return NULL;
> +}
> +
> +/* This constructor is mainly to give plugins a way to wrap their
> + * own allocations
> + */
> +struct drm_gem_object * omap_gem_new_ext(struct drm_device *dev,
> +		size_t size, uint32_t flags, unsigned long paddr, struct
> page **pages,
> +		struct omap_gem_vm_ops *ops)
> +{
> +	return omap_gem_new_impl(dev, size, flags | OMAP_BO_EXT_MEM,
> +			paddr, pages, ops);
> +}
> +EXPORT_SYMBOL(omap_gem_new_ext);
> +
> +/* normal GEM buffer object constructor */
> +struct drm_gem_object * omap_gem_new(struct drm_device *dev,
> +		size_t size, uint32_t flags)
> +{
> +	unsigned long paddr = 0;
> +
> +	if (flags & OMAP_BO_SCANOUT) {
> +		/* attempt to allocate contiguous memory */
> +		int ret = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size,
> &paddr);
> +		if (ret) {
> +			dev_err(dev->dev, "failed to allocate vram\n");
> +			/* fallback to non-contiguous memory which will need
> to be
> +			 * remapped later..
> +			 */
> +			paddr = 0;
> +		}
> +	}
> +
> +	return omap_gem_new_impl(dev, size, flags, paddr, NULL, NULL);
> +}
> +EXPORT_SYMBOL(omap_gem_new);
> diff --git a/drivers/video/omap2/omapfb/Kconfig
> b/drivers/video/omap2/omapfb/Kconfig
> index aa33386..6c94841 100644
> --- a/drivers/video/omap2/omapfb/Kconfig
> +++ b/drivers/video/omap2/omapfb/Kconfig
> @@ -1,6 +1,6 @@
>  menuconfig FB_OMAP2
>          tristate "OMAP2+ frame buffer support (EXPERIMENTAL)"
> -        depends on FB && OMAP2_DSS
> +        depends on FB && OMAP2_DSS && !DRM_OMAP
> 
>  	select OMAP2_VRAM
>  	select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
> @@ -8,7 +8,7 @@ menuconfig FB_OMAP2
>          select FB_CFB_COPYAREA
>          select FB_CFB_IMAGEBLIT
>          help
> -	  Frame buffer driver for OMAP2+ based boards.
> +	  Legacy frame buffer driver for OMAP2/3 based boards.
> 
>  config FB_OMAP2_DEBUG_SUPPORT
>          bool "Debug support for OMAP2+ FB"
> diff --git a/include/linux/Kbuild b/include/linux/Kbuild
> index 01f6362..ca53772 100644
> --- a/include/linux/Kbuild
> +++ b/include/linux/Kbuild
> @@ -280,6 +280,7 @@ header-y += nubus.h
>  header-y += nvram.h
>  header-y += omap3isp.h
>  header-y += omapfb.h
> +header-y += omap_gpu.h
>  header-y += oom.h
>  header-y += param.h
>  header-y += parport.h
> diff --git a/include/linux/omap_drm.h b/include/linux/omap_drm.h
> new file mode 100644
> index 0000000..81af200
> --- /dev/null
> +++ b/include/linux/omap_drm.h
> @@ -0,0 +1,191 @@
> +/*
> + * linux/include/linux/omap_drm.h
> + *
> + * Copyright (C) 2011 Texas Instruments
> + * Author: Rob Clark <rob@ti.com>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __OMAP_DRM_H__
> +#define __OMAP_DRM_H__
> +
> +#include <linux/module.h>
> +#include <drm/drmP.h>
> +
> +#define OMAP_PARAM_CHIPSET_ID	1	/* ie. 0x3430, 0x4430, etc
*/
> +
> +struct drm_omap_param {
> +	uint64_t param;			/* in */
> +	uint64_t value;			/* in (set_param), out (get_param)
> */
> +};
> +
> +struct drm_omap_get_base {
> +	char plugin_name[64];		/* in */
> +	uint32_t ioctl_base;		/* out */
> +};
> +
> +#define OMAP_BO_SCANOUT		0x00000001	/* scanout capable
(phys
> contiguous) */
> +#define OMAP_BO_CACHE_MASK	0x00000006	/* cache type mask, see
> cache modes */
> +#define OMAP_BO_TILED_MASK	0x00000f00	/* tiled mapping mask, see
> tiled modes */
> +
> +/* cache modes */
> +#define OMAP_BO_CACHED		0x00000000	/* default */
> +#define OMAP_BO_WC		0x00000002	/* write-combine */
> +#define OMAP_BO_UNCACHED	0x00000004	/* strongly-ordered
(uncached) */
> +
> +/* tiled modes */
> +#define OMAP_BO_TILED_8		0x00000100
> +#define OMAP_BO_TILED_16	0x00000200
> +#define OMAP_BO_TILED_32	0x00000300
> +
> +struct drm_omap_gem_new {
> +	union {				/* in */
> +		uint32_t bytes;		/* (for non-tiled formats) */
> +		struct {
> +			uint16_t width;
> +			uint16_t height;
> +		} tiled;		/* (for tiled formats) */
> +	} size;
> +	uint32_t flags;			/* in */
> +	uint64_t offset;		/* out */
> +	uint32_t handle;		/* out */
> +};
> +
> +/* mask of operations: */
> +enum omap_gem_op {
> +	OMAP_GEM_READ = 0x01,
> +	OMAP_GEM_WRITE = 0x02,
> +};
> +
> +struct drm_omap_gem_cpu_prep {
> +	uint32_t handle;		/* buffer handle (in) */
> +	uint32_t op;			/* mask of omap_gem_op (in) */
> +};
> +
> +struct drm_omap_gem_cpu_fini {
> +	uint32_t handle;		/* buffer handle (in) */
> +	uint32_t op;			/* mask of omap_gem_op (in) */
> +	/* TODO maybe here we pass down info about what regions are touched
> +	 * by sw so we can be clever about cache ops?  For now a
placeholder,
> +	 * set to zero and we just do full buffer flush..
> +	 */
> +	uint32_t nregions;
> +};
> +
> +struct drm_omap_gem_info {
> +	uint32_t handle;		/* buffer handle (in) */
> +	uint32_t pad;
> +	uint64_t offset;		/* out */
> +};
> +
> +#define DRM_OMAP_GET_PARAM		0x00
> +#define DRM_OMAP_SET_PARAM		0x01
> +#define DRM_OMAP_GET_BASE		0x02
> +#define DRM_OMAP_GEM_NEW		0x03
> +#define DRM_OMAP_GEM_CPU_PREP	0x04
> +#define DRM_OMAP_GEM_CPU_FINI	0x05
> +#define DRM_OMAP_GEM_INFO	0x06
> +#define DRM_OMAP_NUM_IOCTLS		0x07
> +
> +#define DRM_IOCTL_OMAP_GET_PARAM	DRM_IOWR(DRM_COMMAND_BASE +
> DRM_OMAP_GET_PARAM, struct drm_omap_param)
> +#define DRM_IOCTL_OMAP_SET_PARAM	DRM_IOW (DRM_COMMAND_BASE +
> DRM_OMAP_SET_PARAM, struct drm_omap_param)
> +#define DRM_IOCTL_OMAP_GET_BASE		DRM_IOWR(DRM_COMMAND_BASE +
> DRM_OMAP_GET_BASE, struct drm_omap_get_base)
> +#define DRM_IOCTL_OMAP_GEM_NEW		DRM_IOWR(DRM_COMMAND_BASE +
> DRM_OMAP_GEM_NEW, struct drm_omap_gem_new)
> +#define DRM_IOCTL_OMAP_GEM_CPU_PREP	DRM_IOW (DRM_COMMAND_BASE +
> DRM_OMAP_GEM_CPU_PREP, struct drm_omap_gem_cpu_prep)
> +#define DRM_IOCTL_OMAP_GEM_CPU_FINI	DRM_IOW (DRM_COMMAND_BASE +
> DRM_OMAP_GEM_CPU_FINI, struct drm_omap_gem_cpu_fini)
> +#define DRM_IOCTL_OMAP_GEM_INFO		DRM_IOWR(DRM_COMMAND_BASE +
> DRM_OMAP_GEM_INFO, struct drm_omap_gem_info)
> +
> +/* interface that plug-in drivers can implement */
> +struct omap_drm_plugin {
> +	const char *name;
> +
> +	/* drm functions */
> +	int (*open)(struct drm_device *dev, struct drm_file *file);
> +	int (*load)(struct drm_device *dev, unsigned long flags);
> +	int (*unload)(struct drm_device *dev);
> +	int (*release)(struct drm_device *dev, struct drm_file *file);
> +
> +	struct drm_ioctl_desc *ioctls;
> +	int num_ioctls;
> +	int ioctl_base;
> +
> +	struct list_head list;  /* note, this means struct can't be const..
> */
> +};
> +
> +int omap_drm_register_plugin(struct omap_drm_plugin *plugin);
> +int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin);
> +
> +int omap_drm_register_mapper(void);
> +void omap_drm_unregister_mapper(int id);
> +
> +/* external mappers should get paddr or pages when it needs the pages
> pinned
> + * and put when done..
> + */
> +int omap_gem_get_paddr(struct drm_gem_object *obj,
> +		unsigned long *paddr, bool remap);
> +int omap_gem_put_paddr(struct drm_gem_object *obj);
> +int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages);
> +int omap_gem_put_pages(struct drm_gem_object *obj);
> +
> +uint32_t omap_gem_flags(struct drm_gem_object *obj);
> +void * omap_gem_priv(struct drm_gem_object *obj, int mapper_id);
> +void omap_gem_set_priv(struct drm_gem_object *obj, int mapper_id, void
> *priv);
> +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj);
> +
> +/* for external plugin buffers wrapped as GEM object (via.
> omap_gem_new_ext())
> + * a vm_ops struct can be provided to get callback notification of
> various
> + * events..
> + */
> +struct omap_gem_vm_ops {
> +	void (*open)(struct vm_area_struct * area);
> +	void (*close)(struct vm_area_struct * area);
> +
> +	/* note: mmap is not expected to do anything.. it is just to allow
> buffer
> +	 * allocate to update it's own internal state
> +	 */
> +	void (*mmap)(struct file *, struct vm_area_struct *);
> +};
> +
> +struct drm_gem_object * omap_gem_new_ext(struct drm_device *dev,
> +		size_t size, uint32_t flags, unsigned long paddr, struct
> page **pages,
> +		struct omap_gem_vm_ops *ops);
> +struct drm_gem_object * omap_gem_new(struct drm_device *dev,
> +		size_t size, uint32_t flags);
> +
> +void omap_gem_op_update(void);
> +void omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op);
> +void omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op);
> +int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op);
> +int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
> +		void (*fxn)(void *arg), void *arg);
> +void omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj);
> +
> +
> +/* optional platform data to configure the default configuration of which
> + * pipes/overlays/CRTCs are used.. if this is not provided, then instead
> the
> + * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected
> to
> + * one manager, with priority given to managers that are connected to
> + * detected devices.  This should be a good default behavior for most
> cases,
> + * but yet there still might be times when you wish to do something
> different.
> + */
> +struct omap_drm_platform_data {
> +	int ovl_cnt;
> +	const int *ovl_ids;
> +	int mgr_cnt;
> +	const int *mgr_ids;
> +	int dev_cnt;
> +	const char **dev_names;
> +};
> +
> +#endif /* __OMAP_DRM_H__ */
> --

Remove device specific functions from linux/include/linux/omap_drm.h and
move them to driver folder. I was told that include/linux/*.h file should
contain only interfaces for user process from Dave.

> 1.7.5.4
Rob Clark Sept. 5, 2011, 4:05 p.m. UTC | #8
On Mon, Sep 5, 2011 at 4:58 AM, Inki Dae <inki.dae@samsung.com> wrote:
> Hello, Rob.
> I didn't look into any comments from other develops yet , so my comments
> could be duplicated with other ones. below is my opinions and I commented
> your codes also.
>
> We can discuss crtc, encoder and connector. in x86 world, my understanding,
> each one means the following.

just fwiw, some of current implementation in the KMS code is result of
how the DSS2 API works..  ie. a dssdev (panel driver) is really part
encoder and part connector.

But on other hand, there are customers who write their own panel
drivers in DSS2 so I'm not sure that we want to completely change
this.  And the encoder/connector distinction gets a bit more blurry w/
smart-panel type displays.

[snip]
>> +static int omap_connector_mode_valid(struct drm_connector *connector,
>> +                              struct drm_display_mode *mode)
>> +{
>> +     struct omap_connector *omap_connector =
>> to_omap_connector(connector);
>> +     struct omap_dss_device *dssdev = omap_connector->dssdev;
>> +     struct omap_dss_driver *dssdrv = dssdev->driver;
>> +     struct omap_video_timings timings = {0};
>> +     struct drm_device *dev = connector->dev;
>> +     struct drm_display_mode *new_mode;
>> +     int ret = MODE_BAD;
>> +
>> +     copy_timings_drm_to_omap(&timings, mode);
>> +     mode->vrefresh = drm_mode_vrefresh(mode);
>> +
>> +     if (!dssdrv->check_timings(dssdev, &timings)) {
>> +             /* check if vrefresh is still valid */
>> +             new_mode = drm_mode_duplicate(dev, mode);
>> +             new_mode->clock = timings.pixel_clock;
>> +             new_mode->vrefresh = 0;
>> +             if (mode->vrefresh == drm_mode_vrefresh(new_mode))
>> +                     ret = MODE_OK;
>> +             drm_mode_destroy(dev, new_mode);
>> +     }
>> +
>
> is there any reason that you call drm_mode_duplicate() to get a clone of a
> mode? I just wonder it.

yes, because the new_mode is modified and I didn't want this fxn to
have unintended side-effects.

this works in a slightly funny way, because dssdev->check_timings() is
actually a dssdev->check_and_modify_timings(), modifying the pixel
clock to the closest thing that is supported.  We need to compare the
resulting vrefresh to see if it is something matching..  otherwise we
ask for some resolution @ 60Hz and maybe get 30Hz instead.

[snip]
>> +/* initialize connector */
>> +struct drm_connector * omap_connector_init(struct drm_device *dev,
>> +             int connector_type, struct omap_dss_device *dssdev)
>> +{
>> +     struct drm_connector *connector = NULL;
>
> It appears that this connector is initialized with NULL. This is just minor
> issue. :)

it is so it isn't uninitialized if kzalloc fails and we 'goto fail'..
I guess in this particular case there is only a single fail point,
although it is a coding pattern that I follow elsewhere where there
are multiple fail points.  And at least this way if I added some other
step later where it might fail after the kzalloc(), I won't
accidentally miss the appropriate cleanup. I mostly prefer to have a
single cleanup path when possible so I don't confuse myself later

[snip]
>> +static void page_flip_cb(void *arg)
>> +{
>> +     struct drm_crtc *crtc = arg;
>> +     struct drm_device *dev = crtc->dev;
>> +     struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>> +     struct drm_pending_vblank_event *event = omap_crtc->event;
>> +     struct timeval now;
>> +     unsigned long flags;
>> +
>> +     WARN_ON(!event);
>> +
>> +     omap_crtc->event = NULL;
>> +
>> +     update_scanout(crtc);
>> +     commit(crtc);
>> +
>> +     /* wakeup userspace */
>> +     // TODO: this should happen *after* flip.. somehow..
>> +     if (event) {
>> +             spin_lock_irqsave(&dev->event_lock, flags);
>> +             event->event.sequence =
>> +                             drm_vblank_count_and_time(dev,
> omap_crtc->id,
>> &now);
>> +             event->event.tv_sec = now.tv_sec;
>> +             event->event.tv_usec = now.tv_usec;
>> +             list_add_tail(&event->base.link,
>> +                             &event->base.file_priv->event_list);
>> +             wake_up_interruptible(&event->base.file_priv->event_wait);
>> +             spin_unlock_irqrestore(&dev->event_lock, flags);
>> +     }
>
> How about moving codes above into interrupt handler for vblank?
>  maybe there

I should mention a couple of things:

1) drm vblank stuff isn't really used currently.. it is actually DSS2
layer that is registering for the display related interrupt(s).  I'm
not really sure how to handle this best.  I suppose the DRM driver
could *also* register for these interrupts, but that seems a bit odd..

Also, I guess it is also worth mentioning.. when it comes to vblank,
there are two fundamentally different sorts of displays we deal with.
Something like DVI/HDMI/etc which need constant refreshing.  In these
cases we constantly scan-out the buffer until the next page
flip+vsync.  And smart panels, where they get scanned out once and
then DSS is no longer reading the scanout buffer until we manually
trigger an update.

2) updating things like scanout address for framebuffers, these are
actually double-buffered registers in the DSS block.  So the update of
actual scanout address is automatically synchronized by the hardware
w/ the vblank.  The one thing I need to do still is defer the event
back to userspace until after the flip so it doesn't start rendering
onto the buffer that is still being scanned out.  I'm not quite sure
the best way to do this yet.  DSS2 driver has an API that I can
register for IRQ callbacks, although the knowledge about the right
callback to register (VSYNC, VSYNC2, FRAMEDONE, EVSYNC_ODD,
EVSYNC_EVEN, etc) is in the dssdev/connector.  I could use
omap_connector_sync() to block until vsync/framedone but I'd prefer a
non-blocking mechanism.

> would be some reason that spin_lock_* are used here.

dev->event_lock seems to be used to protect modifying event_list elsewhere..

>> +}
>> +
>> +static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
>> +              struct drm_framebuffer *fb,
>> +              struct drm_pending_vblank_event *event)
>> +{
>> +     struct drm_device *dev = crtc->dev;
>> +     struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>> +
>> +     DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
>> +
>> +     if (omap_crtc->event) {
>> +             dev_err(dev->dev, "already a pending flip\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     crtc->fb = fb;
>> +     omap_crtc->event = event;
>> +
>> +     omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ,
>> +                     page_flip_cb, crtc);
>> +
>> +     return 0;
>> +}
>> +
>
> Where are drm_vblank_get() and drm_vblank_put() called to enable and disable
> vsync interrupt? and does the this page flip need user-requested drm_control
> ioctl to install/uninstall a handler?. I think the page_flip feature should
> work it fine itself.
>
> like this:
> at omap_crtc_page_flip_locked()
> drm_vblank_get();   <- enable vsync interrupt.
> update famebuffer. <- update overlay and set it to hw.
>
> at interrupt handler
> handle page flip event.
> wake_up_interruptible() <- notify  user of page flip event.
> drm_vblank_put();  <- disable vsync interrupt.
>
> I think that page flip callback would be called after vsync interrupt
> occurred. for this, you can refer to set_mode function of libdrm library. a
> event has user's apage flip handler and when user received any event from
> kernel side, this handler would be called to update framebuffer repeatedly.

The main reason for the page-flip cb is actually not vsync
synchronization, but rather synchronizing with other hw blocks that
might be rendering to the buffer..  the page flip can be submitted
from userspace while some other hw block (3d, 2d, etc) is still
DMA'ing to the buffer.  The sync-obj is intended to give a way to
defer the (in this case) page flip until other hw blocks are done
writing to the buffer.

(I guess in the same way, I could use the sync-obj to prevent someone
else writing to frame n-1 until the vsync irq.. but as mentioned
earlier I still need to sort out a sane way to handle the different
sorts of irq's we get back for different sorts of panels when they are
done reading from a buffer.)

[snip]
>> +static int omap_modeset_init(struct drm_device *dev)
>> +{
>> +     const struct omap_drm_platform_data *pdata = dev->dev-
>> >platform_data;
>> +     struct omap_drm_private *priv = dev->dev_private;
>> +     struct omap_dss_device *dssdev = NULL;
>> +     int i, j;
>> +     unsigned int connected_connectors = 0;
>> +
>> +     /* create encoders for each manager */
>> +     int create_encoder(int i) {
>> +             struct omap_overlay_manager *mgr =
>> +                             omap_dss_get_overlay_manager(i);
>> +             struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
>> +
>> +             if (!encoder) {
>> +                     dev_err(dev->dev, "could not create encoder\n");
>> +                     return -ENOMEM;
>> +             }
>> +
>> +             priv->encoders[priv->num_encoders++] = encoder;
>> +
>> +             return 0;
>> +     }
>> +
>> +     /* create connectors for each display device */
>> +     int create_connector(struct omap_dss_device *dssdev) {
>> +             static struct notifier_block *notifier;
>> +             struct drm_connector *connector;
>> +
>> +             if (!dssdev->driver) {
>> +                     dev_warn(dev->dev, "%s has no driver.. skipping
> it\n",
>> +                                     dssdev->name);
>> +                     return 0;
>> +             }
>> +
>> +             if (!(dssdev->driver->get_timings ||
>> +                                     dssdev->driver->read_edid)) {
>> +                     dev_warn(dev->dev, "%s driver does not support "
>> +                             "get_timings or read_edid.. skipping it!\n",
>> +                             dssdev->name);
>> +                     return 0;
>> +             }
>> +
>> +             connector = omap_connector_init(dev,
>> +                             get_connector_type(dssdev), dssdev);
>> +
>> +             if (!connector) {
>> +                     dev_err(dev->dev, "could not create connector\n");
>> +                     return -ENOMEM;
>> +             }
>> +
>> +             /* track what is already connected.. rather than looping
>> thru
>> +              * all connectors twice later, first for connected then for
>> +              * remainder (which could be a race condition if connected
>> +              * status changes)
>> +              */
>> +             if (omap_connector_detect(connector, true) ==
>> +                             connector_status_connected) {
>> +                     connected_connectors |= (1 << priv->num_connectors);
>> +             }
>> +
>> +             priv->connectors[priv->num_connectors++] = connector;
>> +
>> +#if 0 /* enable when dss2 supports hotplug */
>> +             notifier = kzalloc(sizeof(struct notifier_block),
>> GFP_KERNEL);
>> +             notifier->notifier_call = omap_drm_notifier;
>> +             omap_dss_add_notify(dssdev, notifier);
>> +#else
>> +             notifier = NULL;
>> +#endif
>> +
>> +             for (j = 0; j < priv->num_encoders; j++) {
>> +                     struct omap_overlay_manager *mgr =
>> +                             omap_encoder_get_manager(priv->encoders[j]);
>> +                     if (mgr->device == dssdev) {
>> +                             drm_mode_connector_attach_encoder(connector,
>> +                                             priv->encoders[j]);
>> +                     }
>> +             }
>> +
>> +             return 0;
>> +     }
>> +
>> +     /* create up to max_overlays CRTCs mapping to overlays.. by default,
>> +      * connect the overlays to different managers/encoders, giving
>> priority
>> +      * to encoders connected to connectors with a detected connection
>> +      */
>> +     int create_crtc(int i) {
>> +             struct omap_overlay *ovl = omap_dss_get_overlay(i);
>> +             struct omap_overlay_manager *mgr = NULL;
>> +             struct drm_crtc *crtc;
>> +
>> +             if (ovl->manager) {
>> +                     DBG("disconnecting %s from %s", ovl->name,
>> +                                             ovl->manager->name);
>> +                     ovl->unset_manager(ovl);
>> +             }
>> +
>> +             /* find next best connector, ones with detected connection
>> first
>> +              */
>> +             while (j < priv->num_connectors && !mgr) {
>> +                     if (connected_connectors & (1 << j)) {
>> +                             struct drm_encoder * encoder =
>> +                                     omap_connector_attached_encoder(
>> +
> priv->connectors[j]);
>> +                             if (encoder) {
>> +                                     mgr =
> omap_encoder_get_manager(encoder);
>> +                             }
>> +                     }
>> +                     j++;
>> +             }
>> +
>> +             /* if we couldn't find another connected connector, lets
>> start
>> +              * looking at the unconnected connectors:
>> +              */
>> +             while (j < 2 * priv->num_connectors && !mgr) {
>> +                     int idx = j - priv->num_connectors;
>> +                     if (!(connected_connectors & (1 << idx))) {
>> +                             struct drm_encoder * encoder =
>> +                                     omap_connector_attached_encoder(
>> +
> priv->connectors[idx]);
>> +                             if (encoder) {
>> +                                     mgr =
> omap_encoder_get_manager(encoder);
>> +                             }
>> +                     }
>> +                     j++;
>> +             }
>> +
>> +             if (mgr) {
>> +                     DBG("connecting %s to %s", ovl->name, mgr->name);
>> +                     ovl->set_manager(ovl, mgr);
>> +             }
>> +
>> +             crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
>> +
>> +             if (!crtc) {
>> +                     dev_err(dev->dev, "could not create CRTC\n");
>> +                     return -ENOMEM;
>> +             }
>> +
>> +             priv->crtcs[priv->num_crtcs++] = crtc;
>> +
>> +             return 0;
>> +     }
>> +
>> +     drm_mode_config_init(dev);
>> +
>> +     if (pdata) {
>> +             /* if platform data is provided by the board file, use it to
>> +              * control which overlays, managers, and devices we own.
>> +              */
>> +             for (i = 0; i < pdata->mgr_cnt; i++) {
>> +                     create_encoder(pdata->mgr_ids[i]);
>> +             }
>> +
>> +             for (i = 0; i < pdata->dev_cnt; i++) {
>> +                     int m(struct omap_dss_device *dssdev, void *data) {
>> +                             return ! strcmp(dssdev->name, data);
>> +                     }
>> +                     struct omap_dss_device *dssdev =
>> +                             omap_dss_find_device(
>> +                                     (void *)pdata->dev_names[i], m);
>> +                     if (!dssdev) {
>> +                             dev_warn(dev->dev, "no such dssdev: %s\n",
>> +                                             pdata->dev_names[i]);
>> +                             continue;
>> +                     }
>> +                     create_connector(dssdev);
>> +             }
>> +
>> +             j = 0;
>> +             for (i = 0; i < pdata->ovl_cnt; i++) {
>> +                     create_crtc(pdata->ovl_ids[i]);
>> +             }
>> +     } else {
>> +             /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and
>> try
>> +              * to make educated guesses about everything else
>> +              */
>> +             int max_overlays = min(omap_dss_get_num_overlays(),
>> +                                     CONFIG_DRM_OMAP_NUM_CRTCS);
>> +
>> +             for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
>> +                     create_encoder(i);
>> +             }
>> +
>> +             for_each_dss_dev(dssdev) {
>> +                     create_connector(dssdev);
>> +             }
>> +
>> +             j = 0;
>> +             for (i = 0; i < max_overlays; i++) {
>> +                     create_crtc(i);
>> +             }
>> +     }
>> +
>> +     /* for now keep the mapping of CRTCs and encoders static.. */
>> +     for (i = 0; i < priv->num_encoders; i++) {
>> +             struct drm_encoder *encoder = priv->encoders[i];
>> +             struct omap_overlay_manager *mgr =
>> +                             omap_encoder_get_manager(encoder);
>> +
>> +             encoder->possible_crtcs = 0;
>> +
>> +             for (j = 0; j < priv->num_crtcs; j++) {
>> +                     struct omap_overlay *ovl =
>> +
> omap_crtc_get_overlay(priv->crtcs[j]);
>> +                     if (ovl->manager == mgr) {
>> +                             encoder->possible_crtcs |= (1 << j);
>> +                     }
>> +             }
>> +
>> +             DBG("%s: possible_crtcs=%08x", mgr->name,
>> +                                     encoder->possible_crtcs);
>> +     }
>> +
>> +     dump_video_chains();
>> +
>> +     dev->mode_config.min_width = 640;
>> +     dev->mode_config.min_height = 480;
>> +
>> +     /* note: pvr can't currently handle dst surfaces larger than 2k by
>> 2k */
>> +     dev->mode_config.max_width = 2048;
>> +     dev->mode_config.max_height = 2048;
>> +
>> +     dev->mode_config.funcs = &omap_mode_config_funcs;
>> +
>> +     return 0;
>> +}
>
> For code clean, how about moving create_encoder(), create_connector() and
> create_crtc() into omap_encoder.c, omap_connector.c and omap_crtc.c file?.
> omap_modeset_init function contains so many codes. it looks like not good.

yeah, this is the way it is mainly because we are still allowing two
cases:  (1) omapdrm uses all the DSS2 resources, and (2) the case of
using some platform-data to tell the omapdrm to not use all the
available resources in DSS.  This is mainly a transition thing, so you
could (for example) use omapdrm to control some outputs or video
pipes, and omapfb (fbdev) or omap_vout (v4l2) for others.  Have a look
at the if (pdata) { ... } else { ... } part at the later part of the
fxn.

I expect eventually, when we have overlay support in the DRM driver
(drm_plane stuff) and enough time has elapsed for folks to move away
from legacy fbdev/v4l2 interfaces, that this platform-data stuff can
be removed and this part greatly simplified.  But I guess that will be
a couple years.  For now I wanted to try and keep the ugly bits
localized in one place ;-)

[snip]

>> +}
>> +
>> +struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
>> +     DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param,
>> DRM_UNLOCKED|DRM_AUTH),
>> +     DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param,
>> DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
>> +     DRM_IOCTL_DEF_DRV(OMAP_GET_BASE, ioctl_get_base,
>> DRM_UNLOCKED|DRM_AUTH),
>> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new,
>> DRM_UNLOCKED|DRM_AUTH),
>> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep,
>> DRM_UNLOCKED|DRM_AUTH),
>> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini,
>> DRM_UNLOCKED|DRM_AUTH),
>> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info,
>> DRM_UNLOCKED|DRM_AUTH),
>> +};
>
> You also should consider security issue. :)


All the ioctls do have the DRM_AUTH bit set, so any userspace process
that uses them needs to at least be authenticated with the DRM master.
 I don't really see any way to avoid needing to support buffer
allocating in an authenticated client process.

So I think this is sufficient, but always possible that I missed something..

[snip]
>> +
>> +static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
>> +{
>> +     struct drm_device *dev = fb->dev;
>> +     struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
>> +
>> +     DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
>> +
>> +     drm_framebuffer_cleanup(fb);
>> +
>> +     if (omap_fb->vaddr) {
>> +             iounmap(omap_fb->vaddr);
>> +     }
>
> You always use reserved memory for drm framebuffer.? If not so, for
> instance, any physical memory was allocated by dma api such as dma_alloc_*
> then you should call dma_free_*.

currently it is always reserved memory.. I've not moved to CMA stuff yet.

So currently we have no vaddr in the GEM parts, and only place where
vaddr is created/mapped is in omap_framebuffer part.

I think this will have to change though..  partly because
dma_alloc_coherent() also allocates a vaddr.  Partly because there
will be some cases where kernel side plugin code might need access to
the buffer.  (Not so much for pixel buffers, but for command-stream
type buffers.)

Possibly I should pull this into the GEM code w/ a
omap_gem_{get,put}_vaddr() which could do the right thing depending on
where the memory for the buffer came from.  Whatever I do, I don't
want to force to have a kernel virtual mapping for all pixel buffers
when it can otherwise be avoided, because I think in a generation or
two kernel address space will start being a limited resource.


>> +
>> +     if (omap_gem_put_paddr(omap_fb->bo)) {
>> +             dev_err(dev->dev, "could not unmap!\n");
>> +     }
>> +
>> +     if (omap_fb->bo) {
>> +             drm_gem_object_unreference_unlocked(omap_fb->bo);
>> +     }
>> +
>> +     kfree(omap_fb);
>> +}
>> +
>> +static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
>> +             struct drm_file *file_priv, unsigned flags, unsigned color,
>> +             struct drm_clip_rect *clips, unsigned num_clips)
>> +{
>> +     int i;
>> +
>> +     for (i = 0; i < num_clips; i++) {
>> +             omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
>> +                                     clips[i].x2 - clips[i].x1,
>> +                                     clips[i].y2 - clips[i].y1);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
>> +     .create_handle = omap_framebuffer_create_handle,
>> +     .destroy = omap_framebuffer_destroy,
>> +     .dirty = omap_framebuffer_dirty,
>> +};
>> +
>> +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
>> +             void **vaddr, unsigned long *paddr, int *screen_width)
>> +{
>> +     struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
>> +     int bpp = fb->depth / 8;
>> +     unsigned long offset;
>> +
>> +     offset = (x * bpp) + (y * fb->pitch);
>> +
>> +     if (vaddr) {
>> +             if (!omap_fb->vaddr) {
>> +                     omap_fb->vaddr = ioremap_wc(omap_fb->paddr, omap_fb-
>> >size);
>> +             }
>> +             *vaddr = omap_fb->vaddr + offset;
>> +     }
>
> Did you use ioremap_wc() to map physical memory(reserved memory) that kernel
> doesn't aware of to kernel space.? if not so, the memory region mapped to
> kernel space as 1:1,  this way would be faced with duplicated cache
> attribute issue mentioned by Russel King before. 1:1 mapping region is
> mapped to kernel space with cachable attribute.

Today the carveout memory does not have a kernel virtual mapping.  So
we are ok.  And I think this should still be the case w/ CMA.

[snip]
>> +
>> +static int omap_fbdev_create(struct drm_fb_helper *helper,
>> +             struct drm_fb_helper_surface_size *sizes)
>> +{
>> +     struct omap_fbdev *fbdev = to_omap_fbdev(helper);
>> +     struct drm_device *dev = helper->dev;
>> +     struct drm_framebuffer *fb;
>> +     struct fb_info *fbi;
>> +     struct drm_mode_fb_cmd mode_cmd = {0};
>> +     unsigned long paddr;
>> +     void __iomem *vaddr;
>> +     int size, screen_width;
>> +     int ret;
>> +
>> +     /* only doing ARGB32 since this is what is needed to alpha-blend
>> +      * with video overlays:
>> +      */
>> +     sizes->surface_bpp = 32;
>> +     sizes->surface_depth = 32;
>> +
>> +     DBG("create fbdev: %dx%d@%d", sizes->surface_width,
>> +                     sizes->surface_height, sizes->surface_bpp);
>> +
>> +     mode_cmd.width = sizes->surface_width;
>> +     mode_cmd.height = sizes->surface_height;
>> +
>> +     mode_cmd.bpp = sizes->surface_bpp;
>> +     mode_cmd.depth = sizes->surface_depth;
>> +
>> +     mutex_lock(&dev->struct_mutex);
>> +
>> +     fbi = framebuffer_alloc(0, dev->dev);
>> +     if (!fbi) {
>> +             dev_err(dev->dev, "failed to allocate fb info\n");
>> +             ret = -ENOMEM;
>> +             goto fail;
>> +     }
>> +
>> +     DBG("fbi=%p, dev=%p", fbi, dev);
>> +
>> +     fbdev->fb = omap_framebuffer_init(dev, &mode_cmd, NULL);
>> +     if (!fbdev->fb) {
>> +             dev_err(dev->dev, "failed to allocate fb\n");
>> +             ret = -ENOMEM;
>> +             goto fail;
>
> In case of fail, framebuffer_release should be called.

oh, yeah.. // TODO cleanup ;-)

>
>> +     }
>> +
>> +     fb = fbdev->fb;
>> +     helper->fb = fb;
>> +     helper->fbdev = fbi;
>> +
>> +     fbi->par = helper;
>> +     fbi->flags = FBINFO_DEFAULT;
>> +     fbi->fbops = &omap_fb_ops;
>> +
>> +     strcpy(fbi->fix.id, MODULE_NAME);
>> +
>> +     ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
>> +     if (ret) {
>> +             ret = -ENOMEM;
>> +             goto fail;
>
> Also, omap_framebuffer_destroy() should be called to release resource.
>
>> +     }
>> +
>> +     drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
>> +     drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
>> +
>> +     size = omap_framebuffer_get_buffer(fb, 0, 0,
>> +                     &vaddr, &paddr, &screen_width);
>> +
>> +     dev->mode_config.fb_base = paddr;
>> +
>> +     fbi->screen_base = vaddr;
>> +     fbi->screen_size = size;
>> +     fbi->fix.smem_start = paddr;
>> +     fbi->fix.smem_len = size;
>> +
>> +     DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
>> +     DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
>> +
>> +     mutex_unlock(&dev->struct_mutex);
>> +
>> +     return 0;
>> +
>> +fail:
>> +     mutex_unlock(&dev->struct_mutex);
>> +     // TODO cleanup?
>> +     return ret;
>> +}
>> +
>> +static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
>> +             u16 red, u16 green, u16 blue, int regno)
>> +{
>> +     DBG("fbdev: set gamma");
>> +}
>> +
>> +static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
>> +             u16 *red, u16 *green, u16 *blue, int regno)
>> +{
>> +     DBG("fbdev: get gamma");
>> +}
>> +
>> +static int omap_fbdev_probe(struct drm_fb_helper *helper,
>> +             struct drm_fb_helper_surface_size *sizes)
>> +{
>> +     int new_fb = 0;
>> +     int ret;
>> +
>> +     if (!helper->fb) {
>> +             ret = omap_fbdev_create(helper, sizes);
>> +             if (ret)
>> +                     return ret;
>> +             new_fb = 1;
>> +     }
>> +     return new_fb;
>> +}
>> +
>
> This strange code must be because of mainline code below.
> fb_probe callback of drm_fb_helper_single_fb_probe function needs return
> values of three types. :(  I think this mainline code should be modified
> properly.
>

yeah

[snip]
>> +
>> +struct omap_gem_object {
>> +     struct drm_gem_object base;
>> +
>> +     uint32_t flags;
>> +
>> +     /**
>> +      * if buffer is physically contiguous or remapped in TILER, the
>> +      * OMAP_BO_DMA flag is set and the paddr is valid.
>> +      *
>> +      * Note that OMAP_BO_SCANOUT is a hint from userspace that DMA
>> capable
>> +      * buffer is requested, but doesn't mean that it is.  Use the
>> +      * OMAP_BO_DMA flag to determine if the buffer has a DMA capable
>> +      * physical address.
>> +      */
>> +     unsigned long paddr;
>> +
>> +     /**
>> +      * Array of backing pages, if allocated.  Note that pages are never
>> +      * allocated for buffers originally allocated from contiguous
>> memory
>> +      * (which will have obj->filp==NULL)
>> +      */
>> +     struct page **pages;
>
> Is this array of backing pages used for physically non-continuous memory?.
> it appears that sgx core with iommu is considered.


yes (and will be similar for 2d hw).  But even in case of scanout
buffers, we can remap them into TILER (which you can think of as a
sort of IOMMU on OMAP4+).  The code for this isn't in place yet, but
the intention is to

a) if userspace knows it will be a scannout buffer, allocate
physically contiguous to begin with.. if that fails, fall back to
dis-contiguous.
b) if we need to scan it out, but it isn't contiguous, fall back to
remapping into TILER.

That is a bit of over-simplification.. for tiled buffers, and buffers
for video encode/decode, these would always be allocated as pages and
remapped into TILER.

[sync]
>> +
>> +static inline void sync_op(struct drm_gem_object *obj,
>> +             enum omap_gem_op op, bool start)
>> +{
>> +     struct omap_gem_object *omap_obj = to_omap_bo(obj);
>> +
>> +     spin_lock(&sync_lock);
>> +
>> +     if (!omap_obj->sync) {
>> +             omap_obj->sync = kzalloc(sizeof(*omap_obj->sync),
>> GFP_KERNEL);
>> +     }
>
> If sync_op function is called at interrupt context, use GFP_ATOMIC instead
> of GFP_KERNEL. from what you use spin_lock/unlock here, it appears that this
> function would be called at interrupt context.

yeah, that is an oversight.. I updated omap_gem_set_sync_object() but
missed this one


[snip]
>> +
>> +/* common constructor body */
>> +static struct drm_gem_object * omap_gem_new_impl(struct drm_device *dev,
>> +             size_t size, uint32_t flags, unsigned long paddr, struct
>> page **pages,
>> +             struct omap_gem_vm_ops *ops)
>> +{
>
> so many arguments are used at this function. I encourage you to use maximum
> four arguments for ARM system.
>

I know the reasoning for this.. although the alternative is to stick
extra param's in a struct on the stack, which roughly amounts to what
the compiler is doing for you anyways for args above the 1st four (ie.
pushing them to the stack).

anyways it is a static fxn so compiler has some flexibility to inline
or optimize however it sees fit..

[snip]
>> diff --git a/include/linux/omap_drm.h b/include/linux/omap_drm.h
>> new file mode 100644
>> index 0000000..81af200
>> --- /dev/null
>> +++ b/include/linux/omap_drm.h
>> @@ -0,0 +1,191 @@
>> +/*
>> + * linux/include/linux/omap_drm.h
>> + *
>> + * Copyright (C) 2011 Texas Instruments
>> + * Author: Rob Clark <rob@ti.com>
>> + *
>> + * 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.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
>> for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __OMAP_DRM_H__
>> +#define __OMAP_DRM_H__
>> +
>> +#include <linux/module.h>
>> +#include <drm/drmP.h>
>> +
>> +#define OMAP_PARAM_CHIPSET_ID        1       /* ie. 0x3430, 0x4430, etc
> */
>> +
>> +struct drm_omap_param {
>> +     uint64_t param;                 /* in */
>> +     uint64_t value;                 /* in (set_param), out (get_param)
>> */
>> +};
>> +
>> +struct drm_omap_get_base {
>> +     char plugin_name[64];           /* in */
>> +     uint32_t ioctl_base;            /* out */
>> +};
>> +
>> +#define OMAP_BO_SCANOUT              0x00000001      /* scanout capable
> (phys
>> contiguous) */
>> +#define OMAP_BO_CACHE_MASK   0x00000006      /* cache type mask, see
>> cache modes */
>> +#define OMAP_BO_TILED_MASK   0x00000f00      /* tiled mapping mask, see
>> tiled modes */
>> +
>> +/* cache modes */
>> +#define OMAP_BO_CACHED               0x00000000      /* default */
>> +#define OMAP_BO_WC           0x00000002      /* write-combine */
>> +#define OMAP_BO_UNCACHED     0x00000004      /* strongly-ordered
> (uncached) */
>> +
>> +/* tiled modes */
>> +#define OMAP_BO_TILED_8              0x00000100
>> +#define OMAP_BO_TILED_16     0x00000200
>> +#define OMAP_BO_TILED_32     0x00000300
>> +
>> +struct drm_omap_gem_new {
>> +     union {                         /* in */
>> +             uint32_t bytes;         /* (for non-tiled formats) */
>> +             struct {
>> +                     uint16_t width;
>> +                     uint16_t height;
>> +             } tiled;                /* (for tiled formats) */
>> +     } size;
>> +     uint32_t flags;                 /* in */
>> +     uint64_t offset;                /* out */
>> +     uint32_t handle;                /* out */
>> +};
>> +
>> +/* mask of operations: */
>> +enum omap_gem_op {
>> +     OMAP_GEM_READ = 0x01,
>> +     OMAP_GEM_WRITE = 0x02,
>> +};
>> +
>> +struct drm_omap_gem_cpu_prep {
>> +     uint32_t handle;                /* buffer handle (in) */
>> +     uint32_t op;                    /* mask of omap_gem_op (in) */
>> +};
>> +
>> +struct drm_omap_gem_cpu_fini {
>> +     uint32_t handle;                /* buffer handle (in) */
>> +     uint32_t op;                    /* mask of omap_gem_op (in) */
>> +     /* TODO maybe here we pass down info about what regions are touched
>> +      * by sw so we can be clever about cache ops?  For now a
> placeholder,
>> +      * set to zero and we just do full buffer flush..
>> +      */
>> +     uint32_t nregions;
>> +};
>> +
>> +struct drm_omap_gem_info {
>> +     uint32_t handle;                /* buffer handle (in) */
>> +     uint32_t pad;
>> +     uint64_t offset;                /* out */
>> +};
>> +
>> +#define DRM_OMAP_GET_PARAM           0x00
>> +#define DRM_OMAP_SET_PARAM           0x01
>> +#define DRM_OMAP_GET_BASE            0x02
>> +#define DRM_OMAP_GEM_NEW             0x03
>> +#define DRM_OMAP_GEM_CPU_PREP        0x04
>> +#define DRM_OMAP_GEM_CPU_FINI        0x05
>> +#define DRM_OMAP_GEM_INFO    0x06
>> +#define DRM_OMAP_NUM_IOCTLS          0x07
>> +
>> +#define DRM_IOCTL_OMAP_GET_PARAM     DRM_IOWR(DRM_COMMAND_BASE +
>> DRM_OMAP_GET_PARAM, struct drm_omap_param)
>> +#define DRM_IOCTL_OMAP_SET_PARAM     DRM_IOW (DRM_COMMAND_BASE +
>> DRM_OMAP_SET_PARAM, struct drm_omap_param)
>> +#define DRM_IOCTL_OMAP_GET_BASE              DRM_IOWR(DRM_COMMAND_BASE +
>> DRM_OMAP_GET_BASE, struct drm_omap_get_base)
>> +#define DRM_IOCTL_OMAP_GEM_NEW               DRM_IOWR(DRM_COMMAND_BASE +
>> DRM_OMAP_GEM_NEW, struct drm_omap_gem_new)
>> +#define DRM_IOCTL_OMAP_GEM_CPU_PREP  DRM_IOW (DRM_COMMAND_BASE +
>> DRM_OMAP_GEM_CPU_PREP, struct drm_omap_gem_cpu_prep)
>> +#define DRM_IOCTL_OMAP_GEM_CPU_FINI  DRM_IOW (DRM_COMMAND_BASE +
>> DRM_OMAP_GEM_CPU_FINI, struct drm_omap_gem_cpu_fini)
>> +#define DRM_IOCTL_OMAP_GEM_INFO              DRM_IOWR(DRM_COMMAND_BASE +
>> DRM_OMAP_GEM_INFO, struct drm_omap_gem_info)
>> +
>> +/* interface that plug-in drivers can implement */
>> +struct omap_drm_plugin {
>> +     const char *name;
>> +
>> +     /* drm functions */
>> +     int (*open)(struct drm_device *dev, struct drm_file *file);
>> +     int (*load)(struct drm_device *dev, unsigned long flags);
>> +     int (*unload)(struct drm_device *dev);
>> +     int (*release)(struct drm_device *dev, struct drm_file *file);
>> +
>> +     struct drm_ioctl_desc *ioctls;
>> +     int num_ioctls;
>> +     int ioctl_base;
>> +
>> +     struct list_head list;  /* note, this means struct can't be const..
>> */
>> +};
>> +
>> +int omap_drm_register_plugin(struct omap_drm_plugin *plugin);
>> +int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin);
>> +
>> +int omap_drm_register_mapper(void);
>> +void omap_drm_unregister_mapper(int id);
>> +
>> +/* external mappers should get paddr or pages when it needs the pages
>> pinned
>> + * and put when done..
>> + */
>> +int omap_gem_get_paddr(struct drm_gem_object *obj,
>> +             unsigned long *paddr, bool remap);
>> +int omap_gem_put_paddr(struct drm_gem_object *obj);
>> +int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages);
>> +int omap_gem_put_pages(struct drm_gem_object *obj);
>> +
>> +uint32_t omap_gem_flags(struct drm_gem_object *obj);
>> +void * omap_gem_priv(struct drm_gem_object *obj, int mapper_id);
>> +void omap_gem_set_priv(struct drm_gem_object *obj, int mapper_id, void
>> *priv);
>> +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj);
>> +
>> +/* for external plugin buffers wrapped as GEM object (via.
>> omap_gem_new_ext())
>> + * a vm_ops struct can be provided to get callback notification of
>> various
>> + * events..
>> + */
>> +struct omap_gem_vm_ops {
>> +     void (*open)(struct vm_area_struct * area);
>> +     void (*close)(struct vm_area_struct * area);
>> +
>> +     /* note: mmap is not expected to do anything.. it is just to allow
>> buffer
>> +      * allocate to update it's own internal state
>> +      */
>> +     void (*mmap)(struct file *, struct vm_area_struct *);
>> +};
>> +
>> +struct drm_gem_object * omap_gem_new_ext(struct drm_device *dev,
>> +             size_t size, uint32_t flags, unsigned long paddr, struct
>> page **pages,
>> +             struct omap_gem_vm_ops *ops);
>> +struct drm_gem_object * omap_gem_new(struct drm_device *dev,
>> +             size_t size, uint32_t flags);
>> +
>> +void omap_gem_op_update(void);
>> +void omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op);
>> +void omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op);
>> +int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op);
>> +int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
>> +             void (*fxn)(void *arg), void *arg);
>> +void omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj);
>> +
>> +
>> +/* optional platform data to configure the default configuration of which
>> + * pipes/overlays/CRTCs are used.. if this is not provided, then instead
>> the
>> + * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected
>> to
>> + * one manager, with priority given to managers that are connected to
>> + * detected devices.  This should be a good default behavior for most
>> cases,
>> + * but yet there still might be times when you wish to do something
>> different.
>> + */
>> +struct omap_drm_platform_data {
>> +     int ovl_cnt;
>> +     const int *ovl_ids;
>> +     int mgr_cnt;
>> +     const int *mgr_ids;
>> +     int dev_cnt;
>> +     const char **dev_names;
>> +};
>> +
>> +#endif /* __OMAP_DRM_H__ */
>> --
>
> Remove device specific functions from linux/include/linux/omap_drm.h and
> move them to driver folder. I was told that include/linux/*.h file should
> contain only interfaces for user process from Dave.


fwiw, the functions in this header are already only ones used by other
kernel drivers.. everything else internal to omapdrm is already in
omap_drv.h.  I'll remove these for now (see discussion about plugin
API), although when it is re-introduced they need to be in a header
accessible from other drivers.  Although should maybe still be split
from what is needed by userspace?  (Something like omapdrm_plugin.h?)


BR,
-R

>> 1.7.5.4
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Hello, Rob.

> -----Original Message-----
> From: robdclark@gmail.com [mailto:robdclark@gmail.com] On Behalf Of Rob
> Clark
> Sent: Tuesday, September 06, 2011 1:05 AM
> To: Inki Dae
> Cc: dri-devel@lists.freedesktop.org; linaro-dev@lists.linaro.org;
> Valkeinen, Tomi
> Subject: Re: [PATCH] RFC: omapdrm DRM/KMS driver for TI OMAP platforms
> 
> On Mon, Sep 5, 2011 at 4:58 AM, Inki Dae <inki.dae@samsung.com> wrote:
> > Hello, Rob.
> > I didn't look into any comments from other develops yet , so my comments
> > could be duplicated with other ones. below is my opinions and I
> commented
> > your codes also.
> >
> > We can discuss crtc, encoder and connector. in x86 world, my
> understanding,
> > each one means the following.
> 
> just fwiw, some of current implementation in the KMS code is result of
> how the DSS2 API works..  ie. a dssdev (panel driver) is really part
> encoder and part connector.
> 
> But on other hand, there are customers who write their own panel
> drivers in DSS2 so I'm not sure that we want to completely change
> this.  And the encoder/connector distinction gets a bit more blurry w/
> smart-panel type displays.
> 
> [snip]
> >> +static int omap_connector_mode_valid(struct drm_connector *connector,
> >> +                              struct drm_display_mode *mode)
> >> +{
> >> +     struct omap_connector *omap_connector =
> >> to_omap_connector(connector);
> >> +     struct omap_dss_device *dssdev = omap_connector->dssdev;
> >> +     struct omap_dss_driver *dssdrv = dssdev->driver;
> >> +     struct omap_video_timings timings = {0};
> >> +     struct drm_device *dev = connector->dev;
> >> +     struct drm_display_mode *new_mode;
> >> +     int ret = MODE_BAD;
> >> +
> >> +     copy_timings_drm_to_omap(&timings, mode);
> >> +     mode->vrefresh = drm_mode_vrefresh(mode);
> >> +
> >> +     if (!dssdrv->check_timings(dssdev, &timings)) {
> >> +             /* check if vrefresh is still valid */
> >> +             new_mode = drm_mode_duplicate(dev, mode);
> >> +             new_mode->clock = timings.pixel_clock;
> >> +             new_mode->vrefresh = 0;
> >> +             if (mode->vrefresh == drm_mode_vrefresh(new_mode))
> >> +                     ret = MODE_OK;
> >> +             drm_mode_destroy(dev, new_mode);
> >> +     }
> >> +
> >
> > is there any reason that you call drm_mode_duplicate() to get a clone of
> a
> > mode? I just wonder it.
> 
> yes, because the new_mode is modified and I didn't want this fxn to
> have unintended side-effects.
> 
> this works in a slightly funny way, because dssdev->check_timings() is
> actually a dssdev->check_and_modify_timings(), modifying the pixel
> clock to the closest thing that is supported.  We need to compare the
> resulting vrefresh to see if it is something matching..  otherwise we
> ask for some resolution @ 60Hz and maybe get 30Hz instead.
> 
> [snip]
> >> +/* initialize connector */
> >> +struct drm_connector * omap_connector_init(struct drm_device *dev,
> >> +             int connector_type, struct omap_dss_device *dssdev)
> >> +{
> >> +     struct drm_connector *connector = NULL;
> >
> > It appears that this connector is initialized with NULL. This is just
> minor
> > issue. :)
> 
> it is so it isn't uninitialized if kzalloc fails and we 'goto fail'..
> I guess in this particular case there is only a single fail point,
> although it is a coding pattern that I follow elsewhere where there
> are multiple fail points.  And at least this way if I added some other
> step later where it might fail after the kzalloc(), I won't
> accidentally miss the appropriate cleanup. I mostly prefer to have a
> single cleanup path when possible so I don't confuse myself later
> 
> [snip]
> >> +static void page_flip_cb(void *arg)
> >> +{
> >> +     struct drm_crtc *crtc = arg;
> >> +     struct drm_device *dev = crtc->dev;
> >> +     struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> >> +     struct drm_pending_vblank_event *event = omap_crtc->event;
> >> +     struct timeval now;
> >> +     unsigned long flags;
> >> +
> >> +     WARN_ON(!event);
> >> +
> >> +     omap_crtc->event = NULL;
> >> +
> >> +     update_scanout(crtc);
> >> +     commit(crtc);
> >> +
> >> +     /* wakeup userspace */
> >> +     // TODO: this should happen *after* flip.. somehow..
> >> +     if (event) {
> >> +             spin_lock_irqsave(&dev->event_lock, flags);
> >> +             event->event.sequence =
> >> +                             drm_vblank_count_and_time(dev,
> > omap_crtc->id,
> >> &now);
> >> +             event->event.tv_sec = now.tv_sec;
> >> +             event->event.tv_usec = now.tv_usec;
> >> +             list_add_tail(&event->base.link,
> >> +                             &event->base.file_priv->event_list);
> >> +            
wake_up_interruptible(&event->base.file_priv->event_wait);
> >> +             spin_unlock_irqrestore(&dev->event_lock, flags);
> >> +     }
> >
> > How about moving codes above into interrupt handler for vblank?
> >  maybe there
> 
> I should mention a couple of things:
> 
> 1) drm vblank stuff isn't really used currently.. it is actually DSS2
> layer that is registering for the display related interrupt(s).  I'm
> not really sure how to handle this best.  I suppose the DRM driver
> could *also* register for these interrupts, but that seems a bit odd..
> 

DRM Framework supports only one interrupt handler. this issue should be
resolved. and currently I made our driver to use its own request_irq, not
DRM Framework side. we only sets drm_device->irq_enabled to 1 and interrupt
handler is registered at display controller or hdmi driver respectively. but
I'm not sure that this way is best so I will look over more. Anyway current
drm framework should be fixed to be considered with multiple irq.

> Also, I guess it is also worth mentioning.. when it comes to vblank,
> there are two fundamentally different sorts of displays we deal with.
> Something like DVI/HDMI/etc which need constant refreshing.  In these
> cases we constantly scan-out the buffer until the next page
> flip+vsync.  And smart panels, where they get scanned out once and
> then DSS is no longer reading the scanout buffer until we manually
> trigger an update.
> 

Is the Smart panel CPU interface based lcd panel that has its own
framebuffer internally.?

> 2) updating things like scanout address for framebuffers, these are
> actually double-buffered registers in the DSS block.  So the update of
> actual scanout address is automatically synchronized by the hardware
> w/ the vblank.  The one thing I need to do still is defer the event
> back to userspace until after the flip so it doesn't start rendering
> onto the buffer that is still being scanned out.  I'm not quite sure
> the best way to do this yet.  DSS2 driver has an API that I can
> register for IRQ callbacks, although the knowledge about the right
> callback to register (VSYNC, VSYNC2, FRAMEDONE, EVSYNC_ODD,
> EVSYNC_EVEN, etc) is in the dssdev/connector.  I could use
> omap_connector_sync() to block until vsync/framedone but I'd prefer a
> non-blocking mechanism.
> 
> > would be some reason that spin_lock_* are used here.
> 
> dev->event_lock seems to be used to protect modifying event_list
> elsewhere..
> 
> >> +}
> >> +
> >> +static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
> >> +              struct drm_framebuffer *fb,
> >> +              struct drm_pending_vblank_event *event)
> >> +{
> >> +     struct drm_device *dev = crtc->dev;
> >> +     struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
> >> +
> >> +     DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
> >> +
> >> +     if (omap_crtc->event) {
> >> +             dev_err(dev->dev, "already a pending flip\n");
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     crtc->fb = fb;
> >> +     omap_crtc->event = event;
> >> +
> >> +     omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ,
> >> +                     page_flip_cb, crtc);
> >> +
> >> +     return 0;
> >> +}
> >> +
> >
> > Where are drm_vblank_get() and drm_vblank_put() called to enable and
> disable
> > vsync interrupt? and does the this page flip need user-requested
> drm_control
> > ioctl to install/uninstall a handler?. I think the page_flip feature
> should
> > work it fine itself.
> >
> > like this:
> > at omap_crtc_page_flip_locked()
> > drm_vblank_get();   <- enable vsync interrupt.
> > update famebuffer. <- update overlay and set it to hw.
> >
> > at interrupt handler
> > handle page flip event.
> > wake_up_interruptible() <- notify  user of page flip event.
> > drm_vblank_put();  <- disable vsync interrupt.
> >
> > I think that page flip callback would be called after vsync interrupt
> > occurred. for this, you can refer to set_mode function of libdrm
library.
> a
> > event has user's apage flip handler and when user received any event
> from
> > kernel side, this handler would be called to update framebuffer
> repeatedly.
> 
> The main reason for the page-flip cb is actually not vsync
> synchronization, but rather synchronizing with other hw blocks that
> might be rendering to the buffer..  the page flip can be submitted
> from userspace while some other hw block (3d, 2d, etc) is still
> DMA'ing to the buffer.  The sync-obj is intended to give a way to
> defer the (in this case) page flip until other hw blocks are done
> writing to the buffer.

I thought page-flip is used to change buffer register value of display
controller into another one like the Pan Display feature of linux
framebuffer. actually page flip interface of libdrm library,
page_flip_handler, is called with new framebuffer id that has its own
buffer. and then the controller using current crtc would be set to buffer
address of new framebuffer. and I understood that for other blocks such as
2d/3d accelerators, rendering synchronization you already mentioned above,
is not depend on page flip action. It?s a sequence with my understanding
below.

1. allocate a new buffer through drm interface such as DUMB_* or SPECIFIC
IOCTLs.
2. render something on this buffer through DRM interfaces that handle 2D/3D
Accelerators and also it would use their own interfaces for synchronization.
3. allocate a new framebuffer and attach the buffer to this framebuffer.
4. call page flip to change current framebuffer to the framebuffer above at
vblank moment. at this time, buffer address of the framebuffer would be set
to a controller.

finally, we can see some image rendered on diplay. thus, I think page flip
and rendering synchronization have no any relationship. if I missed any
points, please give me your comments.

> 
> (I guess in the same way, I could use the sync-obj to prevent someone
> else writing to frame n-1 until the vsync irq.. but as mentioned
> earlier I still need to sort out a sane way to handle the different
> sorts of irq's we get back for different sorts of panels when they are
> done reading from a buffer.)
> 
> [snip]
> >> +static int omap_modeset_init(struct drm_device *dev)
> >> +{
> >> +     const struct omap_drm_platform_data *pdata = dev->dev-
> >> >platform_data;
> >> +     struct omap_drm_private *priv = dev->dev_private;
> >> +     struct omap_dss_device *dssdev = NULL;
> >> +     int i, j;
> >> +     unsigned int connected_connectors = 0;
> >> +
> >> +     /* create encoders for each manager */
> >> +     int create_encoder(int i) {
> >> +             struct omap_overlay_manager *mgr =
> >> +                             omap_dss_get_overlay_manager(i);
> >> +             struct drm_encoder *encoder = omap_encoder_init(dev,
mgr);
> >> +
> >> +             if (!encoder) {
> >> +                     dev_err(dev->dev, "could not create encoder\n");
> >> +                     return -ENOMEM;
> >> +             }
> >> +
> >> +             priv->encoders[priv->num_encoders++] = encoder;
> >> +
> >> +             return 0;
> >> +     }
> >> +
> >> +     /* create connectors for each display device */
> >> +     int create_connector(struct omap_dss_device *dssdev) {
> >> +             static struct notifier_block *notifier;
> >> +             struct drm_connector *connector;
> >> +
> >> +             if (!dssdev->driver) {
> >> +                     dev_warn(dev->dev, "%s has no driver.. skipping
> > it\n",
> >> +                                     dssdev->name);
> >> +                     return 0;
> >> +             }
> >> +
> >> +             if (!(dssdev->driver->get_timings ||
> >> +                                     dssdev->driver->read_edid)) {
> >> +                     dev_warn(dev->dev, "%s driver does not support "
> >> +                             "get_timings or read_edid.. skipping
it!\n",
> >> +                             dssdev->name);
> >> +                     return 0;
> >> +             }
> >> +
> >> +             connector = omap_connector_init(dev,
> >> +                             get_connector_type(dssdev), dssdev);
> >> +
> >> +             if (!connector) {
> >> +                     dev_err(dev->dev, "could not create
connector\n");
> >> +                     return -ENOMEM;
> >> +             }
> >> +
> >> +             /* track what is already connected.. rather than looping
> >> thru
> >> +              * all connectors twice later, first for connected then
> for
> >> +              * remainder (which could be a race condition if
connected
> >> +              * status changes)
> >> +              */
> >> +             if (omap_connector_detect(connector, true) ==
> >> +                             connector_status_connected) {
> >> +                     connected_connectors |= (1 <<
priv->num_connectors);
> >> +             }
> >> +
> >> +             priv->connectors[priv->num_connectors++] = connector;
> >> +
> >> +#if 0 /* enable when dss2 supports hotplug */
> >> +             notifier = kzalloc(sizeof(struct notifier_block),
> >> GFP_KERNEL);
> >> +             notifier->notifier_call = omap_drm_notifier;
> >> +             omap_dss_add_notify(dssdev, notifier);
> >> +#else
> >> +             notifier = NULL;
> >> +#endif
> >> +
> >> +             for (j = 0; j < priv->num_encoders; j++) {
> >> +                     struct omap_overlay_manager *mgr =
> >> +                            
omap_encoder_get_manager(priv->encoders[j]);
> >> +                     if (mgr->device == dssdev) {
> >> +                            
drm_mode_connector_attach_encoder(connector,
> >> +                                             priv->encoders[j]);
> >> +                     }
> >> +             }
> >> +
> >> +             return 0;
> >> +     }
> >> +
> >> +     /* create up to max_overlays CRTCs mapping to overlays.. by
> default,
> >> +      * connect the overlays to different managers/encoders, giving
> >> priority
> >> +      * to encoders connected to connectors with a detected connection
> >> +      */
> >> +     int create_crtc(int i) {
> >> +             struct omap_overlay *ovl = omap_dss_get_overlay(i);
> >> +             struct omap_overlay_manager *mgr = NULL;
> >> +             struct drm_crtc *crtc;
> >> +
> >> +             if (ovl->manager) {
> >> +                     DBG("disconnecting %s from %s", ovl->name,
> >> +                                             ovl->manager->name);
> >> +                     ovl->unset_manager(ovl);
> >> +             }
> >> +
> >> +             /* find next best connector, ones with detected
connection
> >> first
> >> +              */
> >> +             while (j < priv->num_connectors && !mgr) {
> >> +                     if (connected_connectors & (1 << j)) {
> >> +                             struct drm_encoder * encoder =
> >> +                                     omap_connector_attached_encoder(
> >> +
> > priv->connectors[j]);
> >> +                             if (encoder) {
> >> +                                     mgr =
> > omap_encoder_get_manager(encoder);
> >> +                             }
> >> +                     }
> >> +                     j++;
> >> +             }
> >> +
> >> +             /* if we couldn't find another connected connector, lets
> >> start
> >> +              * looking at the unconnected connectors:
> >> +              */
> >> +             while (j < 2 * priv->num_connectors && !mgr) {
> >> +                     int idx = j - priv->num_connectors;
> >> +                     if (!(connected_connectors & (1 << idx))) {
> >> +                             struct drm_encoder * encoder =
> >> +                                     omap_connector_attached_encoder(
> >> +
> > priv->connectors[idx]);
> >> +                             if (encoder) {
> >> +                                     mgr =
> > omap_encoder_get_manager(encoder);
> >> +                             }
> >> +                     }
> >> +                     j++;
> >> +             }
> >> +
> >> +             if (mgr) {
> >> +                     DBG("connecting %s to %s", ovl->name, mgr->name);
> >> +                     ovl->set_manager(ovl, mgr);
> >> +             }
> >> +
> >> +             crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
> >> +
> >> +             if (!crtc) {
> >> +                     dev_err(dev->dev, "could not create CRTC\n");
> >> +                     return -ENOMEM;
> >> +             }
> >> +
> >> +             priv->crtcs[priv->num_crtcs++] = crtc;
> >> +
> >> +             return 0;
> >> +     }
> >> +
> >> +     drm_mode_config_init(dev);
> >> +
> >> +     if (pdata) {
> >> +             /* if platform data is provided by the board file, use it
> to
> >> +              * control which overlays, managers, and devices we own.
> >> +              */
> >> +             for (i = 0; i < pdata->mgr_cnt; i++) {
> >> +                     create_encoder(pdata->mgr_ids[i]);
> >> +             }
> >> +
> >> +             for (i = 0; i < pdata->dev_cnt; i++) {
> >> +                     int m(struct omap_dss_device *dssdev, void *data)
{
> >> +                             return ! strcmp(dssdev->name, data);
> >> +                     }
> >> +                     struct omap_dss_device *dssdev =
> >> +                             omap_dss_find_device(
> >> +                                     (void *)pdata->dev_names[i], m);
> >> +                     if (!dssdev) {
> >> +                             dev_warn(dev->dev, "no such dssdev:
%s\n",
> >> +                                             pdata->dev_names[i]);
> >> +                             continue;
> >> +                     }
> >> +                     create_connector(dssdev);
> >> +             }
> >> +
> >> +             j = 0;
> >> +             for (i = 0; i < pdata->ovl_cnt; i++) {
> >> +                     create_crtc(pdata->ovl_ids[i]);
> >> +             }
> >> +     } else {
> >> +             /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS
and
> >> try
> >> +              * to make educated guesses about everything else
> >> +              */
> >> +             int max_overlays = min(omap_dss_get_num_overlays(),
> >> +                                     CONFIG_DRM_OMAP_NUM_CRTCS);
> >> +
> >> +             for (i = 0; i < omap_dss_get_num_overlay_managers(); i++)
> {
> >> +                     create_encoder(i);
> >> +             }
> >> +
> >> +             for_each_dss_dev(dssdev) {
> >> +                     create_connector(dssdev);
> >> +             }
> >> +
> >> +             j = 0;
> >> +             for (i = 0; i < max_overlays; i++) {
> >> +                     create_crtc(i);
> >> +             }
> >> +     }
> >> +
> >> +     /* for now keep the mapping of CRTCs and encoders static.. */
> >> +     for (i = 0; i < priv->num_encoders; i++) {
> >> +             struct drm_encoder *encoder = priv->encoders[i];
> >> +             struct omap_overlay_manager *mgr =
> >> +                             omap_encoder_get_manager(encoder);
> >> +
> >> +             encoder->possible_crtcs = 0;
> >> +
> >> +             for (j = 0; j < priv->num_crtcs; j++) {
> >> +                     struct omap_overlay *ovl =
> >> +
> > omap_crtc_get_overlay(priv->crtcs[j]);
> >> +                     if (ovl->manager == mgr) {
> >> +                             encoder->possible_crtcs |= (1 << j);
> >> +                     }
> >> +             }
> >> +
> >> +             DBG("%s: possible_crtcs=%08x", mgr->name,
> >> +                                     encoder->possible_crtcs);
> >> +     }
> >> +
> >> +     dump_video_chains();
> >> +
> >> +     dev->mode_config.min_width = 640;
> >> +     dev->mode_config.min_height = 480;
> >> +
> >> +     /* note: pvr can't currently handle dst surfaces larger than 2k
> by
> >> 2k */
> >> +     dev->mode_config.max_width = 2048;
> >> +     dev->mode_config.max_height = 2048;
> >> +
> >> +     dev->mode_config.funcs = &omap_mode_config_funcs;
> >> +
> >> +     return 0;
> >> +}
> >
> > For code clean, how about moving create_encoder(), create_connector()
> and
> > create_crtc() into omap_encoder.c, omap_connector.c and omap_crtc.c
> file?.
> > omap_modeset_init function contains so many codes. it looks like not
> good.
> 
> yeah, this is the way it is mainly because we are still allowing two
> cases:  (1) omapdrm uses all the DSS2 resources, and (2) the case of
> using some platform-data to tell the omapdrm to not use all the
> available resources in DSS.  This is mainly a transition thing, so you
> could (for example) use omapdrm to control some outputs or video
> pipes, and omapfb (fbdev) or omap_vout (v4l2) for others.  Have a look
> at the if (pdata) { ... } else { ... } part at the later part of the
> fxn.
> 
> I expect eventually, when we have overlay support in the DRM driver
> (drm_plane stuff) and enough time has elapsed for folks to move away
> from legacy fbdev/v4l2 interfaces, that this platform-data stuff can
> be removed and this part greatly simplified.  But I guess that will be
> a couple years.  For now I wanted to try and keep the ugly bits
> localized in one place ;-)
> 
> [snip]
> 
> >> +}
> >> +
> >> +struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
> >> +     DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param,
> >> DRM_UNLOCKED|DRM_AUTH),
> >> +     DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param,
> >> DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
> >> +     DRM_IOCTL_DEF_DRV(OMAP_GET_BASE, ioctl_get_base,
> >> DRM_UNLOCKED|DRM_AUTH),
> >> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new,
> >> DRM_UNLOCKED|DRM_AUTH),
> >> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep,
> >> DRM_UNLOCKED|DRM_AUTH),
> >> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini,
> >> DRM_UNLOCKED|DRM_AUTH),
> >> +     DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info,
> >> DRM_UNLOCKED|DRM_AUTH),
> >> +};
> >
> > You also should consider security issue. :)
> 
> 
> All the ioctls do have the DRM_AUTH bit set, so any userspace process
> that uses them needs to at least be authenticated with the DRM master.
>  I don't really see any way to avoid needing to support buffer
> allocating in an authenticated client process.
> 
> So I think this is sufficient, but always possible that I missed
> something..
> 

Oh, you are right, it seems sufficient.

> [snip]
> >> +
> >> +static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
> >> +{
> >> +     struct drm_device *dev = fb->dev;
> >> +     struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
> >> +
> >> +     DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
> >> +
> >> +     drm_framebuffer_cleanup(fb);
> >> +
> >> +     if (omap_fb->vaddr) {
> >> +             iounmap(omap_fb->vaddr);
> >> +     }
> >
> > You always use reserved memory for drm framebuffer.? If not so, for
> > instance, any physical memory was allocated by dma api such as
> dma_alloc_*
> > then you should call dma_free_*.
> 
> currently it is always reserved memory.. I've not moved to CMA stuff yet.
> 
> So currently we have no vaddr in the GEM parts, and only place where
> vaddr is created/mapped is in omap_framebuffer part.
> 
> I think this will have to change though..  partly because
> dma_alloc_coherent() also allocates a vaddr.  Partly because there
> will be some cases where kernel side plugin code might need access to
> the buffer.  (Not so much for pixel buffers, but for command-stream
> type buffers.)
> 
> Possibly I should pull this into the GEM code w/ a
> omap_gem_{get,put}_vaddr() which could do the right thing depending on
> where the memory for the buffer came from.  Whatever I do, I don't
> want to force to have a kernel virtual mapping for all pixel buffers
> when it can otherwise be avoided, because I think in a generation or
> two kernel address space will start being a limited resource.
> 
> 
> >> +
> >> +     if (omap_gem_put_paddr(omap_fb->bo)) {
> >> +             dev_err(dev->dev, "could not unmap!\n");
> >> +     }
> >> +
> >> +     if (omap_fb->bo) {
> >> +             drm_gem_object_unreference_unlocked(omap_fb->bo);
> >> +     }
> >> +
> >> +     kfree(omap_fb);
> >> +}
> >> +
> >> +static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
> >> +             struct drm_file *file_priv, unsigned flags, unsigned
color,
> >> +             struct drm_clip_rect *clips, unsigned num_clips)
> >> +{
> >> +     int i;
> >> +
> >> +     for (i = 0; i < num_clips; i++) {
> >> +             omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
> >> +                                     clips[i].x2 - clips[i].x1,
> >> +                                     clips[i].y2 - clips[i].y1);
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
> >> +     .create_handle = omap_framebuffer_create_handle,
> >> +     .destroy = omap_framebuffer_destroy,
> >> +     .dirty = omap_framebuffer_dirty,
> >> +};
> >> +
> >> +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int
> y,
> >> +             void **vaddr, unsigned long *paddr, int *screen_width)
> >> +{
> >> +     struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
> >> +     int bpp = fb->depth / 8;
> >> +     unsigned long offset;
> >> +
> >> +     offset = (x * bpp) + (y * fb->pitch);
> >> +
> >> +     if (vaddr) {
> >> +             if (!omap_fb->vaddr) {
> >> +                     omap_fb->vaddr = ioremap_wc(omap_fb->paddr,
> omap_fb-
> >> >size);
> >> +             }
> >> +             *vaddr = omap_fb->vaddr + offset;
> >> +     }
> >
> > Did you use ioremap_wc() to map physical memory(reserved memory) that
> kernel
> > doesn't aware of to kernel space.? if not so, the memory region mapped
> to
> > kernel space as 1:1,  this way would be faced with duplicated cache
> > attribute issue mentioned by Russel King before. 1:1 mapping region is
> > mapped to kernel space with cachable attribute.
> 
> Today the carveout memory does not have a kernel virtual mapping.  So
> we are ok.  And I think this should still be the case w/ CMA.
> 

I wonder what is the carveout and sacnout memory. carvout is physically non
continuous memory and scanout is physically continuous memory?

> [snip]
> >> +
> >> +static int omap_fbdev_create(struct drm_fb_helper *helper,
> >> +             struct drm_fb_helper_surface_size *sizes)
> >> +{
> >> +     struct omap_fbdev *fbdev = to_omap_fbdev(helper);
> >> +     struct drm_device *dev = helper->dev;
> >> +     struct drm_framebuffer *fb;
> >> +     struct fb_info *fbi;
> >> +     struct drm_mode_fb_cmd mode_cmd = {0};
> >> +     unsigned long paddr;
> >> +     void __iomem *vaddr;
> >> +     int size, screen_width;
> >> +     int ret;
> >> +
> >> +     /* only doing ARGB32 since this is what is needed to alpha-blend
> >> +      * with video overlays:
> >> +      */
> >> +     sizes->surface_bpp = 32;
> >> +     sizes->surface_depth = 32;
> >> +
> >> +     DBG("create fbdev: %dx%d@%d", sizes->surface_width,
> >> +                     sizes->surface_height, sizes->surface_bpp);
> >> +
> >> +     mode_cmd.width = sizes->surface_width;
> >> +     mode_cmd.height = sizes->surface_height;
> >> +
> >> +     mode_cmd.bpp = sizes->surface_bpp;
> >> +     mode_cmd.depth = sizes->surface_depth;
> >> +
> >> +     mutex_lock(&dev->struct_mutex);
> >> +
> >> +     fbi = framebuffer_alloc(0, dev->dev);
> >> +     if (!fbi) {
> >> +             dev_err(dev->dev, "failed to allocate fb info\n");
> >> +             ret = -ENOMEM;
> >> +             goto fail;
> >> +     }
> >> +
> >> +     DBG("fbi=%p, dev=%p", fbi, dev);
> >> +
> >> +     fbdev->fb = omap_framebuffer_init(dev, &mode_cmd, NULL);
> >> +     if (!fbdev->fb) {
> >> +             dev_err(dev->dev, "failed to allocate fb\n");
> >> +             ret = -ENOMEM;
> >> +             goto fail;
> >
> > In case of fail, framebuffer_release should be called.
> 
> oh, yeah.. // TODO cleanup ;-)
> 
> >
> >> +     }
> >> +
> >> +     fb = fbdev->fb;
> >> +     helper->fb = fb;
> >> +     helper->fbdev = fbi;
> >> +
> >> +     fbi->par = helper;
> >> +     fbi->flags = FBINFO_DEFAULT;
> >> +     fbi->fbops = &omap_fb_ops;
> >> +
> >> +     strcpy(fbi->fix.id, MODULE_NAME);
> >> +
> >> +     ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
> >> +     if (ret) {
> >> +             ret = -ENOMEM;
> >> +             goto fail;
> >
> > Also, omap_framebuffer_destroy() should be called to release resource.
> >
> >> +     }
> >> +
> >> +     drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
> >> +     drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
> >> +
> >> +     size = omap_framebuffer_get_buffer(fb, 0, 0,
> >> +                     &vaddr, &paddr, &screen_width);
> >> +
> >> +     dev->mode_config.fb_base = paddr;
> >> +
> >> +     fbi->screen_base = vaddr;
> >> +     fbi->screen_size = size;
> >> +     fbi->fix.smem_start = paddr;
> >> +     fbi->fix.smem_len = size;
> >> +
> >> +     DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
> >> +     DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
> >> +
> >> +     mutex_unlock(&dev->struct_mutex);
> >> +
> >> +     return 0;
> >> +
> >> +fail:
> >> +     mutex_unlock(&dev->struct_mutex);
> >> +     // TODO cleanup?
> >> +     return ret;
> >> +}
> >> +
> >> +static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
> >> +             u16 red, u16 green, u16 blue, int regno)
> >> +{
> >> +     DBG("fbdev: set gamma");
> >> +}
> >> +
> >> +static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
> >> +             u16 *red, u16 *green, u16 *blue, int regno)
> >> +{
> >> +     DBG("fbdev: get gamma");
> >> +}
> >> +
> >> +static int omap_fbdev_probe(struct drm_fb_helper *helper,
> >> +             struct drm_fb_helper_surface_size *sizes)
> >> +{
> >> +     int new_fb = 0;
> >> +     int ret;
> >> +
> >> +     if (!helper->fb) {
> >> +             ret = omap_fbdev_create(helper, sizes);
> >> +             if (ret)
> >> +                     return ret;
> >> +             new_fb = 1;
> >> +     }
> >> +     return new_fb;
> >> +}
> >> +
> >
> > This strange code must be because of mainline code below.
> > fb_probe callback of drm_fb_helper_single_fb_probe function needs return
> > values of three types. :(  I think this mainline code should be modified
> > properly.
> >
> 
> yeah
> 
> [snip]
> >> +
> >> +struct omap_gem_object {
> >> +     struct drm_gem_object base;
> >> +
> >> +     uint32_t flags;
> >> +
> >> +     /**
> >> +      * if buffer is physically contiguous or remapped in TILER, the
> >> +      * OMAP_BO_DMA flag is set and the paddr is valid.
> >> +      *
> >> +      * Note that OMAP_BO_SCANOUT is a hint from userspace that DMA
> >> capable
> >> +      * buffer is requested, but doesn't mean that it is.  Use the
> >> +      * OMAP_BO_DMA flag to determine if the buffer has a DMA capable
> >> +      * physical address.
> >> +      */
> >> +     unsigned long paddr;
> >> +
> >> +     /**
> >> +      * Array of backing pages, if allocated.  Note that pages are
> never
> >> +      * allocated for buffers originally allocated from contiguous
> >> memory
> >> +      * (which will have obj->filp==NULL)
> >> +      */
> >> +     struct page **pages;
> >
> > Is this array of backing pages used for physically non-continuous
> memory?.
> > it appears that sgx core with iommu is considered.
> 
> 
> yes (and will be similar for 2d hw).  But even in case of scanout
> buffers, we can remap them into TILER (which you can think of as a
> sort of IOMMU on OMAP4+).  The code for this isn't in place yet, but
> the intention is to
> 
> a) if userspace knows it will be a scannout buffer, allocate
> physically contiguous to begin with.. if that fails, fall back to
> dis-contiguous.
> b) if we need to scan it out, but it isn't contiguous, fall back to
> remapping into TILER.
> 
> That is a bit of over-simplification.. for tiled buffers, and buffers
> for video encode/decode, these would always be allocated as pages and
> remapped into TILER.
> 
> [sync]
> >> +
> >> +static inline void sync_op(struct drm_gem_object *obj,
> >> +             enum omap_gem_op op, bool start)
> >> +{
> >> +     struct omap_gem_object *omap_obj = to_omap_bo(obj);
> >> +
> >> +     spin_lock(&sync_lock);
> >> +
> >> +     if (!omap_obj->sync) {
> >> +             omap_obj->sync = kzalloc(sizeof(*omap_obj->sync),
> >> GFP_KERNEL);
> >> +     }
> >
> > If sync_op function is called at interrupt context, use GFP_ATOMIC
> instead
> > of GFP_KERNEL. from what you use spin_lock/unlock here, it appears that
> this
> > function would be called at interrupt context.
> 
> yeah, that is an oversight.. I updated omap_gem_set_sync_object() but
> missed this one
> 
> 
> [snip]
> >> +
> >> +/* common constructor body */
> >> +static struct drm_gem_object * omap_gem_new_impl(struct drm_device
> *dev,
> >> +             size_t size, uint32_t flags, unsigned long paddr, struct
> >> page **pages,
> >> +             struct omap_gem_vm_ops *ops)
> >> +{
> >
> > so many arguments are used at this function. I encourage you to use
> maximum
> > four arguments for ARM system.
> >
> 
> I know the reasoning for this.. although the alternative is to stick
> extra param's in a struct on the stack, which roughly amounts to what
> the compiler is doing for you anyways for args above the 1st four (ie.
> pushing them to the stack).
> 
> anyways it is a static fxn so compiler has some flexibility to inline
> or optimize however it sees fit..
> 
> [snip]
> >> diff --git a/include/linux/omap_drm.h b/include/linux/omap_drm.h
> >> new file mode 100644
> >> index 0000000..81af200
> >> --- /dev/null
> >> +++ b/include/linux/omap_drm.h
> >> @@ -0,0 +1,191 @@
> >> +/*
> >> + * linux/include/linux/omap_drm.h
> >> + *
> >> + * Copyright (C) 2011 Texas Instruments
> >> + * Author: Rob Clark <rob@ti.com>
> >> + *
> >> + * 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.
> >> + *
> >> + * This program is distributed in the hope that it will be useful, but
> >> WITHOUT
> >> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> or
> >> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License
> >> for
> >> + * more details.
> >> + *
> >> + * You should have received a copy of the GNU General Public License
> >> along with
> >> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> >> + */
> >> +
> >> +#ifndef __OMAP_DRM_H__
> >> +#define __OMAP_DRM_H__
> >> +
> >> +#include <linux/module.h>
> >> +#include <drm/drmP.h>
> >> +
> >> +#define OMAP_PARAM_CHIPSET_ID        1       /* ie. 0x3430, 0x4430,
etc
> > */
> >> +
> >> +struct drm_omap_param {
> >> +     uint64_t param;                 /* in */
> >> +     uint64_t value;                 /* in (set_param), out
(get_param)
> >> */
> >> +};
> >> +
> >> +struct drm_omap_get_base {
> >> +     char plugin_name[64];           /* in */
> >> +     uint32_t ioctl_base;            /* out */
> >> +};
> >> +
> >> +#define OMAP_BO_SCANOUT              0x00000001      /* scanout
capable
> > (phys
> >> contiguous) */
> >> +#define OMAP_BO_CACHE_MASK   0x00000006      /* cache type mask, see
> >> cache modes */
> >> +#define OMAP_BO_TILED_MASK   0x00000f00      /* tiled mapping mask,
see
> >> tiled modes */
> >> +
> >> +/* cache modes */
> >> +#define OMAP_BO_CACHED               0x00000000      /* default */
> >> +#define OMAP_BO_WC           0x00000002      /* write-combine */
> >> +#define OMAP_BO_UNCACHED     0x00000004      /* strongly-ordered
> > (uncached) */
> >> +
> >> +/* tiled modes */
> >> +#define OMAP_BO_TILED_8              0x00000100
> >> +#define OMAP_BO_TILED_16     0x00000200
> >> +#define OMAP_BO_TILED_32     0x00000300
> >> +
> >> +struct drm_omap_gem_new {
> >> +     union {                         /* in */
> >> +             uint32_t bytes;         /* (for non-tiled formats) */
> >> +             struct {
> >> +                     uint16_t width;
> >> +                     uint16_t height;
> >> +             } tiled;                /* (for tiled formats) */
> >> +     } size;
> >> +     uint32_t flags;                 /* in */
> >> +     uint64_t offset;                /* out */
> >> +     uint32_t handle;                /* out */
> >> +};
> >> +
> >> +/* mask of operations: */
> >> +enum omap_gem_op {
> >> +     OMAP_GEM_READ = 0x01,
> >> +     OMAP_GEM_WRITE = 0x02,
> >> +};
> >> +
> >> +struct drm_omap_gem_cpu_prep {
> >> +     uint32_t handle;                /* buffer handle (in) */
> >> +     uint32_t op;                    /* mask of omap_gem_op (in) */
> >> +};
> >> +
> >> +struct drm_omap_gem_cpu_fini {
> >> +     uint32_t handle;                /* buffer handle (in) */
> >> +     uint32_t op;                    /* mask of omap_gem_op (in) */
> >> +     /* TODO maybe here we pass down info about what regions are
> touched
> >> +      * by sw so we can be clever about cache ops?  For now a
> > placeholder,
> >> +      * set to zero and we just do full buffer flush..
> >> +      */
> >> +     uint32_t nregions;
> >> +};
> >> +
> >> +struct drm_omap_gem_info {
> >> +     uint32_t handle;                /* buffer handle (in) */
> >> +     uint32_t pad;
> >> +     uint64_t offset;                /* out */
> >> +};
> >> +
> >> +#define DRM_OMAP_GET_PARAM           0x00
> >> +#define DRM_OMAP_SET_PARAM           0x01
> >> +#define DRM_OMAP_GET_BASE            0x02
> >> +#define DRM_OMAP_GEM_NEW             0x03
> >> +#define DRM_OMAP_GEM_CPU_PREP        0x04
> >> +#define DRM_OMAP_GEM_CPU_FINI        0x05
> >> +#define DRM_OMAP_GEM_INFO    0x06
> >> +#define DRM_OMAP_NUM_IOCTLS          0x07
> >> +
> >> +#define DRM_IOCTL_OMAP_GET_PARAM     DRM_IOWR(DRM_COMMAND_BASE +
> >> DRM_OMAP_GET_PARAM, struct drm_omap_param)
> >> +#define DRM_IOCTL_OMAP_SET_PARAM     DRM_IOW (DRM_COMMAND_BASE +
> >> DRM_OMAP_SET_PARAM, struct drm_omap_param)
> >> +#define DRM_IOCTL_OMAP_GET_BASE              DRM_IOWR(DRM_COMMAND_BASE
> +
> >> DRM_OMAP_GET_BASE, struct drm_omap_get_base)
> >> +#define DRM_IOCTL_OMAP_GEM_NEW               DRM_IOWR(DRM_COMMAND_BASE
+
> >> DRM_OMAP_GEM_NEW, struct drm_omap_gem_new)
> >> +#define DRM_IOCTL_OMAP_GEM_CPU_PREP  DRM_IOW (DRM_COMMAND_BASE +
> >> DRM_OMAP_GEM_CPU_PREP, struct drm_omap_gem_cpu_prep)
> >> +#define DRM_IOCTL_OMAP_GEM_CPU_FINI  DRM_IOW (DRM_COMMAND_BASE +
> >> DRM_OMAP_GEM_CPU_FINI, struct drm_omap_gem_cpu_fini)
> >> +#define DRM_IOCTL_OMAP_GEM_INFO              DRM_IOWR(DRM_COMMAND_BASE
> +
> >> DRM_OMAP_GEM_INFO, struct drm_omap_gem_info)
> >> +
> >> +/* interface that plug-in drivers can implement */
> >> +struct omap_drm_plugin {
> >> +     const char *name;
> >> +
> >> +     /* drm functions */
> >> +     int (*open)(struct drm_device *dev, struct drm_file *file);
> >> +     int (*load)(struct drm_device *dev, unsigned long flags);
> >> +     int (*unload)(struct drm_device *dev);
> >> +     int (*release)(struct drm_device *dev, struct drm_file *file);
> >> +
> >> +     struct drm_ioctl_desc *ioctls;
> >> +     int num_ioctls;
> >> +     int ioctl_base;
> >> +
> >> +     struct list_head list;  /* note, this means struct can't be
const..
> >> */
> >> +};
> >> +
> >> +int omap_drm_register_plugin(struct omap_drm_plugin *plugin);
> >> +int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin);
> >> +
> >> +int omap_drm_register_mapper(void);
> >> +void omap_drm_unregister_mapper(int id);
> >> +
> >> +/* external mappers should get paddr or pages when it needs the pages
> >> pinned
> >> + * and put when done..
> >> + */
> >> +int omap_gem_get_paddr(struct drm_gem_object *obj,
> >> +             unsigned long *paddr, bool remap);
> >> +int omap_gem_put_paddr(struct drm_gem_object *obj);
> >> +int omap_gem_get_pages(struct drm_gem_object *obj, struct page
> ***pages);
> >> +int omap_gem_put_pages(struct drm_gem_object *obj);
> >> +
> >> +uint32_t omap_gem_flags(struct drm_gem_object *obj);
> >> +void * omap_gem_priv(struct drm_gem_object *obj, int mapper_id);
> >> +void omap_gem_set_priv(struct drm_gem_object *obj, int mapper_id, void
> >> *priv);
> >> +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj);
> >> +
> >> +/* for external plugin buffers wrapped as GEM object (via.
> >> omap_gem_new_ext())
> >> + * a vm_ops struct can be provided to get callback notification of
> >> various
> >> + * events..
> >> + */
> >> +struct omap_gem_vm_ops {
> >> +     void (*open)(struct vm_area_struct * area);
> >> +     void (*close)(struct vm_area_struct * area);
> >> +
> >> +     /* note: mmap is not expected to do anything.. it is just to
> allow
> >> buffer
> >> +      * allocate to update it's own internal state
> >> +      */
> >> +     void (*mmap)(struct file *, struct vm_area_struct *);
> >> +};
> >> +
> >> +struct drm_gem_object * omap_gem_new_ext(struct drm_device *dev,
> >> +             size_t size, uint32_t flags, unsigned long paddr, struct
> >> page **pages,
> >> +             struct omap_gem_vm_ops *ops);
> >> +struct drm_gem_object * omap_gem_new(struct drm_device *dev,
> >> +             size_t size, uint32_t flags);
> >> +
> >> +void omap_gem_op_update(void);
> >> +void omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op
> op);
> >> +void omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op
> op);
> >> +int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op);
> >> +int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
> >> +             void (*fxn)(void *arg), void *arg);
> >> +void omap_gem_set_sync_object(struct drm_gem_object *obj, void
> *syncobj);
> >> +
> >> +
> >> +/* optional platform data to configure the default configuration of
> which
> >> + * pipes/overlays/CRTCs are used.. if this is not provided, then
> instead
> >> the
> >> + * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each
> connected
> >> to
> >> + * one manager, with priority given to managers that are connected to
> >> + * detected devices.  This should be a good default behavior for most
> >> cases,
> >> + * but yet there still might be times when you wish to do something
> >> different.
> >> + */
> >> +struct omap_drm_platform_data {
> >> +     int ovl_cnt;
> >> +     const int *ovl_ids;
> >> +     int mgr_cnt;
> >> +     const int *mgr_ids;
> >> +     int dev_cnt;
> >> +     const char **dev_names;
> >> +};
> >> +
> >> +#endif /* __OMAP_DRM_H__ */
> >> --
> >
> > Remove device specific functions from linux/include/linux/omap_drm.h and
> > move them to driver folder. I was told that include/linux/*.h file
> should
> > contain only interfaces for user process from Dave.
> 
> 
> fwiw, the functions in this header are already only ones used by other
> kernel drivers.. everything else internal to omapdrm is already in
> omap_drv.h.  I'll remove these for now (see discussion about plugin
> API), although when it is re-introduced they need to be in a header
> accessible from other drivers.  Although should maybe still be split
> from what is needed by userspace?  (Something like omapdrm_plugin.h?)
> 
> 

I'm afraid I don't understand what you mean, "Although should maybe still be
split from what is needed by userspace?  (Something like
omapdrm_plugin.h?)", could you please clarify that again?

> BR,
> -R
> 
> >> 1.7.5.4
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/dri-devel
> >
Tomi Valkeinen Sept. 7, 2011, 6:24 a.m. UTC | #10
On Wed, 2011-09-07 at 15:00 +0900, Inki Dae wrote:
> Hello, Rob.
> 
> > -----Original Message-----
> > From: robdclark@gmail.com [mailto:robdclark@gmail.com] On Behalf Of Rob
> > Clark
> > Sent: Tuesday, September 06, 2011 1:05 AM
> > To: Inki Dae
> > Cc: dri-devel@lists.freedesktop.org; linaro-dev@lists.linaro.org;
> > Valkeinen, Tomi
> > Subject: Re: [PATCH] RFC: omapdrm DRM/KMS driver for TI OMAP platforms
> > 
> > On Mon, Sep 5, 2011 at 4:58 AM, Inki Dae <inki.dae@samsung.com> wrote:

<snip>

> > > How about moving codes above into interrupt handler for vblank?
> > >  maybe there
> > 
> > I should mention a couple of things:
> > 
> > 1) drm vblank stuff isn't really used currently.. it is actually DSS2
> > layer that is registering for the display related interrupt(s).  I'm
> > not really sure how to handle this best.  I suppose the DRM driver
> > could *also* register for these interrupts, but that seems a bit odd..
> > 
> 
> DRM Framework supports only one interrupt handler. this issue should be
> resolved. and currently I made our driver to use its own request_irq, not
> DRM Framework side. we only sets drm_device->irq_enabled to 1 and interrupt
> handler is registered at display controller or hdmi driver respectively. but
> I'm not sure that this way is best so I will look over more. Anyway current
> drm framework should be fixed to be considered with multiple irq.

I am actually going to hide the display subsystem interrupts, as they
are really a DSS2 driver internal thing, and create some kind of
notifications for the events that the users of DSS2 need to see. I don't
know how that affects the OMAP DRM driver, but it should be a much
cleaner interface.

> > Also, I guess it is also worth mentioning.. when it comes to vblank,
> > there are two fundamentally different sorts of displays we deal with.
> > Something like DVI/HDMI/etc which need constant refreshing.  In these
> > cases we constantly scan-out the buffer until the next page
> > flip+vsync.  And smart panels, where they get scanned out once and
> > then DSS is no longer reading the scanout buffer until we manually
> > trigger an update.
> > 
> 
> Is the Smart panel CPU interface based lcd panel that has its own
> framebuffer internally.?

I don't like the "smart panel" term very much, but yes, it has an
internal framebuffer. The panels are connected via DSI command mode or
DBI bus. They can be updated with CPU, but normally they are not and use
the same display controller hardware as other displays to update the
screen.

The biggest difference with these smart panels compared to conventional
ones is that pixel data is only sent to the panel when needed (i.e. when
somebody has changed the pixel data), and the panel retains the last
sent frame independently on the screen, and the SoC can even go to
sleep.

 Tomi
Rob Clark Sept. 7, 2011, 6:43 p.m. UTC | #11
On Wed, Sep 7, 2011 at 1:00 AM, Inki Dae <inki.dae@samsung.com> wrote:
> Hello, Rob.
>
[snip]
>> >> +static void page_flip_cb(void *arg)
>> >> +{
>> >> +     struct drm_crtc *crtc = arg;
>> >> +     struct drm_device *dev = crtc->dev;
>> >> +     struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>> >> +     struct drm_pending_vblank_event *event = omap_crtc->event;
>> >> +     struct timeval now;
>> >> +     unsigned long flags;
>> >> +
>> >> +     WARN_ON(!event);
>> >> +
>> >> +     omap_crtc->event = NULL;
>> >> +
>> >> +     update_scanout(crtc);
>> >> +     commit(crtc);
>> >> +
>> >> +     /* wakeup userspace */
>> >> +     // TODO: this should happen *after* flip.. somehow..
>> >> +     if (event) {
>> >> +             spin_lock_irqsave(&dev->event_lock, flags);
>> >> +             event->event.sequence =
>> >> +                             drm_vblank_count_and_time(dev,
>> > omap_crtc->id,
>> >> &now);
>> >> +             event->event.tv_sec = now.tv_sec;
>> >> +             event->event.tv_usec = now.tv_usec;
>> >> +             list_add_tail(&event->base.link,
>> >> +                             &event->base.file_priv->event_list);
>> >> +
> wake_up_interruptible(&event->base.file_priv->event_wait);
>> >> +             spin_unlock_irqrestore(&dev->event_lock, flags);
>> >> +     }
>> >
>> > How about moving codes above into interrupt handler for vblank?
>> >  maybe there
>>
>> I should mention a couple of things:
>>
>> 1) drm vblank stuff isn't really used currently.. it is actually DSS2
>> layer that is registering for the display related interrupt(s).  I'm
>> not really sure how to handle this best.  I suppose the DRM driver
>> could *also* register for these interrupts, but that seems a bit odd..
>>
>
> DRM Framework supports only one interrupt handler. this issue should be
> resolved. and currently I made our driver to use its own request_irq, not
> DRM Framework side. we only sets drm_device->irq_enabled to 1 and interrupt
> handler is registered at display controller or hdmi driver respectively. but
> I'm not sure that this way is best so I will look over more. Anyway current
> drm framework should be fixed to be considered with multiple irq.

Or perhaps even callbacks (some other driver handling the irq's directly)?

>> Also, I guess it is also worth mentioning.. when it comes to vblank,
>> there are two fundamentally different sorts of displays we deal with.
>> Something like DVI/HDMI/etc which need constant refreshing.  In these
>> cases we constantly scan-out the buffer until the next page
>> flip+vsync.  And smart panels, where they get scanned out once and
>> then DSS is no longer reading the scanout buffer until we manually
>> trigger an update.
>>
>
> Is the Smart panel CPU interface based lcd panel that has its own
> framebuffer internally.?

yes

[snip]
>>
>> The main reason for the page-flip cb is actually not vsync
>> synchronization, but rather synchronizing with other hw blocks that
>> might be rendering to the buffer..  the page flip can be submitted
>> from userspace while some other hw block (3d, 2d, etc) is still
>> DMA'ing to the buffer.  The sync-obj is intended to give a way to
>> defer the (in this case) page flip until other hw blocks are done
>> writing to the buffer.
>
> I thought page-flip is used to change buffer register value of display
> controller into another one like the Pan Display feature of linux
> framebuffer. actually page flip interface of libdrm library,
> page_flip_handler, is called with new framebuffer id that has its own
> buffer. and then the controller using current crtc would be set to buffer
> address of new framebuffer. and I understood that for other blocks such as
> 2d/3d accelerators, rendering synchronization you already mentioned above,
> is not depend on page flip action. It’s a sequence with my understanding
> below.
>
> 1. allocate a new buffer through drm interface such as DUMB_* or SPECIFIC
> IOCTLs.
> 2. render something on this buffer through DRM interfaces that handle 2D/3D
> Accelerators and also it would use their own interfaces for synchronization.
> 3. allocate a new framebuffer and attach the buffer to this framebuffer.
> 4. call page flip to change current framebuffer to the framebuffer above at
> vblank moment. at this time, buffer address of the framebuffer would be set
> to a controller.
>
> finally, we can see some image rendered on diplay. thus, I think page flip
> and rendering synchronization have no any relationship. if I missed any
> points, please give me your comments.

Well, I guess it depends on whether synchronization of the 3d/2d
render is done in userspace, or whether you just submit all the render
commands and then immediately submit the page-flip.

The actual page-flip should be a fairly light operation (writing a few
registers) and could be done directly from some sort of
render-complete interrupt from 2d/3d core.

[snip]
>> >> +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int
>> y,
>> >> +             void **vaddr, unsigned long *paddr, int *screen_width)
>> >> +{
>> >> +     struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
>> >> +     int bpp = fb->depth / 8;
>> >> +     unsigned long offset;
>> >> +
>> >> +     offset = (x * bpp) + (y * fb->pitch);
>> >> +
>> >> +     if (vaddr) {
>> >> +             if (!omap_fb->vaddr) {
>> >> +                     omap_fb->vaddr = ioremap_wc(omap_fb->paddr,
>> omap_fb-
>> >> >size);
>> >> +             }
>> >> +             *vaddr = omap_fb->vaddr + offset;
>> >> +     }
>> >
>> > Did you use ioremap_wc() to map physical memory(reserved memory) that
>> kernel
>> > doesn't aware of to kernel space.? if not so, the memory region mapped
>> to
>> > kernel space as 1:1,  this way would be faced with duplicated cache
>> > attribute issue mentioned by Russel King before. 1:1 mapping region is
>> > mapped to kernel space with cachable attribute.
>>
>> Today the carveout memory does not have a kernel virtual mapping.  So
>> we are ok.  And I think this should still be the case w/ CMA.
>>
>
> I wonder what is the carveout and sacnout memory. carvout is physically non
> continuous memory and scanout is physically continuous memory?

today, scanout buffers are required to be contiguous.  There is the
possibility to remap non-contiguous buffers in TILER/DMM (which you
can think of as a sort of IOMMU).  Although adding support for this is
still on my TODO list.

So today, carveout is used to allocate scanout buffers to ensure
physically contiguous.

[snip]
>> >
>> > Remove device specific functions from linux/include/linux/omap_drm.h and
>> > move them to driver folder. I was told that include/linux/*.h file
>> should
>> > contain only interfaces for user process from Dave.
>>
>>
>> fwiw, the functions in this header are already only ones used by other
>> kernel drivers.. everything else internal to omapdrm is already in
>> omap_drv.h.  I'll remove these for now (see discussion about plugin
>> API), although when it is re-introduced they need to be in a header
>> accessible from other drivers.  Although should maybe still be split
>> from what is needed by userspace?  (Something like omapdrm_plugin.h?)
>>
>>
>
> I'm afraid I don't understand what you mean, "Although should maybe still be
> split from what is needed by userspace?  (Something like
> omapdrm_plugin.h?)", could you please clarify that again?

I mean one header file for userspace facing API, and a separate one
for kernel facing API used by other drivers/modules..

(In the first pass, that second header would not exist because I'll
remove the plugin API until we have one w/ open userspace)

BR,
-R
diff mbox

Patch

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 196284d..99b7547 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -160,4 +160,6 @@  source "drivers/staging/mei/Kconfig"
 
 source "drivers/staging/nvec/Kconfig"
 
+source "drivers/staging/omapdrm/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index fa41b9c..91ec21f 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -71,3 +71,4 @@  obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4)	+= ste_rmi4/
 obj-$(CONFIG_DRM_PSB)		+= gma500/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
 obj-$(CONFIG_MFD_NVEC)		+= nvec/
+obj-$(CONFIG_DRM_OMAP)		+= omapdrm/
diff --git a/drivers/staging/omapdrm/Kconfig b/drivers/staging/omapdrm/Kconfig
new file mode 100644
index 0000000..bddf52b
--- /dev/null
+++ b/drivers/staging/omapdrm/Kconfig
@@ -0,0 +1,25 @@ 
+
+config DRM_OMAP
+	tristate "OMAP DRM (EXPERIMENTAL)"
+	depends on DRM && !CONFIG_FB_OMAP2
+	select DRM_KMS_HELPER
+	select OMAP2_VRAM
+	select OMAP2_DSS
+	select FB_SYS_FILLRECT
+	select FB_SYS_COPYAREA
+	select FB_SYS_IMAGEBLIT
+	select FB_SYS_FOPS
+	default n
+	help
+	  DRM display driver for OMAP2/3/4 based boards.
+
+config DRM_OMAP_NUM_CRTCS
+	int "Number of CRTCs"
+	range 1 10
+	default 1  if ARCH_OMAP2 || ARCH_OMAP3
+	default 2  if ARCH_OMAP4
+	depends on DRM_OMAP
+	help
+	  Select the number of video overlays which can be used as framebuffers.
+	  The remaining overlays are reserved for video.
+
diff --git a/drivers/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile
new file mode 100644
index 0000000..da74337
--- /dev/null
+++ b/drivers/staging/omapdrm/Makefile
@@ -0,0 +1,8 @@ 
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm -Werror
+omapdrm-y := omap_drv.o omap_crtc.o omap_encoder.o omap_connector.o omap_fb.o omap_fbdev.o omap_gem.o
+
+obj-$(CONFIG_DRM_OMAP)	+= omapdrm.o
diff --git a/drivers/staging/omapdrm/TODO.txt b/drivers/staging/omapdrm/TODO.txt
new file mode 100644
index 0000000..b64f067
--- /dev/null
+++ b/drivers/staging/omapdrm/TODO.txt
@@ -0,0 +1,15 @@ 
+TODO
+. move to CMA for scanout buffer allocation
+. check error handling/cleanup paths
+. add drm_plane / overlay support
+. add video decode/encode support (via syslink3 + codec-engine)
+. still some rough edges with flipping.. event back to userspace should
+  really come after VSYNC interrupt
+. support for tiled buffers
+
+Userspace:
+. git://github.com/robclark/xf86-video-omap.git
+
+Currently tested on
+. OMAP3530 beagleboard
+. OMAP4430 pandaboard
+. OMAP4460 pandaboard
diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/staging/omapdrm/omap_connector.c
new file mode 100644
index 0000000..7f7403b
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_connector.c
@@ -0,0 +1,375 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_connector.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_drm.h>
+#include "omap_drv.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/*
+ * connector funcs
+ */
+
+#define to_omap_connector(x) container_of(x, struct omap_connector, base)
+
+struct omap_connector {
+	struct drm_connector base;
+	struct omap_dss_device *dssdev;
+};
+
+static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
+		struct omap_video_timings *timings)
+{
+	mode->clock = timings->pixel_clock;
+
+	mode->hdisplay = timings->x_res;
+	mode->hsync_start = mode->hdisplay + timings->hfp;
+	mode->hsync_end = mode->hsync_start + timings->hsw;
+	mode->htotal = mode->hsync_end + timings->hbp;
+
+	mode->vdisplay = timings->y_res;
+	mode->vsync_start = mode->vdisplay + timings->vfp;
+	mode->vsync_end = mode->vsync_start + timings->vsw;
+	mode->vtotal = mode->vsync_end + timings->vbp;
+
+	/* note: whether or not it is interlaced, +/- h/vsync, etc,
+	 * which should be set in the mode flags, is not exposed in
+	 * the omap_video_timings struct.. but hdmi driver tracks
+	 * those separately so all we have to have to set the mode
+	 * is the way to recover these timings values, and the
+	 * omap_dss_driver would do the rest.
+	 */
+}
+
+static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
+		struct drm_display_mode *mode)
+{
+	timings->pixel_clock = mode->clock;
+
+	timings->x_res = mode->hdisplay;
+	timings->hfp = mode->hsync_start - mode->hdisplay;
+	timings->hsw = mode->hsync_end - mode->hsync_start;
+	timings->hbp = mode->htotal - mode->hsync_end;
+
+	timings->y_res = mode->vdisplay;
+	timings->vfp = mode->vsync_start - mode->vdisplay;
+	timings->vsw = mode->vsync_end - mode->vsync_start;
+	timings->vbp = mode->vtotal - mode->vsync_end;
+}
+
+void omap_connector_dpms(struct drm_connector *connector, int mode)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev = omap_connector->dssdev;
+	int ret;
+
+	DBG("%s: %d", dssdev->name, mode);
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		/* store resume info for suspended displays */
+		switch (dssdev->state) {
+		case OMAP_DSS_DISPLAY_SUSPENDED:
+			dssdev->activate_after_resume = true;
+			break;
+		case OMAP_DSS_DISPLAY_DISABLED:
+			ret = dssdev->driver->enable(dssdev);
+			if (ret) {
+				DBG("%s: failed to enable: %d", dssdev->name, ret);
+				dssdev->driver->disable(dssdev);
+			}
+			break;
+		default:
+			break;
+		}
+	} else {
+		/* TODO */
+	}
+}
+
+enum drm_connector_status omap_connector_detect(
+		struct drm_connector *connector, bool force)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev = omap_connector->dssdev;
+	struct omap_dss_driver *dssdrv = dssdev->driver;
+	enum drm_connector_status ret;
+
+	/* hmm.. ideally dssdrv would take care of this internally if (and
+	 * only if) power-on was required to detect connection..
+	 */
+	omap_connector_dpms(connector, DRM_MODE_DPMS_ON);
+
+	if (dssdrv->is_detected(dssdev)) {
+		ret = connector_status_connected;
+	} else {
+		ret = connector_status_disconnected;
+	}
+
+	VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
+
+	return ret;
+}
+
+static void omap_connector_destroy(struct drm_connector *connector)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev = omap_connector->dssdev;
+
+	dssdev->driver->disable(dssdev);
+
+	DBG("%s", omap_connector->dssdev->name);
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+	kfree(omap_connector);
+
+	omap_dss_put_device(dssdev);
+}
+
+#define MAX_EDID  512
+
+static int omap_connector_get_modes(struct drm_connector *connector)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev = omap_connector->dssdev;
+	struct omap_dss_driver *dssdrv = dssdev->driver;
+	struct drm_device *dev = connector->dev;
+	int n = 0;
+
+	DBG("%s", omap_connector->dssdev->name);
+
+	/* if display exposes EDID, then we parse that in the normal way to
+	 * build table of supported modes.. otherwise (ie. fixed resolution
+	 * LCD panels) we just return a single mode corresponding to the
+	 * currently configured timings:
+	 */
+	if (dssdrv->read_edid) {
+		void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
+
+		if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) == 0) &&
+				drm_edid_is_valid(edid)) {
+			drm_mode_connector_update_edid_property(connector, edid);
+			n = drm_add_edid_modes(connector, edid);
+			kfree(connector->display_info.raw_edid);
+			connector->display_info.raw_edid = edid;
+		} else {
+			drm_mode_connector_update_edid_property(connector, NULL);
+			connector->display_info.raw_edid = NULL;
+			kfree(edid);
+		}
+	} else {
+		struct drm_display_mode *mode = drm_mode_create(dev);
+		struct omap_video_timings timings;
+
+		dssdrv->get_timings(dssdev, &timings);
+
+		copy_timings_omap_to_drm(mode, &timings);
+
+		mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+		drm_mode_set_name(mode);
+		drm_mode_probed_add(connector, mode);
+
+		n = 1;
+	}
+
+	return n;
+}
+
+static int omap_connector_mode_valid(struct drm_connector *connector,
+				 struct drm_display_mode *mode)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev = omap_connector->dssdev;
+	struct omap_dss_driver *dssdrv = dssdev->driver;
+	struct omap_video_timings timings = {0};
+	struct drm_device *dev = connector->dev;
+	struct drm_display_mode *new_mode;
+	int ret = MODE_BAD;
+
+	copy_timings_drm_to_omap(&timings, mode);
+	mode->vrefresh = drm_mode_vrefresh(mode);
+
+	if (!dssdrv->check_timings(dssdev, &timings)) {
+		/* check if vrefresh is still valid */
+		new_mode = drm_mode_duplicate(dev, mode);
+		new_mode->clock = timings.pixel_clock;
+		new_mode->vrefresh = 0;
+		if (mode->vrefresh == drm_mode_vrefresh(new_mode))
+			ret = MODE_OK;
+		drm_mode_destroy(dev, new_mode);
+	}
+
+	DBG("connector: mode %s: "
+			"%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+			(ret == MODE_OK) ? "valid" : "invalid",
+			mode->base.id, mode->name, mode->vrefresh, mode->clock,
+			mode->hdisplay, mode->hsync_start,
+			mode->hsync_end, mode->htotal,
+			mode->vdisplay, mode->vsync_start,
+			mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+
+	return ret;
+}
+
+struct drm_encoder * omap_connector_attached_encoder(
+		struct drm_connector *connector)
+{
+	int i;
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+
+	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+		struct drm_mode_object *obj;
+
+		if (connector->encoder_ids[i] == 0)
+			break;
+
+		obj = drm_mode_object_find(connector->dev,
+				connector->encoder_ids[i],
+				DRM_MODE_OBJECT_ENCODER);
+
+		if (obj) {
+			struct drm_encoder *encoder = obj_to_encoder(obj);
+			struct omap_overlay_manager *mgr =
+					omap_encoder_get_manager(encoder);
+			DBG("%s: found %s", omap_connector->dssdev->name,
+					mgr->name);
+			return encoder;
+		}
+	}
+
+	DBG("%s: no encoder", omap_connector->dssdev->name);
+
+	return NULL;
+}
+
+static const struct drm_connector_funcs omap_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = omap_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = omap_connector_destroy,
+};
+
+static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
+	.get_modes = omap_connector_get_modes,
+	.mode_valid = omap_connector_mode_valid,
+	.best_encoder = omap_connector_attached_encoder,
+};
+
+/* called from encoder when mode is set, to propagate settings to the dssdev */
+void omap_connector_mode_set(struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	struct drm_device *dev = connector->dev;
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev = omap_connector->dssdev;
+	struct omap_dss_driver *dssdrv = dssdev->driver;
+	struct omap_video_timings timings;
+
+	copy_timings_drm_to_omap(&timings, mode);
+
+	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+			omap_connector->dssdev->name,
+			mode->base.id, mode->name, mode->vrefresh, mode->clock,
+			mode->hdisplay, mode->hsync_start,
+			mode->hsync_end, mode->htotal,
+			mode->vdisplay, mode->vsync_start,
+			mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+
+	if (dssdrv->check_timings(dssdev, &timings)) {
+		dev_err(dev->dev, "could not set timings\n");
+		return;
+	}
+
+	dssdrv->set_timings(dssdev, &timings);
+}
+
+int omap_connector_sync(struct drm_connector *connector)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev = omap_connector->dssdev;
+	struct omap_dss_driver *dssdrv = dssdev->driver;
+
+	DBG("%s", omap_connector->dssdev->name);
+
+	if (dssdrv->sync) {
+		return dssdrv->sync(dssdev);
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(omap_connector_sync);
+
+/* flush an area of the framebuffer (in case of manual update display that
+ * is not automatically flushed)
+ */
+void omap_connector_flush(struct drm_connector *connector,
+		int x, int y, int w, int h)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+
+	/* TODO: enable when supported in dss */
+	VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
+}
+
+/* initialize connector */
+struct drm_connector * omap_connector_init(struct drm_device *dev,
+		int connector_type, struct omap_dss_device *dssdev)
+{
+	struct drm_connector *connector = NULL;
+	struct omap_connector *omap_connector;
+
+	DBG("%s", dssdev->name);
+
+	omap_dss_get_device(dssdev);
+
+	omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);
+	if (!omap_connector) {
+		dev_err(dev->dev, "could not allocate connector\n");
+		goto fail;
+	}
+
+	omap_connector->dssdev = dssdev;
+	connector = &omap_connector->base;
+
+	drm_connector_init(dev, connector, &omap_connector_funcs,
+				connector_type);
+	drm_connector_helper_add(connector, &omap_connector_helper_funcs);
+
+#if 0 /* enable when dss2 supports hotplug */
+	if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD)
+		connector->polled = 0;
+	else
+#endif
+		connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+				DRM_CONNECTOR_POLL_DISCONNECT;
+
+	connector->interlace_allowed = 1;
+	connector->doublescan_allowed = 0;
+
+	drm_sysfs_connector_add(connector);
+
+	return connector;
+
+fail:
+	if (connector) {
+		omap_connector_destroy(connector);
+	}
+
+	return NULL;
+}
diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
new file mode 100644
index 0000000..3714caa
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_crtc.c
@@ -0,0 +1,348 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_crtc.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_drm.h>
+#include "omap_drv.h"
+
+#include "drm_mode.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
+
+struct omap_crtc {
+	struct drm_crtc base;
+	struct omap_overlay *ovl;
+	struct omap_overlay_info info;
+	int id;
+
+	/* if there is a pending flip, this will be non-null: */
+	struct drm_pending_vblank_event *event;
+};
+
+/* push changes down to dss2 */
+static int commit(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	struct omap_overlay *ovl = omap_crtc->ovl;
+	struct omap_overlay_info *info = &omap_crtc->info;
+	int ret;
+
+	DBG("%s", omap_crtc->ovl->name);
+	DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width,
+			info->out_height, info->screen_width);
+	DBG("%d,%d %p %08x", info->pos_x, info->pos_y, info->vaddr,
+			info->paddr);
+
+	/* NOTE: do we want to do this at all here, or just wait
+	 * for dpms(ON) since other CRTC's may not have their mode
+	 * set yet, so fb dimensions may still change..
+	 */
+	ret = ovl->set_overlay_info(ovl, info);
+	if (ret) {
+		dev_err(dev->dev, "could not set overlay info\n");
+		return ret;
+	}
+
+	/* our encoder doesn't necessarily get a commit() after this, in
+	 * particular in the dpms() and mode_set_base() cases, so force the
+	 * manager to update:
+	 *
+	 * could this be in the encoder somehow?
+	 */
+	if (ovl->manager) {
+		ret = ovl->manager->apply(ovl->manager);
+		if (ret) {
+			dev_err(dev->dev, "could not apply\n");
+			return ret;
+		}
+	}
+
+	if (info->enabled) {
+		omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y,
+				crtc->fb->width, crtc->fb->height);
+	}
+
+	return 0;
+}
+
+/* update parameters that are dependent on the framebuffer dimensions and
+ * position within the fb that this crtc scans out from. This is called
+ * when framebuffer dimensions or x,y base may have changed, either due
+ * to our mode, or a change in another crtc that is scanning out of the
+ * same fb.
+ */
+static void update_scanout(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	unsigned long paddr;
+	void __iomem *vaddr;
+	int screen_width;
+
+	omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y,
+			&vaddr, &paddr, &screen_width);
+
+	DBG("%s: %d,%d: %p %08lx (%d)", omap_crtc->ovl->name,
+			crtc->x, crtc->y, vaddr, paddr, screen_width);
+
+	omap_crtc->info.paddr = paddr;
+	omap_crtc->info.vaddr = vaddr;
+	omap_crtc->info.screen_width = screen_width;
+}
+
+static void omap_crtc_gamma_set(struct drm_crtc *crtc,
+		u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t size)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	DBG("%s", omap_crtc->ovl->name);
+}
+
+static void omap_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	DBG("%s", omap_crtc->ovl->name);
+	drm_crtc_cleanup(crtc);
+	kfree(omap_crtc);
+}
+
+static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+	DBG("%s: %d", omap_crtc->ovl->name, mode);
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		update_scanout(crtc);
+		omap_crtc->info.enabled = true;
+	} else {
+		omap_crtc->info.enabled = false;
+	}
+
+	commit(crtc);
+}
+
+static bool omap_crtc_mode_fixup(struct drm_crtc *crtc,
+				  struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	DBG("%s", omap_crtc->ovl->name);
+	return true;
+}
+
+static int omap_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 omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+	DBG("%s: %d,%d: %dx%d",omap_crtc->ovl->name, x, y,
+			mode->hdisplay, mode->vdisplay);
+
+	/* just use adjusted mode */
+	mode = adjusted_mode;
+
+	omap_crtc->info.width = mode->hdisplay;
+	omap_crtc->info.height = mode->vdisplay;
+	omap_crtc->info.out_width = mode->hdisplay;
+	omap_crtc->info.out_height = mode->vdisplay;
+	omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U;
+	omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA;
+	omap_crtc->info.rotation = OMAP_DSS_ROT_0;
+	omap_crtc->info.global_alpha = 0xff;
+	omap_crtc->info.mirror = 0;
+	omap_crtc->info.mirror = 0;
+	omap_crtc->info.pos_x = 0;
+	omap_crtc->info.pos_y = 0;
+#if 0 /* re-enable when these are available in DSS2 driver */
+	omap_crtc->info.zorder = 3;        /* GUI in the front, video behind */
+	omap_crtc->info.min_x_decim = 1;
+	omap_crtc->info.max_x_decim = 1;
+	omap_crtc->info.min_y_decim = 1;
+	omap_crtc->info.max_y_decim = 1;
+#endif
+
+	update_scanout(crtc);
+
+	return 0;
+}
+
+static void omap_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	struct omap_overlay *ovl = omap_crtc->ovl;
+
+	DBG("%s", omap_crtc->ovl->name);
+
+	ovl->get_overlay_info(ovl, &omap_crtc->info);
+
+	omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void omap_crtc_commit(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	DBG("%s", omap_crtc->ovl->name);
+	omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+		    struct drm_framebuffer *old_fb)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+	DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb);
+
+	update_scanout(crtc);
+
+	return commit(crtc);
+}
+
+static void omap_crtc_load_lut(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	DBG("%s", omap_crtc->ovl->name);
+}
+
+static void page_flip_cb(void *arg)
+{
+	struct drm_crtc *crtc = arg;
+	struct drm_device *dev = crtc->dev;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	struct drm_pending_vblank_event *event = omap_crtc->event;
+	struct timeval now;
+	unsigned long flags;
+
+	WARN_ON(!event);
+
+	omap_crtc->event = NULL;
+
+	update_scanout(crtc);
+	commit(crtc);
+
+	/* wakeup userspace */
+	// TODO: this should happen *after* flip.. somehow..
+	if (event) {
+		spin_lock_irqsave(&dev->event_lock, flags);
+		event->event.sequence =
+				drm_vblank_count_and_time(dev, omap_crtc->id, &now);
+		event->event.tv_sec = now.tv_sec;
+		event->event.tv_usec = now.tv_usec;
+		list_add_tail(&event->base.link,
+				&event->base.file_priv->event_list);
+		wake_up_interruptible(&event->base.file_priv->event_wait);
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+}
+
+static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
+		 struct drm_framebuffer *fb,
+		 struct drm_pending_vblank_event *event)
+{
+	struct drm_device *dev = crtc->dev;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+	DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
+
+	if (omap_crtc->event) {
+		dev_err(dev->dev, "already a pending flip\n");
+		return -EINVAL;
+	}
+
+	crtc->fb = fb;
+	omap_crtc->event = event;
+
+	omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ,
+			page_flip_cb, crtc);
+
+	return 0;
+}
+
+int omap_crtc_page_flip(struct drm_crtc *crtc,
+		 struct drm_framebuffer *fb,
+		 struct drm_pending_vblank_event *event)
+{
+	struct drm_device *dev = crtc->dev;
+	int ret;
+
+	mutex_lock(&dev->mode_config.mutex);
+	ret = omap_crtc_page_flip_locked(crtc, fb, event);
+	mutex_unlock(&dev->mode_config.mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(omap_crtc_page_flip);
+
+static const struct drm_crtc_funcs omap_crtc_funcs = {
+	.gamma_set = omap_crtc_gamma_set,
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = omap_crtc_destroy,
+	.page_flip = omap_crtc_page_flip_locked,
+};
+
+static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
+	.dpms = omap_crtc_dpms,
+	.mode_fixup = omap_crtc_mode_fixup,
+	.mode_set = omap_crtc_mode_set,
+	.prepare = omap_crtc_prepare,
+	.commit = omap_crtc_commit,
+	.mode_set_base = omap_crtc_mode_set_base,
+	.load_lut = omap_crtc_load_lut,
+};
+
+struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc)
+{
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	return omap_crtc->ovl;
+}
+
+/* initialize crtc */
+struct drm_crtc * omap_crtc_init(struct drm_device *dev,
+		struct omap_overlay *ovl, int id)
+{
+	struct drm_crtc *crtc = NULL;
+	struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
+
+	DBG("%s", ovl->name);
+
+	if (!omap_crtc) {
+		dev_err(dev->dev, "could not allocate CRTC\n");
+		goto fail;
+	}
+
+	omap_crtc->ovl = ovl;
+	omap_crtc->id = id;
+	crtc = &omap_crtc->base;
+	drm_crtc_init(dev, crtc, &omap_crtc_funcs);
+	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
+
+	return crtc;
+
+fail:
+	if (crtc) {
+		drm_crtc_cleanup(crtc);
+		kfree(omap_crtc);
+	}
+	return NULL;
+}
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
new file mode 100644
index 0000000..720f89e
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -0,0 +1,910 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_drv.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_drm.h>
+#include "omap_drv.h"
+
+#include "drm_crtc_helper.h"
+#include "drm_fb_helper.h"
+
+#define DRIVER_NAME		MODULE_NAME
+#define DRIVER_DESC		"OMAP DRM"
+#define DRIVER_DATE		"20110717"
+#define DRIVER_MAJOR		1
+#define DRIVER_MINOR		0
+#define DRIVER_PATCHLEVEL	0
+
+struct drm_device *drm_device;
+
+LIST_HEAD(plugin_list);
+
+/* keep track of whether we are already loaded.. we may need to call
+ * plugin's load() if they register after we are already loaded
+ */
+static bool loaded = false;
+
+/*
+ * mode config funcs
+ */
+
+/* Notes about mapping DSS and DRM entities:
+ *    CRTC:        overlay
+ *    encoder:     manager.. with some extension to allow one primary CRTC
+ *                 and zero or more video CRTC's to be mapped to one encoder?
+ *    connector:   dssdev.. manager can be attached/detached from different
+ *                 devices
+ */
+
+static void omap_fb_output_poll_changed(struct drm_device *dev)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	DBG("dev=%p", dev);
+	if (priv->fbdev) {
+		drm_fb_helper_hotplug_event(priv->fbdev);
+	}
+}
+
+static struct drm_mode_config_funcs omap_mode_config_funcs = {
+	.fb_create = omap_framebuffer_create,
+	.output_poll_changed = omap_fb_output_poll_changed,
+};
+
+static int get_connector_type(struct omap_dss_device *dssdev)
+{
+	switch (dssdev->type) {
+	case OMAP_DISPLAY_TYPE_HDMI:
+		return DRM_MODE_CONNECTOR_HDMIA;
+	case OMAP_DISPLAY_TYPE_DPI:
+		if (!strcmp(dssdev->name, "dvi"))
+			return DRM_MODE_CONNECTOR_DVID;
+	default:
+		return DRM_MODE_CONNECTOR_Unknown;
+	}
+}
+
+#if 0 /* enable when dss2 supports hotplug */
+static int omap_drm_notifier(struct notifier_block *nb,
+		unsigned long evt, void *arg)
+{
+	switch (evt) {
+	case OMAP_DSS_SIZE_CHANGE:
+	case OMAP_DSS_HOTPLUG_CONNECT:
+	case OMAP_DSS_HOTPLUG_DISCONNECT: {
+		struct drm_device *dev = drm_device;
+		DBG("hotplug event: evt=%d, dev=%p", evt, dev);
+		if (dev) {
+			drm_sysfs_hotplug_event(dev);
+		}
+		return NOTIFY_OK;
+	}
+	default:  /* don't care about other events for now */
+		return NOTIFY_DONE;
+	}
+}
+#endif
+
+static void dump_video_chains(void)
+{
+	int i;
+
+	DBG("dumping video chains: ");
+	for (i = 0; i < omap_dss_get_num_overlays(); i++) {
+		struct omap_overlay *ovl = omap_dss_get_overlay(i);
+		struct omap_overlay_manager *mgr = ovl->manager;
+		struct omap_dss_device *dssdev = mgr ? mgr->device : NULL;
+		if (dssdev) {
+			DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name,
+						dssdev->name);
+		} else if (mgr) {
+			DBG("%d: %s -> %s", i, ovl->name, mgr->name);
+		} else {
+			DBG("%d: %s", i, ovl->name);
+		}
+	}
+}
+
+static int omap_modeset_init(struct drm_device *dev)
+{
+	const struct omap_drm_platform_data *pdata = dev->dev->platform_data;
+	struct omap_drm_private *priv = dev->dev_private;
+	struct omap_dss_device *dssdev = NULL;
+	int i, j;
+	unsigned int connected_connectors = 0;
+
+	/* create encoders for each manager */
+	int create_encoder(int i) {
+		struct omap_overlay_manager *mgr =
+				omap_dss_get_overlay_manager(i);
+		struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
+
+		if (!encoder) {
+			dev_err(dev->dev, "could not create encoder\n");
+			return -ENOMEM;
+		}
+
+		priv->encoders[priv->num_encoders++] = encoder;
+
+		return 0;
+	}
+
+	/* create connectors for each display device */
+	int create_connector(struct omap_dss_device *dssdev) {
+		static struct notifier_block *notifier;
+		struct drm_connector *connector;
+
+		if (!dssdev->driver) {
+			dev_warn(dev->dev, "%s has no driver.. skipping it\n",
+					dssdev->name);
+			return 0;
+		}
+
+		if (!(dssdev->driver->get_timings ||
+					dssdev->driver->read_edid)) {
+			dev_warn(dev->dev, "%s driver does not support "
+				"get_timings or read_edid.. skipping it!\n",
+				dssdev->name);
+			return 0;
+		}
+
+		connector = omap_connector_init(dev,
+				get_connector_type(dssdev), dssdev);
+
+		if (!connector) {
+			dev_err(dev->dev, "could not create connector\n");
+			return -ENOMEM;
+		}
+
+		/* track what is already connected.. rather than looping thru
+		 * all connectors twice later, first for connected then for
+		 * remainder (which could be a race condition if connected
+		 * status changes)
+		 */
+		if (omap_connector_detect(connector, true) ==
+				connector_status_connected) {
+			connected_connectors |= (1 << priv->num_connectors);
+		}
+
+		priv->connectors[priv->num_connectors++] = connector;
+
+#if 0 /* enable when dss2 supports hotplug */
+		notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
+		notifier->notifier_call = omap_drm_notifier;
+		omap_dss_add_notify(dssdev, notifier);
+#else
+		notifier = NULL;
+#endif
+
+		for (j = 0; j < priv->num_encoders; j++) {
+			struct omap_overlay_manager *mgr =
+				omap_encoder_get_manager(priv->encoders[j]);
+			if (mgr->device == dssdev) {
+				drm_mode_connector_attach_encoder(connector,
+						priv->encoders[j]);
+			}
+		}
+
+		return 0;
+	}
+
+	/* create up to max_overlays CRTCs mapping to overlays.. by default,
+	 * connect the overlays to different managers/encoders, giving priority
+	 * to encoders connected to connectors with a detected connection
+	 */
+	int create_crtc(int i) {
+		struct omap_overlay *ovl = omap_dss_get_overlay(i);
+		struct omap_overlay_manager *mgr = NULL;
+		struct drm_crtc *crtc;
+
+		if (ovl->manager) {
+			DBG("disconnecting %s from %s", ovl->name,
+						ovl->manager->name);
+			ovl->unset_manager(ovl);
+		}
+
+		/* find next best connector, ones with detected connection first
+		 */
+		while (j < priv->num_connectors && !mgr) {
+			if (connected_connectors & (1 << j)) {
+				struct drm_encoder * encoder =
+					omap_connector_attached_encoder(
+							priv->connectors[j]);
+				if (encoder) {
+					mgr = omap_encoder_get_manager(encoder);
+				}
+			}
+			j++;
+		}
+
+		/* if we couldn't find another connected connector, lets start
+		 * looking at the unconnected connectors:
+		 */
+		while (j < 2 * priv->num_connectors && !mgr) {
+			int idx = j - priv->num_connectors;
+			if (!(connected_connectors & (1 << idx))) {
+				struct drm_encoder * encoder =
+					omap_connector_attached_encoder(
+							priv->connectors[idx]);
+				if (encoder) {
+					mgr = omap_encoder_get_manager(encoder);
+				}
+			}
+			j++;
+		}
+
+		if (mgr) {
+			DBG("connecting %s to %s", ovl->name, mgr->name);
+			ovl->set_manager(ovl, mgr);
+		}
+
+		crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
+
+		if (!crtc) {
+			dev_err(dev->dev, "could not create CRTC\n");
+			return -ENOMEM;
+		}
+
+		priv->crtcs[priv->num_crtcs++] = crtc;
+
+		return 0;
+	}
+
+	drm_mode_config_init(dev);
+
+	if (pdata) {
+		/* if platform data is provided by the board file, use it to
+		 * control which overlays, managers, and devices we own.
+		 */
+		for (i = 0; i < pdata->mgr_cnt; i++) {
+			create_encoder(pdata->mgr_ids[i]);
+		}
+
+		for (i = 0; i < pdata->dev_cnt; i++) {
+			int m(struct omap_dss_device *dssdev, void *data) {
+				return ! strcmp(dssdev->name, data);
+			}
+			struct omap_dss_device *dssdev =
+				omap_dss_find_device(
+					(void *)pdata->dev_names[i], m);
+			if (!dssdev) {
+				dev_warn(dev->dev, "no such dssdev: %s\n",
+						pdata->dev_names[i]);
+				continue;
+			}
+			create_connector(dssdev);
+		}
+
+		j = 0;
+		for (i = 0; i < pdata->ovl_cnt; i++) {
+			create_crtc(pdata->ovl_ids[i]);
+		}
+	} else {
+		/* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try
+		 * to make educated guesses about everything else
+		 */
+		int max_overlays = min(omap_dss_get_num_overlays(),
+					CONFIG_DRM_OMAP_NUM_CRTCS);
+
+		for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
+			create_encoder(i);
+		}
+
+		for_each_dss_dev(dssdev) {
+			create_connector(dssdev);
+		}
+
+		j = 0;
+		for (i = 0; i < max_overlays; i++) {
+			create_crtc(i);
+		}
+	}
+
+	/* for now keep the mapping of CRTCs and encoders static.. */
+	for (i = 0; i < priv->num_encoders; i++) {
+		struct drm_encoder *encoder = priv->encoders[i];
+		struct omap_overlay_manager *mgr =
+				omap_encoder_get_manager(encoder);
+
+		encoder->possible_crtcs = 0;
+
+		for (j = 0; j < priv->num_crtcs; j++) {
+			struct omap_overlay *ovl =
+					omap_crtc_get_overlay(priv->crtcs[j]);
+			if (ovl->manager == mgr) {
+				encoder->possible_crtcs |= (1 << j);
+			}
+		}
+
+		DBG("%s: possible_crtcs=%08x", mgr->name,
+					encoder->possible_crtcs);
+	}
+
+	dump_video_chains();
+
+	dev->mode_config.min_width = 640;
+	dev->mode_config.min_height = 480;
+
+	/* note: pvr can't currently handle dst surfaces larger than 2k by 2k */
+	dev->mode_config.max_width = 2048;
+	dev->mode_config.max_height = 2048;
+
+	dev->mode_config.funcs = &omap_mode_config_funcs;
+
+	return 0;
+}
+
+static void omap_modeset_free(struct drm_device *dev)
+{
+	drm_mode_config_cleanup(dev);
+}
+
+/*
+ * drm ioctl funcs
+ */
+
+
+static int ioctl_get_param(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct drm_omap_param *args = data;
+
+	DBG("%p: param=%llu", dev, args->param);
+
+	switch (args->param) {
+	case OMAP_PARAM_CHIPSET_ID:
+		args->value = GET_OMAP_TYPE;
+		break;
+	default:
+		DBG("unknown parameter %lld", args->param);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ioctl_set_param(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct drm_omap_param *args = data;
+
+	switch (args->param) {
+	default:
+		DBG("unknown parameter %lld", args->param);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ioctl_get_base(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct drm_omap_get_base *args = data;
+	struct omap_drm_plugin *plugin;
+
+	/* be safe: */
+	args->plugin_name[ARRAY_SIZE(args->plugin_name) - 1] = '\0';
+
+	DBG("%p: plugin_name=%s", dev, args->plugin_name);
+
+	list_for_each_entry(plugin, &plugin_list, list) {
+		if (!strcmp(args->plugin_name, plugin->name)) {
+			args->ioctl_base = plugin->ioctl_base;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int ioctl_gem_new(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct drm_omap_gem_new *args = data;
+	DBG("%p: size=%d, flags=%08x", dev, args->size.bytes, args->flags);
+	return omap_gem_new_handle(dev, file_priv, args->size.bytes,
+			args->flags, &args->handle, &args->offset);
+}
+
+static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct drm_omap_gem_cpu_prep *args = data;
+	struct drm_gem_object *obj;
+	int ret;
+
+	DBG("%p: handle=%d, op=%x", dev, args->handle, args->op);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		return -ENOENT;
+	}
+
+	ret = omap_gem_op_sync(obj, args->op);
+
+	if (!ret) {
+		omap_gem_op_start(obj, args->op);
+	}
+
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct drm_omap_gem_cpu_fini *args = data;
+	struct drm_gem_object *obj;
+	int ret;
+
+	DBG("%p: handle=%d", dev, args->handle);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		return -ENOENT;
+	}
+
+	/* XXX flushy, flushy */
+	ret = 0;
+
+	if (!ret) {
+		omap_gem_op_finish(obj, args->op);
+	}
+
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+static int ioctl_gem_info(struct drm_device *dev, void *data,
+		struct drm_file *file_priv)
+{
+	struct drm_omap_gem_info *args = data;
+	struct drm_gem_object *obj;
+	int ret;
+
+	DBG("%p: handle=%d", dev, args->handle);
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (!obj) {
+		return -ENOENT;
+	}
+
+	args->offset = omap_gem_mmap_offset(obj);
+
+	drm_gem_object_unreference_unlocked(obj);
+
+	return ret;
+}
+
+struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
+	DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, DRM_UNLOCKED|DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+	DRM_IOCTL_DEF_DRV(OMAP_GET_BASE, ioctl_get_base, DRM_UNLOCKED|DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH),
+};
+
+/*
+ * drm driver funcs
+ */
+
+/**
+ * load - setup chip and create an initial config
+ * @dev: DRM device
+ * @flags: startup flags
+ *
+ * The driver load routine has to do several things:
+ *   - initialize the memory manager
+ *   - allocate initial config memory
+ *   - setup the DRM framebuffer with the allocated memory
+ */
+static int dev_load(struct drm_device *dev, unsigned long flags)
+{
+	struct omap_drm_private *priv;
+	struct omap_drm_plugin *plugin;
+	int ret;
+
+	DBG("load: dev=%p", dev);
+
+	drm_device = dev;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(dev->dev, "could not allocate priv\n");
+		return -1;
+	}
+
+	dev->dev_private = priv;
+
+	ret = omap_modeset_init(dev);
+	if (ret) {
+		dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
+	}
+
+	priv->fbdev = omap_fbdev_init(dev);
+	if (!priv->fbdev) {
+		dev_err(dev->dev, "omap_fbdev_init failed\n");
+	}
+
+	drm_kms_helper_poll_init(dev);
+
+	ret = drm_vblank_init(dev, priv->num_crtcs);
+	if (ret) {
+		dev_err(dev->dev, "could not init vblank\n");
+	}
+
+	loaded = true;
+
+	list_for_each_entry(plugin, &plugin_list, list) {
+		ret = plugin->load(dev, flags);
+	}
+
+	return 0;
+}
+
+static int dev_unload(struct drm_device *dev)
+{
+	struct omap_drm_plugin *plugin;
+	int ret;
+
+	DBG("unload: dev=%p", dev);
+
+	list_for_each_entry(plugin, &plugin_list, list) {
+		ret = plugin->unload(dev);
+	}
+
+	drm_vblank_cleanup(dev);
+	drm_kms_helper_poll_fini(dev);
+
+	omap_fbdev_free(dev);
+
+	omap_modeset_free(dev);
+
+	kfree(dev->dev_private);
+	dev->dev_private = NULL;
+
+	loaded = false;
+
+	return 0;
+}
+
+static int dev_open(struct drm_device *dev, struct drm_file *file)
+{
+	struct omap_drm_plugin *plugin;
+	bool found_pvr = false;
+	int ret;
+
+	file->driver_priv = NULL;
+
+	DBG("open: dev=%p, file=%p", dev, file);
+
+	list_for_each_entry(plugin, &plugin_list, list) {
+		if (!strcmp(DRIVER_NAME "_pvr", plugin->name)) {
+			found_pvr = true;
+			break;
+		}
+	}
+
+	if (!found_pvr) {
+		DBG("open: PVR submodule not loaded.. let's try now");
+		request_module(DRIVER_NAME "_pvr");
+	}
+
+	list_for_each_entry(plugin, &plugin_list, list) {
+		ret = plugin->open(dev, file);
+	}
+
+	return 0;
+}
+
+static int dev_firstopen(struct drm_device *dev)
+{
+	DBG("firstopen: dev=%p", dev);
+	return 0;
+}
+
+/**
+ * lastclose - clean up after all DRM clients have exited
+ * @dev: DRM device
+ *
+ * Take care of cleaning up after all DRM clients have exited.  In the
+ * mode setting case, we want to restore the kernel's initial mode (just
+ * in case the last client left us in a bad state).
+ *
+ * Additionally, in the non-mode setting case, we'll tear down the AGP
+ * and DMA structures, since the kernel won't be using them, and clean
+ * up any GEM state.
+ */
+static void dev_lastclose(struct drm_device * dev)
+{
+	DBG("lastclose: dev=%p", dev);
+}
+
+static void dev_preclose(struct drm_device * dev, struct drm_file *file)
+{
+	DBG("preclose: dev=%p", dev);
+}
+
+static void dev_postclose(struct drm_device *dev, struct drm_file *file)
+{
+	struct omap_drm_plugin *plugin;
+	int ret;
+
+	DBG("postclose: dev=%p, file=%p", dev, file);
+
+	list_for_each_entry(plugin, &plugin_list, list) {
+		ret = plugin->release(dev, file);
+	}
+
+	return;
+}
+
+/**
+ * enable_vblank - enable vblank interrupt events
+ * @dev: DRM device
+ * @crtc: which irq to enable
+ *
+ * Enable vblank interrupts for @crtc.  If the device doesn't have
+ * a hardware vblank counter, this routine should be a no-op, since
+ * interrupts will have to stay on to keep the count accurate.
+ *
+ * RETURNS
+ * Zero on success, appropriate errno if the given @crtc's vblank
+ * interrupt cannot be enabled.
+ */
+static int dev_enable_vblank(struct drm_device *dev, int crtc)
+{
+	DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc);
+	return 0;
+}
+
+/**
+ * disable_vblank - disable vblank interrupt events
+ * @dev: DRM device
+ * @crtc: which irq to enable
+ *
+ * Disable vblank interrupts for @crtc.  If the device doesn't have
+ * a hardware vblank counter, this routine should be a no-op, since
+ * interrupts will have to stay on to keep the count accurate.
+ */
+static void dev_disable_vblank(struct drm_device *dev, int crtc)
+{
+	DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc);
+}
+
+static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS)
+{
+	return IRQ_HANDLED;
+}
+
+static void dev_irq_preinstall(struct drm_device *dev)
+{
+	DBG("irq_preinstall: dev=%p", dev);
+}
+
+static int dev_irq_postinstall(struct drm_device *dev)
+{
+	DBG("irq_postinstall: dev=%p", dev);
+	return 0;
+}
+
+static void dev_irq_uninstall(struct drm_device *dev)
+{
+	DBG("irq_uninstall: dev=%p", dev);
+}
+
+static struct vm_operations_struct omap_gem_vm_ops = {
+	.fault = omap_gem_fault,
+	.open = omap_gem_vm_open,
+	.close = omap_gem_vm_close,
+};
+
+static struct drm_driver omap_drm_driver = {
+		.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM,
+		.load = dev_load,
+		.unload = dev_unload,
+		.open = dev_open,
+		.firstopen = dev_firstopen,
+		.lastclose = dev_lastclose,
+		.preclose = dev_preclose,
+		.postclose = dev_postclose,
+		.get_vblank_counter = drm_vblank_count,
+		.enable_vblank = dev_enable_vblank,
+		.disable_vblank = dev_disable_vblank,
+		.irq_preinstall = dev_irq_preinstall,
+		.irq_postinstall = dev_irq_postinstall,
+		.irq_uninstall = dev_irq_uninstall,
+		.irq_handler = dev_irq_handler,
+		.reclaim_buffers = drm_core_reclaim_buffers,
+		.gem_init_object = omap_gem_init_object,
+		.gem_free_object = omap_gem_free_object,
+		.gem_vm_ops = &omap_gem_vm_ops,
+		.dumb_create = omap_gem_dumb_create,
+		.dumb_map_offset = omap_gem_dumb_map_offset,
+		.dumb_destroy = omap_gem_dumb_destroy,
+		.ioctls = ioctls,
+		.num_ioctls = DRM_OMAP_NUM_IOCTLS,
+		.fops = {
+				.owner = THIS_MODULE,
+				.open = drm_open,
+				.unlocked_ioctl = drm_ioctl,
+				.release = drm_release,
+				.mmap = omap_gem_mmap,
+				.poll = drm_poll,
+				.fasync = drm_fasync,
+				.read = drm_read,
+				.llseek = noop_llseek,
+		},
+		.name = DRIVER_NAME,
+		.desc = DRIVER_DESC,
+		.date = DRIVER_DATE,
+		.major = DRIVER_MAJOR,
+		.minor = DRIVER_MINOR,
+		.patchlevel = DRIVER_PATCHLEVEL,
+};
+
+int omap_drm_register_plugin(struct omap_drm_plugin *plugin)
+{
+	struct drm_device *dev = drm_device;
+	static int ioctl_base = DRM_OMAP_NUM_IOCTLS;
+	int i;
+
+	DBG("register plugin: %p (%s)", plugin, plugin->name);
+
+	plugin->ioctl_base = ioctl_base;
+
+	list_add_tail(&plugin->list, &plugin_list);
+
+	/* register the plugin's ioctl's */
+	for (i = 0; i < plugin->num_ioctls; i++) {
+		int nr = i + ioctl_base;
+
+		/* check for out of bounds ioctl nr or already registered ioctl */
+		if (nr > ARRAY_SIZE(ioctls) || ioctls[nr].func) {
+			dev_err(dev->dev, "invalid ioctl: %d (nr=%d)\n", i, nr);
+			return -EINVAL;
+		}
+
+		DBG("register ioctl: %d %08x", nr, plugin->ioctls[i].cmd);
+
+		ioctls[nr] = plugin->ioctls[i];
+
+		if (nr >= omap_drm_driver.num_ioctls) {
+			omap_drm_driver.num_ioctls = nr + 1;
+		}
+	}
+
+	ioctl_base += plugin->num_ioctls;
+
+	if (loaded) {
+		plugin->load(dev, 0);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(omap_drm_register_plugin);
+
+int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin)
+{
+	list_del(&plugin->list);
+	/* TODO remove ioctl fxns */
+	return 0;
+}
+EXPORT_SYMBOL(omap_drm_unregister_plugin);
+
+static int nmappers = 0;
+
+/* create buffer mapper id, to access per-mapper private data.  See
+ * omap_gem_{get,set}_priv().
+ */
+int omap_drm_register_mapper(void)
+{
+	if (nmappers >= MAX_MAPPERS) {
+		return -ENOMEM;
+	}
+	return nmappers++;
+}
+EXPORT_SYMBOL(omap_drm_register_mapper);
+
+/* retire a mapper id, previously acquired from omap_drm_register_mapper()
+ */
+void omap_drm_unregister_mapper(int mapper_id)
+{
+	/* currently no-op.. */
+}
+EXPORT_SYMBOL(omap_drm_unregister_mapper);
+
+static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
+{
+	DBG("");
+	return 0;
+}
+
+static int pdev_resume(struct platform_device *device)
+{
+	DBG("");
+	return 0;
+}
+
+static void pdev_shutdown(struct platform_device *device)
+{
+	DBG("");
+}
+
+static int pdev_probe(struct platform_device *device)
+{
+	DBG("%s", device->name);
+	return drm_platform_init(&omap_drm_driver, device);
+}
+
+static int pdev_remove(struct platform_device *device)
+{
+	DBG("");
+	drm_platform_exit(&omap_drm_driver, device);
+	return 0;
+}
+
+struct platform_driver pdev = {
+		.driver = {
+			.name = DRIVER_NAME,
+			.owner = THIS_MODULE,
+		},
+		.probe = pdev_probe,
+		.remove = pdev_remove,
+		.suspend = pdev_suspend,
+		.resume = pdev_resume,
+		.shutdown = pdev_shutdown,
+};
+
+static int __init omap_drm_init(void)
+{
+	DBG("init");
+	return platform_driver_register(&pdev);
+}
+
+static void __exit omap_drm_fini(void)
+{
+	DBG("fini");
+	platform_driver_unregister(&pdev);
+}
+
+/* need late_initcall() so we load after dss_driver's are loaded */
+late_initcall(omap_drm_init);
+module_exit(omap_drm_fini);
+
+MODULE_AUTHOR("Rob Clark <rob@ti.com>");
+MODULE_DESCRIPTION("OMAP DRM Display Driver");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
new file mode 100644
index 0000000..d17e3a8
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_drv.h
@@ -0,0 +1,117 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_drv.h
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OMAP_DRV_H__
+#define __OMAP_DRV_H__
+
+#include <video/omapdss.h>
+#include <linux/module.h>
+
+#define DBG(fmt,...)  DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
+#define VERB(fmt,...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
+
+#define MODULE_NAME     "omapdrm"
+
+/* max # of mapper-id's that can be assigned.. todo, come up with a better
+ * (but still inexpensive) way to store/access per-buffer mapper private
+ * data..
+ */
+#define MAX_MAPPERS 2
+
+struct omap_drm_private {
+	int num_crtcs;
+	struct drm_crtc *crtcs[8];
+	int num_encoders;
+	struct drm_encoder *encoders[8];
+	int num_connectors;
+	struct drm_connector *connectors[8];
+
+	struct drm_fb_helper *fbdev;
+};
+
+struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev);
+void omap_fbdev_free(struct drm_device *dev);
+
+struct drm_crtc * omap_crtc_init(struct drm_device *dev,
+		struct omap_overlay *ovl, int id);
+struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc);
+int omap_crtc_page_flip(struct drm_crtc *crtc,
+		 struct drm_framebuffer *fb,
+		 struct drm_pending_vblank_event *event);
+
+struct drm_encoder * omap_encoder_init(struct drm_device *dev,
+		struct omap_overlay_manager *mgr);
+struct omap_overlay_manager * omap_encoder_get_manager(
+		struct drm_encoder *encoder);
+struct drm_encoder * omap_connector_attached_encoder (
+		struct drm_connector *connector);
+enum drm_connector_status omap_connector_detect(
+		struct drm_connector *connector, bool force);
+int omap_encoder_wait_for_vsync(struct drm_encoder *encoder);
+
+struct drm_connector * omap_connector_init(struct drm_device *dev,
+		int connector_type, struct omap_dss_device *dssdev);
+void omap_connector_mode_set(struct drm_connector *connector,
+		struct drm_display_mode *mode);
+void omap_connector_flush(struct drm_connector *connector,
+		int x, int y, int w, int h);
+void omap_connector_dpms(struct drm_connector *connector, int mode);
+int omap_connector_sync(struct drm_connector *connector);
+
+struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
+		struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd);
+struct drm_gem_object * omap_framebuffer_bo(struct drm_framebuffer *fb);
+struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev,
+		struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo);
+int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
+		void **vaddr, unsigned long *paddr, int *screen_width);
+struct drm_connector * omap_framebuffer_get_next_connector(
+		struct drm_framebuffer *fb, struct drm_connector *from);
+void omap_framebuffer_flush(struct drm_framebuffer *fb,
+		int x, int y, int w, int h);
+
+struct drm_gem_object *omap_gem_new(struct drm_device *dev,
+						  size_t size, uint32_t flags);
+int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
+		size_t size, uint32_t flags, uint32_t *handle, uint64_t *offset);
+void omap_gem_free_object(struct drm_gem_object *obj);
+int omap_gem_init_object(struct drm_gem_object *obj);
+int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+			 uint32_t handle, uint64_t *offset);
+int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
+			uint32_t handle);
+int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+			struct drm_mode_create_dumb *args);
+int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+void omap_gem_vm_open(struct vm_area_struct *vma);
+void omap_gem_vm_close(struct vm_area_struct *vma);
+
+static inline int align_pitch(int pitch, int width, int bpp)
+{
+	int bytespp = (bpp + 7) / 8;
+	/* in case someone tries to feed us a completely bogus stride: */
+	pitch = max(pitch, width * bytespp);
+	/* PVR needs alignment to 8 pixels.. right now that is the most
+	 * restrictive stride requirement..
+	 */
+	return ALIGN(pitch, 8 * bytespp);
+}
+
+#endif /* __OMAP_DRV_H__ */
diff --git a/drivers/staging/omapdrm/omap_encoder.c b/drivers/staging/omapdrm/omap_encoder.c
new file mode 100644
index 0000000..810c7e2
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_encoder.c
@@ -0,0 +1,198 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_encoder.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_drm.h>
+#include "omap_drv.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/*
+ * encoder funcs
+ */
+
+#define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
+
+struct omap_encoder {
+	struct drm_encoder base;
+	struct omap_overlay_manager *mgr;
+};
+
+static void omap_encoder_destroy(struct drm_encoder *encoder)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	DBG("%s", omap_encoder->mgr->name);
+	drm_encoder_cleanup(encoder);
+	kfree(omap_encoder);
+}
+
+static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct omap_drm_private *priv = dev->dev_private;
+	int i;
+
+	DBG("%s: %d", omap_encoder->mgr->name, mode);
+
+	/* managers don't need to do anything for DPMS.. but we do
+	 * need to propagate to the connector, who is actually going
+	 * to enable/disable as needed:
+	 */
+	for (i = 0; i < priv->num_connectors; i++) {
+		struct drm_connector *connector = priv->connectors[i];
+		if (connector->encoder == encoder) {
+			omap_connector_dpms(connector, mode);
+		}
+	}
+}
+
+static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
+				  struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	DBG("%s", omap_encoder->mgr->name);
+	return true;
+}
+
+static void omap_encoder_mode_set(struct drm_encoder *encoder,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct omap_drm_private *priv = dev->dev_private;
+	int i;
+
+	mode = adjusted_mode;
+
+	DBG("%s: set mode: %dx%d", omap_encoder->mgr->name,
+			mode->hdisplay, mode->vdisplay);
+
+	for (i = 0; i < priv->num_connectors; i++) {
+		struct drm_connector *connector = priv->connectors[i];
+		if (connector->encoder == encoder) {
+			omap_connector_mode_set(connector, mode);
+		}
+	}
+}
+
+static void omap_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	struct drm_encoder_helper_funcs *encoder_funcs =
+				encoder->helper_private;
+	DBG("%s", omap_encoder->mgr->name);
+	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void omap_encoder_commit(struct drm_encoder *encoder)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	struct drm_encoder_helper_funcs *encoder_funcs =
+				encoder->helper_private;
+	DBG("%s", omap_encoder->mgr->name);
+	omap_encoder->mgr->apply(omap_encoder->mgr);
+	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static const struct drm_encoder_funcs omap_encoder_funcs = {
+	.destroy = omap_encoder_destroy,
+};
+
+static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
+	.dpms = omap_encoder_dpms,
+	.mode_fixup = omap_encoder_mode_fixup,
+	.mode_set = omap_encoder_mode_set,
+	.prepare = omap_encoder_prepare,
+	.commit = omap_encoder_commit,
+};
+
+struct omap_overlay_manager * omap_encoder_get_manager(
+		struct drm_encoder *encoder)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	return omap_encoder->mgr;
+}
+
+/* maybe this could go away and we just use drm_vblank_wait()? */
+int omap_encoder_wait_for_vsync(struct drm_encoder *encoder)
+{
+	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	DBG("%s", omap_encoder->mgr->name);
+	return omap_encoder->mgr->wait_for_vsync(omap_encoder->mgr);
+}
+EXPORT_SYMBOL(omap_encoder_wait_for_vsync);
+
+/* initialize encoder */
+struct drm_encoder * omap_encoder_init(struct drm_device *dev,
+		struct omap_overlay_manager *mgr)
+{
+	struct drm_encoder *encoder = NULL;
+	struct omap_encoder *omap_encoder;
+	struct omap_overlay_manager_info info;
+	int ret;
+
+	DBG("%s", mgr->name);
+
+	omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
+	if (!omap_encoder) {
+		dev_err(dev->dev, "could not allocate encoder\n");
+		goto fail;
+	}
+
+	omap_encoder->mgr = mgr;
+	encoder = &omap_encoder->base;
+
+	drm_encoder_init(dev, encoder, &omap_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
+
+	mgr->get_manager_info(mgr, &info);
+
+	/* TODO: fix hard-coded setup.. */
+	info.default_color = 0x00000000;
+	info.trans_key = 0x00000000;
+	info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+	info.trans_enabled = false;
+	info.alpha_enabled = true;
+
+	ret = mgr->set_manager_info(mgr, &info);
+	if (ret) {
+		dev_err(dev->dev, "could not set manager info\n");
+		goto fail;
+	}
+
+	ret = mgr->apply(mgr);
+	if (ret) {
+		dev_err(dev->dev, "could not apply\n");
+		goto fail;
+	}
+
+	return encoder;
+
+fail:
+	if (encoder) {
+		drm_encoder_cleanup(encoder);
+		kfree(omap_encoder);
+	}
+
+	return NULL;
+}
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c
new file mode 100644
index 0000000..42bc146
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_fb.c
@@ -0,0 +1,265 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_fb.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_drm.h>
+#include "omap_drv.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+
+/*
+ * framebuffer funcs
+ */
+
+#define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base)
+
+struct omap_framebuffer {
+	struct drm_framebuffer base;
+	struct drm_gem_object *bo;
+	int size;
+	void __iomem *vaddr;
+	unsigned long paddr;
+};
+
+static int omap_framebuffer_create_handle(struct drm_framebuffer *fb,
+		struct drm_file *file_priv,
+		unsigned int *handle)
+{
+	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+    return drm_gem_handle_create(file_priv, omap_fb->bo, handle);
+}
+
+static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+	struct drm_device *dev = fb->dev;
+	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+
+	DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
+
+	drm_framebuffer_cleanup(fb);
+
+	if (omap_fb->vaddr) {
+		iounmap(omap_fb->vaddr);
+	}
+
+	if (omap_gem_put_paddr(omap_fb->bo)) {
+		dev_err(dev->dev, "could not unmap!\n");
+	}
+
+	if (omap_fb->bo) {
+		drm_gem_object_unreference_unlocked(omap_fb->bo);
+	}
+
+	kfree(omap_fb);
+}
+
+static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
+		struct drm_file *file_priv, unsigned flags, unsigned color,
+		struct drm_clip_rect *clips, unsigned num_clips)
+{
+	int i;
+
+	for (i = 0; i < num_clips; i++) {
+		omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
+					clips[i].x2 - clips[i].x1,
+					clips[i].y2 - clips[i].y1);
+	}
+
+	return 0;
+}
+
+static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
+	.create_handle = omap_framebuffer_create_handle,
+	.destroy = omap_framebuffer_destroy,
+	.dirty = omap_framebuffer_dirty,
+};
+
+int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y,
+		void **vaddr, unsigned long *paddr, int *screen_width)
+{
+	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+	int bpp = fb->depth / 8;
+	unsigned long offset;
+
+	offset = (x * bpp) + (y * fb->pitch);
+
+	if (vaddr) {
+		if (!omap_fb->vaddr) {
+			omap_fb->vaddr = ioremap_wc(omap_fb->paddr, omap_fb->size);
+		}
+		*vaddr = omap_fb->vaddr + offset;
+	}
+
+	*paddr = omap_fb->paddr + offset;
+	*screen_width = fb->pitch / bpp;
+
+	return omap_fb->size - offset;
+}
+EXPORT_SYMBOL(omap_framebuffer_get_buffer);
+
+struct drm_gem_object * omap_framebuffer_bo(struct drm_framebuffer *fb)
+{
+	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+	return omap_fb->bo;
+}
+
+/* iterate thru all the connectors, returning ones that are attached
+ * to the same fb..
+ */
+struct drm_connector * omap_framebuffer_get_next_connector(
+		struct drm_framebuffer *fb, struct drm_connector *from)
+{
+	struct drm_device *dev = fb->dev;
+	struct list_head *connector_list = &dev->mode_config.connector_list;
+	struct drm_connector *connector = from;
+
+	if (!from) {
+		return list_first_entry(connector_list, typeof(*from), head);
+	}
+
+	list_for_each_entry_from(connector, connector_list, head) {
+		if (connector != from) {
+			struct drm_encoder *encoder = connector->encoder;
+			struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
+			if (crtc && crtc->fb == fb) {
+				return connector;
+			}
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(omap_framebuffer_get_next_connector);
+
+/* flush an area of the framebuffer (in case of manual update display that
+ * is not automatically flushed)
+ */
+void omap_framebuffer_flush(struct drm_framebuffer *fb,
+		int x, int y, int w, int h)
+{
+	struct drm_connector *connector = NULL;
+
+	VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
+
+	while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
+		/* only consider connectors that are part of a chain */
+		if (connector->encoder && connector->encoder->crtc) {
+			/* TODO: maybe this should propagate thru the crtc who
+			 * could do the coordinate translation..
+			 */
+			struct drm_crtc *crtc = connector->encoder->crtc;
+			int cx = max(0, x - crtc->x);
+			int cy = max(0, y - crtc->y);
+			int cw = w + (x - crtc->x) - cx;
+			int ch = h + (y - crtc->y) - cy;
+
+			omap_connector_flush(connector, cx, cy, cw, ch);
+		}
+	}
+}
+EXPORT_SYMBOL(omap_framebuffer_flush);
+
+struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev,
+        struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd)
+{
+	struct drm_gem_object *bo;
+	struct drm_framebuffer *fb;
+	bo = drm_gem_object_lookup(dev, file, mode_cmd->handle);
+	if (!bo) {
+		return ERR_PTR(-ENOENT);
+	}
+	fb = omap_framebuffer_init(dev, mode_cmd, bo);
+	if (!fb) {
+		return ERR_PTR(-ENOMEM);
+	}
+	return fb;
+}
+
+struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev,
+		struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo)
+{
+	struct omap_framebuffer *omap_fb;
+	struct drm_framebuffer *fb = NULL;
+	int size, ret;
+
+	DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%d)", dev,
+			mode_cmd, mode_cmd->width, mode_cmd->height, mode_cmd->bpp);
+
+	/* in case someone tries to feed us a completely bogus stride: */
+	mode_cmd->pitch = align_pitch(mode_cmd->pitch,
+			mode_cmd->width, mode_cmd->bpp);
+
+	omap_fb = kzalloc(sizeof(*omap_fb), GFP_KERNEL);
+	if (!omap_fb) {
+		dev_err(dev->dev, "could not allocate fb\n");
+		goto fail;
+	}
+
+	fb = &omap_fb->base;
+	ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs);
+	if (ret) {
+		dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
+		goto fail;
+	}
+
+	DBG("create: FB ID: %d (%p)", fb->base.id, fb);
+
+	size = PAGE_ALIGN(mode_cmd->pitch * mode_cmd->height);
+
+	if (bo) {
+		DBG("using existing %d byte buffer (needed %d)", bo->size, size);
+		if (size > bo->size) {
+			dev_err(dev->dev, "provided buffer object is too small!\n");
+			goto fail;
+		}
+	} else {
+		/* for convenience of all the various callers who don't want
+		 * to be bothered to allocate their own buffer..
+		 */
+		DBG("allocating %d bytes for fb %d", size, dev->primary->index);
+		bo = omap_gem_new(dev, size, OMAP_BO_SCANOUT | OMAP_BO_WC);
+		if (!bo) {
+			dev_err(dev->dev, "failed to allocate buffer object\n");
+			goto fail;
+		}
+	}
+
+	omap_fb->bo = bo;
+	omap_fb->size = size;
+
+	/* TODO get paddr when buffer is being scanned out, put paddr when it
+	 * is no longer scanned out..
+	 */
+	if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) {
+		dev_err(dev->dev, "could not map!\n");
+		goto fail;
+	}
+
+	drm_helper_mode_fill_fb_struct(fb, mode_cmd);
+
+	return fb;
+
+fail:
+	if (fb) {
+		omap_framebuffer_destroy(fb);
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(omap_framebuffer_init);
diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c
new file mode 100644
index 0000000..ca6c28c
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_fbdev.c
@@ -0,0 +1,303 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_fbdev.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/omap_drm.h>
+#include "omap_drv.h"
+
+#include "drm_crtc.h"
+#include "drm_fb_helper.h"
+
+/*
+ * fbdev funcs, to implement legacy fbdev interface on top of drm driver
+ */
+
+#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
+
+struct omap_fbdev {
+	struct drm_fb_helper base;
+	struct drm_framebuffer *fb;
+};
+
+static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
+
+static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	ssize_t res;
+
+	res = fb_sys_write(fbi, buf, count, ppos);
+	omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
+
+	return res;
+}
+
+static void omap_fbdev_fillrect(struct fb_info *fbi,
+		const struct fb_fillrect *rect)
+{
+	sys_fillrect(fbi, rect);
+	omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
+}
+
+static void omap_fbdev_copyarea(struct fb_info *fbi,
+		const struct fb_copyarea *area)
+{
+	sys_copyarea(fbi, area);
+	omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
+}
+
+static void omap_fbdev_imageblit(struct fb_info *fbi,
+		const struct fb_image *image)
+{
+	sys_imageblit(fbi, image);
+	omap_fbdev_flush(fbi, image->dx, image->dy,
+				image->width, image->height);
+}
+
+static struct fb_ops omap_fb_ops = {
+	.owner = THIS_MODULE,
+
+	/* Note: to properly handle manual update displays, we wrap the
+	 * basic fbdev ops which write to the framebuffer
+	 */
+	.fb_read = fb_sys_read,
+	.fb_write = omap_fbdev_write,
+	.fb_fillrect = omap_fbdev_fillrect,
+	.fb_copyarea = omap_fbdev_copyarea,
+	.fb_imageblit = omap_fbdev_imageblit,
+
+	.fb_check_var = drm_fb_helper_check_var,
+	.fb_set_par = drm_fb_helper_set_par,
+	.fb_pan_display = drm_fb_helper_pan_display,
+	.fb_blank = drm_fb_helper_blank,
+	.fb_setcmap = drm_fb_helper_setcmap,
+
+	.fb_debug_enter = drm_fb_helper_debug_enter,
+	.fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+static int omap_fbdev_create(struct drm_fb_helper *helper,
+		struct drm_fb_helper_surface_size *sizes)
+{
+	struct omap_fbdev *fbdev = to_omap_fbdev(helper);
+	struct drm_device *dev = helper->dev;
+	struct drm_framebuffer *fb;
+	struct fb_info *fbi;
+	struct drm_mode_fb_cmd mode_cmd = {0};
+	unsigned long paddr;
+	void __iomem *vaddr;
+	int size, screen_width;
+	int ret;
+
+	/* only doing ARGB32 since this is what is needed to alpha-blend
+	 * with video overlays:
+	 */
+	sizes->surface_bpp = 32;
+	sizes->surface_depth = 32;
+
+	DBG("create fbdev: %dx%d@%d", sizes->surface_width,
+			sizes->surface_height, sizes->surface_bpp);
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+
+	mode_cmd.bpp = sizes->surface_bpp;
+	mode_cmd.depth = sizes->surface_depth;
+
+	mutex_lock(&dev->struct_mutex);
+
+	fbi = framebuffer_alloc(0, dev->dev);
+	if (!fbi) {
+		dev_err(dev->dev, "failed to allocate fb info\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	DBG("fbi=%p, dev=%p", fbi, dev);
+
+	fbdev->fb = omap_framebuffer_init(dev, &mode_cmd, NULL);
+	if (!fbdev->fb) {
+		dev_err(dev->dev, "failed to allocate fb\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	fb = fbdev->fb;
+	helper->fb = fb;
+	helper->fbdev = fbi;
+
+	fbi->par = helper;
+	fbi->flags = FBINFO_DEFAULT;
+	fbi->fbops = &omap_fb_ops;
+
+	strcpy(fbi->fix.id, MODULE_NAME);
+
+	ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+	if (ret) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
+	drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+	size = omap_framebuffer_get_buffer(fb, 0, 0,
+			&vaddr, &paddr, &screen_width);
+
+	dev->mode_config.fb_base = paddr;
+
+	fbi->screen_base = vaddr;
+	fbi->screen_size = size;
+	fbi->fix.smem_start = paddr;
+	fbi->fix.smem_len = size;
+
+	DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
+	DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
+
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+
+fail:
+	mutex_unlock(&dev->struct_mutex);
+	// TODO cleanup?
+	return ret;
+}
+
+static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
+		u16 red, u16 green, u16 blue, int regno)
+{
+	DBG("fbdev: set gamma");
+}
+
+static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
+		u16 *red, u16 *green, u16 *blue, int regno)
+{
+	DBG("fbdev: get gamma");
+}
+
+static int omap_fbdev_probe(struct drm_fb_helper *helper,
+		struct drm_fb_helper_surface_size *sizes)
+{
+	int new_fb = 0;
+	int ret;
+
+	if (!helper->fb) {
+		ret = omap_fbdev_create(helper, sizes);
+		if (ret)
+			return ret;
+		new_fb = 1;
+	}
+	return new_fb;
+}
+
+static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
+	.gamma_set = omap_crtc_fb_gamma_set,
+	.gamma_get = omap_crtc_fb_gamma_get,
+	.fb_probe = omap_fbdev_probe,
+};
+
+static struct drm_fb_helper * get_fb(struct fb_info *fbi)
+{
+	if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
+		/* these are not the fb's you're looking for */
+		return NULL;
+	}
+	return fbi->par;
+}
+
+/* flush an area of the framebuffer (in case of manual update display that
+ * is not automatically flushed)
+ */
+static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
+{
+	struct drm_fb_helper *helper = get_fb(fbi);
+
+	if (!helper)
+		return;
+
+	VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
+
+	omap_framebuffer_flush(helper->fb, x, y, w, h);
+}
+EXPORT_SYMBOL(omap_fbdev_flush);
+
+/* initialize fbdev helper */
+struct drm_fb_helper * omap_fbdev_init(struct drm_device *dev)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	struct omap_fbdev *fbdev = NULL;
+	struct drm_fb_helper *helper;
+	int ret = 0;
+
+	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+	if (!fbdev) {
+		dev_err(dev->dev, "could not allocate fbdev\n");
+		goto fail;
+	}
+
+	helper = &fbdev->base;
+
+	helper->funcs = &omap_fb_helper_funcs;
+
+	ret = drm_fb_helper_init(dev, helper,
+			priv->num_crtcs, priv->num_connectors);
+	if (ret) {
+		dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
+		goto fail;
+	}
+
+	drm_fb_helper_single_add_all_connectors(helper);
+	drm_fb_helper_initial_config(helper, 32);
+
+	priv->fbdev = helper;
+
+	return helper;
+
+fail:
+	if (fbdev) {
+		kfree(fbdev);
+	}
+	return NULL;
+}
+
+void omap_fbdev_free(struct drm_device *dev)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	struct drm_fb_helper *helper = priv->fbdev;
+	struct omap_fbdev *fbdev;
+	struct fb_info *fbi;
+
+	DBG();
+
+	fbi = helper->fbdev;
+
+	unregister_framebuffer(fbi);
+	framebuffer_release(fbi);
+
+	drm_fb_helper_fini(helper);
+
+	fbdev = to_omap_fbdev(priv->fbdev);
+
+	kfree(fbdev);
+
+	priv->fbdev = NULL;
+}
+
diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c
new file mode 100644
index 0000000..b172c49
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_gem.c
@@ -0,0 +1,851 @@ 
+/*
+ * linux/drivers/staging/omapdrm/omap_gem.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <plat/vram.h>
+#include <linux/spinlock.h>
+#include <linux/shmem_fs.h>
+
+#include <linux/omap_drm.h>
+#include "omap_drv.h"
+
+/*
+ * GEM buffer object implementation.
+ */
+
+#define to_omap_bo(x) container_of(x, struct omap_gem_object, base)
+
+/* note: we use upper 8 bits of flags for driver-internal flags: */
+#define OMAP_BO_DMA		0x01000000	/* actually is physically contiguous */
+#define OMAP_BO_EXT_SYNC	0x02000000	/* externally allocated sync object */
+#define OMAP_BO_EXT_MEM	0x04000000	/* externally allocated memory */
+
+
+struct omap_gem_object {
+	struct drm_gem_object base;
+
+	uint32_t flags;
+
+	/**
+	 * if buffer is physically contiguous or remapped in TILER, the
+	 * OMAP_BO_DMA flag is set and the paddr is valid.
+	 *
+	 * Note that OMAP_BO_SCANOUT is a hint from userspace that DMA capable
+	 * buffer is requested, but doesn't mean that it is.  Use the
+	 * OMAP_BO_DMA flag to determine if the buffer has a DMA capable
+	 * physical address.
+	 */
+	unsigned long paddr;
+
+	/**
+	 * Array of backing pages, if allocated.  Note that pages are never
+	 * allocated for buffers originally allocated from contiguous memory
+	 * (which will have obj->filp==NULL)
+	 */
+	struct page **pages;
+
+	/* note: intentionally nothing in here about vaddr right now..
+	 * currently everything that might need a virtual address has a
+	 * drm_framebuffer wrapping the GEM bo, so virtual mapping is
+	 * handled there..
+	 *
+	 * But if there are other cases where kernel virtual mapping is
+	 * needed, then I may revisit this..
+	 */
+
+	/**
+	 * sync-object allocated on demand (if needed)
+	 */
+	struct {
+		uint32_t write_pending;
+		uint32_t write_complete;
+		uint32_t read_pending;
+		uint32_t read_complete;
+	} *sync;
+
+	struct omap_gem_vm_ops *ops;
+
+	/**
+	 * per-mapper private data..
+	 *
+	 * TODO maybe there can be a more flexible way to store per-mapper data..
+	 * for now I just keep it simple, and since this is only accessible
+	 * externally via omap_gem_priv()/omap_get_set_priv()
+	 */
+	void *priv[MAX_MAPPERS];
+};
+
+DEFINE_SPINLOCK(sync_lock);
+
+/** ensure backing pages are allocated */
+/* NOTE: a bit similar to psb_gtt_attach_pages().. perhaps could be common?
+ * and maybe a bit similar to i915_gem_object_get_pages_gtt()
+ */
+static int omap_gem_attach_pages(struct drm_gem_object *obj)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+	struct inode *inode;
+	struct address_space *mapping;
+	struct page *p;
+	int i, npages;
+	gfp_t gfpmask;
+
+	WARN_ON(omap_obj->pages);
+
+	/* This is the shared memory object that backs the GEM resource */
+	inode = obj->filp->f_path.dentry->d_inode;
+	mapping = inode->i_mapping;
+
+	npages = obj->size >> PAGE_SHIFT;
+
+	omap_obj->pages = kmalloc(npages * sizeof(struct page *), GFP_KERNEL);
+	if (omap_obj->pages == NULL)
+		return -ENOMEM;
+
+	/* FIXME: review flags later */
+	gfpmask = __GFP_DMA32 | mapping_gfp_mask(mapping);
+
+	for (i = 0; i < npages; i++) {
+		p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+		if (IS_ERR(p))
+			goto fail;
+		omap_obj->pages[i] = p;
+	}
+
+	return 0;
+
+fail:
+	while (i--) {
+		page_cache_release(omap_obj->pages[i]);
+	}
+	kfree(omap_obj->pages);
+	omap_obj->pages = NULL;
+	return PTR_ERR(p);
+
+
+}
+
+/** release backing pages */
+/* NOTE: a bit similar to psb_gtt_detatch_pages().. perhaps could be common? */
+static void omap_gem_detach_pages(struct drm_gem_object *obj)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+	int i, npages;
+
+	npages = obj->size >> PAGE_SHIFT;
+
+	for (i = 0; i < npages; i++) {
+		/* FIXME: do we need to force dirty */
+		set_page_dirty(omap_obj->pages[i]);
+		/* Undo the reference we took when populating the table */
+		page_cache_release(omap_obj->pages[i]);
+	}
+
+	kfree(omap_obj->pages);
+	omap_obj->pages = NULL;
+}
+
+/** get mmap offset */
+uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
+{
+	return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT;
+}
+EXPORT_SYMBOL(omap_gem_mmap_offset);
+
+/**
+ * omap_gem_fault		-	pagefault handler for GEM objects
+ * @vma: the VMA of the GEM object
+ * @vmf: fault detail
+ *
+ * Invoked when a fault occurs on an mmap of a GEM managed area. GEM
+ * does most of the work for us including the actual map/unmap calls
+ * but we need to do the actual page work.
+ *
+ * The VMA was set up by GEM. In doing so it also ensured that the
+ * vma->vm_private_data points to the GEM object that is backing this
+ * mapping.
+ */
+int omap_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+	struct drm_device *dev = obj->dev;
+	unsigned long pfn;
+	pgoff_t page_offset;
+	int ret;
+
+	/* We don't use vmf->pgoff since that has the fake offset */
+	page_offset = ((unsigned long)vmf->virtual_address -
+			vma->vm_start) >> PAGE_SHIFT;
+
+	/* Make sure we don't parallel update on a fault, nor move or remove
+	 * something from beneath our feet
+	 */
+	mutex_lock(&dev->struct_mutex);
+
+	/* if a shmem backed object, make sure we have pages attached now */
+	if (obj->filp && !omap_obj->pages) {
+		ret = omap_gem_attach_pages(obj);
+		if (ret) {
+			dev_err(dev->dev, "could not attach pages\n");
+			goto fail;
+		}
+	}
+
+	if (omap_obj->pages) {
+		pfn = page_to_pfn(omap_obj->pages[page_offset]);
+	} else {
+		BUG_ON(!(omap_obj->flags & OMAP_BO_DMA));
+		pfn = (omap_obj->paddr >> PAGE_SHIFT) + page_offset;
+	}
+
+	VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
+			pfn, pfn << PAGE_SHIFT);
+
+	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+
+fail:
+	mutex_unlock(&dev->struct_mutex);
+	switch (ret) {
+	case 0:
+	case -ERESTARTSYS:
+	case -EINTR:
+		return VM_FAULT_NOPAGE;
+	case -ENOMEM:
+		return VM_FAULT_OOM;
+	default:
+		return VM_FAULT_SIGBUS;
+	}
+}
+
+void omap_gem_vm_open(struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+	if (omap_obj->ops && omap_obj->ops->open) {
+		omap_obj->ops->open(vma);
+	} else {
+		drm_gem_vm_open(vma);
+	}
+}
+
+void omap_gem_vm_close(struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+	if (omap_obj->ops && omap_obj->ops->close) {
+		omap_obj->ops->close(vma);
+	} else {
+		drm_gem_vm_close(vma);
+	}
+}
+
+/** We override mainly to fix up some of the vm mapping flags.. */
+int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct omap_gem_object *omap_obj;
+	int ret;
+
+	ret = drm_gem_mmap(filp, vma);
+	if (ret) {
+		DBG("mmap failed: %d", ret);
+		return ret;
+	}
+
+	/* after drm_gem_mmap(), it is safe to access the obj */
+	omap_obj = to_omap_bo(vma->vm_private_data);
+
+	vma->vm_flags &= ~VM_PFNMAP;
+	vma->vm_flags |= VM_MIXEDMAP;
+
+	if (omap_obj->flags & OMAP_BO_WC) {
+		vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+	} else if (omap_obj->flags & OMAP_BO_UNCACHED) {
+		vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
+	} else {
+		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+	}
+
+	if (omap_obj->ops && omap_obj->ops->mmap) {
+		omap_obj->ops->mmap(filp, vma);
+	}
+
+	return ret;
+}
+
+/**
+ * omap_gem_dumb_create	-	create a dumb buffer
+ * @drm_file: our client file
+ * @dev: our device
+ * @args: the requested arguments copied from userspace
+ *
+ * Allocate a buffer suitable for use for a frame buffer of the
+ * form described by user space. Give userspace a handle by which
+ * to reference it.
+ */
+int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+		struct drm_mode_create_dumb *args)
+{
+	/* in case someone tries to feed us a completely bogus stride: */
+	args->pitch = align_pitch(args->pitch, args->width, args->bpp);
+	args->size = PAGE_ALIGN(args->pitch * args->height);
+
+	return omap_gem_new_handle(dev, file, args->size,
+			OMAP_BO_SCANOUT | OMAP_BO_WC, &args->handle, NULL);
+}
+
+/**
+ * omap_gem_dumb_destroy	-	destroy a dumb buffer
+ * @file: client file
+ * @dev: our DRM device
+ * @handle: the object handle
+ *
+ * Destroy a handle that was created via omap_gem_dumb_create.
+ */
+int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
+		uint32_t handle)
+{
+	/* No special work needed, drop the reference and see what falls out */
+	return drm_gem_handle_delete(file, handle);
+}
+
+/**
+ * omap_gem_dumb_map	-	buffer mapping for dumb interface
+ * @file: our drm client file
+ * @dev: drm device
+ * @handle: GEM handle to the object (from dumb_create)
+ *
+ * Do the necessary setup to allow the mapping of the frame buffer
+ * into user memory. We don't have to do much here at the moment.
+ */
+int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
+		uint32_t handle, uint64_t *offset)
+{
+	int ret = 0;
+	struct drm_gem_object *obj;
+
+	mutex_lock(&dev->struct_mutex);
+
+	/* GEM does all our handle to object mapping */
+	obj = drm_gem_object_lookup(dev, file, handle);
+	if (obj == NULL) {
+		ret = -ENOENT;
+		goto fail;
+	}
+
+	/* GEM should really work out the hash offsets for us */
+	*offset = omap_gem_mmap_offset(obj);
+
+	drm_gem_object_unreference_unlocked(obj);
+
+fail:
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+/* Get physical address for DMA.. if 'remap' is true, and the buffer is not
+ * already contiguous, remap it to pin in physically contiguous memory.. (ie.
+ * map in TILER)
+ */
+int omap_gem_get_paddr(struct drm_gem_object *obj,
+		unsigned long *paddr, bool remap)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+	if (!(omap_obj->flags & OMAP_BO_DMA)) {
+		/* TODO: remap to TILER */
+		return -ENOMEM;
+	}
+
+	*paddr = omap_obj->paddr;
+
+	return 0;
+}
+EXPORT_SYMBOL(omap_gem_get_paddr);
+
+/* Release physical address, when DMA is no longer being performed.. this
+ * could potentially unpin and unmap buffers from TILER
+ */
+int omap_gem_put_paddr(struct drm_gem_object *obj)
+{
+	/* do something here when remap to TILER is used.. */
+	return 0;
+}
+EXPORT_SYMBOL(omap_gem_put_paddr);
+
+/* acquire pages when needed (for example, for DMA where physically
+ * contiguous buffer is not required
+ */
+int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+	/* TODO: we could attach/detach pages on demand */
+	int ret;  // XXX below is common in _fault()..
+	if (obj->filp && !omap_obj->pages) {
+		ret = omap_gem_attach_pages(obj);
+		if (ret) {
+			dev_err(obj->dev->dev, "could not attach pages\n");
+			return ret;
+		}
+	}
+	/* TODO: even phys-contig.. we should have a list of pages */
+	*pages = omap_obj->pages;
+	return 0;
+}
+EXPORT_SYMBOL(omap_gem_get_pages);
+
+/* release pages when DMA no longer being performed */
+int omap_gem_put_pages(struct drm_gem_object *obj)
+{
+	/* do something here if we dynamically attach/detach pages.. at
+	 * least they would no longer need to be pinned if everyone has
+	 * released the pages..
+	 */
+	return 0;
+}
+EXPORT_SYMBOL(omap_gem_put_pages);
+
+/* get buffer flags */
+uint32_t omap_gem_flags(struct drm_gem_object *obj)
+{
+	return to_omap_bo(obj)->flags;
+}
+EXPORT_SYMBOL(omap_gem_flags);
+
+void * omap_gem_priv(struct drm_gem_object *obj, int mapper_id)
+{
+	BUG_ON((mapper_id >= MAX_MAPPERS) || (mapper_id < 0));
+	return to_omap_bo(obj)->priv[mapper_id];
+}
+EXPORT_SYMBOL(omap_gem_priv);
+
+void omap_gem_set_priv(struct drm_gem_object *obj, int mapper_id, void *priv)
+{
+	BUG_ON((mapper_id >= MAX_MAPPERS) || (mapper_id < 0));
+	to_omap_bo(obj)->priv[mapper_id] = priv;
+}
+EXPORT_SYMBOL(omap_gem_set_priv);
+
+/* Buffer Synchronization:
+ */
+
+struct omap_gem_sync_waiter {
+	struct list_head list;
+	struct omap_gem_object *omap_obj;
+	enum omap_gem_op op;
+	uint32_t read_target, write_target;
+	/* notify called w/ sync_lock held */
+	void (*notify)(void *arg);
+	void *arg;
+};
+
+/* list of omap_gem_sync_waiter.. the notify fxn gets called back when
+ * the read and/or write target count is achieved which can call a user
+ * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for
+ * cpu access), etc.
+ */
+static LIST_HEAD(waiters);
+
+static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
+{
+	struct omap_gem_object *omap_obj = waiter->omap_obj;
+	if ((waiter->op & OMAP_GEM_READ) &&
+			(omap_obj->sync->read_complete < waiter->read_target))
+		return true;
+	if ((waiter->op & OMAP_GEM_WRITE) &&
+			(omap_obj->sync->write_complete < waiter->write_target))
+		return true;
+	return false;
+}
+
+/* macro for sync debug.. */
+#define SYNCDBG 0
+#define SYNC(fmt,...) do { if(SYNCDBG) \
+		printk(KERN_ERR "%s:%d: "fmt"\n", __func__, __LINE__, ##__VA_ARGS__); \
+	} while (0)
+
+
+static void sync_op_update(void)
+{
+	struct omap_gem_sync_waiter *waiter, *n;
+	list_for_each_entry_safe(waiter, n, &waiters, list) {
+		if (!is_waiting(waiter)) {
+			list_del(&waiter->list);
+			SYNC("notify: %p", waiter);
+			waiter->notify(waiter->arg);
+			kfree(waiter);
+		}
+	}
+}
+
+static inline void sync_op(struct drm_gem_object *obj,
+		enum omap_gem_op op, bool start)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+	spin_lock(&sync_lock);
+
+	if (!omap_obj->sync) {
+		omap_obj->sync = kzalloc(sizeof(*omap_obj->sync), GFP_KERNEL);
+	}
+
+	if (start) {
+		if (op & OMAP_GEM_READ)  omap_obj->sync->read_pending++;
+		if (op & OMAP_GEM_WRITE) omap_obj->sync->write_pending++;
+	} else {
+		if (op & OMAP_GEM_READ)  omap_obj->sync->read_complete++;
+		if (op & OMAP_GEM_WRITE) omap_obj->sync->write_complete++;
+		sync_op_update();
+	}
+
+	spin_unlock(&sync_lock);
+}
+
+/* it is a bit lame to handle updates in this sort of polling way, but
+ * in case of PVR, the GPU can directly update read/write complete
+ * values, and not really tell us which ones it updated.. this also
+ * means that sync_lock is not quite sufficient.  So we'll need to
+ * do something a bit better when it comes time to add support for
+ * separate 2d hw..
+ */
+void omap_gem_op_update(void)
+{
+	spin_lock(&sync_lock);
+	sync_op_update();
+	spin_unlock(&sync_lock);
+}
+EXPORT_SYMBOL(omap_gem_op_update);
+
+/* mark the start of read and/or write operation */
+void omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op)
+{
+	sync_op(obj, op, true);
+}
+EXPORT_SYMBOL(omap_gem_op_start);
+
+void omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op)
+{
+	sync_op(obj, op, false);
+}
+EXPORT_SYMBOL(omap_gem_op_finish);
+
+static DECLARE_WAIT_QUEUE_HEAD(sync_event);
+
+static void sync_notify(void *arg)
+{
+	struct task_struct **waiter_task = arg;
+	*waiter_task = NULL;
+	wake_up_all(&sync_event);
+}
+
+int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+	int ret = 0;
+	if (omap_obj->sync)
+	{
+		volatile struct task_struct *waiter_task = current;
+		struct omap_gem_sync_waiter *waiter =
+				kzalloc(sizeof(*waiter), GFP_KERNEL);
+
+		if (!waiter) {
+			return -ENOMEM;
+		}
+
+		waiter->omap_obj = omap_obj;
+		waiter->op = op;
+		waiter->read_target = omap_obj->sync->read_pending;
+		waiter->write_target = omap_obj->sync->write_pending;
+		waiter->notify = sync_notify;
+		waiter->arg = &waiter_task;
+
+		spin_lock(&sync_lock);
+		if (is_waiting(waiter)) {
+			SYNC("waited: %p", waiter);
+			list_add_tail(&waiter->list, &waiters);
+			spin_unlock(&sync_lock);
+			ret = wait_event_interruptible(sync_event,
+					(waiter_task == NULL));
+			spin_lock(&sync_lock);
+			if (waiter_task) {
+				SYNC("interrupted: %p", waiter);
+				/* we were interrupted */
+				list_del(&waiter->list);
+				waiter_task = NULL;
+			} else {
+				/* freed in sync_op_update() */
+				waiter = NULL;
+			}
+		}
+		spin_unlock(&sync_lock);
+
+		if (waiter) {
+			kfree(waiter);
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(omap_gem_op_sync);
+
+/* call fxn(arg), either synchronously or asynchronously if the op
+ * is currently blocked..  fxn() can be called from any context
+ *
+ * (TODO for now fxn is called back from whichever context calls
+ * omap_gem_op_update().. but this could be better defined later
+ * if needed)
+ *
+ * TODO more code in common w/ _sync()..
+ */
+int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
+		void (*fxn)(void *arg), void *arg)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+	if (omap_obj->sync) {
+		struct omap_gem_sync_waiter *waiter =
+				kzalloc(sizeof(*waiter), GFP_KERNEL|GFP_ATOMIC);
+
+		if (!waiter) {
+			return -ENOMEM;
+		}
+
+		waiter->omap_obj = omap_obj;
+		waiter->op = op;
+		waiter->read_target = omap_obj->sync->read_pending;
+		waiter->write_target = omap_obj->sync->write_pending;
+		waiter->notify = fxn;
+		waiter->arg = arg;
+
+		spin_lock(&sync_lock);
+		if (is_waiting(waiter)) {
+			SYNC("waited: %p", waiter);
+			list_add_tail(&waiter->list, &waiters);
+			spin_unlock(&sync_lock);
+			return 0;
+		}
+
+		spin_unlock(&sync_lock);
+	}
+
+	/* no waiting.. */
+	fxn(arg);
+
+	return 0;
+}
+EXPORT_SYMBOL(omap_gem_op_async);
+
+/* special API so PVR can update the buffer to use a sync-object allocated
+ * from it's sync-obj heap.  Only used for a newly allocated (from PVR's
+ * perspective) sync-object, so we overwrite the new syncobj w/ values
+ * from the already allocated syncobj (if there is one)
+ */
+void omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+	spin_lock(&sync_lock);
+
+	if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) {
+		/* clearing a previously set syncobj */
+		syncobj = kzalloc(sizeof(*omap_obj->sync), GFP_KERNEL|GFP_ATOMIC);
+		memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
+		omap_obj->flags &= ~OMAP_BO_EXT_SYNC;
+		omap_obj->sync = syncobj;
+	} else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
+		/* replacing an existing syncobj */
+		if (omap_obj->sync) {
+			memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync));
+			kfree(omap_obj->sync);
+		}
+		omap_obj->flags |= OMAP_BO_EXT_SYNC;
+		omap_obj->sync = syncobj;
+	}
+	spin_unlock(&sync_lock);
+}
+EXPORT_SYMBOL(omap_gem_set_sync_object);
+
+
+int omap_gem_init_object(struct drm_gem_object *obj)
+{
+	return -EINVAL;          /* unused */
+}
+
+/* don't call directly.. called from GEM core when it is time to actually
+ * free the object..
+ */
+void omap_gem_free_object(struct drm_gem_object *obj)
+{
+	struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+	if (obj->map_list.map) {
+		drm_gem_free_mmap_offset(obj);
+	}
+
+	/* don't free externally allocated backing memory */
+	if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) {
+		if (!obj->filp) {
+			/* TODO: is this the best way to check? */
+			omap_vram_free(omap_obj->paddr, obj->size);
+		}
+
+		if (omap_obj->pages) {
+			omap_gem_detach_pages(obj);
+		}
+	}
+
+	/* don't free externally allocated syncobj */
+	if (!(omap_obj->flags & OMAP_BO_EXT_SYNC)) {
+		kfree(omap_obj->sync);
+	}
+
+	drm_gem_object_release(obj);
+
+	kfree(obj);
+}
+
+/* convenience method to construct a GEM buffer object, and userspace handle */
+int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
+		size_t size, uint32_t flags, uint32_t *handle, uint64_t *offset)
+{
+	struct drm_gem_object *obj;
+	int ret;
+
+	obj = omap_gem_new(dev, size, flags);
+	if (!obj)
+		return -ENOMEM;
+
+	ret = drm_gem_handle_create(file, obj, handle);
+	if (ret) {
+		drm_gem_object_release(obj);
+		kfree(obj); /* TODO isn't there a dtor to call? just copying i915 */
+		return ret;
+	}
+
+	if (offset) {
+		*offset = omap_gem_mmap_offset(obj);
+	}
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_unreference_unlocked(obj);
+
+	return 0;
+}
+
+/* common constructor body */
+static struct drm_gem_object * omap_gem_new_impl(struct drm_device *dev,
+		size_t size, uint32_t flags, unsigned long paddr, struct page **pages,
+		struct omap_gem_vm_ops *ops)
+{
+	struct omap_gem_object *omap_obj;
+	struct drm_gem_object *obj;
+	int ret;
+
+	size = PAGE_ALIGN(size);
+
+	omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL);
+	if (!omap_obj) {
+		dev_err(dev->dev, "could not allocate GEM object\n");
+		goto fail;
+	}
+
+	obj = &omap_obj->base;
+
+	if (paddr) {
+		flags |= OMAP_BO_DMA;
+	}
+
+	omap_obj->paddr = paddr;
+	omap_obj->pages = pages;
+	omap_obj->flags = flags;
+	omap_obj->ops   = ops;
+
+	if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
+		ret = drm_gem_private_object_init(dev, obj, size);
+	} else {
+		ret = drm_gem_object_init(dev, obj, size);
+	}
+
+	if (ret) {
+		/* Yikes!  need to clean up! XXX */
+		goto fail;
+	}
+
+	/* Make it mmapable */
+	ret = drm_gem_create_mmap_offset(obj);
+	if (ret) {
+		dev_err(dev->dev, "could not allocate mmap offset");
+		goto fail;
+	}
+
+	return obj;
+
+fail:
+	if (omap_obj) {
+		kfree(omap_obj);
+	}
+	return NULL;
+}
+
+/* This constructor is mainly to give plugins a way to wrap their
+ * own allocations
+ */
+struct drm_gem_object * omap_gem_new_ext(struct drm_device *dev,
+		size_t size, uint32_t flags, unsigned long paddr, struct page **pages,
+		struct omap_gem_vm_ops *ops)
+{
+	return omap_gem_new_impl(dev, size, flags | OMAP_BO_EXT_MEM,
+			paddr, pages, ops);
+}
+EXPORT_SYMBOL(omap_gem_new_ext);
+
+/* normal GEM buffer object constructor */
+struct drm_gem_object * omap_gem_new(struct drm_device *dev,
+		size_t size, uint32_t flags)
+{
+	unsigned long paddr = 0;
+
+	if (flags & OMAP_BO_SCANOUT) {
+		/* attempt to allocate contiguous memory */
+		int ret = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr);
+		if (ret) {
+			dev_err(dev->dev, "failed to allocate vram\n");
+			/* fallback to non-contiguous memory which will need to be
+			 * remapped later..
+			 */
+			paddr = 0;
+		}
+	}
+
+	return omap_gem_new_impl(dev, size, flags, paddr, NULL, NULL);
+}
+EXPORT_SYMBOL(omap_gem_new);
diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig
index aa33386..6c94841 100644
--- a/drivers/video/omap2/omapfb/Kconfig
+++ b/drivers/video/omap2/omapfb/Kconfig
@@ -1,6 +1,6 @@ 
 menuconfig FB_OMAP2
         tristate "OMAP2+ frame buffer support (EXPERIMENTAL)"
-        depends on FB && OMAP2_DSS
+        depends on FB && OMAP2_DSS && !DRM_OMAP
 
 	select OMAP2_VRAM
 	select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
@@ -8,7 +8,7 @@  menuconfig FB_OMAP2
         select FB_CFB_COPYAREA
         select FB_CFB_IMAGEBLIT
         help
-	  Frame buffer driver for OMAP2+ based boards.
+	  Legacy frame buffer driver for OMAP2/3 based boards.
 
 config FB_OMAP2_DEBUG_SUPPORT
         bool "Debug support for OMAP2+ FB"
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 01f6362..ca53772 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -280,6 +280,7 @@  header-y += nubus.h
 header-y += nvram.h
 header-y += omap3isp.h
 header-y += omapfb.h
+header-y += omap_gpu.h
 header-y += oom.h
 header-y += param.h
 header-y += parport.h
diff --git a/include/linux/omap_drm.h b/include/linux/omap_drm.h
new file mode 100644
index 0000000..81af200
--- /dev/null
+++ b/include/linux/omap_drm.h
@@ -0,0 +1,191 @@ 
+/*
+ * linux/include/linux/omap_drm.h
+ *
+ * Copyright (C) 2011 Texas Instruments
+ * Author: Rob Clark <rob@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OMAP_DRM_H__
+#define __OMAP_DRM_H__
+
+#include <linux/module.h>
+#include <drm/drmP.h>
+
+#define OMAP_PARAM_CHIPSET_ID	1	/* ie. 0x3430, 0x4430, etc */
+
+struct drm_omap_param {
+	uint64_t param;			/* in */
+	uint64_t value;			/* in (set_param), out (get_param) */
+};
+
+struct drm_omap_get_base {
+	char plugin_name[64];		/* in */
+	uint32_t ioctl_base;		/* out */
+};
+
+#define OMAP_BO_SCANOUT		0x00000001	/* scanout capable (phys contiguous) */
+#define OMAP_BO_CACHE_MASK	0x00000006	/* cache type mask, see cache modes */
+#define OMAP_BO_TILED_MASK	0x00000f00	/* tiled mapping mask, see tiled modes */
+
+/* cache modes */
+#define OMAP_BO_CACHED		0x00000000	/* default */
+#define OMAP_BO_WC		0x00000002	/* write-combine */
+#define OMAP_BO_UNCACHED	0x00000004	/* strongly-ordered (uncached) */
+
+/* tiled modes */
+#define OMAP_BO_TILED_8		0x00000100
+#define OMAP_BO_TILED_16	0x00000200
+#define OMAP_BO_TILED_32	0x00000300
+
+struct drm_omap_gem_new {
+	union {				/* in */
+		uint32_t bytes;		/* (for non-tiled formats) */
+		struct {
+			uint16_t width;
+			uint16_t height;
+		} tiled;		/* (for tiled formats) */
+	} size;
+	uint32_t flags;			/* in */
+	uint64_t offset;		/* out */
+	uint32_t handle;		/* out */
+};
+
+/* mask of operations: */
+enum omap_gem_op {
+	OMAP_GEM_READ = 0x01,
+	OMAP_GEM_WRITE = 0x02,
+};
+
+struct drm_omap_gem_cpu_prep {
+	uint32_t handle;		/* buffer handle (in) */
+	uint32_t op;			/* mask of omap_gem_op (in) */
+};
+
+struct drm_omap_gem_cpu_fini {
+	uint32_t handle;		/* buffer handle (in) */
+	uint32_t op;			/* mask of omap_gem_op (in) */
+	/* TODO maybe here we pass down info about what regions are touched
+	 * by sw so we can be clever about cache ops?  For now a placeholder,
+	 * set to zero and we just do full buffer flush..
+	 */
+	uint32_t nregions;
+};
+
+struct drm_omap_gem_info {
+	uint32_t handle;		/* buffer handle (in) */
+	uint32_t pad;
+	uint64_t offset;		/* out */
+};
+
+#define DRM_OMAP_GET_PARAM		0x00
+#define DRM_OMAP_SET_PARAM		0x01
+#define DRM_OMAP_GET_BASE		0x02
+#define DRM_OMAP_GEM_NEW		0x03
+#define DRM_OMAP_GEM_CPU_PREP	0x04
+#define DRM_OMAP_GEM_CPU_FINI	0x05
+#define DRM_OMAP_GEM_INFO	0x06
+#define DRM_OMAP_NUM_IOCTLS		0x07
+
+#define DRM_IOCTL_OMAP_GET_PARAM	DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GET_PARAM, struct drm_omap_param)
+#define DRM_IOCTL_OMAP_SET_PARAM	DRM_IOW (DRM_COMMAND_BASE + DRM_OMAP_SET_PARAM, struct drm_omap_param)
+#define DRM_IOCTL_OMAP_GET_BASE		DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GET_BASE, struct drm_omap_get_base)
+#define DRM_IOCTL_OMAP_GEM_NEW		DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GEM_NEW, struct drm_omap_gem_new)
+#define DRM_IOCTL_OMAP_GEM_CPU_PREP	DRM_IOW (DRM_COMMAND_BASE + DRM_OMAP_GEM_CPU_PREP, struct drm_omap_gem_cpu_prep)
+#define DRM_IOCTL_OMAP_GEM_CPU_FINI	DRM_IOW (DRM_COMMAND_BASE + DRM_OMAP_GEM_CPU_FINI, struct drm_omap_gem_cpu_fini)
+#define DRM_IOCTL_OMAP_GEM_INFO		DRM_IOWR(DRM_COMMAND_BASE + DRM_OMAP_GEM_INFO, struct drm_omap_gem_info)
+
+/* interface that plug-in drivers can implement */
+struct omap_drm_plugin {
+	const char *name;
+
+	/* drm functions */
+	int (*open)(struct drm_device *dev, struct drm_file *file);
+	int (*load)(struct drm_device *dev, unsigned long flags);
+	int (*unload)(struct drm_device *dev);
+	int (*release)(struct drm_device *dev, struct drm_file *file);
+
+	struct drm_ioctl_desc *ioctls;
+	int num_ioctls;
+	int ioctl_base;
+
+	struct list_head list;  /* note, this means struct can't be const.. */
+};
+
+int omap_drm_register_plugin(struct omap_drm_plugin *plugin);
+int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin);
+
+int omap_drm_register_mapper(void);
+void omap_drm_unregister_mapper(int id);
+
+/* external mappers should get paddr or pages when it needs the pages pinned
+ * and put when done..
+ */
+int omap_gem_get_paddr(struct drm_gem_object *obj,
+		unsigned long *paddr, bool remap);
+int omap_gem_put_paddr(struct drm_gem_object *obj);
+int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages);
+int omap_gem_put_pages(struct drm_gem_object *obj);
+
+uint32_t omap_gem_flags(struct drm_gem_object *obj);
+void * omap_gem_priv(struct drm_gem_object *obj, int mapper_id);
+void omap_gem_set_priv(struct drm_gem_object *obj, int mapper_id, void *priv);
+uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj);
+
+/* for external plugin buffers wrapped as GEM object (via. omap_gem_new_ext())
+ * a vm_ops struct can be provided to get callback notification of various
+ * events..
+ */
+struct omap_gem_vm_ops {
+	void (*open)(struct vm_area_struct * area);
+	void (*close)(struct vm_area_struct * area);
+
+	/* note: mmap is not expected to do anything.. it is just to allow buffer
+	 * allocate to update it's own internal state
+	 */
+	void (*mmap)(struct file *, struct vm_area_struct *);
+};
+
+struct drm_gem_object * omap_gem_new_ext(struct drm_device *dev,
+		size_t size, uint32_t flags, unsigned long paddr, struct page **pages,
+		struct omap_gem_vm_ops *ops);
+struct drm_gem_object * omap_gem_new(struct drm_device *dev,
+		size_t size, uint32_t flags);
+
+void omap_gem_op_update(void);
+void omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op);
+void omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op);
+int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op);
+int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
+		void (*fxn)(void *arg), void *arg);
+void omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj);
+
+
+/* optional platform data to configure the default configuration of which
+ * pipes/overlays/CRTCs are used.. if this is not provided, then instead the
+ * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected to
+ * one manager, with priority given to managers that are connected to
+ * detected devices.  This should be a good default behavior for most cases,
+ * but yet there still might be times when you wish to do something different.
+ */
+struct omap_drm_platform_data {
+	int ovl_cnt;
+	const int *ovl_ids;
+	int mgr_cnt;
+	const int *mgr_ids;
+	int dev_cnt;
+	const char **dev_names;
+};
+
+#endif /* __OMAP_DRM_H__ */