From mboxrd@z Thu Jan 1 00:00:00 1970 From: Inki Dae Subject: RE: [PATCH] RFC: omapdrm DRM/KMS driver for TI OMAP platforms Date: Mon, 05 Sep 2011 18:58:36 +0900 Message-ID: <002201cc6bb2$61779780$2466c680$%dae@samsung.com> References: <1314994047-4101-1-git-send-email-rob.clark@linaro.org> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) by gabe.freedesktop.org (Postfix) with ESMTP id F177BA1D21 for ; Mon, 5 Sep 2011 02:58:59 -0700 (PDT) Received: from epcpsbgm1.samsung.com (mailout3.samsung.com [203.254.224.33]) by mailout3.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTP id <0LR100C32NPIJ5I0@mailout3.samsung.com> for dri-devel@lists.freedesktop.org; Mon, 05 Sep 2011 18:58:57 +0900 (KST) Received: from TNRNDGASPAPP1.tn.corp.samsungelectronics.net ([165.213.149.150]) by mmp1.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTPA id <0LR10063QNQ8A220@mmp1.samsung.com> for dri-devel@lists.freedesktop.org; Mon, 05 Sep 2011 18:58:56 +0900 (KST) In-reply-to: <1314994047-4101-1-git-send-email-rob.clark@linaro.org> Content-language: ko List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: dri-devel-bounces+sf-dri-devel=m.gmane.org@lists.freedesktop.org Errors-To: dri-devel-bounces+sf-dri-devel=m.gmane.org@lists.freedesktop.org To: 'Rob Clark' , dri-devel@lists.freedesktop.org Cc: 'Rob Clark' , linaro-dev@lists.linaro.org List-Id: dri-devel@lists.freedesktop.org Hello, Rob. I didn't look into any comments from other develops yet , so my comments could be duplicated with other ones. below is my opinions and I commented your codes also. We can discuss crtc, encoder and connector. in x86 world, my understanding, each one means the following. Crtc : - independent of hardware device, this means a crtc object has no any hw specific callbacks. - having information common to all HWs such as overlay, framebuffer, mode data and offset to any region of the framebuffer to be displayed on output device. Encoder: - dependent of a hardware device, this means a encoder object has hw specific callbacks. - taking the digital bitstream from the crtc and converting it to appropriate analog levels for transmission across connector to the monitor. And for this, containing some callbacks which are pointing to device-specific functions to control these HWs. Connector: - dependent of a hardware device, this means a connector object has hw specific callbacks. - providing appropriate physical plug (HDMI, DVI-D, VGA, S-VIDEO and so on) for the monitor to connect to. And maybe now drm framework have been considered for only x86 system. :( x86 system and embedded SoCs would be different each other. so I presume the difference between x86 and embedded system are as the following. - for mode setting, output devices such as lcd panel, analog tv and digital tv, have no any registers. These registers belong to display controller and hdmi controller. in case of graphic cares for x86 system, containing all controllers, not so. please could anyone give me the explanation about x86 system? With the things I mentioned above, we need to think of which object, crtc, encoder or encoder, should contain hw specific callbacks, and common data structures. In my opinion, it's as the following: Crtc: - having a overlay structure common to all HWs. this overlay information would be set to real hw driver through encoder/connector object. the reason, crtc should be independent object, is that for clone, crtc object should be only one object. you can refer to drm_target_cloned function of drm_fb_helper.c. in this function, it is just returned if fb_helper->crtc_count is more than 1. - created as independent object and any crtc object could be connected to any encoder/connector. Encoder: - having hw specific callbacks for overlay setting, clock on/off and vblank on/off. Connector: - having hw specific callbacks for display timing setting and display devices, such as digital tv, analog tv or lcd panel on/off. Call path for accessing to any hw is as the following: 1. setcrtc is called with fb id, crtc id, connector id, and mode data, 2. the crtc corresponding to crtc id is connected to the encoder/connector corresponding to each id. 3. mode and overlay information are updated by crtc. 4. it gets the encoder/connector connected to this crtc and then access to any hw through this encoder/connector with hw specific callbacks. Above is just my opinions. if you have other any ideas, I'd be pleased you to give me your opinions. Best Regards, Inki Dae. > -----Original Message----- > From: Rob Clark [mailto:robdclark@gmail.com] On Behalf Of Rob Clark > Sent: Saturday, September 03, 2011 5:07 AM > To: dri-devel@lists.freedesktop.org > Cc: linaro-dev@lists.linaro.org; inki.dae@samsung.com; Rob Clark > Subject: [PATCH] RFC: omapdrm DRM/KMS driver for TI OMAP platforms > > From: Rob Clark > > A DRM display driver for TI OMAP platform. Similar to omapfb (fbdev) > and omap_vout (v4l2 display) drivers in the past, this driver uses the > DSS2 driver to access the display hardware, including support for > HDMI, DVI, and various types of LCD panels. And it implements GEM > support for buffer allocation (for KMS as well as offscreen buffers > used by the xf86-video-omap userspace xorg driver). > > The driver maps CRTCs to overlays, encoders to overlay-managers, and > connectors to dssdev's. Note that this arrangement might change slightly > when support for drm_plane overlays is added. > > For GEM support, non-scanout buffers are using the shmem backed pages > provided by GEM core (In drm_gem_object_init()). In the case of scanout > buffers, which need to be physically contiguous, those are allocated from > omap_vram and use drm_gem_private_object_init() (Thanks to "gem: RFC: add > support for private objects" patch from Alan Cox.) Also used is the > "drm/gem: add functions for mmap offset creation" patch to avoid > duplicating some functions for mmap offset management in the omapdrm > driver. > > Note that the usage of omap_vram carveout will be replaced by CMA in > the future. > > A simple "plugin" mechanism is provided to allow integration with > external kernel modules (*cough* PVR), but also to keep the code more > modular as support is added for various other accelerator blocks that > exist in various different OMAP variants. (2D accel, video encode/ > decode, etc.) However this driver and userspace xorg driver are > themselves fully functional without any closed-source components. It > doesn't (and won't) provide any command submission to IP blocks with > closed-source kernel or userspace components. > > See userspace xorg driver: git://github.com/robclark/xf86-video-omap.git > --- > Note that this patch depends on a few other patches which have been > submitted to respective mailing lists (linux-omap for DSS2 and > dri-devel for DRM), but not yet merged. But this is only a RFC, and > I thought it was about time to put on the asbestos pants and let the > good folks of dri-devel get their flame on. > > Special thanks to Tomi Valkeinen (DSS2 author) for patches to add the > needed APIs for hotplug detection and EDID to DSS2 and various other > help with the DRM driver. > > drivers/staging/Kconfig | 2 + > drivers/staging/Makefile | 1 + > drivers/staging/omapdrm/Kconfig | 25 + > drivers/staging/omapdrm/Makefile | 8 + > drivers/staging/omapdrm/TODO.txt | 15 + > drivers/staging/omapdrm/omap_connector.c | 375 ++++++++++++ > drivers/staging/omapdrm/omap_crtc.c | 348 ++++++++++++ > drivers/staging/omapdrm/omap_drv.c | 910 > ++++++++++++++++++++++++++++++ > drivers/staging/omapdrm/omap_drv.h | 117 ++++ > drivers/staging/omapdrm/omap_encoder.c | 198 +++++++ > drivers/staging/omapdrm/omap_fb.c | 265 +++++++++ > drivers/staging/omapdrm/omap_fbdev.c | 303 ++++++++++ > drivers/staging/omapdrm/omap_gem.c | 851 > ++++++++++++++++++++++++++++ > drivers/video/omap2/omapfb/Kconfig | 4 +- > include/linux/Kbuild | 1 + > include/linux/omap_drm.h | 191 +++++++ > 16 files changed, 3612 insertions(+), 2 deletions(-) > create mode 100644 drivers/staging/omapdrm/Kconfig > create mode 100644 drivers/staging/omapdrm/Makefile > create mode 100644 drivers/staging/omapdrm/TODO.txt > create mode 100644 drivers/staging/omapdrm/omap_connector.c > create mode 100644 drivers/staging/omapdrm/omap_crtc.c > create mode 100644 drivers/staging/omapdrm/omap_drv.c > create mode 100644 drivers/staging/omapdrm/omap_drv.h > create mode 100644 drivers/staging/omapdrm/omap_encoder.c > create mode 100644 drivers/staging/omapdrm/omap_fb.c > create mode 100644 drivers/staging/omapdrm/omap_fbdev.c > create mode 100644 drivers/staging/omapdrm/omap_gem.c > create mode 100644 include/linux/omap_drm.h > > diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig > index 196284d..99b7547 100644 > --- a/drivers/staging/Kconfig > +++ b/drivers/staging/Kconfig > @@ -160,4 +160,6 @@ source "drivers/staging/mei/Kconfig" > > source "drivers/staging/nvec/Kconfig" > > +source "drivers/staging/omapdrm/Kconfig" > + > endif # STAGING > diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile > index fa41b9c..91ec21f 100644 > --- a/drivers/staging/Makefile > +++ b/drivers/staging/Makefile > @@ -71,3 +71,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += > ste_rmi4/ > obj-$(CONFIG_DRM_PSB) += gma500/ > obj-$(CONFIG_INTEL_MEI) += mei/ > obj-$(CONFIG_MFD_NVEC) += nvec/ > +obj-$(CONFIG_DRM_OMAP) += omapdrm/ > diff --git a/drivers/staging/omapdrm/Kconfig > b/drivers/staging/omapdrm/Kconfig > new file mode 100644 > index 0000000..bddf52b > --- /dev/null > +++ b/drivers/staging/omapdrm/Kconfig > @@ -0,0 +1,25 @@ > + > +config DRM_OMAP > + tristate "OMAP DRM (EXPERIMENTAL)" > + depends on DRM && !CONFIG_FB_OMAP2 > + select DRM_KMS_HELPER > + select OMAP2_VRAM > + select OMAP2_DSS > + select FB_SYS_FILLRECT > + select FB_SYS_COPYAREA > + select FB_SYS_IMAGEBLIT > + select FB_SYS_FOPS > + default n > + help > + DRM display driver for OMAP2/3/4 based boards. > + > +config DRM_OMAP_NUM_CRTCS > + int "Number of CRTCs" > + range 1 10 > + default 1 if ARCH_OMAP2 || ARCH_OMAP3 > + default 2 if ARCH_OMAP4 > + depends on DRM_OMAP > + help > + Select the number of video overlays which can be used as > framebuffers. > + The remaining overlays are reserved for video. > + > diff --git a/drivers/staging/omapdrm/Makefile > b/drivers/staging/omapdrm/Makefile > new file mode 100644 > index 0000000..da74337 > --- /dev/null > +++ b/drivers/staging/omapdrm/Makefile > @@ -0,0 +1,8 @@ > +# > +# Makefile for the drm device driver. This driver provides support for > the > +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. > + > +ccflags-y := -Iinclude/drm -Werror > +omapdrm-y := omap_drv.o omap_crtc.o omap_encoder.o omap_connector.o > omap_fb.o omap_fbdev.o omap_gem.o > + > +obj-$(CONFIG_DRM_OMAP) += omapdrm.o > diff --git a/drivers/staging/omapdrm/TODO.txt > b/drivers/staging/omapdrm/TODO.txt > new file mode 100644 > index 0000000..b64f067 > --- /dev/null > +++ b/drivers/staging/omapdrm/TODO.txt > @@ -0,0 +1,15 @@ > +TODO > +. move to CMA for scanout buffer allocation > +. check error handling/cleanup paths > +. add drm_plane / overlay support > +. add video decode/encode support (via syslink3 + codec-engine) > +. still some rough edges with flipping.. event back to userspace should > + really come after VSYNC interrupt > +. support for tiled buffers > + > +Userspace: > +. git://github.com/robclark/xf86-video-omap.git > + > +Currently tested on > +. OMAP3530 beagleboard > +. OMAP4430 pandaboard > +. OMAP4460 pandaboard > diff --git a/drivers/staging/omapdrm/omap_connector.c > b/drivers/staging/omapdrm/omap_connector.c > new file mode 100644 > index 0000000..7f7403b > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_connector.c > @@ -0,0 +1,375 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_connector.c > + * > + * Copyright (C) 2011 Texas Instruments > + * Author: Rob Clark > + * > + * 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 . > + */ > + > +#include > +#include "omap_drv.h" > + > +#include "drm_crtc.h" > +#include "drm_crtc_helper.h" > + > +/* > + * connector funcs > + */ > + > +#define to_omap_connector(x) container_of(x, struct omap_connector, base) > + > +struct omap_connector { > + struct drm_connector base; > + struct omap_dss_device *dssdev; > +}; > + > +static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode, > + struct omap_video_timings *timings) > +{ > + mode->clock = timings->pixel_clock; > + > + mode->hdisplay = timings->x_res; > + mode->hsync_start = mode->hdisplay + timings->hfp; > + mode->hsync_end = mode->hsync_start + timings->hsw; > + mode->htotal = mode->hsync_end + timings->hbp; > + > + mode->vdisplay = timings->y_res; > + mode->vsync_start = mode->vdisplay + timings->vfp; > + mode->vsync_end = mode->vsync_start + timings->vsw; > + mode->vtotal = mode->vsync_end + timings->vbp; > + > + /* note: whether or not it is interlaced, +/- h/vsync, etc, > + * which should be set in the mode flags, is not exposed in > + * the omap_video_timings struct.. but hdmi driver tracks > + * those separately so all we have to have to set the mode > + * is the way to recover these timings values, and the > + * omap_dss_driver would do the rest. > + */ > +} > + > +static inline void copy_timings_drm_to_omap(struct omap_video_timings > *timings, > + struct drm_display_mode *mode) > +{ > + timings->pixel_clock = mode->clock; > + > + timings->x_res = mode->hdisplay; > + timings->hfp = mode->hsync_start - mode->hdisplay; > + timings->hsw = mode->hsync_end - mode->hsync_start; > + timings->hbp = mode->htotal - mode->hsync_end; > + > + timings->y_res = mode->vdisplay; > + timings->vfp = mode->vsync_start - mode->vdisplay; > + timings->vsw = mode->vsync_end - mode->vsync_start; > + timings->vbp = mode->vtotal - mode->vsync_end; > +} > + > +void omap_connector_dpms(struct drm_connector *connector, int mode) > +{ > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + int ret; > + > + DBG("%s: %d", dssdev->name, mode); > + > + if (mode == DRM_MODE_DPMS_ON) { > + /* store resume info for suspended displays */ > + switch (dssdev->state) { > + case OMAP_DSS_DISPLAY_SUSPENDED: > + dssdev->activate_after_resume = true; > + break; > + case OMAP_DSS_DISPLAY_DISABLED: > + ret = dssdev->driver->enable(dssdev); > + if (ret) { > + DBG("%s: failed to enable: %d", dssdev->name, > ret); > + dssdev->driver->disable(dssdev); > + } > + break; > + default: > + break; > + } > + } else { > + /* TODO */ > + } > +} > + > +enum drm_connector_status omap_connector_detect( > + struct drm_connector *connector, bool force) > +{ > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + enum drm_connector_status ret; > + > + /* hmm.. ideally dssdrv would take care of this internally if (and > + * only if) power-on was required to detect connection.. > + */ > + omap_connector_dpms(connector, DRM_MODE_DPMS_ON); > + > + if (dssdrv->is_detected(dssdev)) { > + ret = connector_status_connected; > + } else { > + ret = connector_status_disconnected; > + } > + > + VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force); > + > + return ret; > +} > + > +static void omap_connector_destroy(struct drm_connector *connector) > +{ > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + > + dssdev->driver->disable(dssdev); > + > + DBG("%s", omap_connector->dssdev->name); > + drm_sysfs_connector_remove(connector); > + drm_connector_cleanup(connector); > + kfree(omap_connector); > + > + omap_dss_put_device(dssdev); > +} > + > +#define MAX_EDID 512 > + > +static int omap_connector_get_modes(struct drm_connector *connector) > +{ > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + struct drm_device *dev = connector->dev; > + int n = 0; > + > + DBG("%s", omap_connector->dssdev->name); > + > + /* if display exposes EDID, then we parse that in the normal way to > + * build table of supported modes.. otherwise (ie. fixed resolution > + * LCD panels) we just return a single mode corresponding to the > + * currently configured timings: > + */ > + if (dssdrv->read_edid) { > + void *edid = kzalloc(MAX_EDID, GFP_KERNEL); > + > + if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) == 0) && > + drm_edid_is_valid(edid)) { > + drm_mode_connector_update_edid_property(connector, > edid); > + n = drm_add_edid_modes(connector, edid); > + kfree(connector->display_info.raw_edid); > + connector->display_info.raw_edid = edid; > + } else { > + drm_mode_connector_update_edid_property(connector, > NULL); > + connector->display_info.raw_edid = NULL; > + kfree(edid); > + } > + } else { > + struct drm_display_mode *mode = drm_mode_create(dev); > + struct omap_video_timings timings; > + > + dssdrv->get_timings(dssdev, &timings); > + > + copy_timings_omap_to_drm(mode, &timings); > + > + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; > + drm_mode_set_name(mode); > + drm_mode_probed_add(connector, mode); > + > + n = 1; > + } > + > + return n; > +} > + > +static int omap_connector_mode_valid(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + struct omap_video_timings timings = {0}; > + struct drm_device *dev = connector->dev; > + struct drm_display_mode *new_mode; > + int ret = MODE_BAD; > + > + copy_timings_drm_to_omap(&timings, mode); > + mode->vrefresh = drm_mode_vrefresh(mode); > + > + if (!dssdrv->check_timings(dssdev, &timings)) { > + /* check if vrefresh is still valid */ > + new_mode = drm_mode_duplicate(dev, mode); > + new_mode->clock = timings.pixel_clock; > + new_mode->vrefresh = 0; > + if (mode->vrefresh == drm_mode_vrefresh(new_mode)) > + ret = MODE_OK; > + drm_mode_destroy(dev, new_mode); > + } > + is there any reason that you call drm_mode_duplicate() to get a clone of a mode? I just wonder it. > + DBG("connector: mode %s: " > + "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", > + (ret == MODE_OK) ? "valid" : "invalid", > + mode->base.id, mode->name, mode->vrefresh, mode->clock, > + mode->hdisplay, mode->hsync_start, > + mode->hsync_end, mode->htotal, > + mode->vdisplay, mode->vsync_start, > + mode->vsync_end, mode->vtotal, mode->type, mode- > >flags); > + > + return ret; > +} > + > +struct drm_encoder * omap_connector_attached_encoder( > + struct drm_connector *connector) > +{ > + int i; > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + > + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { > + struct drm_mode_object *obj; > + > + if (connector->encoder_ids[i] == 0) > + break; > + > + obj = drm_mode_object_find(connector->dev, > + connector->encoder_ids[i], > + DRM_MODE_OBJECT_ENCODER); > + > + if (obj) { > + struct drm_encoder *encoder = obj_to_encoder(obj); > + struct omap_overlay_manager *mgr = > + omap_encoder_get_manager(encoder); > + DBG("%s: found %s", omap_connector->dssdev->name, > + mgr->name); > + return encoder; > + } > + } > + > + DBG("%s: no encoder", omap_connector->dssdev->name); > + > + return NULL; > +} > + > +static const struct drm_connector_funcs omap_connector_funcs = { > + .dpms = drm_helper_connector_dpms, > + .detect = omap_connector_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = omap_connector_destroy, > +}; > + > +static const struct drm_connector_helper_funcs > omap_connector_helper_funcs = { > + .get_modes = omap_connector_get_modes, > + .mode_valid = omap_connector_mode_valid, > + .best_encoder = omap_connector_attached_encoder, > +}; > + > +/* called from encoder when mode is set, to propagate settings to the > dssdev */ > +void omap_connector_mode_set(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct drm_device *dev = connector->dev; > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + struct omap_video_timings timings; > + > + copy_timings_drm_to_omap(&timings, mode); > + > + DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x > 0x%x", > + omap_connector->dssdev->name, > + mode->base.id, mode->name, mode->vrefresh, mode->clock, > + mode->hdisplay, mode->hsync_start, > + mode->hsync_end, mode->htotal, > + mode->vdisplay, mode->vsync_start, > + mode->vsync_end, mode->vtotal, mode->type, mode- > >flags); > + > + if (dssdrv->check_timings(dssdev, &timings)) { > + dev_err(dev->dev, "could not set timings\n"); > + return; > + } > + > + dssdrv->set_timings(dssdev, &timings); > +} > + > +int omap_connector_sync(struct drm_connector *connector) > +{ > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + struct omap_dss_device *dssdev = omap_connector->dssdev; > + struct omap_dss_driver *dssdrv = dssdev->driver; > + > + DBG("%s", omap_connector->dssdev->name); > + > + if (dssdrv->sync) { > + return dssdrv->sync(dssdev); > + } > + > + return -EINVAL; > +} > +EXPORT_SYMBOL(omap_connector_sync); > + > +/* flush an area of the framebuffer (in case of manual update display > that > + * is not automatically flushed) > + */ > +void omap_connector_flush(struct drm_connector *connector, > + int x, int y, int w, int h) > +{ > + struct omap_connector *omap_connector = > to_omap_connector(connector); > + > + /* TODO: enable when supported in dss */ > + VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h); > +} > + > +/* initialize connector */ > +struct drm_connector * omap_connector_init(struct drm_device *dev, > + int connector_type, struct omap_dss_device *dssdev) > +{ > + struct drm_connector *connector = NULL; It appears that this connector is initialized with NULL. This is just minor issue. :) > + struct omap_connector *omap_connector; > + > + DBG("%s", dssdev->name); > + > + omap_dss_get_device(dssdev); > + > + omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL); > + if (!omap_connector) { > + dev_err(dev->dev, "could not allocate connector\n"); > + goto fail; > + } > + > + omap_connector->dssdev = dssdev; > + connector = &omap_connector->base; > + > + drm_connector_init(dev, connector, &omap_connector_funcs, > + connector_type); > + drm_connector_helper_add(connector, &omap_connector_helper_funcs); > + > +#if 0 /* enable when dss2 supports hotplug */ > + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD) > + connector->polled = 0; > + else > +#endif > + connector->polled = DRM_CONNECTOR_POLL_CONNECT | > + DRM_CONNECTOR_POLL_DISCONNECT; > + > + connector->interlace_allowed = 1; > + connector->doublescan_allowed = 0; > + > + drm_sysfs_connector_add(connector); > + > + return connector; > + > +fail: > + if (connector) { > + omap_connector_destroy(connector); > + } it doesn't need the code below because if "omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);" is fail then omap_connector object has no any instance, please so remove it. just minor issue. if (connector) { omap_connector_destroy(connector); } > + > + return NULL; > +} > diff --git a/drivers/staging/omapdrm/omap_crtc.c > b/drivers/staging/omapdrm/omap_crtc.c > new file mode 100644 > index 0000000..3714caa > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_crtc.c > @@ -0,0 +1,348 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_crtc.c > + * > + * Copyright (C) 2011 Texas Instruments > + * Author: Rob Clark > + * > + * 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 . > + */ > + > +#include > +#include "omap_drv.h" > + > +#include "drm_mode.h" > +#include "drm_crtc.h" > +#include "drm_crtc_helper.h" > + > +#define to_omap_crtc(x) container_of(x, struct omap_crtc, base) > + > +struct omap_crtc { > + struct drm_crtc base; > + struct omap_overlay *ovl; > + struct omap_overlay_info info; > + int id; > + > + /* if there is a pending flip, this will be non-null: */ > + struct drm_pending_vblank_event *event; > +}; > + > +/* push changes down to dss2 */ > +static int commit(struct drm_crtc *crtc) > +{ > + struct drm_device *dev = crtc->dev; > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + struct omap_overlay *ovl = omap_crtc->ovl; > + struct omap_overlay_info *info = &omap_crtc->info; > + int ret; > + > + DBG("%s", omap_crtc->ovl->name); > + DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info- > >out_width, > + info->out_height, info->screen_width); > + DBG("%d,%d %p %08x", info->pos_x, info->pos_y, info->vaddr, > + info->paddr); > + > + /* NOTE: do we want to do this at all here, or just wait > + * for dpms(ON) since other CRTC's may not have their mode > + * set yet, so fb dimensions may still change.. > + */ > + ret = ovl->set_overlay_info(ovl, info); > + if (ret) { > + dev_err(dev->dev, "could not set overlay info\n"); > + return ret; > + } > + > + /* our encoder doesn't necessarily get a commit() after this, in > + * particular in the dpms() and mode_set_base() cases, so force the > + * manager to update: > + * > + * could this be in the encoder somehow? > + */ > + if (ovl->manager) { > + ret = ovl->manager->apply(ovl->manager); > + if (ret) { > + dev_err(dev->dev, "could not apply\n"); > + return ret; > + } > + } > + > + if (info->enabled) { > + omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y, > + crtc->fb->width, crtc->fb->height); > + } > + > + return 0; > +} > + > +/* update parameters that are dependent on the framebuffer dimensions and > + * position within the fb that this crtc scans out from. This is called > + * when framebuffer dimensions or x,y base may have changed, either due > + * to our mode, or a change in another crtc that is scanning out of the > + * same fb. > + */ > +static void update_scanout(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + unsigned long paddr; > + void __iomem *vaddr; > + int screen_width; > + > + omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, > + &vaddr, &paddr, &screen_width); > + > + DBG("%s: %d,%d: %p %08lx (%d)", omap_crtc->ovl->name, > + crtc->x, crtc->y, vaddr, paddr, screen_width); > + > + omap_crtc->info.paddr = paddr; > + omap_crtc->info.vaddr = vaddr; > + omap_crtc->info.screen_width = screen_width; > +} > + > +static void omap_crtc_gamma_set(struct drm_crtc *crtc, > + u16 *red, u16 *green, u16 *blue, uint32_t start, uint32_t > size) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > +} > + > +static void omap_crtc_destroy(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > + drm_crtc_cleanup(crtc); > + kfree(omap_crtc); > +} > + > +static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%s: %d", omap_crtc->ovl->name, mode); > + > + if (mode == DRM_MODE_DPMS_ON) { > + update_scanout(crtc); > + omap_crtc->info.enabled = true; > + } else { > + omap_crtc->info.enabled = false; > + } > + > + commit(crtc); > +} > + > +static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > + return true; > +} > + > +static int omap_crtc_mode_set(struct drm_crtc *crtc, > + struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode, > + int x, int y, > + struct drm_framebuffer *old_fb) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%s: %d,%d: %dx%d",omap_crtc->ovl->name, x, y, > + mode->hdisplay, mode->vdisplay); > + > + /* just use adjusted mode */ > + mode = adjusted_mode; > + > + omap_crtc->info.width = mode->hdisplay; > + omap_crtc->info.height = mode->vdisplay; > + omap_crtc->info.out_width = mode->hdisplay; > + omap_crtc->info.out_height = mode->vdisplay; > + omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U; > + omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA; > + omap_crtc->info.rotation = OMAP_DSS_ROT_0; > + omap_crtc->info.global_alpha = 0xff; > + omap_crtc->info.mirror = 0; > + omap_crtc->info.mirror = 0; > + omap_crtc->info.pos_x = 0; > + omap_crtc->info.pos_y = 0; > +#if 0 /* re-enable when these are available in DSS2 driver */ > + omap_crtc->info.zorder = 3; /* GUI in the front, video behind > */ > + omap_crtc->info.min_x_decim = 1; > + omap_crtc->info.max_x_decim = 1; > + omap_crtc->info.min_y_decim = 1; > + omap_crtc->info.max_y_decim = 1; > +#endif > + > + update_scanout(crtc); > + > + return 0; > +} > + > +static void omap_crtc_prepare(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + struct omap_overlay *ovl = omap_crtc->ovl; > + > + DBG("%s", omap_crtc->ovl->name); > + > + ovl->get_overlay_info(ovl, &omap_crtc->info); > + > + omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); > +} > + > +static void omap_crtc_commit(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > + omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); > +} > + > +static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, > + struct drm_framebuffer *old_fb) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb); > + > + update_scanout(crtc); > + > + return commit(crtc); > +} > + > +static void omap_crtc_load_lut(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + DBG("%s", omap_crtc->ovl->name); > +} > + > +static void page_flip_cb(void *arg) > +{ > + struct drm_crtc *crtc = arg; > + struct drm_device *dev = crtc->dev; > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + struct drm_pending_vblank_event *event = omap_crtc->event; > + struct timeval now; > + unsigned long flags; > + > + WARN_ON(!event); > + > + omap_crtc->event = NULL; > + > + update_scanout(crtc); > + commit(crtc); > + > + /* wakeup userspace */ > + // TODO: this should happen *after* flip.. somehow.. > + if (event) { > + spin_lock_irqsave(&dev->event_lock, flags); > + event->event.sequence = > + drm_vblank_count_and_time(dev, omap_crtc->id, > &now); > + event->event.tv_sec = now.tv_sec; > + event->event.tv_usec = now.tv_usec; > + list_add_tail(&event->base.link, > + &event->base.file_priv->event_list); > + wake_up_interruptible(&event->base.file_priv->event_wait); > + spin_unlock_irqrestore(&dev->event_lock, flags); > + } How about moving codes above into interrupt handler for vblank? maybe there would be some reason that spin_lock_* are used here. > +} > + > +static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, > + struct drm_framebuffer *fb, > + struct drm_pending_vblank_event *event) > +{ > + struct drm_device *dev = crtc->dev; > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + > + DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id); > + > + if (omap_crtc->event) { > + dev_err(dev->dev, "already a pending flip\n"); > + return -EINVAL; > + } > + > + crtc->fb = fb; > + omap_crtc->event = event; > + > + omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, > + page_flip_cb, crtc); > + > + return 0; > +} > + Where are drm_vblank_get() and drm_vblank_put() called to enable and disable vsync interrupt? and does the this page flip need user-requested drm_control ioctl to install/uninstall a handler?. I think the page_flip feature should work it fine itself. like this: at omap_crtc_page_flip_locked() drm_vblank_get(); <- enable vsync interrupt. update famebuffer. <- update overlay and set it to hw. at interrupt handler handle page flip event. wake_up_interruptible() <- notify user of page flip event. drm_vblank_put(); <- disable vsync interrupt. I think that page flip callback would be called after vsync interrupt occurred. for this, you can refer to set_mode function of libdrm library. a event has user's apage flip handler and when user received any event from kernel side, this handler would be called to update framebuffer repeatedly. > +int omap_crtc_page_flip(struct drm_crtc *crtc, > + struct drm_framebuffer *fb, > + struct drm_pending_vblank_event *event) > +{ > + struct drm_device *dev = crtc->dev; > + int ret; > + > + mutex_lock(&dev->mode_config.mutex); > + ret = omap_crtc_page_flip_locked(crtc, fb, event); > + mutex_unlock(&dev->mode_config.mutex); > + > + return ret; > +} > +EXPORT_SYMBOL(omap_crtc_page_flip); > + > +static const struct drm_crtc_funcs omap_crtc_funcs = { > + .gamma_set = omap_crtc_gamma_set, > + .set_config = drm_crtc_helper_set_config, > + .destroy = omap_crtc_destroy, > + .page_flip = omap_crtc_page_flip_locked, > +}; > + > +static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { > + .dpms = omap_crtc_dpms, > + .mode_fixup = omap_crtc_mode_fixup, > + .mode_set = omap_crtc_mode_set, > + .prepare = omap_crtc_prepare, > + .commit = omap_crtc_commit, > + .mode_set_base = omap_crtc_mode_set_base, > + .load_lut = omap_crtc_load_lut, > +}; > + > +struct omap_overlay * omap_crtc_get_overlay(struct drm_crtc *crtc) > +{ > + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); > + return omap_crtc->ovl; > +} > + > +/* initialize crtc */ > +struct drm_crtc * omap_crtc_init(struct drm_device *dev, > + struct omap_overlay *ovl, int id) > +{ > + struct drm_crtc *crtc = NULL; > + struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), > GFP_KERNEL); > + > + DBG("%s", ovl->name); > + > + if (!omap_crtc) { > + dev_err(dev->dev, "could not allocate CRTC\n"); > + goto fail; > + } > + > + omap_crtc->ovl = ovl; > + omap_crtc->id = id; > + crtc = &omap_crtc->base; > + drm_crtc_init(dev, crtc, &omap_crtc_funcs); > + drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); > + > + return crtc; > + > +fail: > + if (crtc) { > + drm_crtc_cleanup(crtc); > + kfree(omap_crtc); > + } > + return NULL; > +} > diff --git a/drivers/staging/omapdrm/omap_drv.c > b/drivers/staging/omapdrm/omap_drv.c > new file mode 100644 > index 0000000..720f89e > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.c > @@ -0,0 +1,910 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_drv.c > + * > + * Copyright (C) 2011 Texas Instruments > + * Author: Rob Clark > + * > + * 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 . > + */ > + > +#include > +#include "omap_drv.h" > + > +#include "drm_crtc_helper.h" > +#include "drm_fb_helper.h" > + > +#define DRIVER_NAME MODULE_NAME > +#define DRIVER_DESC "OMAP DRM" > +#define DRIVER_DATE "20110717" > +#define DRIVER_MAJOR 1 > +#define DRIVER_MINOR 0 > +#define DRIVER_PATCHLEVEL 0 > + > +struct drm_device *drm_device; > + > +LIST_HEAD(plugin_list); > + > +/* keep track of whether we are already loaded.. we may need to call > + * plugin's load() if they register after we are already loaded > + */ > +static bool loaded = false; > + > +/* > + * mode config funcs > + */ > + > +/* Notes about mapping DSS and DRM entities: > + * CRTC: overlay > + * encoder: manager.. with some extension to allow one primary CRTC > + * and zero or more video CRTC's to be mapped to one encoder? > + * connector: dssdev.. manager can be attached/detached from > different > + * devices > + */ > + > +static void omap_fb_output_poll_changed(struct drm_device *dev) > +{ > + struct omap_drm_private *priv = dev->dev_private; > + DBG("dev=%p", dev); > + if (priv->fbdev) { > + drm_fb_helper_hotplug_event(priv->fbdev); > + } > +} > + > +static struct drm_mode_config_funcs omap_mode_config_funcs = { > + .fb_create = omap_framebuffer_create, > + .output_poll_changed = omap_fb_output_poll_changed, > +}; > + > +static int get_connector_type(struct omap_dss_device *dssdev) > +{ > + switch (dssdev->type) { > + case OMAP_DISPLAY_TYPE_HDMI: > + return DRM_MODE_CONNECTOR_HDMIA; > + case OMAP_DISPLAY_TYPE_DPI: > + if (!strcmp(dssdev->name, "dvi")) > + return DRM_MODE_CONNECTOR_DVID; > + default: > + return DRM_MODE_CONNECTOR_Unknown; > + } > +} > + > +#if 0 /* enable when dss2 supports hotplug */ > +static int omap_drm_notifier(struct notifier_block *nb, > + unsigned long evt, void *arg) > +{ > + switch (evt) { > + case OMAP_DSS_SIZE_CHANGE: > + case OMAP_DSS_HOTPLUG_CONNECT: > + case OMAP_DSS_HOTPLUG_DISCONNECT: { > + struct drm_device *dev = drm_device; > + DBG("hotplug event: evt=%d, dev=%p", evt, dev); > + if (dev) { > + drm_sysfs_hotplug_event(dev); > + } > + return NOTIFY_OK; > + } > + default: /* don't care about other events for now */ > + return NOTIFY_DONE; > + } > +} > +#endif > + > +static void dump_video_chains(void) > +{ > + int i; > + > + DBG("dumping video chains: "); > + for (i = 0; i < omap_dss_get_num_overlays(); i++) { > + struct omap_overlay *ovl = omap_dss_get_overlay(i); > + struct omap_overlay_manager *mgr = ovl->manager; > + struct omap_dss_device *dssdev = mgr ? mgr->device : NULL; > + if (dssdev) { > + DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name, > + dssdev->name); > + } else if (mgr) { > + DBG("%d: %s -> %s", i, ovl->name, mgr->name); > + } else { > + DBG("%d: %s", i, ovl->name); > + } > + } > +} > + > +static int omap_modeset_init(struct drm_device *dev) > +{ > + const struct omap_drm_platform_data *pdata = dev->dev- > >platform_data; > + struct omap_drm_private *priv = dev->dev_private; > + struct omap_dss_device *dssdev = NULL; > + int i, j; > + unsigned int connected_connectors = 0; > + > + /* create encoders for each manager */ > + int create_encoder(int i) { > + struct omap_overlay_manager *mgr = > + omap_dss_get_overlay_manager(i); > + struct drm_encoder *encoder = omap_encoder_init(dev, mgr); > + > + if (!encoder) { > + dev_err(dev->dev, "could not create encoder\n"); > + return -ENOMEM; > + } > + > + priv->encoders[priv->num_encoders++] = encoder; > + > + return 0; > + } > + > + /* create connectors for each display device */ > + int create_connector(struct omap_dss_device *dssdev) { > + static struct notifier_block *notifier; > + struct drm_connector *connector; > + > + if (!dssdev->driver) { > + dev_warn(dev->dev, "%s has no driver.. skipping it\n", > + dssdev->name); > + return 0; > + } > + > + if (!(dssdev->driver->get_timings || > + dssdev->driver->read_edid)) { > + dev_warn(dev->dev, "%s driver does not support " > + "get_timings or read_edid.. skipping it!\n", > + dssdev->name); > + return 0; > + } > + > + connector = omap_connector_init(dev, > + get_connector_type(dssdev), dssdev); > + > + if (!connector) { > + dev_err(dev->dev, "could not create connector\n"); > + return -ENOMEM; > + } > + > + /* track what is already connected.. rather than looping > thru > + * all connectors twice later, first for connected then for > + * remainder (which could be a race condition if connected > + * status changes) > + */ > + if (omap_connector_detect(connector, true) == > + connector_status_connected) { > + connected_connectors |= (1 << priv->num_connectors); > + } > + > + priv->connectors[priv->num_connectors++] = connector; > + > +#if 0 /* enable when dss2 supports hotplug */ > + notifier = kzalloc(sizeof(struct notifier_block), > GFP_KERNEL); > + notifier->notifier_call = omap_drm_notifier; > + omap_dss_add_notify(dssdev, notifier); > +#else > + notifier = NULL; > +#endif > + > + for (j = 0; j < priv->num_encoders; j++) { > + struct omap_overlay_manager *mgr = > + omap_encoder_get_manager(priv->encoders[j]); > + if (mgr->device == dssdev) { > + drm_mode_connector_attach_encoder(connector, > + priv->encoders[j]); > + } > + } > + > + return 0; > + } > + > + /* create up to max_overlays CRTCs mapping to overlays.. by default, > + * connect the overlays to different managers/encoders, giving > priority > + * to encoders connected to connectors with a detected connection > + */ > + int create_crtc(int i) { > + struct omap_overlay *ovl = omap_dss_get_overlay(i); > + struct omap_overlay_manager *mgr = NULL; > + struct drm_crtc *crtc; > + > + if (ovl->manager) { > + DBG("disconnecting %s from %s", ovl->name, > + ovl->manager->name); > + ovl->unset_manager(ovl); > + } > + > + /* find next best connector, ones with detected connection > first > + */ > + while (j < priv->num_connectors && !mgr) { > + if (connected_connectors & (1 << j)) { > + struct drm_encoder * encoder = > + omap_connector_attached_encoder( > + priv->connectors[j]); > + if (encoder) { > + mgr = omap_encoder_get_manager(encoder); > + } > + } > + j++; > + } > + > + /* if we couldn't find another connected connector, lets > start > + * looking at the unconnected connectors: > + */ > + while (j < 2 * priv->num_connectors && !mgr) { > + int idx = j - priv->num_connectors; > + if (!(connected_connectors & (1 << idx))) { > + struct drm_encoder * encoder = > + omap_connector_attached_encoder( > + priv->connectors[idx]); > + if (encoder) { > + mgr = omap_encoder_get_manager(encoder); > + } > + } > + j++; > + } > + > + if (mgr) { > + DBG("connecting %s to %s", ovl->name, mgr->name); > + ovl->set_manager(ovl, mgr); > + } > + > + crtc = omap_crtc_init(dev, ovl, priv->num_crtcs); > + > + if (!crtc) { > + dev_err(dev->dev, "could not create CRTC\n"); > + return -ENOMEM; > + } > + > + priv->crtcs[priv->num_crtcs++] = crtc; > + > + return 0; > + } > + > + drm_mode_config_init(dev); > + > + if (pdata) { > + /* if platform data is provided by the board file, use it to > + * control which overlays, managers, and devices we own. > + */ > + for (i = 0; i < pdata->mgr_cnt; i++) { > + create_encoder(pdata->mgr_ids[i]); > + } > + > + for (i = 0; i < pdata->dev_cnt; i++) { > + int m(struct omap_dss_device *dssdev, void *data) { > + return ! strcmp(dssdev->name, data); > + } > + struct omap_dss_device *dssdev = > + omap_dss_find_device( > + (void *)pdata->dev_names[i], m); > + if (!dssdev) { > + dev_warn(dev->dev, "no such dssdev: %s\n", > + pdata->dev_names[i]); > + continue; > + } > + create_connector(dssdev); > + } > + > + j = 0; > + for (i = 0; i < pdata->ovl_cnt; i++) { > + create_crtc(pdata->ovl_ids[i]); > + } > + } else { > + /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and > try > + * to make educated guesses about everything else > + */ > + int max_overlays = min(omap_dss_get_num_overlays(), > + CONFIG_DRM_OMAP_NUM_CRTCS); > + > + for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) { > + create_encoder(i); > + } > + > + for_each_dss_dev(dssdev) { > + create_connector(dssdev); > + } > + > + j = 0; > + for (i = 0; i < max_overlays; i++) { > + create_crtc(i); > + } > + } > + > + /* for now keep the mapping of CRTCs and encoders static.. */ > + for (i = 0; i < priv->num_encoders; i++) { > + struct drm_encoder *encoder = priv->encoders[i]; > + struct omap_overlay_manager *mgr = > + omap_encoder_get_manager(encoder); > + > + encoder->possible_crtcs = 0; > + > + for (j = 0; j < priv->num_crtcs; j++) { > + struct omap_overlay *ovl = > + omap_crtc_get_overlay(priv->crtcs[j]); > + if (ovl->manager == mgr) { > + encoder->possible_crtcs |= (1 << j); > + } > + } > + > + DBG("%s: possible_crtcs=%08x", mgr->name, > + encoder->possible_crtcs); > + } > + > + dump_video_chains(); > + > + dev->mode_config.min_width = 640; > + dev->mode_config.min_height = 480; > + > + /* note: pvr can't currently handle dst surfaces larger than 2k by > 2k */ > + dev->mode_config.max_width = 2048; > + dev->mode_config.max_height = 2048; > + > + dev->mode_config.funcs = &omap_mode_config_funcs; > + > + return 0; > +} For code clean, how about moving create_encoder(), create_connector() and create_crtc() into omap_encoder.c, omap_connector.c and omap_crtc.c file?. omap_modeset_init function contains so many codes. it looks like not good. > + > +static void omap_modeset_free(struct drm_device *dev) > +{ > + drm_mode_config_cleanup(dev); > +} > + > +/* > + * drm ioctl funcs > + */ > + > + > +static int ioctl_get_param(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_param *args = data; > + > + DBG("%p: param=%llu", dev, args->param); > + > + switch (args->param) { > + case OMAP_PARAM_CHIPSET_ID: > + args->value = GET_OMAP_TYPE; > + break; > + default: > + DBG("unknown parameter %lld", args->param); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int ioctl_set_param(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_param *args = data; > + > + switch (args->param) { > + default: > + DBG("unknown parameter %lld", args->param); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int ioctl_get_base(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_get_base *args = data; > + struct omap_drm_plugin *plugin; > + > + /* be safe: */ > + args->plugin_name[ARRAY_SIZE(args->plugin_name) - 1] = '\0'; > + > + DBG("%p: plugin_name=%s", dev, args->plugin_name); > + > + list_for_each_entry(plugin, &plugin_list, list) { > + if (!strcmp(args->plugin_name, plugin->name)) { > + args->ioctl_base = plugin->ioctl_base; > + return 0; > + } > + } > + > + return -EINVAL; > +} > + > +static int ioctl_gem_new(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_new *args = data; > + DBG("%p: size=%d, flags=%08x", dev, args->size.bytes, args->flags); > + return omap_gem_new_handle(dev, file_priv, args->size.bytes, > + args->flags, &args->handle, &args->offset); > +} > + > +static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_cpu_prep *args = data; > + struct drm_gem_object *obj; > + int ret; > + > + DBG("%p: handle=%d, op=%x", dev, args->handle, args->op); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + ret = omap_gem_op_sync(obj, args->op); > + > + if (!ret) { > + omap_gem_op_start(obj, args->op); > + } > + > + drm_gem_object_unreference_unlocked(obj); > + > + return ret; > +} > + > +static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_cpu_fini *args = data; > + struct drm_gem_object *obj; > + int ret; > + > + DBG("%p: handle=%d", dev, args->handle); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + /* XXX flushy, flushy */ > + ret = 0; > + > + if (!ret) { > + omap_gem_op_finish(obj, args->op); > + } > + > + drm_gem_object_unreference_unlocked(obj); > + > + return ret; > +} > + > +static int ioctl_gem_info(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_omap_gem_info *args = data; > + struct drm_gem_object *obj; > + int ret; > + > + DBG("%p: handle=%d", dev, args->handle); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + return -ENOENT; > + } > + > + args->offset = omap_gem_mmap_offset(obj); > + > + drm_gem_object_unreference_unlocked(obj); > + > + return ret; > +} > + > +struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { > + DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, > DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, > DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), > + DRM_IOCTL_DEF_DRV(OMAP_GET_BASE, ioctl_get_base, > DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, > DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep, > DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini, > DRM_UNLOCKED|DRM_AUTH), > + DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, > DRM_UNLOCKED|DRM_AUTH), > +}; You also should consider security issue. :) > + > +/* > + * drm driver funcs > + */ > + > +/** > + * load - setup chip and create an initial config > + * @dev: DRM device > + * @flags: startup flags > + * > + * The driver load routine has to do several things: > + * - initialize the memory manager > + * - allocate initial config memory > + * - setup the DRM framebuffer with the allocated memory > + */ > +static int dev_load(struct drm_device *dev, unsigned long flags) > +{ > + struct omap_drm_private *priv; > + struct omap_drm_plugin *plugin; > + int ret; > + > + DBG("load: dev=%p", dev); > + > + drm_device = dev; > + > + priv = kzalloc(sizeof(*priv), GFP_KERNEL); > + if (!priv) { > + dev_err(dev->dev, "could not allocate priv\n"); > + return -1; > + } > + > + dev->dev_private = priv; > + > + ret = omap_modeset_init(dev); > + if (ret) { > + dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); > + } > + > + priv->fbdev = omap_fbdev_init(dev); > + if (!priv->fbdev) { > + dev_err(dev->dev, "omap_fbdev_init failed\n"); > + } > + > + drm_kms_helper_poll_init(dev); > + > + ret = drm_vblank_init(dev, priv->num_crtcs); > + if (ret) { > + dev_err(dev->dev, "could not init vblank\n"); > + } > + > + loaded = true; > + > + list_for_each_entry(plugin, &plugin_list, list) { > + ret = plugin->load(dev, flags); > + } > + > + return 0; > +} > + > +static int dev_unload(struct drm_device *dev) > +{ > + struct omap_drm_plugin *plugin; > + int ret; > + > + DBG("unload: dev=%p", dev); > + > + list_for_each_entry(plugin, &plugin_list, list) { > + ret = plugin->unload(dev); > + } > + > + drm_vblank_cleanup(dev); > + drm_kms_helper_poll_fini(dev); > + > + omap_fbdev_free(dev); > + > + omap_modeset_free(dev); > + > + kfree(dev->dev_private); > + dev->dev_private = NULL; > + > + loaded = false; > + > + return 0; > +} > + > +static int dev_open(struct drm_device *dev, struct drm_file *file) > +{ > + struct omap_drm_plugin *plugin; > + bool found_pvr = false; > + int ret; > + > + file->driver_priv = NULL; > + > + DBG("open: dev=%p, file=%p", dev, file); > + > + list_for_each_entry(plugin, &plugin_list, list) { > + if (!strcmp(DRIVER_NAME "_pvr", plugin->name)) { > + found_pvr = true; > + break; > + } > + } > + > + if (!found_pvr) { > + DBG("open: PVR submodule not loaded.. let's try now"); > + request_module(DRIVER_NAME "_pvr"); > + } > + > + list_for_each_entry(plugin, &plugin_list, list) { > + ret = plugin->open(dev, file); > + } > + > + return 0; > +} > + > +static int dev_firstopen(struct drm_device *dev) > +{ > + DBG("firstopen: dev=%p", dev); > + return 0; > +} > + > +/** > + * lastclose - clean up after all DRM clients have exited > + * @dev: DRM device > + * > + * Take care of cleaning up after all DRM clients have exited. In the > + * mode setting case, we want to restore the kernel's initial mode (just > + * in case the last client left us in a bad state). > + * > + * Additionally, in the non-mode setting case, we'll tear down the AGP > + * and DMA structures, since the kernel won't be using them, and clean > + * up any GEM state. > + */ > +static void dev_lastclose(struct drm_device * dev) > +{ > + DBG("lastclose: dev=%p", dev); > +} > + > +static void dev_preclose(struct drm_device * dev, struct drm_file *file) > +{ > + DBG("preclose: dev=%p", dev); > +} > + > +static void dev_postclose(struct drm_device *dev, struct drm_file *file) > +{ > + struct omap_drm_plugin *plugin; > + int ret; > + > + DBG("postclose: dev=%p, file=%p", dev, file); > + > + list_for_each_entry(plugin, &plugin_list, list) { > + ret = plugin->release(dev, file); > + } > + > + return; > +} > + > +/** > + * enable_vblank - enable vblank interrupt events > + * @dev: DRM device > + * @crtc: which irq to enable > + * > + * Enable vblank interrupts for @crtc. If the device doesn't have > + * a hardware vblank counter, this routine should be a no-op, since > + * interrupts will have to stay on to keep the count accurate. > + * > + * RETURNS > + * Zero on success, appropriate errno if the given @crtc's vblank > + * interrupt cannot be enabled. > + */ > +static int dev_enable_vblank(struct drm_device *dev, int crtc) > +{ > + DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc); > + return 0; > +} > + > +/** > + * disable_vblank - disable vblank interrupt events > + * @dev: DRM device > + * @crtc: which irq to enable > + * > + * Disable vblank interrupts for @crtc. If the device doesn't have > + * a hardware vblank counter, this routine should be a no-op, since > + * interrupts will have to stay on to keep the count accurate. > + */ > +static void dev_disable_vblank(struct drm_device *dev, int crtc) > +{ > + DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc); > +} > + > +static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS) > +{ > + return IRQ_HANDLED; > +} > + > +static void dev_irq_preinstall(struct drm_device *dev) > +{ > + DBG("irq_preinstall: dev=%p", dev); > +} > + > +static int dev_irq_postinstall(struct drm_device *dev) > +{ > + DBG("irq_postinstall: dev=%p", dev); > + return 0; > +} > + > +static void dev_irq_uninstall(struct drm_device *dev) > +{ > + DBG("irq_uninstall: dev=%p", dev); > +} > + > +static struct vm_operations_struct omap_gem_vm_ops = { > + .fault = omap_gem_fault, > + .open = omap_gem_vm_open, > + .close = omap_gem_vm_close, > +}; > + > +static struct drm_driver omap_drm_driver = { > + .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | > DRIVER_GEM, > + .load = dev_load, > + .unload = dev_unload, > + .open = dev_open, > + .firstopen = dev_firstopen, > + .lastclose = dev_lastclose, > + .preclose = dev_preclose, > + .postclose = dev_postclose, > + .get_vblank_counter = drm_vblank_count, > + .enable_vblank = dev_enable_vblank, > + .disable_vblank = dev_disable_vblank, > + .irq_preinstall = dev_irq_preinstall, > + .irq_postinstall = dev_irq_postinstall, > + .irq_uninstall = dev_irq_uninstall, > + .irq_handler = dev_irq_handler, > + .reclaim_buffers = drm_core_reclaim_buffers, > + .gem_init_object = omap_gem_init_object, > + .gem_free_object = omap_gem_free_object, > + .gem_vm_ops = &omap_gem_vm_ops, > + .dumb_create = omap_gem_dumb_create, > + .dumb_map_offset = omap_gem_dumb_map_offset, > + .dumb_destroy = omap_gem_dumb_destroy, > + .ioctls = ioctls, > + .num_ioctls = DRM_OMAP_NUM_IOCTLS, > + .fops = { > + .owner = THIS_MODULE, > + .open = drm_open, > + .unlocked_ioctl = drm_ioctl, > + .release = drm_release, > + .mmap = omap_gem_mmap, > + .poll = drm_poll, > + .fasync = drm_fasync, > + .read = drm_read, > + .llseek = noop_llseek, > + }, > + .name = DRIVER_NAME, > + .desc = DRIVER_DESC, > + .date = DRIVER_DATE, > + .major = DRIVER_MAJOR, > + .minor = DRIVER_MINOR, > + .patchlevel = DRIVER_PATCHLEVEL, > +}; > + > +int omap_drm_register_plugin(struct omap_drm_plugin *plugin) > +{ > + struct drm_device *dev = drm_device; > + static int ioctl_base = DRM_OMAP_NUM_IOCTLS; > + int i; > + > + DBG("register plugin: %p (%s)", plugin, plugin->name); > + > + plugin->ioctl_base = ioctl_base; > + > + list_add_tail(&plugin->list, &plugin_list); > + > + /* register the plugin's ioctl's */ > + for (i = 0; i < plugin->num_ioctls; i++) { > + int nr = i + ioctl_base; > + > + /* check for out of bounds ioctl nr or already registered > ioctl */ > + if (nr > ARRAY_SIZE(ioctls) || ioctls[nr].func) { > + dev_err(dev->dev, "invalid ioctl: %d (nr=%d)\n", i, > nr); > + return -EINVAL; > + } > + > + DBG("register ioctl: %d %08x", nr, plugin->ioctls[i].cmd); > + > + ioctls[nr] = plugin->ioctls[i]; > + > + if (nr >= omap_drm_driver.num_ioctls) { > + omap_drm_driver.num_ioctls = nr + 1; > + } > + } > + > + ioctl_base += plugin->num_ioctls; > + > + if (loaded) { > + plugin->load(dev, 0); > + } > + > + return 0; > +} > +EXPORT_SYMBOL(omap_drm_register_plugin); > + > +int omap_drm_unregister_plugin(struct omap_drm_plugin *plugin) > +{ > + list_del(&plugin->list); > + /* TODO remove ioctl fxns */ > + return 0; > +} > +EXPORT_SYMBOL(omap_drm_unregister_plugin); > + > +static int nmappers = 0; > + > +/* create buffer mapper id, to access per-mapper private data. See > + * omap_gem_{get,set}_priv(). > + */ > +int omap_drm_register_mapper(void) > +{ > + if (nmappers >= MAX_MAPPERS) { > + return -ENOMEM; > + } > + return nmappers++; > +} > +EXPORT_SYMBOL(omap_drm_register_mapper); > + > +/* retire a mapper id, previously acquired from omap_drm_register_mapper() > + */ > +void omap_drm_unregister_mapper(int mapper_id) > +{ > + /* currently no-op.. */ > +} > +EXPORT_SYMBOL(omap_drm_unregister_mapper); > + > +static int pdev_suspend(struct platform_device *pDevice, pm_message_t > state) > +{ > + DBG(""); > + return 0; > +} > + > +static int pdev_resume(struct platform_device *device) > +{ > + DBG(""); > + return 0; > +} > + > +static void pdev_shutdown(struct platform_device *device) > +{ > + DBG(""); > +} > + > +static int pdev_probe(struct platform_device *device) > +{ > + DBG("%s", device->name); > + return drm_platform_init(&omap_drm_driver, device); > +} > + > +static int pdev_remove(struct platform_device *device) > +{ > + DBG(""); > + drm_platform_exit(&omap_drm_driver, device); > + return 0; > +} > + > +struct platform_driver pdev = { > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + }, > + .probe = pdev_probe, > + .remove = pdev_remove, > + .suspend = pdev_suspend, > + .resume = pdev_resume, > + .shutdown = pdev_shutdown, > +}; > + > +static int __init omap_drm_init(void) > +{ > + DBG("init"); > + return platform_driver_register(&pdev); > +} > + > +static void __exit omap_drm_fini(void) > +{ > + DBG("fini"); > + platform_driver_unregister(&pdev); > +} > + > +/* need late_initcall() so we load after dss_driver's are loaded */ > +late_initcall(omap_drm_init); > +module_exit(omap_drm_fini); > + > +MODULE_AUTHOR("Rob Clark "); > +MODULE_DESCRIPTION("OMAP DRM Display Driver"); > +MODULE_ALIAS("platform:" DRIVER_NAME); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/staging/omapdrm/omap_drv.h > b/drivers/staging/omapdrm/omap_drv.h > new file mode 100644 > index 0000000..d17e3a8 > --- /dev/null > +++ b/drivers/staging/omapdrm/omap_drv.h > @@ -0,0 +1,117 @@ > +/* > + * linux/drivers/staging/omapdrm/omap_drv.h > + * > + * Copyright (C) 2011 Texas Instruments > + * Author: Rob Clark > + * > + * 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 . > + */ > + > +#ifndef __OMAP_DRV_H__ > +#define __OMAP_DRV_H__ > + > +#include