Message ID | 1316295129-3600-1-git-send-email-rob.clark@linaro.org |
---|---|
State | New |
Headers | show |
On Sat, Sep 17, 2011 at 04:32:09PM -0500, Rob Clark wrote: > 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 > with CMA and use drm_gem_private_object_init(). > > See userspace xorg driver: > git://github.com/robclark/xf86-video-omap.git > > Refer to this link for CMA (Continuous Memory Allocator): > http://lkml.org/lkml/2011/8/19/302 > > Links to previous versions of the patch: > v1: http://lwn.net/Articles/458137/ > > History: > > v2: replace omap_vram with CMA for scanout buffer allocation, remove > unneeded functions, use dma_addr_t for physical addresses, error > handling cleanup, refactor attach/detach pages into common drm > functions, split non-userspace-facing API into omap_priv.h, remove > plugin API > > v1: original This looks good. A few minors things to add to the TODO to be looked at before moving the driver out of staging: - review the dss/kms interface mismatch issues you've noted in the code - review the sync object implementation when an actual gpu side user (not just pageflip) shows up. Also a minor comment on your ioctl interface, see below. A haven't looked too closely at the fbdev stuff, simply because that's way outside my expertise. I don't think there's anything in there that can't be fixed up in staging. Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> (for -staging) > diff --git a/drivers/staging/omapdrm/TODO.txt b/drivers/staging/omapdrm/TODO.txt > new file mode 100644 > index 0000000..af81989 > --- /dev/null > +++ b/drivers/staging/omapdrm/TODO.txt > @@ -0,0 +1,14 @@ > +TODO > +. 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 > diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h > new file mode 100644 > index 0000000..ea0ae8e > --- /dev/null > +++ b/include/drm/omap_drm.h > @@ -0,0 +1,111 @@ > +/* > + * linux/include/drm/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 "drm.h" > + > +/* Please note that modifications to all structs defined here are > + * subject to backwards-compatibility constraints. > + */ > + > +#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 */ > + 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; > +}; Note that you cannot extend ioctl structures in an easy way with the drm ioctl infrastructure. So I think you need at least a pointer here, too. > +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 Unused ;-) > +#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) > + > +#endif /* __OMAP_DRM_H__ */ > diff --git a/include/drm/omap_priv.h b/include/drm/omap_priv.h > new file mode 100644 > index 0000000..ca7d975 > --- /dev/null > +++ b/include/drm/omap_priv.h > @@ -0,0 +1,42 @@ > +/* > + * linux/include/drm/omap_priv.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_PRIV_H__ > +#define __OMAP_PRIV_H__ > + > +/* Non-userspace facing APIs > + */ > + > +/* 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__ */ > -- > 1.7.5.4 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Sun, Sep 18, 2011 at 5:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote: > On Sat, Sep 17, 2011 at 04:32:09PM -0500, Rob Clark wrote: >> 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 >> with CMA and use drm_gem_private_object_init(). >> >> See userspace xorg driver: >> git://github.com/robclark/xf86-video-omap.git >> >> Refer to this link for CMA (Continuous Memory Allocator): >> http://lkml.org/lkml/2011/8/19/302 >> >> Links to previous versions of the patch: >> v1: http://lwn.net/Articles/458137/ >> >> History: >> >> v2: replace omap_vram with CMA for scanout buffer allocation, remove >> unneeded functions, use dma_addr_t for physical addresses, error >> handling cleanup, refactor attach/detach pages into common drm >> functions, split non-userspace-facing API into omap_priv.h, remove >> plugin API >> >> v1: original > > This looks good. A few minors things to add to the TODO to be looked at > before moving the driver out of staging: > - review the dss/kms interface mismatch issues you've noted in the code > - review the sync object implementation when an actual gpu side user > (not just pageflip) shows up. > Also a minor comment on your ioctl interface, see below. A haven't looked > too closely at the fbdev stuff, simply because that's way outside my > expertise. I don't think there's anything in there that can't be fixed up > in staging. > > Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> (for -staging) > Thanks Daniel [snip] On Sun, Sep 18, 2011 at 5:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote: >> +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; >> +}; > > Note that you cannot extend ioctl structures in an easy way with the drm > ioctl infrastructure. So I think you need at least a pointer here, too. oh, hrm.. it did look like drm_ioctl() code was handling the case where userspace size was smaller than current kernel side size, ie. the 'if (drv_size > asize)' stuff.. or am I missing something else? If so, opinions on adding a padding field to the end vs ptr? >> +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 > > Unused ;-) well, err, placeholder ;-) I hope it is tolerable to leave a placeholder for this.. otherwise it becomes a royal PITA if the ioctl nr for the plugin part is moving upwards every time I add a new ioctl.. although I could change that to a comment about the skipped ioctl cmd nr if that is better BR, -R >> +#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
On Sun, Sep 18, 2011 at 10:31:45AM -0500, Rob Clark wrote: > On Sun, Sep 18, 2011 at 5:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote: > > On Sat, Sep 17, 2011 at 04:32:09PM -0500, Rob Clark wrote: > On Sun, Sep 18, 2011 at 5:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote: > >> +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; > >> +}; > > > > Note that you cannot extend ioctl structures in an easy way with the drm > > ioctl infrastructure. So I think you need at least a pointer here, too. > > oh, hrm.. it did look like drm_ioctl() code was handling the case > where userspace size was smaller than current kernel side size, ie. > the 'if (drv_size > asize)' stuff.. or am I missing something else? Oops, misconception on my side - I've had a year long typo in my i915 overlay code, but that was not about the size (as I've thought), but about the ioctl number (which libdrm somehow ignores because it stitches stuff together on my own). Indeed, upon reading that code you seem to be right. There's the little funny caveat though that if userspace passes in less data than the kernel expects, it's not being cleared. But that can be fixed. > If so, opinions on adding a padding field to the end vs ptr? > > >> +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 > > > > Unused ;-) > > well, err, placeholder ;-) I know, and I don't mind. Because not having it is just a unnecessary pain for you ;-) Maybe just add a comment. > I hope it is tolerable to leave a placeholder for this.. otherwise it > becomes a royal PITA if the ioctl nr for the plugin part is moving > upwards every time I add a new ioctl.. although I could change that > to a comment about the skipped ioctl cmd nr if that is better Cheers, Daniel
On Sun, Sep 18, 2011 at 10:55 AM, Daniel Vetter <daniel@ffwll.ch> wrote: > There's the little funny caveat though that if userspace passes in less > data than the kernel expects, it's not being cleared. But that can be > fixed. oh, heh, good catch.. I'll send a patch for that shortly, before I forget and spend a bunch of time debugging weird issues later ;-) BR, -R
On 09/17/2011 11:32 PM, Rob Clark wrote: > 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 > with CMA and use drm_gem_private_object_init(). > > See userspace xorg driver: > git://github.com/robclark/xf86-video-omap.git > > Refer to this link for CMA (Continuous Memory Allocator): > http://lkml.org/lkml/2011/8/19/302 > > Links to previous versions of the patch: > v1: http://lwn.net/Articles/458137/ > > History: > > v2: replace omap_vram with CMA for scanout buffer allocation, remove > unneeded functions, use dma_addr_t for physical addresses, error > handling cleanup, refactor attach/detach pages into common drm > functions, split non-userspace-facing API into omap_priv.h, remove > plugin API > > v1: original > --- > drivers/staging/Kconfig | 2 + > drivers/staging/Makefile | 1 + > drivers/staging/omapdrm/Kconfig | 24 + > drivers/staging/omapdrm/Makefile | 9 + > drivers/staging/omapdrm/TODO.txt | 14 + > drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ > drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ > drivers/staging/omapdrm/omap_drv.c | 766 ++++++++++++++++++++++++++++++ > drivers/staging/omapdrm/omap_drv.h | 126 +++++ > drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ > drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ > drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ > drivers/staging/omapdrm/omap_gem.c | 720 ++++++++++++++++++++++++++++ > drivers/video/omap2/omapfb/Kconfig | 2 +- > include/drm/Kbuild | 1 + > include/drm/omap_drm.h | 111 +++++ > include/drm/omap_priv.h | 42 ++ > 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h > create mode 100644 include/drm/omap_priv.h > > ... > diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h > new file mode 100644 > index 0000000..ea0ae8e > --- /dev/null > +++ b/include/drm/omap_drm.h > @@ -0,0 +1,111 @@ > +/* > + * linux/include/drm/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 "drm.h" > + > +/* Please note that modifications to all structs defined here are > + * subject to backwards-compatibility constraints. > + */ > + > +#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 */ > +}; > What about future ARM 64-bit vs 32-bit structure sizes? On x86 we always take care to make structures appearing in the drm user-space interfaces having sizes that are a multiple of 64-bits, to avoid having to maintain compat code for 32-bit apps running on 64 bit kernels. For the same reasons, structure members with 64 bit alignment requirements on 64-bit systems need to be carefully places. I don't know whether there is or will be a 64-bit ARM, but it might be worth taking into consideration. /Thomas > + > +#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 */ > + 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) > + > +#endif /* __OMAP_DRM_H__ */ > diff --git a/include/drm/omap_priv.h b/include/drm/omap_priv.h > new file mode 100644 > index 0000000..ca7d975 > --- /dev/null > +++ b/include/drm/omap_priv.h > @@ -0,0 +1,42 @@ > +/* > + * linux/include/drm/omap_priv.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_PRIV_H__ > +#define __OMAP_PRIV_H__ > + > +/* Non-userspace facing APIs > + */ > + > +/* 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__ */ >
On Sun, Sep 18, 2011 at 2:36 PM, Thomas Hellstrom <thomas@shipmail.org> wrote: > On 09/17/2011 11:32 PM, Rob Clark wrote: >> >> 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 >> with CMA and use drm_gem_private_object_init(). >> >> See userspace xorg driver: >> git://github.com/robclark/xf86-video-omap.git >> >> Refer to this link for CMA (Continuous Memory Allocator): >> http://lkml.org/lkml/2011/8/19/302 >> >> Links to previous versions of the patch: >> v1: http://lwn.net/Articles/458137/ >> >> History: >> >> v2: replace omap_vram with CMA for scanout buffer allocation, remove >> unneeded functions, use dma_addr_t for physical addresses, error >> handling cleanup, refactor attach/detach pages into common drm >> functions, split non-userspace-facing API into omap_priv.h, remove >> plugin API >> >> v1: original >> --- >> drivers/staging/Kconfig | 2 + >> drivers/staging/Makefile | 1 + >> drivers/staging/omapdrm/Kconfig | 24 + >> drivers/staging/omapdrm/Makefile | 9 + >> drivers/staging/omapdrm/TODO.txt | 14 + >> drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ >> drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ >> drivers/staging/omapdrm/omap_drv.c | 766 >> ++++++++++++++++++++++++++++++ >> drivers/staging/omapdrm/omap_drv.h | 126 +++++ >> drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ >> drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ >> drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ >> drivers/staging/omapdrm/omap_gem.c | 720 >> ++++++++++++++++++++++++++++ >> drivers/video/omap2/omapfb/Kconfig | 2 +- >> include/drm/Kbuild | 1 + >> include/drm/omap_drm.h | 111 +++++ >> include/drm/omap_priv.h | 42 ++ >> 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h >> create mode 100644 include/drm/omap_priv.h >> >> > > ... > >> diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h >> new file mode 100644 >> index 0000000..ea0ae8e >> --- /dev/null >> +++ b/include/drm/omap_drm.h >> @@ -0,0 +1,111 @@ >> +/* >> + * linux/include/drm/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 "drm.h" >> + >> +/* Please note that modifications to all structs defined here are >> + * subject to backwards-compatibility constraints. >> + */ >> + >> +#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 */ >> +}; >> > > What about future ARM 64-bit vs 32-bit structure sizes? On x86 we always > take care to make structures appearing in the drm user-space interfaces > having sizes that are a multiple of 64-bits, to avoid having to maintain > compat code for 32-bit apps running on 64 bit kernels. For the same > reasons, structure members with 64 bit alignment requirements on 64-bit > systems need to be carefully places. > > I don't know whether there is or will be a 64-bit ARM, but it might be worth > taking into consideration. There isn't currently any 64-bit ARM, but it is a safe assumption that there will be some day.. I guess we'll have enough fun w/ various random 32b devices when LPAE arrives w/ the cortex-a15.. I did try to make sure any uint64_t's were aligned to a 64bit offset, but beyond that I confess to not being an expert on how 64 vs 32b ioctl compat stuff is handled or what the issues going from 32->64b are. If there are some additional considerations that should be taken care of, then now is the time. So far I don't have any pointer fields in any of the ioctl structs. Beyond that, I'm not entirely sure what else needs to be done, but would appreciate any pointers to docs about how the compat stuff works. BR, -R > /Thomas > > > > > >> + >> +#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 */ >> + 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) >> + >> +#endif /* __OMAP_DRM_H__ */ >> diff --git a/include/drm/omap_priv.h b/include/drm/omap_priv.h >> new file mode 100644 >> index 0000000..ca7d975 >> --- /dev/null >> +++ b/include/drm/omap_priv.h >> @@ -0,0 +1,42 @@ >> +/* >> + * linux/include/drm/omap_priv.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_PRIV_H__ >> +#define __OMAP_PRIV_H__ >> + >> +/* Non-userspace facing APIs >> + */ >> + >> +/* 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__ */ >> > > > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel >
On 09/18/2011 09:50 PM, Rob Clark wrote: > On Sun, Sep 18, 2011 at 2:36 PM, Thomas Hellstrom<thomas@shipmail.org> wrote: > >> On 09/17/2011 11:32 PM, Rob Clark wrote: >> >>> 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 >>> with CMA and use drm_gem_private_object_init(). >>> >>> See userspace xorg driver: >>> git://github.com/robclark/xf86-video-omap.git >>> >>> Refer to this link for CMA (Continuous Memory Allocator): >>> http://lkml.org/lkml/2011/8/19/302 >>> >>> Links to previous versions of the patch: >>> v1: http://lwn.net/Articles/458137/ >>> >>> History: >>> >>> v2: replace omap_vram with CMA for scanout buffer allocation, remove >>> unneeded functions, use dma_addr_t for physical addresses, error >>> handling cleanup, refactor attach/detach pages into common drm >>> functions, split non-userspace-facing API into omap_priv.h, remove >>> plugin API >>> >>> v1: original >>> --- >>> drivers/staging/Kconfig | 2 + >>> drivers/staging/Makefile | 1 + >>> drivers/staging/omapdrm/Kconfig | 24 + >>> drivers/staging/omapdrm/Makefile | 9 + >>> drivers/staging/omapdrm/TODO.txt | 14 + >>> drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ >>> drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ >>> drivers/staging/omapdrm/omap_drv.c | 766 >>> ++++++++++++++++++++++++++++++ >>> drivers/staging/omapdrm/omap_drv.h | 126 +++++ >>> drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ >>> drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ >>> drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ >>> drivers/staging/omapdrm/omap_gem.c | 720 >>> ++++++++++++++++++++++++++++ >>> drivers/video/omap2/omapfb/Kconfig | 2 +- >>> include/drm/Kbuild | 1 + >>> include/drm/omap_drm.h | 111 +++++ >>> include/drm/omap_priv.h | 42 ++ >>> 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h >>> create mode 100644 include/drm/omap_priv.h >>> >>> >>> >> ... >> >> >>> diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h >>> new file mode 100644 >>> index 0000000..ea0ae8e >>> --- /dev/null >>> +++ b/include/drm/omap_drm.h >>> @@ -0,0 +1,111 @@ >>> +/* >>> + * linux/include/drm/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 "drm.h" >>> + >>> +/* Please note that modifications to all structs defined here are >>> + * subject to backwards-compatibility constraints. >>> + */ >>> + >>> +#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 */ >>> +}; >>> >>> >> What about future ARM 64-bit vs 32-bit structure sizes? On x86 we always >> take care to make structures appearing in the drm user-space interfaces >> having sizes that are a multiple of 64-bits, to avoid having to maintain >> compat code for 32-bit apps running on 64 bit kernels. For the same >> reasons, structure members with 64 bit alignment requirements on 64-bit >> systems need to be carefully places. >> >> I don't know whether there is or will be a 64-bit ARM, but it might be worth >> taking into consideration. >> > There isn't currently any 64-bit ARM, but it is a safe assumption that > there will be some day.. I guess we'll have enough fun w/ various > random 32b devices when LPAE arrives w/ the cortex-a15.. > > I did try to make sure any uint64_t's were aligned to a 64bit offset, > but beyond that I confess to not being an expert on how 64 vs 32b > ioctl compat stuff is handled or what the issues going from 32->64b > are. If there are some additional considerations that should be taken > care of, then now is the time. So far I don't have any pointer fields > in any of the ioctl structs. Beyond that, I'm not entirely sure what > else needs to be done, but would appreciate any pointers to docs about > how the compat stuff works. > > BR, > -R > I've actually avoided writing compat ioctl code myself, by trying to make sure that structures look identical in the 64-bit and 32-bit x86 ABIs, but the compat code is there to translate pointers and to overcome alignment incompatibilities. A good way to at least try to avoid having to maintain compat code once the 64-bit ABI shows up is to make sure that 64-bit scalars and embedded structures are placed on 64-bit boundaries, padding if necessary, and to make sure (using padding) that struct sizes are always multiples of 64 bits. But since there is no 64-bit ARM yet, it might be better to rely on using compat code in the future than on making guesses about alignment restrictions of the ABI... /Thomas
On Sun, Sep 18, 2011 at 3:00 PM, Thomas Hellstrom <thomas@shipmail.org> wrote: > On 09/18/2011 09:50 PM, Rob Clark wrote: >> >> On Sun, Sep 18, 2011 at 2:36 PM, Thomas Hellstrom<thomas@shipmail.org> >> wrote: >> >>> >>> On 09/17/2011 11:32 PM, Rob Clark wrote: >>> >>>> >>>> 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 >>>> with CMA and use drm_gem_private_object_init(). >>>> >>>> See userspace xorg driver: >>>> git://github.com/robclark/xf86-video-omap.git >>>> >>>> Refer to this link for CMA (Continuous Memory Allocator): >>>> http://lkml.org/lkml/2011/8/19/302 >>>> >>>> Links to previous versions of the patch: >>>> v1: http://lwn.net/Articles/458137/ >>>> >>>> History: >>>> >>>> v2: replace omap_vram with CMA for scanout buffer allocation, remove >>>> unneeded functions, use dma_addr_t for physical addresses, error >>>> handling cleanup, refactor attach/detach pages into common drm >>>> functions, split non-userspace-facing API into omap_priv.h, remove >>>> plugin API >>>> >>>> v1: original >>>> --- >>>> drivers/staging/Kconfig | 2 + >>>> drivers/staging/Makefile | 1 + >>>> drivers/staging/omapdrm/Kconfig | 24 + >>>> drivers/staging/omapdrm/Makefile | 9 + >>>> drivers/staging/omapdrm/TODO.txt | 14 + >>>> drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ >>>> drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ >>>> drivers/staging/omapdrm/omap_drv.c | 766 >>>> ++++++++++++++++++++++++++++++ >>>> drivers/staging/omapdrm/omap_drv.h | 126 +++++ >>>> drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ >>>> drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ >>>> drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ >>>> drivers/staging/omapdrm/omap_gem.c | 720 >>>> ++++++++++++++++++++++++++++ >>>> drivers/video/omap2/omapfb/Kconfig | 2 +- >>>> include/drm/Kbuild | 1 + >>>> include/drm/omap_drm.h | 111 +++++ >>>> include/drm/omap_priv.h | 42 ++ >>>> 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h >>>> create mode 100644 include/drm/omap_priv.h >>>> >>>> >>>> >>> >>> ... >>> >>> >>>> >>>> diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h >>>> new file mode 100644 >>>> index 0000000..ea0ae8e >>>> --- /dev/null >>>> +++ b/include/drm/omap_drm.h >>>> @@ -0,0 +1,111 @@ >>>> +/* >>>> + * linux/include/drm/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 "drm.h" >>>> + >>>> +/* Please note that modifications to all structs defined here are >>>> + * subject to backwards-compatibility constraints. >>>> + */ >>>> + >>>> +#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 */ >>>> +}; >>>> >>>> >>> >>> What about future ARM 64-bit vs 32-bit structure sizes? On x86 we always >>> take care to make structures appearing in the drm user-space interfaces >>> having sizes that are a multiple of 64-bits, to avoid having to maintain >>> compat code for 32-bit apps running on 64 bit kernels. For the same >>> reasons, structure members with 64 bit alignment requirements on 64-bit >>> systems need to be carefully places. >>> >>> I don't know whether there is or will be a 64-bit ARM, but it might be >>> worth >>> taking into consideration. >>> >> >> There isn't currently any 64-bit ARM, but it is a safe assumption that >> there will be some day.. I guess we'll have enough fun w/ various >> random 32b devices when LPAE arrives w/ the cortex-a15.. >> >> I did try to make sure any uint64_t's were aligned to a 64bit offset, >> but beyond that I confess to not being an expert on how 64 vs 32b >> ioctl compat stuff is handled or what the issues going from 32->64b >> are. If there are some additional considerations that should be taken >> care of, then now is the time. So far I don't have any pointer fields >> in any of the ioctl structs. Beyond that, I'm not entirely sure what >> else needs to be done, but would appreciate any pointers to docs about >> how the compat stuff works. >> >> BR, >> -R >> > > I've actually avoided writing compat ioctl code myself, by trying to make > sure that structures look identical in the 64-bit and 32-bit x86 ABIs, but > the compat code is there to translate pointers and to overcome alignment > incompatibilities. > > A good way to at least try to avoid having to maintain compat code once the > 64-bit ABI shows up is to make sure that 64-bit scalars and embedded > structures are placed on 64-bit boundaries, padding if necessary, and to > make sure (using padding) that struct sizes are always multiples of 64 bits. So far this is true for 64bit scalars.. I'm using stdint types everywhere so there is no chance for fields having different sizes on 64b vs 32b. And the only structs contained within ioctl structs so far are starting at offset==0. Is it necessary to ensure that the ioctl structs themselves (as opposed to structs within those structs) have sizes that are multiple of 64b? The ioctl structs are copied (copy_from_user()/copy_to_user()), which I would have assumed would be sufficient? > But since there is no 64-bit ARM yet, it might be better to rely on using > compat code in the future than on making guesses about alignment > restrictions of the ABI... hmm, it might be nice to get some guidelines from ARM on this, since I really have no idea what a 64b ARM architecture would look like.. BR, -R > /Thomas > > > > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel >
On 09/18/2011 10:15 PM, Rob Clark wrote: > On Sun, Sep 18, 2011 at 3:00 PM, Thomas Hellstrom<thomas@shipmail.org> wrote: > >> On 09/18/2011 09:50 PM, Rob Clark wrote: >> >>> On Sun, Sep 18, 2011 at 2:36 PM, Thomas Hellstrom<thomas@shipmail.org> >>> wrote: >>> >>> >>>> On 09/17/2011 11:32 PM, Rob Clark wrote: >>>> >>>> >>>>> 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 >>>>> with CMA and use drm_gem_private_object_init(). >>>>> >>>>> See userspace xorg driver: >>>>> git://github.com/robclark/xf86-video-omap.git >>>>> >>>>> Refer to this link for CMA (Continuous Memory Allocator): >>>>> http://lkml.org/lkml/2011/8/19/302 >>>>> >>>>> Links to previous versions of the patch: >>>>> v1: http://lwn.net/Articles/458137/ >>>>> >>>>> History: >>>>> >>>>> v2: replace omap_vram with CMA for scanout buffer allocation, remove >>>>> unneeded functions, use dma_addr_t for physical addresses, error >>>>> handling cleanup, refactor attach/detach pages into common drm >>>>> functions, split non-userspace-facing API into omap_priv.h, remove >>>>> plugin API >>>>> >>>>> v1: original >>>>> --- >>>>> drivers/staging/Kconfig | 2 + >>>>> drivers/staging/Makefile | 1 + >>>>> drivers/staging/omapdrm/Kconfig | 24 + >>>>> drivers/staging/omapdrm/Makefile | 9 + >>>>> drivers/staging/omapdrm/TODO.txt | 14 + >>>>> drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ >>>>> drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ >>>>> drivers/staging/omapdrm/omap_drv.c | 766 >>>>> ++++++++++++++++++++++++++++++ >>>>> drivers/staging/omapdrm/omap_drv.h | 126 +++++ >>>>> drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ >>>>> drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ >>>>> drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ >>>>> drivers/staging/omapdrm/omap_gem.c | 720 >>>>> ++++++++++++++++++++++++++++ >>>>> drivers/video/omap2/omapfb/Kconfig | 2 +- >>>>> include/drm/Kbuild | 1 + >>>>> include/drm/omap_drm.h | 111 +++++ >>>>> include/drm/omap_priv.h | 42 ++ >>>>> 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h >>>>> create mode 100644 include/drm/omap_priv.h >>>>> >>>>> >>>>> >>>>> >>>> ... >>>> >>>> >>>> >>>>> diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h >>>>> new file mode 100644 >>>>> index 0000000..ea0ae8e >>>>> --- /dev/null >>>>> +++ b/include/drm/omap_drm.h >>>>> @@ -0,0 +1,111 @@ >>>>> +/* >>>>> + * linux/include/drm/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 "drm.h" >>>>> + >>>>> +/* Please note that modifications to all structs defined here are >>>>> + * subject to backwards-compatibility constraints. >>>>> + */ >>>>> + >>>>> +#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 */ >>>>> +}; >>>>> >>>>> >>>>> >>>> What about future ARM 64-bit vs 32-bit structure sizes? On x86 we always >>>> take care to make structures appearing in the drm user-space interfaces >>>> having sizes that are a multiple of 64-bits, to avoid having to maintain >>>> compat code for 32-bit apps running on 64 bit kernels. For the same >>>> reasons, structure members with 64 bit alignment requirements on 64-bit >>>> systems need to be carefully places. >>>> >>>> I don't know whether there is or will be a 64-bit ARM, but it might be >>>> worth >>>> taking into consideration. >>>> >>>> >>> There isn't currently any 64-bit ARM, but it is a safe assumption that >>> there will be some day.. I guess we'll have enough fun w/ various >>> random 32b devices when LPAE arrives w/ the cortex-a15.. >>> >>> I did try to make sure any uint64_t's were aligned to a 64bit offset, >>> but beyond that I confess to not being an expert on how 64 vs 32b >>> ioctl compat stuff is handled or what the issues going from 32->64b >>> are. If there are some additional considerations that should be taken >>> care of, then now is the time. So far I don't have any pointer fields >>> in any of the ioctl structs. Beyond that, I'm not entirely sure what >>> else needs to be done, but would appreciate any pointers to docs about >>> how the compat stuff works. >>> >>> BR, >>> -R >>> >>> >> I've actually avoided writing compat ioctl code myself, by trying to make >> sure that structures look identical in the 64-bit and 32-bit x86 ABIs, but >> the compat code is there to translate pointers and to overcome alignment >> incompatibilities. >> >> A good way to at least try to avoid having to maintain compat code once the >> 64-bit ABI shows up is to make sure that 64-bit scalars and embedded >> structures are placed on 64-bit boundaries, padding if necessary, and to >> make sure (using padding) that struct sizes are always multiples of 64 bits. >> > So far this is true for 64bit scalars.. I'm using stdint types > everywhere so there is no chance for fields having different sizes on > 64b vs 32b. And the only structs contained within ioctl structs so > far are starting at offset==0. > > Is it necessary to ensure that the ioctl structs themselves (as > opposed to structs within those structs) have sizes that are multiple > of 64b? The ioctl structs are copied > (copy_from_user()/copy_to_user()), which I would have assumed would be > sufficient? > > It depends. If a 64 bit kernel calculates the size as sizeof(struct ...) and then tries to copy it to user-space using copy_to_user(), it might want to copy more than the user-space structure size, causing -EFAULTs or overwritten user-space data. (If user-space is 32-bit.) On x86, for example struct { int64_t a; int32_t b; } x; Is 96 byte in the 32 bit ABI, but 128 byte in the 64-bit ABI. So if you issue copy_to_user(user_ptr, &x, sizeof(x)) It will try to copy 128 byte on a 64-bit kernel and will overwrite data or cause segfault with a 32-bit user-space. However, IIRC the drm ioctl copy code uses the size encoded by user-space, which avoids that problem, but that's an implementation specific feature that shouldn't be relied upon. /Thomas >> But since there is no 64-bit ARM yet, it might be better to rely on using >> compat code in the future than on making guesses about alignment >> restrictions of the ABI... >> > hmm, it might be nice to get some guidelines from ARM on this, since I > really have no idea what a 64b ARM architecture would look like.. > > BR, > -R > > >> /Thomas >> >> >> >> >> _______________________________________________ >> dri-devel mailing list >> dri-devel@lists.freedesktop.org >> http://lists.freedesktop.org/mailman/listinfo/dri-devel >> >>
On Sun, Sep 18, 2011 at 3:31 PM, Thomas Hellstrom <thomas@shipmail.org> wrote: > On 09/18/2011 10:15 PM, Rob Clark wrote: >> >> On Sun, Sep 18, 2011 at 3:00 PM, Thomas Hellstrom<thomas@shipmail.org> >> wrote: >> >>> >>> On 09/18/2011 09:50 PM, Rob Clark wrote: >>> >>>> >>>> On Sun, Sep 18, 2011 at 2:36 PM, Thomas Hellstrom<thomas@shipmail.org> >>>> wrote: >>>> >>>> >>>>> >>>>> On 09/17/2011 11:32 PM, Rob Clark wrote: >>>>> >>>>> >>>>>> >>>>>> 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 >>>>>> with CMA and use drm_gem_private_object_init(). >>>>>> >>>>>> See userspace xorg driver: >>>>>> git://github.com/robclark/xf86-video-omap.git >>>>>> >>>>>> Refer to this link for CMA (Continuous Memory Allocator): >>>>>> http://lkml.org/lkml/2011/8/19/302 >>>>>> >>>>>> Links to previous versions of the patch: >>>>>> v1: http://lwn.net/Articles/458137/ >>>>>> >>>>>> History: >>>>>> >>>>>> v2: replace omap_vram with CMA for scanout buffer allocation, remove >>>>>> unneeded functions, use dma_addr_t for physical addresses, error >>>>>> handling cleanup, refactor attach/detach pages into common drm >>>>>> functions, split non-userspace-facing API into omap_priv.h, remove >>>>>> plugin API >>>>>> >>>>>> v1: original >>>>>> --- >>>>>> drivers/staging/Kconfig | 2 + >>>>>> drivers/staging/Makefile | 1 + >>>>>> drivers/staging/omapdrm/Kconfig | 24 + >>>>>> drivers/staging/omapdrm/Makefile | 9 + >>>>>> drivers/staging/omapdrm/TODO.txt | 14 + >>>>>> drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ >>>>>> drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ >>>>>> drivers/staging/omapdrm/omap_drv.c | 766 >>>>>> ++++++++++++++++++++++++++++++ >>>>>> drivers/staging/omapdrm/omap_drv.h | 126 +++++ >>>>>> drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ >>>>>> drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ >>>>>> drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ >>>>>> drivers/staging/omapdrm/omap_gem.c | 720 >>>>>> ++++++++++++++++++++++++++++ >>>>>> drivers/video/omap2/omapfb/Kconfig | 2 +- >>>>>> include/drm/Kbuild | 1 + >>>>>> include/drm/omap_drm.h | 111 +++++ >>>>>> include/drm/omap_priv.h | 42 ++ >>>>>> 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h >>>>>> create mode 100644 include/drm/omap_priv.h >>>>>> >>>>>> >>>>>> >>>>>> >>>>> >>>>> ... >>>>> >>>>> >>>>> >>>>>> >>>>>> diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h >>>>>> new file mode 100644 >>>>>> index 0000000..ea0ae8e >>>>>> --- /dev/null >>>>>> +++ b/include/drm/omap_drm.h >>>>>> @@ -0,0 +1,111 @@ >>>>>> +/* >>>>>> + * linux/include/drm/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 "drm.h" >>>>>> + >>>>>> +/* Please note that modifications to all structs defined here are >>>>>> + * subject to backwards-compatibility constraints. >>>>>> + */ >>>>>> + >>>>>> +#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 */ >>>>>> +}; >>>>>> >>>>>> >>>>>> >>>>> >>>>> What about future ARM 64-bit vs 32-bit structure sizes? On x86 we >>>>> always >>>>> take care to make structures appearing in the drm user-space interfaces >>>>> having sizes that are a multiple of 64-bits, to avoid having to >>>>> maintain >>>>> compat code for 32-bit apps running on 64 bit kernels. For the same >>>>> reasons, structure members with 64 bit alignment requirements on 64-bit >>>>> systems need to be carefully places. >>>>> >>>>> I don't know whether there is or will be a 64-bit ARM, but it might be >>>>> worth >>>>> taking into consideration. >>>>> >>>>> >>>> >>>> There isn't currently any 64-bit ARM, but it is a safe assumption that >>>> there will be some day.. I guess we'll have enough fun w/ various >>>> random 32b devices when LPAE arrives w/ the cortex-a15.. >>>> >>>> I did try to make sure any uint64_t's were aligned to a 64bit offset, >>>> but beyond that I confess to not being an expert on how 64 vs 32b >>>> ioctl compat stuff is handled or what the issues going from 32->64b >>>> are. If there are some additional considerations that should be taken >>>> care of, then now is the time. So far I don't have any pointer fields >>>> in any of the ioctl structs. Beyond that, I'm not entirely sure what >>>> else needs to be done, but would appreciate any pointers to docs about >>>> how the compat stuff works. >>>> >>>> BR, >>>> -R >>>> >>>> >>> >>> I've actually avoided writing compat ioctl code myself, by trying to make >>> sure that structures look identical in the 64-bit and 32-bit x86 ABIs, >>> but >>> the compat code is there to translate pointers and to overcome alignment >>> incompatibilities. >>> >>> A good way to at least try to avoid having to maintain compat code once >>> the >>> 64-bit ABI shows up is to make sure that 64-bit scalars and embedded >>> structures are placed on 64-bit boundaries, padding if necessary, and to >>> make sure (using padding) that struct sizes are always multiples of 64 >>> bits. >>> >> >> So far this is true for 64bit scalars.. I'm using stdint types >> everywhere so there is no chance for fields having different sizes on >> 64b vs 32b. And the only structs contained within ioctl structs so >> far are starting at offset==0. >> >> Is it necessary to ensure that the ioctl structs themselves (as >> opposed to structs within those structs) have sizes that are multiple >> of 64b? The ioctl structs are copied >> (copy_from_user()/copy_to_user()), which I would have assumed would be >> sufficient? >> >> > > It depends. If a 64 bit kernel calculates the size as sizeof(struct ...) and > then tries to copy it to user-space using copy_to_user(), it might want to > copy more > than the user-space structure size, causing -EFAULTs or overwritten > user-space data. (If user-space is 32-bit.) > > On x86, for example > > struct { > int64_t a; > int32_t b; > } x; > > Is 96 byte in the 32 bit ABI, but 128 byte in the 64-bit ABI. So if you > issue > > copy_to_user(user_ptr, &x, sizeof(x)) > > It will try to copy 128 byte on a 64-bit kernel and will overwrite data or > cause segfault with a 32-bit user-space. > > > However, IIRC the drm ioctl copy code uses the size encoded by user-space, > which avoids that problem, but that's an implementation specific feature > that shouldn't be relied upon. Ok, gotcha, thx for the explaination.. I'll add a couple of pad fields where needed BR, -R > > /Thomas > > >>> But since there is no 64-bit ARM yet, it might be better to rely on using >>> compat code in the future than on making guesses about alignment >>> restrictions of the ABI... >>> >> >> hmm, it might be nice to get some guidelines from ARM on this, since I >> really have no idea what a 64b ARM architecture would look like.. >> >> BR, >> -R >> >> >>> >>> /Thomas >>> >>> >>> >>> >>> _______________________________________________ >>> dri-devel mailing list >>> dri-devel@lists.freedesktop.org >>> http://lists.freedesktop.org/mailman/listinfo/dri-devel >>> >>> > > > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel >
Hello Rob. Is there some reason that you don't head your driver gpu/drm, just staging? and below is my short comments. thank you. > -----Original Message----- > From: Rob Clark [mailto:robdclark@gmail.com] On Behalf Of Rob Clark > Sent: Sunday, September 18, 2011 6:32 AM > To: dri-devel@lists.freedesktop.org > Cc: patches@linaro.org; Inki Dae; Rob Clark > Subject: [PATCH] RFCv2: 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 > with CMA and use drm_gem_private_object_init(). > > See userspace xorg driver: > git://github.com/robclark/xf86-video-omap.git > > Refer to this link for CMA (Continuous Memory Allocator): > http://lkml.org/lkml/2011/8/19/302 > > Links to previous versions of the patch: > v1: http://lwn.net/Articles/458137/ > > History: > > v2: replace omap_vram with CMA for scanout buffer allocation, remove > unneeded functions, use dma_addr_t for physical addresses, error > handling cleanup, refactor attach/detach pages into common drm > functions, split non-userspace-facing API into omap_priv.h, remove > plugin API > > v1: original > --- > drivers/staging/Kconfig | 2 + > drivers/staging/Makefile | 1 + > drivers/staging/omapdrm/Kconfig | 24 + > drivers/staging/omapdrm/Makefile | 9 + > drivers/staging/omapdrm/TODO.txt | 14 + > drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ > drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ > drivers/staging/omapdrm/omap_drv.c | 766 > ++++++++++++++++++++++++++++++ > drivers/staging/omapdrm/omap_drv.h | 126 +++++ > drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ > drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ > drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ > drivers/staging/omapdrm/omap_gem.c | 720 > ++++++++++++++++++++++++++++ > drivers/video/omap2/omapfb/Kconfig | 2 +- > include/drm/Kbuild | 1 + > include/drm/omap_drm.h | 111 +++++ > include/drm/omap_priv.h | 42 ++ > 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h > create mode 100644 include/drm/omap_priv.h > > diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig > index 06c9081..09d20a3 100644 > --- a/drivers/staging/Kconfig > +++ b/drivers/staging/Kconfig > @@ -150,4 +150,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 f3c5e33..9a24050 100644 > --- a/drivers/staging/Makefile > +++ b/drivers/staging/Makefile > @@ -66,3 +66,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..bd0d1bd > --- /dev/null > +++ b/drivers/staging/omapdrm/Kconfig > @@ -0,0 +1,24 @@ > + > +config DRM_OMAP > + tristate "OMAP DRM (EXPERIMENTAL)" > + depends on DRM && !CONFIG_FB_OMAP2 > + select DRM_KMS_HELPER > + 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..d0d6c20 > --- /dev/null > +++ b/drivers/staging/omapdrm/Makefile > @@ -0,0 +1,9 @@ > +# > +# Makefile for the drm device driver. This driver provides support for > the > +# Direct Rendering Infrastructure (DRI) > +# > + > +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..af81989 > --- /dev/null > +++ b/drivers/staging/omapdrm/TODO.txt > @@ -0,0 +1,14 @@ > +TODO > +. 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 > + > +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..c41d84f > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_connector.c > @@ -0,0 +1,357 @@ > +/* > + * 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 "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; > + > + if (dssdrv->detect) { > + if (dssdrv->detect(dssdev)) { > + ret = connector_status_connected; > + } else { > + ret = connector_status_disconnected; > + } > + } else { > + ret = connector_status_unknown; > + } > + > + 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); > +} > + > +/* 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 How about removing the codes above until dss2 supports hotplug? > + 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..7da36ba > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_crtc.c > @@ -0,0 +1,332 @@ > +/* > + * 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 "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); > + dma_addr_t 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 %08x (%d)", omap_crtc->ovl->name, > + crtc->x, crtc->y, vaddr, (u32)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); Is there any reason that update_scanout function should be called here to update buffer address.? I think it's enough to call this function at mode_set callback. > + omap_crtc->info.enabled = true; > + } else { > + omap_crtc->info.enabled = false; > + } > + > + commit(crtc); and commit callback to apply those data on hw. > +} > + > +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; > +} > + > +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..88209f4 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.c > @@ -0,0 +1,766 @@ > +/* > + * 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 "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 "20110917" > +#define DRIVER_MAJOR 1 > +#define DRIVER_MINOR 0 > +#define DRIVER_PATCHLEVEL 0 > + > +struct drm_device *drm_device; > + > +/* 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 = 256; > + dev->mode_config.min_height = 256; > + > + /* 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_gem_new(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_new *args = data; > + DBG("%p:%p: size=%d, flags=%08x", dev, file_priv, > + args->size.bytes, args->flags); > + return omap_gem_new_handle(dev, file_priv, args->size.bytes, > + args->flags, &args->handle); > +} > + > +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; > + > + VERB("%p:%p: handle=%d, op=%x", dev, file_priv, 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) { > + 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; > + > + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + /* XXX flushy, flushy */ > + ret = 0; > + > + if (!ret) { > + 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 = 0; > + > + DBG("%p:%p: handle=%d", dev, file_priv, 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_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; > + 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); > + // hmm > + //return ret; Doesn't it need return? you output error message using dev_err(). > + } > + > + priv->fbdev = omap_fbdev_init(dev); > + if (!priv->fbdev) { > + dev_err(dev->dev, "omap_fbdev_init failed\n"); > + ret = -ENOMEM; > + // hmm > + //return ret; Ditto. and also you would have to release some resources. with omap_modeset_init call, connector, encoder, crtc and so on would be created. > + } > + > + 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; > + > + return 0; > +} > + > +static int dev_unload(struct drm_device *dev) > +{ > + DBG("unload: dev=%p", 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) > +{ > + file->driver_priv = NULL; > + > + DBG("open: dev=%p, file=%p", 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) > +{ > + DBG("postclose: dev=%p, file=%p", dev, file); > +} > + > +/** > + * 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 = drm_gem_vm_open, > + .close = drm_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, > +}; > + > +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..acd567d > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.h > @@ -0,0 +1,126 @@ > +/* > + * 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> > +#include <linux/types.h> > +#include <drm/drmP.h> > +#include <drm/omap_drm.h> > +#include <drm/omap_priv.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); > + > +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); > + > +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); > + > +struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev, > + struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd); > +struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev, > + struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo); > +struct drm_gem_object * omap_framebuffer_bo(struct drm_framebuffer *fb); > +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, > + void **vaddr, dma_addr_t *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); > +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); > +int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op); > +int 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); > +int omap_gem_get_paddr(struct drm_gem_object *obj, > + dma_addr_t *paddr, bool remap); > +int omap_gem_put_paddr(struct drm_gem_object *obj); > +int omap_gem_get_vaddr(struct drm_gem_object *obj, void **vaddr); > +int omap_gem_put_vaddr(struct drm_gem_object *obj); > +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj); > + > +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..5f0a01e > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_encoder.c > @@ -0,0 +1,188 @@ > +/* > + * 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 "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; > +} > + > +/* 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..8532466 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_fb.c > @@ -0,0 +1,259 @@ > +/* > + * 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 "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 *vaddr; > + dma_addr_t 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) { > + omap_gem_put_vaddr(omap_fb->bo); > + } > + > + 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, dma_addr_t *paddr, int *screen_width) > +{ > + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); > + int bpp = fb->bits_per_pixel / 8; > + unsigned long offset; > + > + offset = (x * bpp) + (y * fb->pitch); > + > + if (vaddr) { > + *vaddr = omap_fb->vaddr + offset; > + } > + > + *paddr = omap_fb->paddr + offset; > + *screen_width = fb->pitch / bpp; > + > + return omap_fb->size - offset; > +} > + > +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; > +} > + > +/* 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); > + } > + } > +} > + > +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; > + > + if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) { > + dev_err(dev->dev, "could not map (paddr)!\n"); > + goto fail; > + } > + > + if (omap_gem_get_vaddr(bo, &omap_fb->vaddr)) { > + dev_err(dev->dev, "could not map (vaddr)!\n"); > + goto fail; > + } > + > + drm_helper_mode_fill_fb_struct(fb, mode_cmd); > + > + return fb; > + > +fail: > + if (fb) { > + omap_framebuffer_destroy(fb); > + } > + return NULL; > +} > diff --git a/drivers/staging/omapdrm/omap_fbdev.c > b/drivers/staging/omapdrm/omap_fbdev.c > new file mode 100644 > index 0000000..24ab4d9 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_fbdev.c > @@ -0,0 +1,309 @@ > +/* > + * 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 "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 = NULL; > + struct fb_info *fbi = NULL; > + struct drm_mode_fb_cmd mode_cmd = {0}; > + dma_addr_t 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; > + > + fb = omap_framebuffer_init(dev, &mode_cmd, NULL); > + if (!fb) { > + dev_err(dev->dev, "failed to allocate fb\n"); > + ret = -ENOMEM; > + goto fail; > + } > + > + 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_unlock; > + } > + > + DBG("fbi=%p, dev=%p", fbi, dev); > + > + fbdev->fb = 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_unlock; > + } > + > + 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_unlock: > + mutex_unlock(&dev->struct_mutex); > +fail: > + > + if (ret) { > + if (fbi) > + framebuffer_release(fbi); > + if (fb) > + fb->funcs->destroy(fb); > + } > + > + 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); > +} > + > +/* 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..890df83 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_gem.c > @@ -0,0 +1,720 @@ > +/* > + * 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 <linux/spinlock.h> > +#include <linux/shmem_fs.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. > + */ > + dma_addr_t paddr; > + > + /** > + * Array of backing pages, if allocated. Note that pages are never > + * allocated for buffers originally allocated from contiguous > memory > + */ > + struct page **pages; > + > + /** > + * Virtual address, if mapped. > + */ > + void *vaddr; > + > + /** > + * sync-object allocated on demand (if needed) > + * > + * Per-buffer sync-object for tracking pending and completed hw/dma > + * read and write operations. The layout in memory is dictated by > + * the SGX firmware, which uses this information to stall the > command > + * stream if a surface is not ready yet. > + * > + * Note that when buffer is used by SGX, the sync-object needs to > be > + * allocated from a special heap of sync-objects. This way many > sync > + * objects can be packed in a page, and not waste GPU virtual > address > + * space. Because of this we have to have a > omap_gem_set_sync_object() > + * API to allow replacement of the syncobj after it has > (potentially) > + * already been allocated. A bit ugly but I haven't thought of a > + * better alternative. > + */ > + struct { > + uint32_t write_pending; > + uint32_t write_complete; > + uint32_t read_pending; > + uint32_t read_complete; > + } *sync; > +}; > + > + > +/* GEM objects can either be allocated from contiguous memory (in which > + * case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non > + * contiguous buffers can be remapped in TILER/DMM if they need to be > + * contiguous... but we don't do this all the time to reduce pressure > + * on TILER/DMM space when we know at allocation time that the buffer > + * will need to be scanned out. > + */ > +static inline bool is_shmem(struct drm_gem_object *obj) > +{ > + return obj->filp != NULL; > +} > + > + > +DEFINE_SPINLOCK(sync_lock); > + > +/** ensure backing pages are allocated */ > +static int omap_gem_attach_pages(struct drm_gem_object *obj) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + struct page **pages; > + > + WARN_ON(omap_obj->pages); > + > + /* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the > + * mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. > probably > + * we actually want CMA memory for it all anyways.. > + */ > + pages = drm_gem_get_pages(obj, GFP_KERNEL); > + if (IS_ERR(pages)) { > + dev_err(obj->dev->dev, "could not get pages: %ld\n", > PTR_ERR(pages)); > + return PTR_ERR(pages); > + } > + > + omap_obj->pages = pages; > + return 0; > +} > + > +/** release backing pages */ > +static void omap_gem_detach_pages(struct drm_gem_object *obj) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + drm_gem_put_pages(obj, omap_obj->pages, true, false); > + omap_obj->pages = NULL; > +} > + > +/** get mmap offset */ > +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) > +{ > + if (!obj->map_list.map) { > + /* Make it mmapable */ > + int ret = drm_gem_create_mmap_offset(obj); > + if (ret) { > + dev_err(obj->dev->dev, "could not allocate mmap > offset"); > + return 0; > + } > + } > + > + return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT; > +} > + > +/** > + * 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 (is_shmem(obj) && !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; > + } > +} > + > +/** 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); > + } > + > + 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); > +} > + > +/** > + * 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, > + dma_addr_t *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; > +} > + > +/* 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; > +} > + > +/* Get kernel virtual address for CPU access */ > +int omap_gem_get_vaddr(struct drm_gem_object *obj, void **vaddr) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + > + if (!omap_obj->vaddr) { > + /* TODO */ > + return -ENOMEM; > + } > + > + *vaddr = omap_obj->vaddr; > + return 0; > +} > + > +/* Release kernel virtual address */ > +int omap_gem_put_vaddr(struct drm_gem_object *obj) > +{ > + /* do something when remap to kernel virtual space is used.. */ > + return 0; > +} > + > +/* 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 int sync_op(struct drm_gem_object *obj, > + enum omap_gem_op op, bool start) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + int ret = 0; > + > + spin_lock(&sync_lock); > + > + if (!omap_obj->sync) { > + omap_obj->sync = kzalloc(sizeof(*omap_obj->sync), > GFP_ATOMIC); > + if (!omap_obj->sync) { > + ret = -ENOMEM; > + goto unlock; > + } > + } > + > + 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(); > + } > + > +unlock: > + spin_unlock(&sync_lock); > + > + return ret; > +} > + > +/* 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); > +} > + > +/* mark the start of read and/or write operation */ > +int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op) > +{ > + return sync_op(obj, op, true); > +} > + > +int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op) > +{ > + return sync_op(obj, op, false); > +} > + > +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; > +} > + > +/* 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_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; > +} > + > +/* 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) > + */ > +int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + int ret = 0; > + > + 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_ATOMIC); > + if (!syncobj) { > + ret = -ENOMEM; > + goto unlock; > + } > + 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; > + } > + > +unlock: > + spin_unlock(&sync_lock); > + return ret; > +} > + > + > +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 drm_device *dev = obj->dev; > + 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 (omap_obj->pages) { > + omap_gem_detach_pages(obj); > + } > + if (!is_shmem(obj)) { > + dma_free_writecombine(dev->dev, obj->size, > + omap_obj->vaddr, omap_obj->paddr); > + } > + } > + > + /* 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) > +{ > + 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; > + } > + > + /* drop reference from allocate - handle holds it now */ > + drm_gem_object_unreference_unlocked(obj); > + > + return 0; > +} > + > +/* GEM buffer object constructor */ > +struct drm_gem_object * omap_gem_new(struct drm_device *dev, > + size_t size, uint32_t flags) > +{ > + struct omap_gem_object *omap_obj; > + struct drm_gem_object *obj = NULL; > + 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 (flags & OMAP_BO_SCANOUT) { > + /* attempt to allocate contiguous memory */ > + omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size, > + &omap_obj->paddr, GFP_KERNEL); > + if (omap_obj->vaddr) { > + flags |= OMAP_BO_DMA; > + } > + } > + > + omap_obj->flags = flags; > + > + 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) { > + goto fail; > + } > + > + return obj; > + > +fail: > + if (obj) { > + omap_gem_free_object(obj); > + } > + return NULL; > +} > diff --git a/drivers/video/omap2/omapfb/Kconfig > b/drivers/video/omap2/omapfb/Kconfig > index 83d3fe7..4ea17dc 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" > - depends on FB && OMAP2_DSS > + depends on FB && OMAP2_DSS && !DRM_OMAP > > select OMAP2_VRAM > select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 > diff --git a/include/drm/Kbuild b/include/drm/Kbuild > index 3a60ac8..78d451c 100644 > --- a/include/drm/Kbuild > +++ b/include/drm/Kbuild > @@ -11,3 +11,4 @@ header-y += savage_drm.h > header-y += sis_drm.h > header-y += via_drm.h > header-y += vmwgfx_drm.h > +header-y += omap_drm.h > diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h > new file mode 100644 > index 0000000..ea0ae8e > --- /dev/null > +++ b/include/drm/omap_drm.h > @@ -0,0 +1,111 @@ > +/* > + * linux/include/drm/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 "drm.h" > + > +/* Please note that modifications to all structs defined here are > + * subject to backwards-compatibility constraints. > + */ > + > +#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 */ > + 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) > + > +#endif /* __OMAP_DRM_H__ */ > diff --git a/include/drm/omap_priv.h b/include/drm/omap_priv.h > new file mode 100644 > index 0000000..ca7d975 > --- /dev/null > +++ b/include/drm/omap_priv.h > @@ -0,0 +1,42 @@ > +/* > + * linux/include/drm/omap_priv.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_PRIV_H__ > +#define __OMAP_PRIV_H__ > + > +/* Non-userspace facing APIs > + */ > + > +/* 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__ */ > -- > 1.7.5.4
On Mon, Sep 19, 2011 at 2:44 AM, Inki Dae <inki.dae@samsung.com> wrote: > Hello Rob. > > Is there some reason that you don't head your driver gpu/drm, just staging? > and below is my short comments. thank you. > mainly because it is still a work-in-progress.. I expect some re-shuffling of the mode-setting code when drm_plane support is added, the plugin layer, etc. And perhaps at some point merging of the omapdss and omapdrm layers. I don't expect the ioctl ABI to change, and the driver is useful as-is, but I still think there is enough changes that will happen that staging is perhaps the better place for it to start life.. [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; >> + 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 > > How about removing the codes above until dss2 supports hotplug? > easier not to forget about them this way ;-) if someone has a strong opinion, I can remove them.. but I guess it should be not too distant future when support lands in dss2.. I guess I should put a note in the TODO about re-enabling hotplug code >> + 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; >> +} [snip] >> +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); > > Is there any reason that update_scanout function should be called here to > update buffer address.? I think it's enough to call this function at > mode_set callback. In theory it should not be needed.. IIRC at some point I was hitting some problem that the buffer address wasn't set yet, but that was w/ older version of the driver, older kernel, and different xorg driver.. >> + omap_crtc->info.enabled = true; >> + } else { >> + omap_crtc->info.enabled = false; >> + } >> + >> + commit(crtc); > > and commit callback to apply those data on hw. > >> +} >> + [snip] >> + >> +/** >> + * 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; >> + 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); >> + // hmm >> + //return ret; > > Doesn't it need return? you output error message using dev_err(). Well.. currently omap_modeset_init() never returns !=0 I'm still a bit undecided on some of the error cases.. for example if we don't successfully initialize everything, but do at least have >1 of each crtc/encoder/connector, maybe it makes sense to continue in some reduced capacity.. But "check error handling/cleanup paths" is still in the TODO file ;-) >> + } >> + >> + priv->fbdev = omap_fbdev_init(dev); >> + if (!priv->fbdev) { >> + dev_err(dev->dev, "omap_fbdev_init failed\n"); >> + ret = -ENOMEM; >> + // hmm >> + //return ret; > > Ditto. and also you would have to release some resources. with > omap_modeset_init call, connector, encoder, crtc and so on would be created. Currently we continue on w/out an fbdev.. for X11, that is fine, it can function without an fbdev device. So I'm undecided about how fatal of an error this should be BR, -R
So I did a cursory look and stopped at omap_drv.h due to the lack of time. Some comments below. > +++ b/drivers/staging/omapdrm/Kconfig > @@ -0,0 +1,24 @@ > + > +config DRM_OMAP > + tristate "OMAP DRM (EXPERIMENTAL)" > + depends on DRM && !CONFIG_FB_OMAP2 Since it says EXPERIMENTAL should it depend on that config option as well? The CONFIG_EXPERIMENTAL one. > + select DRM_KMS_HELPER > + 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. > + ..snip.. > +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: Move the 'int ret' to here since you only seem to be using it within this code logic. > + 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 */ > + } > +} > + .. snip.. > +#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); > +} > + > +/* 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..7da36ba > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_crtc.c > @@ -0,0 +1,332 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_crtc.c Remove the linux part > + * > + * 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 "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; Where is this defined? > + 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); Any chance that olv->set_overlayinfo could be NULL? > + 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"); Make that more verbose so that in the field you have an idea of what it could not apply. > + 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); > + dma_addr_t paddr; > + void __iomem *vaddr; > + int screen_width; Not unsigned int? Can widths become negative? > + > + omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, > + &vaddr, &paddr, &screen_width); > + > + DBG("%s: %d,%d: %p %08x (%d)", omap_crtc->ovl->name, > + crtc->x, crtc->y, vaddr, (u32)paddr, screen_width); So.. never going to run this on a box with more than 4GB? Perhaps the (u32) casting should be dropped. Does it make sense to report also the return value from omap_framebuffer_get_buffer? > + > + 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); So no checking of its return value.. Should you at least wrap it with WARN_ON(?) > +} > + > +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); So this can be called from an IRQ handler? Why the need to disable the IRQs? Can you just use spin_lock? > + 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; > +} > + > +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..88209f4 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.c > @@ -0,0 +1,766 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_drv.c Remoev the linux > + * > + * 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 "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 "20110917" > +#define DRIVER_MAJOR 1 > +#define DRIVER_MINOR 0 > +#define DRIVER_PATCHLEVEL 0 > + > +struct drm_device *drm_device; > + > +/* 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; Add __read_mostly.. You don't need to set false as by default it will be 0 (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")) strncmp > + 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++) { Any chance omap_dss_get_num_overlays can return a negative value? > + struct omap_overlay *ovl = omap_dss_get_overlay(i); and if so, this could cause this guy to explode. > + 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; Should you check priv->num_encoders to be sure you are not referencing past the priv->encoders size? Or if num_encoders is -1 then hitting some other code and potentially causing a security hole. > + > + 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; Ditto on this check? > + > +#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; You might want to use: unsigned int idx = max(0, j - priv->num_connectors); jsut ot make sure you don't go negative. > + if (!(connected_connectors & (1 << idx))) { > + struct drm_encoder * encoder = > + omap_connector_attached_encoder( > + priv->connectors[idx]); .. and reference in the unknown memory location. > + 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]); No check for return value? What if we get -ENOMEM? > + } > + > + 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 = 256; > + dev->mode_config.min_height = 256; > + > + /* 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_gem_new(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_new *args = data; > + DBG("%p:%p: size=%d, flags=%08x", dev, file_priv, > + args->size.bytes, args->flags); > + return omap_gem_new_handle(dev, file_priv, args->size.bytes, > + args->flags, &args->handle); > +} > + > +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; > + > + VERB("%p:%p: handle=%d, op=%x", dev, file_priv, 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) { > + 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; > + > + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + /* XXX flushy, flushy */ > + ret = 0; > + > + if (!ret) { > + 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 = 0; > + > + DBG("%p:%p: handle=%d", dev, file_priv, 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_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; > + 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; -ENOMEM? > + } > + > + dev->dev_private = priv; > + > + ret = omap_modeset_init(dev); > + if (ret) { > + dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); > + // hmm > + //return ret; > + } > + > + priv->fbdev = omap_fbdev_init(dev); > + if (!priv->fbdev) { > + dev_err(dev->dev, "omap_fbdev_init failed\n"); > + ret = -ENOMEM; > + // hmm > + //return ret; Why not? > + } > + > + 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; > + > + return 0; > +} > + > +static int dev_unload(struct drm_device *dev) > +{ > + DBG("unload: dev=%p", 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) > +{ > + file->driver_priv = NULL; > + > + DBG("open: dev=%p, file=%p", 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) > +{ > + DBG("postclose: dev=%p, file=%p", dev, file); > +} > + > +/** > + * 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 = drm_gem_vm_open, > + .close = drm_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, > +}; > + > +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..acd567d > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.h > @@ -0,0 +1,126 @@ > +/* > + * 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> > +#include <linux/types.h> > +#include <drm/drmP.h> > +#include <drm/omap_drm.h> > +#include <drm/omap_priv.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; Make that unsigned int > + struct drm_encoder *encoders[8]; > + int num_connectors; And also that one. > + 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); > + > +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); > + > +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); > + > +struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev, > + struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd); > +struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev, > + struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo); > +struct drm_gem_object * omap_framebuffer_bo(struct drm_framebuffer *fb); > +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, > + void **vaddr, dma_addr_t *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); > +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); > +int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op); > +int 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); > +int omap_gem_get_paddr(struct drm_gem_object *obj, > + dma_addr_t *paddr, bool remap); > +int omap_gem_put_paddr(struct drm_gem_object *obj); > +int omap_gem_get_vaddr(struct drm_gem_object *obj, void **vaddr); > +int omap_gem_put_vaddr(struct drm_gem_object *obj); > +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj); > + > +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..5f0a01e > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_encoder.c > @@ -0,0 +1,188 @@ > +/* > + * 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 "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; > +} > + > +/* 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..8532466 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_fb.c > @@ -0,0 +1,259 @@ > +/* > + * 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 "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 *vaddr; > + dma_addr_t 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) { > + omap_gem_put_vaddr(omap_fb->bo); > + } > + > + 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, dma_addr_t *paddr, int *screen_width) > +{ > + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); > + int bpp = fb->bits_per_pixel / 8; > + unsigned long offset; > + > + offset = (x * bpp) + (y * fb->pitch); > + > + if (vaddr) { > + *vaddr = omap_fb->vaddr + offset; > + } > + > + *paddr = omap_fb->paddr + offset; > + *screen_width = fb->pitch / bpp; > + > + return omap_fb->size - offset; > +} > + > +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; > +} > + > +/* 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); > + } > + } > +} > + > +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; > + > + if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) { > + dev_err(dev->dev, "could not map (paddr)!\n"); > + goto fail; > + } > + > + if (omap_gem_get_vaddr(bo, &omap_fb->vaddr)) { > + dev_err(dev->dev, "could not map (vaddr)!\n"); > + goto fail; > + } > + > + drm_helper_mode_fill_fb_struct(fb, mode_cmd); > + > + return fb; > + > +fail: > + if (fb) { > + omap_framebuffer_destroy(fb); > + } > + return NULL; > +} > diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c > new file mode 100644 > index 0000000..24ab4d9 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_fbdev.c > @@ -0,0 +1,309 @@ > +/* > + * 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 "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 = NULL; > + struct fb_info *fbi = NULL; > + struct drm_mode_fb_cmd mode_cmd = {0}; > + dma_addr_t 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; > + > + fb = omap_framebuffer_init(dev, &mode_cmd, NULL); > + if (!fb) { > + dev_err(dev->dev, "failed to allocate fb\n"); > + ret = -ENOMEM; > + goto fail; > + } > + > + 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_unlock; > + } > + > + DBG("fbi=%p, dev=%p", fbi, dev); > + > + fbdev->fb = 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_unlock; > + } > + > + 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_unlock: > + mutex_unlock(&dev->struct_mutex); > +fail: > + > + if (ret) { > + if (fbi) > + framebuffer_release(fbi); > + if (fb) > + fb->funcs->destroy(fb); > + } > + > + 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); > +} > + > +/* 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..890df83 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_gem.c > @@ -0,0 +1,720 @@ > +/* > + * 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 <linux/spinlock.h> > +#include <linux/shmem_fs.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. > + */ > + dma_addr_t paddr; > + > + /** > + * Array of backing pages, if allocated. Note that pages are never > + * allocated for buffers originally allocated from contiguous memory > + */ > + struct page **pages; > + > + /** > + * Virtual address, if mapped. > + */ > + void *vaddr; > + > + /** > + * sync-object allocated on demand (if needed) > + * > + * Per-buffer sync-object for tracking pending and completed hw/dma > + * read and write operations. The layout in memory is dictated by > + * the SGX firmware, which uses this information to stall the command > + * stream if a surface is not ready yet. > + * > + * Note that when buffer is used by SGX, the sync-object needs to be > + * allocated from a special heap of sync-objects. This way many sync > + * objects can be packed in a page, and not waste GPU virtual address > + * space. Because of this we have to have a omap_gem_set_sync_object() > + * API to allow replacement of the syncobj after it has (potentially) > + * already been allocated. A bit ugly but I haven't thought of a > + * better alternative. > + */ > + struct { > + uint32_t write_pending; > + uint32_t write_complete; > + uint32_t read_pending; > + uint32_t read_complete; > + } *sync; > +}; > + > + > +/* GEM objects can either be allocated from contiguous memory (in which > + * case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non > + * contiguous buffers can be remapped in TILER/DMM if they need to be > + * contiguous... but we don't do this all the time to reduce pressure > + * on TILER/DMM space when we know at allocation time that the buffer > + * will need to be scanned out. > + */ > +static inline bool is_shmem(struct drm_gem_object *obj) > +{ > + return obj->filp != NULL; > +} > + > + > +DEFINE_SPINLOCK(sync_lock); > + > +/** ensure backing pages are allocated */ > +static int omap_gem_attach_pages(struct drm_gem_object *obj) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + struct page **pages; > + > + WARN_ON(omap_obj->pages); > + > + /* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the > + * mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. probably > + * we actually want CMA memory for it all anyways.. > + */ > + pages = drm_gem_get_pages(obj, GFP_KERNEL); > + if (IS_ERR(pages)) { > + dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages)); > + return PTR_ERR(pages); > + } > + > + omap_obj->pages = pages; > + return 0; > +} > + > +/** release backing pages */ > +static void omap_gem_detach_pages(struct drm_gem_object *obj) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + drm_gem_put_pages(obj, omap_obj->pages, true, false); > + omap_obj->pages = NULL; > +} > + > +/** get mmap offset */ > +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) > +{ > + if (!obj->map_list.map) { > + /* Make it mmapable */ > + int ret = drm_gem_create_mmap_offset(obj); > + if (ret) { > + dev_err(obj->dev->dev, "could not allocate mmap offset"); > + return 0; > + } > + } > + > + return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT; > +} > + > +/** > + * 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 (is_shmem(obj) && !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; > + } > +} > + > +/** 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); > + } > + > + 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); > +} > + > +/** > + * 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, > + dma_addr_t *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; > +} > + > +/* 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; > +} > + > +/* Get kernel virtual address for CPU access */ > +int omap_gem_get_vaddr(struct drm_gem_object *obj, void **vaddr) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + > + if (!omap_obj->vaddr) { > + /* TODO */ > + return -ENOMEM; > + } > + > + *vaddr = omap_obj->vaddr; > + return 0; > +} > + > +/* Release kernel virtual address */ > +int omap_gem_put_vaddr(struct drm_gem_object *obj) > +{ > + /* do something when remap to kernel virtual space is used.. */ > + return 0; > +} > + > +/* 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 int sync_op(struct drm_gem_object *obj, > + enum omap_gem_op op, bool start) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + int ret = 0; > + > + spin_lock(&sync_lock); > + > + if (!omap_obj->sync) { > + omap_obj->sync = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC); > + if (!omap_obj->sync) { > + ret = -ENOMEM; > + goto unlock; > + } > + } > + > + 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(); > + } > + > +unlock: > + spin_unlock(&sync_lock); > + > + return ret; > +} > + > +/* 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); > +} > + > +/* mark the start of read and/or write operation */ > +int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op) > +{ > + return sync_op(obj, op, true); > +} > + > +int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op) > +{ > + return sync_op(obj, op, false); > +} > + > +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; > +} > + > +/* 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_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; > +} > + > +/* 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) > + */ > +int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj) > +{ > + struct omap_gem_object *omap_obj = to_omap_bo(obj); > + int ret = 0; > + > + 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_ATOMIC); > + if (!syncobj) { > + ret = -ENOMEM; > + goto unlock; > + } > + 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; > + } > + > +unlock: > + spin_unlock(&sync_lock); > + return ret; > +} > + > + > +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 drm_device *dev = obj->dev; > + 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 (omap_obj->pages) { > + omap_gem_detach_pages(obj); > + } > + if (!is_shmem(obj)) { > + dma_free_writecombine(dev->dev, obj->size, > + omap_obj->vaddr, omap_obj->paddr); > + } > + } > + > + /* 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) > +{ > + 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; > + } > + > + /* drop reference from allocate - handle holds it now */ > + drm_gem_object_unreference_unlocked(obj); > + > + return 0; > +} > + > +/* GEM buffer object constructor */ > +struct drm_gem_object * omap_gem_new(struct drm_device *dev, > + size_t size, uint32_t flags) > +{ > + struct omap_gem_object *omap_obj; > + struct drm_gem_object *obj = NULL; > + 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 (flags & OMAP_BO_SCANOUT) { > + /* attempt to allocate contiguous memory */ > + omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size, > + &omap_obj->paddr, GFP_KERNEL); > + if (omap_obj->vaddr) { > + flags |= OMAP_BO_DMA; > + } > + } > + > + omap_obj->flags = flags; > + > + 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) { > + goto fail; > + } > + > + return obj; > + > +fail: > + if (obj) { > + omap_gem_free_object(obj); > + } > + return NULL; > +} > diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig > index 83d3fe7..4ea17dc 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" > - depends on FB && OMAP2_DSS > + depends on FB && OMAP2_DSS && !DRM_OMAP > > select OMAP2_VRAM > select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 > diff --git a/include/drm/Kbuild b/include/drm/Kbuild > index 3a60ac8..78d451c 100644 > --- a/include/drm/Kbuild > +++ b/include/drm/Kbuild > @@ -11,3 +11,4 @@ header-y += savage_drm.h > header-y += sis_drm.h > header-y += via_drm.h > header-y += vmwgfx_drm.h > +header-y += omap_drm.h > diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h > new file mode 100644 > index 0000000..ea0ae8e > --- /dev/null > +++ b/include/drm/omap_drm.h > @@ -0,0 +1,111 @@ > +/* > + * linux/include/drm/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 "drm.h" > + > +/* Please note that modifications to all structs defined here are > + * subject to backwards-compatibility constraints. > + */ > + > +#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 */ > + 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) > + > +#endif /* __OMAP_DRM_H__ */ > diff --git a/include/drm/omap_priv.h b/include/drm/omap_priv.h > new file mode 100644 > index 0000000..ca7d975 > --- /dev/null > +++ b/include/drm/omap_priv.h > @@ -0,0 +1,42 @@ > +/* > + * linux/include/drm/omap_priv.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_PRIV_H__ > +#define __OMAP_PRIV_H__ > + > +/* Non-userspace facing APIs > + */ > + > +/* 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__ */ > -- > 1.7.5.4 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wed, Sep 21, 2011 at 5:32 PM, Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> wrote: > So I did a cursory look and stopped at omap_drv.h > due to the lack of time. > > Some comments below. > >> +++ b/drivers/staging/omapdrm/Kconfig >> @@ -0,0 +1,24 @@ >> + >> +config DRM_OMAP >> + tristate "OMAP DRM (EXPERIMENTAL)" >> + depends on DRM && !CONFIG_FB_OMAP2 > > Since it says EXPERIMENTAL should it depend on that config option as well? > > The CONFIG_EXPERIMENTAL one. Well.. I guess it is already in staging. Not sure if that is redundant to be both staging and experimental. I'll just remove the text, it is somewhat less experimental now than when I first started (and wrote the Kconfig file) > >> + select DRM_KMS_HELPER >> + 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. >> + [snip] >> +struct omap_crtc { >> + struct drm_crtc base; >> + struct omap_overlay *ovl; > > Where is this defined? in include/video/omapdss.h >> + 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); > > Any chance that olv->set_overlayinfo could be NULL? I don't believe so.. actually, I'm not entirely sure why it needs to be a fxn ptr, it seems to always be the same fxn. But it is unconditionally populated when the omap_overlay object is constructed. (See dss_init_overlays() in drivers/video/omap2/dss/overlay.c) [snip] >> +/* 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); >> + dma_addr_t paddr; >> + void __iomem *vaddr; >> + int screen_width; > > Not unsigned int? Can widths become negative? I suppose not ;-) >> + >> + omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, >> + &vaddr, &paddr, &screen_width); >> + >> + DBG("%s: %d,%d: %p %08x (%d)", omap_crtc->ovl->name, >> + crtc->x, crtc->y, vaddr, (u32)paddr, screen_width); > > So.. never going to run this on a box with more than 4GB? Perhaps > the (u32) casting should be dropped. > > Does it make sense to report also the return value from omap_framebuffer_get_buffer? it is just returning the buffer size.. which is needed in another place that calls get_buffer(), but not here >> + >> + 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); > > So no checking of its return value.. Should you at least wrap > it with WARN_ON(?) Is it safe to rely on the "payload" of the WARN_ON() always being evaluated, or is there any scenario that you could have something like #define WARN_ON(X) ie., is this safe: WARN_ON(commit(crtc)); it looked like different archs can provide their own WARN_ON, so wasn't sure how much to trust it.. [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); > > So this can be called from an IRQ handler? Why the need to disable > the IRQs? Can you just use spin_lock? not currently.. OTOH, it should be moved somewhere that would be called from an IRQ.. >> + 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); >> + } >> +} >> + [snip] >> + >> +/* 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; > > Add __read_mostly.. You don't need to set false as by default it will > be 0 (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")) > > strncmp In this case, since one of the args is a string literal, I know it is properly terminated.. (or, well, unless it is possible to corrupt .text/.rodata.. although in that case your n value is probably also corrupted and you are more or less up s**t creek..) > >> + 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++) { > > Any chance omap_dss_get_num_overlays can return a negative value? no >> + struct omap_overlay *ovl = omap_dss_get_overlay(i); > > and if so, this could cause this guy to explode. >> + 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; > > Should you check priv->num_encoders to be sure you are not > referencing past the priv->encoders size? Or if num_encoders is -1 then > hitting some other code and potentially causing a security hole. > currently the array size for crtc/encoder/connectors is ~2x the # of corresponding physical resources. But I guess it doesn't hurt to have some BUG_ON()'s.. >> + >> + 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; > > Ditto on this check? >> + >> +#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; > > You might want to use: > unsigned int idx = max(0, j - priv->num_connectors); > > jsut ot make sure you don't go negative. It can't (currently) happen, because you need to go all the way thru the first loop to enter the second loop. Although maybe that bit of code is a bit less than self-apparent.. but I hadn't thought of a way to simplify it. > >> + if (!(connected_connectors & (1 << idx))) { >> + struct drm_encoder * encoder = >> + omap_connector_attached_encoder( >> + priv->connectors[idx]); > > .. and reference in the unknown memory location. > >> + 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]); > > No check for return value? What if we get -ENOMEM? I'm a bit undecided on some of this error handling at startup.. I guess ENOMEM is clear enough. But some of the other parts, like connector initialization, could fail just because some hw is not present/populated. Like missing some LCD. I guess that it is best to try and limp along as best as possible and hope to get some pixels on some screen that the user can see. If you give up and all the screens/monitors/etc stay dark, it might be a bit inconvenient to debug.. BR, -R >> + } >> + >> + 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 = 256; >> + dev->mode_config.min_height = 256; >> + >> + /* 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; >> +} >> +
> >> + commit(crtc); > > > > So no checking of its return value.. Should you at least wrap > > it with WARN_ON(?) > > Is it safe to rely on the "payload" of the WARN_ON() always being > evaluated, or is there any scenario that you could have something like Hmm, good question. I assumed so, but you got me thinking. > > #define WARN_ON(X) > > ie., is this safe: > > WARN_ON(commit(crtc)); > > it looked like different archs can provide their own WARN_ON, so > wasn't sure how much to trust it.. > > > [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); > > > > So this can be called from an IRQ handler? Why the need to disable > > the IRQs? Can you just use spin_lock? > > not currently.. OTOH, it should be moved somewhere that would be > called from an IRQ.. Ok, so laying the ground work for it. That is OK - you might want to mention that in the TODO just in case . > > >> + 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); > >> + } > >> +} > >> + > > [snip] > > >> + > >> +/* 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; > > > > Add __read_mostly.. You don't need to set false as by default it will > > be 0 (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")) > > > > strncmp > > In this case, since one of the args is a string literal, I know it is > properly terminated.. <nods> > > > > Should you check priv->num_encoders to be sure you are not > > referencing past the priv->encoders size? Or if num_encoders is -1 then > > hitting some other code and potentially causing a security hole. > > > > currently the array size for crtc/encoder/connectors is ~2x the # of > corresponding physical resources. But I guess it doesn't hurt to have > some BUG_ON()'s.. Thank you. .. snip.. > >> + /* 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; > > > > You might want to use: > > unsigned int idx = max(0, j - priv->num_connectors); > > > > jsut ot make sure you don't go negative. > > It can't (currently) happen, because you need to go all the way thru > the first loop to enter the second loop. Although maybe that bit of > code is a bit less than self-apparent.. but I hadn't thought of a way > to simplify it. A think expanding on the comment will suffice. .. snip.. > >> + for (i = 0; i < pdata->mgr_cnt; i++) { > >> + create_encoder(pdata->mgr_ids[i]); > > > > No check for return value? What if we get -ENOMEM? > > I'm a bit undecided on some of this error handling at startup.. I > guess ENOMEM is clear enough. But some of the other parts, like > connector initialization, could fail just because some hw is not > present/populated. Like missing some LCD. I guess that it is best to > try and limp along as best as possible and hope to get some pixels on > some screen that the user can see. If you give up and all the > screens/monitors/etc stay dark, it might be a bit inconvenient to > debug.. Perhaps then just do WARN_ON, like: if (create_encode(..)) WARN_ON(1,"Danger Danger! Limping along\n");
On Mon, Sep 26, 2011 at 09:55:44AM -0400, Konrad Rzeszutek Wilk wrote: > > >> + commit(crtc); > > > > > > So no checking of its return value.. Should you at least wrap > > > it with WARN_ON(?) > > > > Is it safe to rely on the "payload" of the WARN_ON() always being > > evaluated, or is there any scenario that you could have something like > > Hmm, good question. I assumed so, but you got me thinking. > > > > #define WARN_ON(X) > > > > ie., is this safe: > > > > WARN_ON(commit(crtc)); > > > > it looked like different archs can provide their own WARN_ON, so > > wasn't sure how much to trust it.. asm-generic/bug.h has the "right" implementation. If other architectures redefine as your have pointed out - then that looks like a bug. A: Yes, it is safe to do WARN_ON(commit(rctc));
On Mon, Sep 26, 2011 at 8:55 AM, Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> wrote: >> I'm a bit undecided on some of this error handling at startup.. I >> guess ENOMEM is clear enough. But some of the other parts, like >> connector initialization, could fail just because some hw is not >> present/populated. Like missing some LCD. I guess that it is best to >> try and limp along as best as possible and hope to get some pixels on >> some screen that the user can see. If you give up and all the >> screens/monitors/etc stay dark, it might be a bit inconvenient to >> debug.. > > Perhaps then just do WARN_ON, like: > > if (create_encode(..)) > WARN_ON(1,"Danger Danger! Limping along\n"); fwiw, in cases where create_{crtc,encoder,connector}() return !=0, there is a dev_err() in the create_*() fxn. I think this should be sufficient, no need to double up the messages in the log.. I've cleaned up the rest of the things that you had pointed out, and will send a v3 shortly. Thanks! BR, -R
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 06c9081..09d20a3 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -150,4 +150,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 f3c5e33..9a24050 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -66,3 +66,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..bd0d1bd --- /dev/null +++ b/drivers/staging/omapdrm/Kconfig @@ -0,0 +1,24 @@ + +config DRM_OMAP + tristate "OMAP DRM (EXPERIMENTAL)" + depends on DRM && !CONFIG_FB_OMAP2 + select DRM_KMS_HELPER + 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..d0d6c20 --- /dev/null +++ b/drivers/staging/omapdrm/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) +# + +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..af81989 --- /dev/null +++ b/drivers/staging/omapdrm/TODO.txt @@ -0,0 +1,14 @@ +TODO +. 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 + +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..c41d84f --- /dev/null +++ b/drivers/staging/omapdrm/omap_connector.c @@ -0,0 +1,357 @@ +/* + * 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 "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; + + if (dssdrv->detect) { + if (dssdrv->detect(dssdev)) { + ret = connector_status_connected; + } else { + ret = connector_status_disconnected; + } + } else { + ret = connector_status_unknown; + } + + 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); +} + +/* 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..7da36ba --- /dev/null +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -0,0 +1,332 @@ +/* + * 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 "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); + dma_addr_t 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 %08x (%d)", omap_crtc->ovl->name, + crtc->x, crtc->y, vaddr, (u32)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; +} + +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..88209f4 --- /dev/null +++ b/drivers/staging/omapdrm/omap_drv.c @@ -0,0 +1,766 @@ +/* + * 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 "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 "20110917" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +struct drm_device *drm_device; + +/* 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 = 256; + dev->mode_config.min_height = 256; + + /* 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_gem_new(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_omap_gem_new *args = data; + DBG("%p:%p: size=%d, flags=%08x", dev, file_priv, + args->size.bytes, args->flags); + return omap_gem_new_handle(dev, file_priv, args->size.bytes, + args->flags, &args->handle); +} + +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; + + VERB("%p:%p: handle=%d, op=%x", dev, file_priv, 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) { + 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; + + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!obj) { + return -ENOENT; + } + + /* XXX flushy, flushy */ + ret = 0; + + if (!ret) { + 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 = 0; + + DBG("%p:%p: handle=%d", dev, file_priv, 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_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; + 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); + // hmm + //return ret; + } + + priv->fbdev = omap_fbdev_init(dev); + if (!priv->fbdev) { + dev_err(dev->dev, "omap_fbdev_init failed\n"); + ret = -ENOMEM; + // hmm + //return ret; + } + + 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; + + return 0; +} + +static int dev_unload(struct drm_device *dev) +{ + DBG("unload: dev=%p", 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) +{ + file->driver_priv = NULL; + + DBG("open: dev=%p, file=%p", 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) +{ + DBG("postclose: dev=%p, file=%p", dev, file); +} + +/** + * 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 = drm_gem_vm_open, + .close = drm_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, +}; + +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..acd567d --- /dev/null +++ b/drivers/staging/omapdrm/omap_drv.h @@ -0,0 +1,126 @@ +/* + * 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> +#include <linux/types.h> +#include <drm/drmP.h> +#include <drm/omap_drm.h> +#include <drm/omap_priv.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); + +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); + +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); + +struct drm_framebuffer * omap_framebuffer_create(struct drm_device *dev, + struct drm_file *file, struct drm_mode_fb_cmd *mode_cmd); +struct drm_framebuffer * omap_framebuffer_init(struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd, struct drm_gem_object *bo); +struct drm_gem_object * omap_framebuffer_bo(struct drm_framebuffer *fb); +int omap_framebuffer_get_buffer(struct drm_framebuffer *fb, int x, int y, + void **vaddr, dma_addr_t *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); +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); +int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op); +int 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); +int omap_gem_get_paddr(struct drm_gem_object *obj, + dma_addr_t *paddr, bool remap); +int omap_gem_put_paddr(struct drm_gem_object *obj); +int omap_gem_get_vaddr(struct drm_gem_object *obj, void **vaddr); +int omap_gem_put_vaddr(struct drm_gem_object *obj); +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj); + +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..5f0a01e --- /dev/null +++ b/drivers/staging/omapdrm/omap_encoder.c @@ -0,0 +1,188 @@ +/* + * 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 "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; +} + +/* 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..8532466 --- /dev/null +++ b/drivers/staging/omapdrm/omap_fb.c @@ -0,0 +1,259 @@ +/* + * 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 "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 *vaddr; + dma_addr_t 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) { + omap_gem_put_vaddr(omap_fb->bo); + } + + 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, dma_addr_t *paddr, int *screen_width) +{ + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + int bpp = fb->bits_per_pixel / 8; + unsigned long offset; + + offset = (x * bpp) + (y * fb->pitch); + + if (vaddr) { + *vaddr = omap_fb->vaddr + offset; + } + + *paddr = omap_fb->paddr + offset; + *screen_width = fb->pitch / bpp; + + return omap_fb->size - offset; +} + +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; +} + +/* 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); + } + } +} + +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; + + if (omap_gem_get_paddr(bo, &omap_fb->paddr, true)) { + dev_err(dev->dev, "could not map (paddr)!\n"); + goto fail; + } + + if (omap_gem_get_vaddr(bo, &omap_fb->vaddr)) { + dev_err(dev->dev, "could not map (vaddr)!\n"); + goto fail; + } + + drm_helper_mode_fill_fb_struct(fb, mode_cmd); + + return fb; + +fail: + if (fb) { + omap_framebuffer_destroy(fb); + } + return NULL; +} diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c new file mode 100644 index 0000000..24ab4d9 --- /dev/null +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -0,0 +1,309 @@ +/* + * 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 "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 = NULL; + struct fb_info *fbi = NULL; + struct drm_mode_fb_cmd mode_cmd = {0}; + dma_addr_t 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; + + fb = omap_framebuffer_init(dev, &mode_cmd, NULL); + if (!fb) { + dev_err(dev->dev, "failed to allocate fb\n"); + ret = -ENOMEM; + goto fail; + } + + 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_unlock; + } + + DBG("fbi=%p, dev=%p", fbi, dev); + + fbdev->fb = 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_unlock; + } + + 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_unlock: + mutex_unlock(&dev->struct_mutex); +fail: + + if (ret) { + if (fbi) + framebuffer_release(fbi); + if (fb) + fb->funcs->destroy(fb); + } + + 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); +} + +/* 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..890df83 --- /dev/null +++ b/drivers/staging/omapdrm/omap_gem.c @@ -0,0 +1,720 @@ +/* + * 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 <linux/spinlock.h> +#include <linux/shmem_fs.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. + */ + dma_addr_t paddr; + + /** + * Array of backing pages, if allocated. Note that pages are never + * allocated for buffers originally allocated from contiguous memory + */ + struct page **pages; + + /** + * Virtual address, if mapped. + */ + void *vaddr; + + /** + * sync-object allocated on demand (if needed) + * + * Per-buffer sync-object for tracking pending and completed hw/dma + * read and write operations. The layout in memory is dictated by + * the SGX firmware, which uses this information to stall the command + * stream if a surface is not ready yet. + * + * Note that when buffer is used by SGX, the sync-object needs to be + * allocated from a special heap of sync-objects. This way many sync + * objects can be packed in a page, and not waste GPU virtual address + * space. Because of this we have to have a omap_gem_set_sync_object() + * API to allow replacement of the syncobj after it has (potentially) + * already been allocated. A bit ugly but I haven't thought of a + * better alternative. + */ + struct { + uint32_t write_pending; + uint32_t write_complete; + uint32_t read_pending; + uint32_t read_complete; + } *sync; +}; + + +/* GEM objects can either be allocated from contiguous memory (in which + * case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non + * contiguous buffers can be remapped in TILER/DMM if they need to be + * contiguous... but we don't do this all the time to reduce pressure + * on TILER/DMM space when we know at allocation time that the buffer + * will need to be scanned out. + */ +static inline bool is_shmem(struct drm_gem_object *obj) +{ + return obj->filp != NULL; +} + + +DEFINE_SPINLOCK(sync_lock); + +/** ensure backing pages are allocated */ +static int omap_gem_attach_pages(struct drm_gem_object *obj) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + struct page **pages; + + WARN_ON(omap_obj->pages); + + /* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the + * mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. probably + * we actually want CMA memory for it all anyways.. + */ + pages = drm_gem_get_pages(obj, GFP_KERNEL); + if (IS_ERR(pages)) { + dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages)); + return PTR_ERR(pages); + } + + omap_obj->pages = pages; + return 0; +} + +/** release backing pages */ +static void omap_gem_detach_pages(struct drm_gem_object *obj) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + drm_gem_put_pages(obj, omap_obj->pages, true, false); + omap_obj->pages = NULL; +} + +/** get mmap offset */ +uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) +{ + if (!obj->map_list.map) { + /* Make it mmapable */ + int ret = drm_gem_create_mmap_offset(obj); + if (ret) { + dev_err(obj->dev->dev, "could not allocate mmap offset"); + return 0; + } + } + + return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT; +} + +/** + * 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 (is_shmem(obj) && !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; + } +} + +/** 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); + } + + 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); +} + +/** + * 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, + dma_addr_t *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; +} + +/* 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; +} + +/* Get kernel virtual address for CPU access */ +int omap_gem_get_vaddr(struct drm_gem_object *obj, void **vaddr) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + + if (!omap_obj->vaddr) { + /* TODO */ + return -ENOMEM; + } + + *vaddr = omap_obj->vaddr; + return 0; +} + +/* Release kernel virtual address */ +int omap_gem_put_vaddr(struct drm_gem_object *obj) +{ + /* do something when remap to kernel virtual space is used.. */ + return 0; +} + +/* 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 int sync_op(struct drm_gem_object *obj, + enum omap_gem_op op, bool start) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + int ret = 0; + + spin_lock(&sync_lock); + + if (!omap_obj->sync) { + omap_obj->sync = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC); + if (!omap_obj->sync) { + ret = -ENOMEM; + goto unlock; + } + } + + 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(); + } + +unlock: + spin_unlock(&sync_lock); + + return ret; +} + +/* 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); +} + +/* mark the start of read and/or write operation */ +int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op) +{ + return sync_op(obj, op, true); +} + +int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op) +{ + return sync_op(obj, op, false); +} + +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; +} + +/* 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_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; +} + +/* 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) + */ +int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + int ret = 0; + + 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_ATOMIC); + if (!syncobj) { + ret = -ENOMEM; + goto unlock; + } + 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; + } + +unlock: + spin_unlock(&sync_lock); + return ret; +} + + +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 drm_device *dev = obj->dev; + 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 (omap_obj->pages) { + omap_gem_detach_pages(obj); + } + if (!is_shmem(obj)) { + dma_free_writecombine(dev->dev, obj->size, + omap_obj->vaddr, omap_obj->paddr); + } + } + + /* 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) +{ + 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; + } + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(obj); + + return 0; +} + +/* GEM buffer object constructor */ +struct drm_gem_object * omap_gem_new(struct drm_device *dev, + size_t size, uint32_t flags) +{ + struct omap_gem_object *omap_obj; + struct drm_gem_object *obj = NULL; + 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 (flags & OMAP_BO_SCANOUT) { + /* attempt to allocate contiguous memory */ + omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size, + &omap_obj->paddr, GFP_KERNEL); + if (omap_obj->vaddr) { + flags |= OMAP_BO_DMA; + } + } + + omap_obj->flags = flags; + + 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) { + goto fail; + } + + return obj; + +fail: + if (obj) { + omap_gem_free_object(obj); + } + return NULL; +} diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig index 83d3fe7..4ea17dc 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" - depends on FB && OMAP2_DSS + depends on FB && OMAP2_DSS && !DRM_OMAP select OMAP2_VRAM select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 diff --git a/include/drm/Kbuild b/include/drm/Kbuild index 3a60ac8..78d451c 100644 --- a/include/drm/Kbuild +++ b/include/drm/Kbuild @@ -11,3 +11,4 @@ header-y += savage_drm.h header-y += sis_drm.h header-y += via_drm.h header-y += vmwgfx_drm.h +header-y += omap_drm.h diff --git a/include/drm/omap_drm.h b/include/drm/omap_drm.h new file mode 100644 index 0000000..ea0ae8e --- /dev/null +++ b/include/drm/omap_drm.h @@ -0,0 +1,111 @@ +/* + * linux/include/drm/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 "drm.h" + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + */ + +#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 */ + 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) + +#endif /* __OMAP_DRM_H__ */ diff --git a/include/drm/omap_priv.h b/include/drm/omap_priv.h new file mode 100644 index 0000000..ca7d975 --- /dev/null +++ b/include/drm/omap_priv.h @@ -0,0 +1,42 @@ +/* + * linux/include/drm/omap_priv.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_PRIV_H__ +#define __OMAP_PRIV_H__ + +/* Non-userspace facing APIs + */ + +/* 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__ */
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 with CMA and use drm_gem_private_object_init(). See userspace xorg driver: git://github.com/robclark/xf86-video-omap.git Refer to this link for CMA (Continuous Memory Allocator): http://lkml.org/lkml/2011/8/19/302 Links to previous versions of the patch: v1: http://lwn.net/Articles/458137/ History: v2: replace omap_vram with CMA for scanout buffer allocation, remove unneeded functions, use dma_addr_t for physical addresses, error handling cleanup, refactor attach/detach pages into common drm functions, split non-userspace-facing API into omap_priv.h, remove plugin API v1: original --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/omapdrm/Kconfig | 24 + drivers/staging/omapdrm/Makefile | 9 + drivers/staging/omapdrm/TODO.txt | 14 + drivers/staging/omapdrm/omap_connector.c | 357 ++++++++++++++ drivers/staging/omapdrm/omap_crtc.c | 332 +++++++++++++ drivers/staging/omapdrm/omap_drv.c | 766 ++++++++++++++++++++++++++++++ drivers/staging/omapdrm/omap_drv.h | 126 +++++ drivers/staging/omapdrm/omap_encoder.c | 188 ++++++++ drivers/staging/omapdrm/omap_fb.c | 259 ++++++++++ drivers/staging/omapdrm/omap_fbdev.c | 309 ++++++++++++ drivers/staging/omapdrm/omap_gem.c | 720 ++++++++++++++++++++++++++++ drivers/video/omap2/omapfb/Kconfig | 2 +- include/drm/Kbuild | 1 + include/drm/omap_drm.h | 111 +++++ include/drm/omap_priv.h | 42 ++ 17 files changed, 3262 insertions(+), 1 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/drm/omap_drm.h create mode 100644 include/drm/omap_priv.h